mirror of
https://github.com/KohakuBlueleaf/KohakuHub.git
synced 2026-03-12 01:45:35 -05:00
support mobile
This commit is contained in:
5
src/kohaku-hub-ui/src/components.d.ts
vendored
5
src/kohaku-hub-ui/src/components.d.ts
vendored
@@ -13,16 +13,21 @@ declare module 'vue' {
|
||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
FileUploader: typeof import('./components/repo/FileUploader.vue')['default']
|
||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
<div
|
||||
class="flex items-center justify-between mb-3 pb-3 border-b border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<el-tag size="small" type="info">{{ language }}</el-tag>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400"
|
||||
<span class="text-xs sm:text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap"
|
||||
>{{ lineCount }} lines</span
|
||||
>
|
||||
</div>
|
||||
<el-button size="small" @click="copyCode">
|
||||
<el-button size="small" @click="copyCode" class="flex-shrink-0">
|
||||
<div class="i-carbon-copy inline-block mr-1" />
|
||||
Copy
|
||||
<span class="hidden sm:inline">Copy</span>
|
||||
<span class="sm:hidden">Copy</span>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -169,13 +170,25 @@ onMounted(() => {
|
||||
font-family:
|
||||
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
|
||||
monospace;
|
||||
font-size: 14px;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
background: transparent !important;
|
||||
padding: 0 !important;
|
||||
color: #24292f;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.code-container :deep(code) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.code-container :deep(code) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .code-container :deep(code) {
|
||||
color: #c9d1d9;
|
||||
}
|
||||
|
||||
@@ -68,11 +68,23 @@ const renderedHTML = computed(() => {
|
||||
|
||||
/* GitHub-like markdown styles */
|
||||
.markdown-body {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.markdown-body {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(h1),
|
||||
.markdown-body :deep(h2),
|
||||
.markdown-body :deep(h3),
|
||||
@@ -86,19 +98,37 @@ const renderedHTML = computed(() => {
|
||||
}
|
||||
|
||||
.markdown-body :deep(h1) {
|
||||
font-size: 2em;
|
||||
font-size: 1.6em;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body :deep(h1) {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(h2) {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.3em;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body :deep(h2) {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(h3) {
|
||||
font-size: 1.25em;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body :deep(h3) {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(p) {
|
||||
@@ -109,22 +139,35 @@ const renderedHTML = computed(() => {
|
||||
.markdown-body :deep(code) {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
font-size: 80%;
|
||||
background-color: rgba(175, 184, 193, 0.2);
|
||||
border-radius: 6px;
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body :deep(code) {
|
||||
font-size: 85%;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(pre) {
|
||||
padding: 16px;
|
||||
padding: 12px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
font-size: 80%;
|
||||
line-height: 1.45;
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.markdown-body :deep(pre) {
|
||||
padding: 16px;
|
||||
font-size: 85%;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body :deep(pre code) {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 mt-4 transition-colors"
|
||||
>
|
||||
<div class="container-main py-8">
|
||||
<div class="grid grid-cols-5 gap-8">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-8">
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">KohakuHub</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
<header
|
||||
class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 sticky top-0 z-50 transition-colors"
|
||||
>
|
||||
<div class="container-main flex items-center justify-between h-12">
|
||||
<div class="container-main flex items-center justify-between h-12 md:h-16">
|
||||
<!-- Logo -->
|
||||
<RouterLink to="/" class="flex items-center gap-2">
|
||||
<div class="i-carbon-cube text-3xl text-blue-500" />
|
||||
<span class="text-xl font-bold">KohakuHub</span>
|
||||
<div class="i-carbon-cube text-2xl md:text-3xl text-blue-500" />
|
||||
<span class="text-lg md:text-xl font-bold">KohakuHub</span>
|
||||
</RouterLink>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="flex items-center gap-6">
|
||||
<!-- Desktop Navigation - hidden on mobile -->
|
||||
<nav class="hidden md:flex items-center gap-6">
|
||||
<RouterLink
|
||||
to="/models"
|
||||
class="text-gray-700 dark:text-gray-300 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
|
||||
@@ -32,8 +32,8 @@
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<!-- User Menu -->
|
||||
<div class="flex items-center gap-4">
|
||||
<!-- Desktop User Menu - hidden on mobile -->
|
||||
<div class="hidden md:flex items-center gap-4">
|
||||
<!-- Dark Mode Toggle -->
|
||||
<el-button
|
||||
@click="themeStore.toggle()"
|
||||
@@ -107,7 +107,173 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<div class="flex md:hidden items-center gap-2">
|
||||
<!-- Dark Mode Toggle - Mobile -->
|
||||
<el-button
|
||||
@click="themeStore.toggle()"
|
||||
circle
|
||||
text
|
||||
size="small"
|
||||
class="!text-gray-700 dark:!text-gray-300"
|
||||
>
|
||||
<div v-if="themeStore.isDark" class="i-carbon-moon text-lg" />
|
||||
<div v-else class="i-carbon-asleep text-lg" />
|
||||
</el-button>
|
||||
|
||||
<!-- Hamburger Menu -->
|
||||
<el-button
|
||||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||
circle
|
||||
text
|
||||
class="!text-gray-700 dark:!text-gray-300"
|
||||
>
|
||||
<div class="i-carbon-menu text-2xl" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Drawer -->
|
||||
<el-drawer
|
||||
v-model="mobileMenuOpen"
|
||||
direction="rtl"
|
||||
size="280px"
|
||||
:show-close="false"
|
||||
>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Navigation Links -->
|
||||
<nav class="flex flex-col gap-1 mb-6">
|
||||
<RouterLink
|
||||
to="/models"
|
||||
@click="mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-model text-blue-500" />
|
||||
Models
|
||||
</div>
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/datasets"
|
||||
@click="mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-data-table text-green-500" />
|
||||
Datasets
|
||||
</div>
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/spaces"
|
||||
@click="mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-application text-purple-500" />
|
||||
Spaces
|
||||
</div>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 mb-4"></div>
|
||||
|
||||
<!-- User Menu -->
|
||||
<template v-if="isAuthenticated">
|
||||
<!-- Create New Options -->
|
||||
<div class="mb-4">
|
||||
<div class="px-4 text-xs text-gray-500 dark:text-gray-400 mb-2">
|
||||
CREATE NEW
|
||||
</div>
|
||||
<div
|
||||
@click="createNew('model'); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-model text-blue-500" />
|
||||
New Model
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="createNew('dataset'); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-data-table text-green-500" />
|
||||
New Dataset
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="createNew('space'); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-application text-purple-500" />
|
||||
New Space
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Divider -->
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 mb-4"></div>
|
||||
|
||||
<!-- User Options -->
|
||||
<div>
|
||||
<div class="px-4 text-xs text-gray-500 dark:text-gray-400 mb-2">
|
||||
{{ username }}
|
||||
</div>
|
||||
<div
|
||||
@click="$router.push(`/${username}`); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-user" />
|
||||
Profile
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="$router.push('/settings'); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-settings" />
|
||||
Settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@click="handleLogout(); mobileMenuOpen = false"
|
||||
class="px-4 py-3 text-red-600 dark:text-red-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded cursor-pointer transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-logout" />
|
||||
Logout
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Not authenticated -->
|
||||
<template v-else>
|
||||
<div class="flex flex-col gap-2 px-4">
|
||||
<el-button
|
||||
@click="$router.push('/login'); mobileMenuOpen = false"
|
||||
text
|
||||
class="w-full"
|
||||
>
|
||||
Login
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="$router.push('/register'); mobileMenuOpen = false"
|
||||
class="w-full"
|
||||
>
|
||||
Sign Up
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
@@ -121,6 +287,7 @@ const authStore = useAuthStore();
|
||||
const themeStore = useThemeStore();
|
||||
const { isAuthenticated, username } = storeToRefs(authStore);
|
||||
const router = useRouter();
|
||||
const mobileMenuOpen = ref(false);
|
||||
|
||||
function createNew(type) {
|
||||
router.push({
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
class="card hover:shadow-md transition-shadow cursor-pointer"
|
||||
@click="goToRepo(repo)"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="flex flex-col sm:flex-row items-start justify-between gap-3">
|
||||
<div class="flex-1 w-full">
|
||||
<div class="flex items-center gap-2 mb-2 flex-wrap">
|
||||
<div :class="getIconClass(type)" />
|
||||
<h3
|
||||
class="text-lg font-semibold text-blue-600 dark:text-blue-400 hover:underline"
|
||||
class="text-base sm:text-lg font-semibold text-blue-600 dark:text-blue-400 hover:underline break-all"
|
||||
>
|
||||
{{ repo.id }}
|
||||
</h3>
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-sm text-gray-600 dark:text-gray-400 flex items-center gap-4"
|
||||
class="text-xs sm:text-sm text-gray-600 dark:text-gray-400 flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-4"
|
||||
>
|
||||
<span>by {{ repo.author }}</span>
|
||||
<span v-if="repo.lastModified">
|
||||
@@ -30,7 +30,7 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="repo.tags && repo.tags.length" class="mt-2 flex gap-2">
|
||||
<div v-if="repo.tags && repo.tags.length" class="mt-2 flex gap-2 flex-wrap">
|
||||
<el-tag
|
||||
v-for="tag in repo.tags.slice(0, 3)"
|
||||
:key="tag"
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400"
|
||||
class="flex items-center gap-4 text-xs sm:text-sm text-gray-500 dark:text-gray-400 w-full sm:w-auto justify-end"
|
||||
>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="i-carbon-download" />
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
<el-button @click="$router.back()">Go Back</el-button>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-[1fr_300px] gap-6">
|
||||
<div v-else class="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-6">
|
||||
<!-- Main Content -->
|
||||
<main class="min-w-0">
|
||||
<!-- Repo Header -->
|
||||
<div class="card mb-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div :class="getIconClass(repoType)" class="text-4xl" />
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold">{{ repoInfo?.id }}</h1>
|
||||
<div class="flex flex-col sm:flex-row items-start justify-between gap-4 mb-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div :class="getIconClass(repoType)" class="text-3xl sm:text-4xl flex-shrink-0" />
|
||||
<div class="min-w-0">
|
||||
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold break-words">{{ repoInfo?.id }}</h1>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<RouterLink
|
||||
:to="`/${namespace}`"
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
<!-- Stats -->
|
||||
<div
|
||||
class="flex items-center gap-6 text-sm text-gray-600 dark:text-gray-400"
|
||||
class="flex flex-wrap items-center gap-3 sm:gap-6 text-xs sm:text-sm text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="i-carbon-download" />
|
||||
@@ -81,7 +81,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex gap-2 mt-4">
|
||||
<div class="flex flex-wrap gap-2 mt-4">
|
||||
<el-button type="primary" @click="showCloneDialog = true">
|
||||
<div class="i-carbon-download inline-block mr-1" />
|
||||
Clone
|
||||
@@ -98,8 +98,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Navigation Tabs -->
|
||||
<div class="mb-6">
|
||||
<div class="flex gap-1 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="mb-6 -mx-4 sm:mx-0 px-4 sm:px-0 overflow-x-auto">
|
||||
<div class="flex gap-1 border-b border-gray-200 dark:border-gray-700 min-w-max sm:min-w-0">
|
||||
<button
|
||||
:class="[
|
||||
'px-4 py-2 font-medium transition-colors',
|
||||
@@ -181,28 +181,29 @@
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'files'" class="card">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div class="mb-4 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<el-select
|
||||
v-model="currentBranch"
|
||||
size="small"
|
||||
style="width: 150px"
|
||||
class="w-full sm:w-37"
|
||||
@change="handleBranchChange"
|
||||
>
|
||||
<el-option label="main" value="main" />
|
||||
</el-select>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 whitespace-nowrap">
|
||||
{{ fileTree.length }}
|
||||
{{ fileTree.length === 1 ? "file" : "files" }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2">
|
||||
<el-button
|
||||
v-if="isOwner"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="navigateToUpload"
|
||||
class="w-full sm:w-auto"
|
||||
>
|
||||
<div class="i-carbon-cloud-upload inline-block mr-1" />
|
||||
Upload Files
|
||||
@@ -211,7 +212,7 @@
|
||||
v-model="fileSearchQuery"
|
||||
placeholder="Search files..."
|
||||
size="small"
|
||||
style="width: 200px"
|
||||
class="w-full sm:w-50"
|
||||
clearable
|
||||
>
|
||||
<template #prefix>
|
||||
@@ -306,7 +307,7 @@
|
||||
</main>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="space-y-4">
|
||||
<aside class="space-y-4 lg:sticky lg:top-20 lg:self-start">
|
||||
<!-- Owner Info -->
|
||||
<div class="card">
|
||||
<h3 class="font-semibold mb-3">Owner</h3>
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
<div v-else>
|
||||
<!-- File Header -->
|
||||
<div class="card mb-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div :class="getFileIcon(fileName)" class="text-3xl" />
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">{{ fileName }}</h1>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
<div class="flex flex-col sm:flex-row items-start justify-between gap-4">
|
||||
<div class="flex items-start gap-3 min-w-0 flex-1">
|
||||
<div :class="getFileIcon(fileName)" class="text-2xl sm:text-3xl flex-shrink-0" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<h1 class="text-lg sm:text-xl md:text-2xl font-bold break-words">{{ fileName }}</h1>
|
||||
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{{ formatSize(fileSize) }}
|
||||
<span v-if="fileSize" class="mx-2">•</span>
|
||||
<span>{{ fileExtension || "No extension" }}</span>
|
||||
@@ -45,16 +45,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<el-button v-if="canEdit" @click="editFile">
|
||||
<div class="flex flex-wrap sm:flex-nowrap items-center gap-2 w-full sm:w-auto">
|
||||
<el-button v-if="canEdit" @click="editFile" size="small" class="flex-1 sm:flex-initial">
|
||||
<div class="i-carbon-edit inline-block mr-1" />
|
||||
Edit
|
||||
<span class="hidden sm:inline">Edit</span>
|
||||
<span class="sm:hidden">Edit</span>
|
||||
</el-button>
|
||||
<el-button @click="copyFileUrl">
|
||||
<el-button @click="copyFileUrl" size="small" class="flex-1 sm:flex-initial">
|
||||
<div class="i-carbon-link inline-block mr-1" />
|
||||
Copy URL
|
||||
<span class="hidden sm:inline">Copy URL</span>
|
||||
<span class="sm:hidden">Copy</span>
|
||||
</el-button>
|
||||
<el-button type="primary" @click="downloadFile">
|
||||
<el-button type="primary" @click="downloadFile" size="small" class="flex-1 sm:flex-initial">
|
||||
<div class="i-carbon-download inline-block mr-1" />
|
||||
Download
|
||||
</el-button>
|
||||
@@ -141,16 +143,17 @@
|
||||
<!-- Markdown Preview -->
|
||||
<div v-else-if="isMarkdown">
|
||||
<div
|
||||
class="flex items-center justify-between mb-4 pb-3 border-b border-gray-200 dark:border-gray-700"
|
||||
class="flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3 mb-4 pb-3 border-b border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<el-radio-group v-model="markdownView" size="small">
|
||||
<el-radio-button label="preview">Preview</el-radio-button>
|
||||
<el-radio-button label="source">Source</el-radio-button>
|
||||
<el-radio-group v-model="markdownView" size="small" class="w-full sm:w-auto">
|
||||
<el-radio-button label="preview" class="flex-1 sm:flex-initial">Preview</el-radio-button>
|
||||
<el-radio-button label="source" class="flex-1 sm:flex-initial">Source</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-button
|
||||
v-if="markdownView === 'source'"
|
||||
size="small"
|
||||
@click="copyContent"
|
||||
class="w-full sm:w-auto"
|
||||
>
|
||||
<div class="i-carbon-copy inline-block mr-1" />
|
||||
Copy
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!-- src/kohaku-hub-ui/src/pages/[type]s/index.vue -->
|
||||
<template>
|
||||
<div class="container-main">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold mb-2">{{ pageTitle }}</h1>
|
||||
<p class="text-gray-600">{{ pageDescription }}</p>
|
||||
<h1 class="text-2xl md:text-3xl font-bold mb-2">{{ pageTitle }}</h1>
|
||||
<p class="text-sm md:text-base text-gray-600 dark:text-gray-400">{{ pageDescription }}</p>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
@@ -12,6 +12,7 @@
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="showCreateDialog = true"
|
||||
class="w-full md:w-auto"
|
||||
>
|
||||
<div class="i-carbon-add inline-block mr-1" />
|
||||
New {{ repoTypeLabel }}
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="card mb-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-4">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
:placeholder="`Search ${repoType}s...`"
|
||||
@@ -32,7 +33,7 @@
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-select v-model="sortBy" placeholder="Sort by" style="width: 200px">
|
||||
<el-select v-model="sortBy" placeholder="Sort by" class="w-full sm:w-50">
|
||||
<el-option label="Recently Updated" value="updated" />
|
||||
<el-option label="Recently Created" value="created" />
|
||||
<el-option label="Most Downloads" value="downloads" />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- src/pages/[username]/index.vue -->
|
||||
<template>
|
||||
<div class="container-main">
|
||||
<div class="grid grid-cols-[280px_1fr] gap-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-[280px_1fr] gap-6">
|
||||
<!-- Sidebar -->
|
||||
<aside class="space-y-4">
|
||||
<aside class="space-y-4 lg:sticky lg:top-20 lg:self-start">
|
||||
<div class="card">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="i-carbon-user-avatar text-5xl text-gray-400" />
|
||||
@@ -105,15 +105,15 @@
|
||||
class="flex items-center justify-between mb-4 pb-3 border-b-2 border-blue-500"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-model text-blue-500 text-2xl" />
|
||||
<h2 class="text-2xl font-bold">Models</h2>
|
||||
<div class="i-carbon-model text-blue-500 text-xl md:text-2xl" />
|
||||
<h2 class="text-xl md:text-2xl font-bold">Models</h2>
|
||||
</div>
|
||||
<el-tag type="info" size="large">{{ getCount("model") }}</el-tag>
|
||||
</div>
|
||||
|
||||
<div v-if="getCount('model') > 0" class="space-y-4">
|
||||
<!-- Grid of repos (2 per row, max 3 rows = 6 repos) -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div
|
||||
v-for="repo in displayedRepos('model')"
|
||||
:key="repo.id"
|
||||
@@ -188,8 +188,8 @@
|
||||
class="flex items-center justify-between mb-4 pb-3 border-b-2 border-green-500"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-data-table text-green-500 text-2xl" />
|
||||
<h2 class="text-2xl font-bold">Datasets</h2>
|
||||
<div class="i-carbon-data-table text-green-500 text-xl md:text-2xl" />
|
||||
<h2 class="text-xl md:text-2xl font-bold">Datasets</h2>
|
||||
</div>
|
||||
<el-tag type="success" size="large">{{
|
||||
getCount("dataset")
|
||||
@@ -198,7 +198,7 @@
|
||||
|
||||
<div v-if="getCount('dataset') > 0" class="space-y-4">
|
||||
<!-- Grid of repos (2 per row, max 3 rows = 6 repos) -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div
|
||||
v-for="repo in displayedRepos('dataset')"
|
||||
:key="repo.id"
|
||||
@@ -273,15 +273,15 @@
|
||||
class="flex items-center justify-between mb-4 pb-3 border-b-2 border-purple-500"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-carbon-application text-purple-500 text-2xl" />
|
||||
<h2 class="text-2xl font-bold">Spaces</h2>
|
||||
<div class="i-carbon-application text-purple-500 text-xl md:text-2xl" />
|
||||
<h2 class="text-xl md:text-2xl font-bold">Spaces</h2>
|
||||
</div>
|
||||
<el-tag type="warning" size="large">{{ getCount("space") }}</el-tag>
|
||||
</div>
|
||||
|
||||
<div v-if="getCount('space') > 0" class="space-y-4">
|
||||
<!-- Grid of repos (2 per row, max 3 rows = 6 repos) -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div
|
||||
v-for="repo in displayedRepos('space')"
|
||||
:key="repo.id"
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
<div>
|
||||
<!-- Hero Section -->
|
||||
<div class="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
|
||||
<div class="container-main py-16 text-center">
|
||||
<h1 class="text-5xl font-bold mb-4">Welcome to KohakuHub</h1>
|
||||
<p class="text-xl mb-8">
|
||||
<div class="container-main py-8 md:py-16 text-center">
|
||||
<h1 class="text-3xl md:text-4xl lg:text-5xl font-bold mb-4">Welcome to KohakuHub</h1>
|
||||
<p class="text-base md:text-lg lg:text-xl mb-6 md:mb-8 px-4">
|
||||
Self-hosted HuggingFace Hub alternative for your AI models and
|
||||
datasets
|
||||
</p>
|
||||
<div class="flex gap-4 justify-center">
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center px-4">
|
||||
<el-button
|
||||
size="large"
|
||||
type="default"
|
||||
@@ -32,9 +32,9 @@
|
||||
|
||||
<!-- Recent Repos - Three Columns -->
|
||||
<div class="container-main py-8">
|
||||
<h2 class="text-3xl font-bold mb-8">Recently Updated</h2>
|
||||
<h2 class="text-2xl md:text-3xl font-bold mb-6 md:mb-8">Recently Updated</h2>
|
||||
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<!-- Models Column -->
|
||||
<div>
|
||||
<div
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<template>
|
||||
<div class="container-main">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<h1 class="text-3xl font-bold mb-2">Create New Repository</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-8">
|
||||
<h1 class="text-2xl md:text-3xl font-bold mb-2">Create New Repository</h1>
|
||||
<p class="text-sm md:text-base text-gray-600 dark:text-gray-400 mb-6 md:mb-8">
|
||||
A repository contains all project files, including revision history.
|
||||
</p>
|
||||
|
||||
@@ -18,20 +18,20 @@
|
||||
<!-- Repository Type -->
|
||||
<el-form-item label="Repository Type" prop="type">
|
||||
<div class="w-full">
|
||||
<el-radio-group v-model="form.type" size="large">
|
||||
<el-radio-button value="model" class="flex-1">
|
||||
<el-radio-group v-model="form.type" size="large" class="w-full grid grid-cols-1 sm:grid-cols-3 gap-2">
|
||||
<el-radio-button value="model">
|
||||
<div class="flex items-center justify-center gap-2 py-2">
|
||||
<div class="i-carbon-model text-xl" />
|
||||
<span>Model</span>
|
||||
</div>
|
||||
</el-radio-button>
|
||||
<el-radio-button value="dataset" class="flex-1">
|
||||
<el-radio-button value="dataset">
|
||||
<div class="flex items-center justify-center gap-2 py-2">
|
||||
<div class="i-carbon-data-table text-xl" />
|
||||
<span>Dataset</span>
|
||||
</div>
|
||||
</el-radio-button>
|
||||
<el-radio-button value="space" class="flex-1">
|
||||
<el-radio-button value="space">
|
||||
<div class="flex items-center justify-center gap-2 py-2">
|
||||
<div class="i-carbon-application text-xl" />
|
||||
<span>Space</span>
|
||||
@@ -121,14 +121,15 @@
|
||||
|
||||
<!-- Actions -->
|
||||
<div
|
||||
class="flex gap-3 mt-8 pt-6 border-t border-gray-200 dark:border-gray-700"
|
||||
class="flex flex-col-reverse sm:flex-row gap-3 mt-8 pt-6 border-t border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<el-button size="large" @click="$router.back()"> Cancel </el-button>
|
||||
<el-button size="large" @click="$router.back()" class="w-full sm:w-auto"> Cancel </el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="creating"
|
||||
@click="handleSubmit"
|
||||
class="w-full sm:w-auto"
|
||||
>
|
||||
<div class="i-carbon-add inline-block mr-1" />
|
||||
Create {{ typeLabel }}
|
||||
|
||||
@@ -58,12 +58,7 @@ button:focus-visible {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
/* Removed #app styles to avoid conflicts with App.vue UnoCSS classes */
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user