forked from EduCraft/curriculum-project-hub
style: update surface colors and typography for improved contrast and readability across various components
- Changed text colors from surface-400 to surface-600 and surface-500 to surface-700 for better visibility in multiple Svelte files. - Updated background and border colors in app.css for a more cohesive industrial design. - Adjusted font weights and sizes for headings, labels, and buttons to enhance clarity and user experience. - Refined styles for tables, badges, and buttons to align with the new design language. - Added new styles for input fields and switches to maintain consistency in the UI.
This commit is contained in:
@@ -12,6 +12,12 @@
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Noto+Sans+SC:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
/* Fallback before CSS bundle: CJK-first industrial base */
|
||||
html {
|
||||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
<title>CPH Admin</title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
</script>
|
||||
|
||||
<div class="saas-empty">
|
||||
<div class="mb-1 flex h-12 w-12 items-center justify-center rounded-2xl bg-surface-100 text-surface-400">
|
||||
<div class="mb-1 flex h-12 w-12 items-center justify-center border border-surface-300 bg-surface-100 text-surface-600">
|
||||
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-surface-700">{title}</p>
|
||||
<p class="text-sm font-medium text-surface-900">{title}</p>
|
||||
{#if description}
|
||||
<p class="max-w-sm text-sm text-surface-400">{description}</p>
|
||||
<p class="max-w-sm text-sm text-surface-600">{description}</p>
|
||||
{/if}
|
||||
{#if action}
|
||||
<div class="mt-2">
|
||||
|
||||
@@ -27,21 +27,21 @@
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2.5 text-left text-sm transition hover:bg-surface-100"
|
||||
class="flex w-full items-center gap-2.5 px-3 py-2.5 text-left text-sm transition hover:bg-surface-100"
|
||||
onclick={() => (open = !open)}
|
||||
>
|
||||
<span class="w-3.5 text-center text-xs text-surface-400">{open ? '▾' : '▸'}</span>
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-md bg-warning-50 text-warning-700">
|
||||
<span class="w-3.5 text-center text-xs text-surface-600">{open ? '▾' : '▸'}</span>
|
||||
<span class="flex h-7 w-7 items-center justify-center border border-warning-300 bg-warning-50 text-warning-800">
|
||||
<Icon name="folder" class="h-4 w-4" />
|
||||
</span>
|
||||
<span class="min-w-0 flex-1 truncate font-medium text-surface-800">{folder.name}</span>
|
||||
<span class="min-w-0 flex-1 truncate font-medium text-surface-900">{folder.name}</span>
|
||||
<span class="saas-badge-neutral">{folder.projectCount} 项目</span>
|
||||
{#if folder.childFolderCount > 0}
|
||||
<span class="saas-badge-neutral">{folder.childFolderCount} 子夹</span>
|
||||
{/if}
|
||||
</button>
|
||||
{#if open}
|
||||
<div class="ml-4 border-l border-surface-200 pl-2">
|
||||
<div class="ml-4 border-l border-surface-300 pl-2">
|
||||
<FolderTree {folders} {projects} parentId={folder.id} {slug} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -30,16 +30,16 @@
|
||||
{#each childProjects as p (p.id)}
|
||||
<a
|
||||
href={`/admin/org/${slug}/projects/${p.id}`}
|
||||
class="flex items-center gap-2.5 rounded-lg px-3 py-2.5 text-sm transition hover:bg-surface-100"
|
||||
class="flex items-center gap-2.5 px-3 py-2.5 text-sm transition hover:bg-surface-100"
|
||||
>
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-md bg-primary-50 text-primary-600">
|
||||
<span class="flex h-7 w-7 items-center justify-center border border-primary-200 bg-primary-50 text-primary-700">
|
||||
<Icon name="file" class="h-4 w-4" />
|
||||
</span>
|
||||
<span class="min-w-0 flex-1 truncate font-medium text-surface-800">{p.name}</span>
|
||||
<span class="min-w-0 flex-1 truncate font-medium text-surface-900">{p.name}</span>
|
||||
{#if p.binding}
|
||||
<span class="saas-badge-success">已绑定</span>
|
||||
{/if}
|
||||
<span class="hidden text-xs text-surface-400 sm:inline">{fmtDate(p.createdAt)}</span>
|
||||
<span class="hidden text-xs text-surface-600 sm:inline">{fmtDate(p.createdAt)}</span>
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
let { label = '加载中…' }: { label?: string } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center justify-center gap-3 py-16 text-surface-400">
|
||||
<div class="flex flex-col items-center justify-center gap-3 py-16 text-surface-600">
|
||||
<svg class="h-7 w-7 animate-spin text-primary-500" viewBox="0 0 24 24" fill="none">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<Dialog.Content class="saas-modal">
|
||||
<div class="mb-4 flex items-start justify-between gap-3">
|
||||
<Dialog.Title class="text-lg font-semibold text-surface-900">{title}</Dialog.Title>
|
||||
<Dialog.Close class="saas-btn-ghost !px-2 !py-1 text-surface-400" aria-label="关闭">
|
||||
<Dialog.Close class="saas-btn-ghost !px-2 !py-1 text-surface-600" aria-label="关闭">
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<div class="saas-card-pad">
|
||||
<div class="mb-4 flex flex-wrap items-center gap-2">
|
||||
<span class="saas-badge-primary font-mono">/{roleId || r.roleId}</span>
|
||||
<span class="text-sm text-surface-500">{label || r.label}</span>
|
||||
<span class="text-sm text-surface-700">{label || r.label}</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<span class="saas-label">工具白名单</span>
|
||||
<label class="mb-3 flex cursor-pointer items-center gap-2 rounded-lg border border-surface-200 bg-surface-100/40 px-3 py-2">
|
||||
<label class="mb-3 flex cursor-pointer items-center gap-2 border border-surface-300 bg-surface-100 px-3 py-2">
|
||||
<CheckboxControl bind:checked={unrestricted} />
|
||||
<span class="text-sm">不限(使用全部注册工具)</span>
|
||||
</label>
|
||||
@@ -105,10 +105,10 @@
|
||||
<Checkbox.Group bind:value={selectedTools} disabled={unrestricted}>
|
||||
{#each Object.entries(groupedTools) as [group, tools]}
|
||||
<div>
|
||||
<p class="mb-1.5 text-xs font-semibold uppercase tracking-wide text-surface-400">{group}</p>
|
||||
<p class="mb-1.5 text-xs font-semibold uppercase tracking-wide text-surface-600">{group}</p>
|
||||
<div class="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
|
||||
{#each tools as t}
|
||||
<label class="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-surface-100">
|
||||
<label class="flex cursor-pointer items-center gap-2 px-2 py-1.5 text-sm hover:bg-surface-100">
|
||||
<Checkbox.Root class="saas-checkbox" value={t.id} id={`tool-${r.id}-${t.id}`}>
|
||||
{#snippet children({ checked })}
|
||||
{#if checked}
|
||||
@@ -138,7 +138,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-wrap items-center gap-3 border-t border-surface-100 pt-4">
|
||||
<span class="text-xs text-surface-400">更新于 {fmtDate(r.updatedAt)}</span>
|
||||
<span class="text-xs text-surface-600">更新于 {fmtDate(r.updatedAt)}</span>
|
||||
<div class="flex-1"></div>
|
||||
<button class="saas-btn-danger" onclick={onremoved}>删除角色</button>
|
||||
<button class="saas-btn-primary" onclick={save} disabled={saving}>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<Select.Value {placeholder} />
|
||||
</span>
|
||||
<svg
|
||||
class="ml-2 h-4 w-4 shrink-0 text-surface-400"
|
||||
class="ml-2 h-4 w-4 shrink-0 text-surface-600"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="pointer-events-none fixed inset-x-0 top-0 z-[100] flex flex-col items-end gap-2 p-4">
|
||||
{#each $toasts as t (t.id)}
|
||||
<div
|
||||
class="pointer-events-auto flex max-w-sm items-start gap-3 rounded-xl border px-4 py-3 text-sm shadow-lg {kindClass[t.kind] ??
|
||||
class="pointer-events-auto flex max-w-sm items-start gap-3 border px-4 py-3 text-sm shadow-[4px_4px_0_rgb(15_23_42_/_0.12)] {kindClass[t.kind] ??
|
||||
kindClass.info}"
|
||||
role="status"
|
||||
>
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
{#if $session.loading || redirecting}
|
||||
<div class="saas-status-panel">
|
||||
<div class="flex flex-col items-center gap-3 text-surface-400">
|
||||
<div class="flex flex-col items-center gap-3 text-surface-600">
|
||||
<svg class="h-8 w-8 animate-spin text-primary-500" viewBox="0 0 24 24" fill="none">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path
|
||||
@@ -143,20 +143,20 @@
|
||||
{:else if $session.error}
|
||||
<div class="saas-status-panel">
|
||||
<div class="saas-status-card">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-error-100 text-error-600">!</div>
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center border border-error-300 bg-error-100 text-error-700 font-bold">!</div>
|
||||
<h2 class="mb-1 text-lg font-semibold">无法连接到后端</h2>
|
||||
<p class="mb-5 text-sm text-surface-500">{$session.error}</p>
|
||||
<p class="mb-5 text-sm text-surface-700">{$session.error}</p>
|
||||
<button class="saas-btn-primary" onclick={() => loadSession()}>重试</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if !$session.me}
|
||||
<div class="saas-status-panel">
|
||||
<div class="saas-status-card">
|
||||
<div class="mx-auto mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary-600 text-white font-bold">
|
||||
<div class="mx-auto mb-5 flex h-12 w-12 items-center justify-center border border-primary-700 bg-primary-600 text-white font-bold">
|
||||
CPH
|
||||
</div>
|
||||
<h1 class="text-xl font-semibold tracking-tight">Curriculum Project Hub</h1>
|
||||
<p class="mt-2 mb-6 text-sm text-surface-500">登录以管理组织、项目、团队与模型供应。</p>
|
||||
<h1 class="text-xl font-semibold">Curriculum Project Hub</h1>
|
||||
<p class="mt-2 mb-6 text-sm text-surface-700">登录以管理组织、项目、团队与模型供应。</p>
|
||||
<button class="saas-btn-primary w-full" onclick={() => redirectToLogin()}>使用飞书登录</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,17 +178,17 @@
|
||||
{mobileNavOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'}"
|
||||
>
|
||||
<div class="flex items-center gap-2.5 px-4 py-4">
|
||||
<div class="flex h-9 w-9 items-center justify-center rounded-xl bg-primary-600 text-xs font-bold tracking-wide text-white">
|
||||
<div class="flex h-9 w-9 items-center justify-center border border-primary-700 bg-primary-600 text-xs font-bold tracking-wide text-white">
|
||||
CPH
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="truncate text-sm font-semibold text-surface-900">组织后台</div>
|
||||
<div class="truncate text-xs text-surface-400">Curriculum Hub</div>
|
||||
<div class="truncate text-xs text-surface-600">Curriculum Hub</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-3 pb-3">
|
||||
<div class="mb-1.5 flex items-center gap-1.5 text-xs font-medium text-surface-500">
|
||||
<div class="mb-1.5 flex items-center gap-1.5 text-xs font-medium text-surface-700">
|
||||
<Icon name="org" class="h-3.5 w-3.5" />
|
||||
组织
|
||||
</div>
|
||||
@@ -200,7 +200,7 @@
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 space-y-0.5 overflow-y-auto px-2 pb-3">
|
||||
<p class="px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-surface-400">工作台</p>
|
||||
<p class="px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-surface-600">工作台</p>
|
||||
{#each navItems as item}
|
||||
{@const active = activeKey() === item.key || (item.key === 'overview' && (activeKey() === '' || activeKey() === 'overview'))}
|
||||
<a href={navHref(item.key)} class="saas-nav-item" data-active={active ? 'true' : 'false'}>
|
||||
@@ -210,22 +210,22 @@
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<div class="border-t border-surface-200 p-3">
|
||||
<div class="flex items-center gap-2.5 rounded-xl bg-surface-100/80 px-2.5 py-2">
|
||||
<div class="border-t border-surface-300 p-3">
|
||||
<div class="flex items-center gap-2.5 border border-surface-300 bg-surface-100 px-2.5 py-2">
|
||||
{#if me.user.avatarUrl}
|
||||
<img src={me.user.avatarUrl} alt="" class="h-8 w-8 rounded-full object-cover" />
|
||||
<img src={me.user.avatarUrl} alt="" class="h-8 w-8 object-cover" />
|
||||
{:else}
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-100 text-xs font-semibold text-primary-700">
|
||||
<div class="flex h-8 w-8 items-center justify-center border border-primary-300 bg-primary-100 text-xs font-semibold text-primary-800">
|
||||
{me.user.displayName.slice(0, 1)}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-sm font-medium text-surface-800">{me.user.displayName}</div>
|
||||
<div class="truncate text-[11px] text-surface-400">{orgRoleLabel(org.role)}</div>
|
||||
<div class="truncate text-sm font-medium text-surface-900">{me.user.displayName}</div>
|
||||
<div class="truncate text-[11px] text-surface-600">{orgRoleLabel(org.role)}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-1.5 text-surface-400 transition hover:bg-surface-200 hover:text-error-600"
|
||||
class="p-1.5 text-surface-600 transition hover:bg-surface-200 hover:text-error-700"
|
||||
title="退出登录"
|
||||
onclick={handleLogout}
|
||||
>
|
||||
@@ -246,10 +246,10 @@
|
||||
<Icon name="menu" class="h-5 w-5" />
|
||||
</button>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-1.5 text-xs text-surface-400">
|
||||
<div class="flex items-center gap-1.5 text-xs text-surface-600">
|
||||
<span class="truncate">{org.name}</span>
|
||||
<span>/</span>
|
||||
<span class="truncate text-surface-600">{pageTitle()}</span>
|
||||
<span class="truncate font-medium text-surface-900">{pageTitle()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-auto hidden items-center gap-2 sm:flex">
|
||||
@@ -269,7 +269,7 @@
|
||||
<div class="saas-status-panel">
|
||||
<div class="saas-status-card">
|
||||
<h2 class="mb-2 text-lg font-semibold">无权访问管理后台</h2>
|
||||
<p class="mb-3 text-sm text-surface-500">
|
||||
<p class="mb-3 text-sm text-surface-700">
|
||||
组织 <strong>{denied.name}</strong>(/{denied.slug})中你的角色是
|
||||
<span class="saas-badge-neutral mx-1">{orgRoleLabel(denied.role)}</span>,
|
||||
需要<strong>所有者</strong>或<strong>管理员</strong>。
|
||||
@@ -293,13 +293,13 @@
|
||||
<div class="saas-status-card">
|
||||
{#if adminOrgs().length === 0}
|
||||
<h2 class="mb-2 text-lg font-semibold">无权访问管理后台</h2>
|
||||
<p class="mb-5 text-sm text-surface-500">
|
||||
<p class="mb-5 text-sm text-surface-700">
|
||||
你加入了 <strong>{denied.name}</strong>,角色是
|
||||
<span class="saas-badge-neutral mx-1">{orgRoleLabel(denied.role)}</span>。仅所有者与管理员可进入后台。
|
||||
</p>
|
||||
{:else}
|
||||
<h2 class="mb-2 text-lg font-semibold">正在跳转…</h2>
|
||||
<p class="mb-5 text-sm text-surface-500">
|
||||
<p class="mb-5 text-sm text-surface-700">
|
||||
即将进入 <strong>{adminOrgs()[0].name}</strong>
|
||||
(/{adminOrgs()[0].slug})。
|
||||
</p>
|
||||
@@ -312,10 +312,10 @@
|
||||
<div class="saas-status-panel">
|
||||
<div class="saas-status-card">
|
||||
<h2 class="mb-2 text-lg font-semibold">未加入组织</h2>
|
||||
<p class="mb-3 text-sm text-surface-500">
|
||||
<p class="mb-3 text-sm text-surface-700">
|
||||
飞书账号已登录,但当前账号尚未加入任何组织。
|
||||
</p>
|
||||
<p class="mb-5 text-left text-xs text-surface-400">
|
||||
<p class="mb-5 text-left text-xs text-surface-600">
|
||||
open_id:
|
||||
<code class="break-all font-mono text-surface-600">{$session.me!.user.feishuOpenId}</code>
|
||||
</p>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</script>
|
||||
|
||||
<div class="saas-status-panel">
|
||||
<div class="flex flex-col items-center gap-3 text-surface-400">
|
||||
<div class="flex flex-col items-center gap-3 text-surface-600">
|
||||
<svg class="h-7 w-7 animate-spin text-primary-500" viewBox="0 0 24 24" fill="none">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path
|
||||
|
||||
@@ -66,15 +66,15 @@
|
||||
<p class="saas-section-title mb-3">组织信息</p>
|
||||
<dl class="space-y-2.5 text-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<dt class="text-surface-500">Slug</dt>
|
||||
<dt class="text-surface-700">Slug</dt>
|
||||
<dd class="font-mono text-xs text-surface-800">/{org.slug}</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<dt class="text-surface-500">状态</dt>
|
||||
<dt class="text-surface-700">状态</dt>
|
||||
<dd><span class="saas-badge-success">{org.status}</span></dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<dt class="text-surface-500">你的角色</dt>
|
||||
<dt class="text-surface-700">你的角色</dt>
|
||||
<dd><span class="saas-badge-primary">{orgRoleLabel(org.role)}</span></dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -85,15 +85,15 @@
|
||||
<p class="saas-muted mb-4">
|
||||
开启后,普通老师可在飞书群自助创建项目;关闭后仅所有者与管理员可建。
|
||||
</p>
|
||||
<div class="flex items-center gap-3 rounded-xl border border-surface-200 bg-surface-100/50 px-4 py-3">
|
||||
<div class="flex items-center gap-3 border border-surface-300 bg-surface-100 px-4 py-3">
|
||||
<SwitchControl
|
||||
checked={settings.membersCanCreateProjects}
|
||||
disabled={saving}
|
||||
onchange={setMembersCanCreate}
|
||||
/>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-surface-800">允许成员自助创建项目</div>
|
||||
<div class="text-xs text-surface-400">普通成员在飞书群中自助建项</div>
|
||||
<div class="text-sm font-medium text-surface-900">允许成员自助创建项目</div>
|
||||
<div class="text-xs text-surface-600">普通成员在飞书群中自助建项</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,7 +121,7 @@
|
||||
</div>
|
||||
{#if usage.projects.length === 0}
|
||||
<div class="saas-empty">
|
||||
<p class="text-sm text-surface-400">暂无项目用量数据</p>
|
||||
<p class="text-sm text-surface-600">暂无项目用量数据</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="overflow-x-auto">
|
||||
@@ -152,6 +152,6 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="saas-empty">
|
||||
<p class="text-sm text-surface-400">组织数据不可用</p>
|
||||
<p class="text-sm text-surface-600">组织数据不可用</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -128,18 +128,18 @@
|
||||
<td>
|
||||
<div class="flex items-center gap-2.5">
|
||||
{#if m.avatarUrl}
|
||||
<img src={m.avatarUrl} alt="" class="h-7 w-7 rounded-full object-cover" />
|
||||
{:else}
|
||||
<div
|
||||
class="flex h-7 w-7 items-center justify-center rounded-full bg-primary-100 text-xs font-semibold text-primary-700"
|
||||
>
|
||||
<img src={m.avatarUrl} alt="" class="h-7 w-7 border border-surface-300 object-cover" />
|
||||
{:else}
|
||||
<div
|
||||
class="flex h-7 w-7 items-center justify-center border border-primary-300 bg-primary-100 text-xs font-semibold text-primary-800"
|
||||
>
|
||||
{m.displayName.slice(0, 1)}
|
||||
</div>
|
||||
{/if}
|
||||
<span class="font-medium">{m.displayName}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="font-mono text-xs text-surface-500">{m.feishuOpenId}</td>
|
||||
<td class="font-mono text-xs text-surface-700">{m.feishuOpenId}</td>
|
||||
<td class="min-w-[9rem]">
|
||||
<SelectField
|
||||
items={roleItems}
|
||||
@@ -149,7 +149,7 @@
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td class="text-surface-500">{fmtDate(m.createdAt)}</td>
|
||||
<td class="text-surface-700">{fmtDate(m.createdAt)}</td>
|
||||
<td class="text-right">
|
||||
<button class="saas-btn-danger !py-1 text-sm" onclick={() => revoke(m)}>移除</button>
|
||||
</td>
|
||||
@@ -159,7 +159,7 @@
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
<p class="border-t border-surface-100 px-5 py-3 text-xs text-surface-400">
|
||||
<p class="border-t border-surface-300 px-5 py-3 text-xs text-surface-600">
|
||||
组织角色控制后台访问;项目级权限由「项目」页团队授权({permHint})决定。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
onchange={(e) => save(m, { sortKey: (e.target as HTMLInputElement).value })}
|
||||
/>
|
||||
</td>
|
||||
<td class="text-xs text-surface-400">{fmtDateOnly(m.createdAt)}</td>
|
||||
<td class="text-xs text-surface-600">{fmtDateOnly(m.createdAt)}</td>
|
||||
<td class="text-right">
|
||||
<button class="saas-btn-danger !py-1 text-xs" onclick={() => remove(m)}>删除</button>
|
||||
</td>
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
<ErrorBanner message={error} onretry={load} />
|
||||
{:else if proj}
|
||||
<div class="mb-2">
|
||||
<a href={`/admin/org/${slug}/projects`} class="inline-flex items-center gap-1 text-sm text-surface-500 hover:text-primary-600">
|
||||
<a href={`/admin/org/${slug}/projects`} class="inline-flex items-center gap-1 text-sm text-surface-700 hover:text-primary-600">
|
||||
← 返回项目列表
|
||||
</a>
|
||||
</div>
|
||||
@@ -171,33 +171,33 @@
|
||||
<div class="saas-card-pad mb-6">
|
||||
<dl class="grid gap-x-8 gap-y-3 text-sm sm:grid-cols-2">
|
||||
<div>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-400">工作区路径</dt>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-600">工作区路径</dt>
|
||||
<dd class="mt-0.5 break-all font-mono text-xs text-surface-700">{proj.workspaceDir}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-400">创建者</dt>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-600">创建者</dt>
|
||||
<dd class="mt-0.5 text-surface-800">
|
||||
{proj.createdBy ? `${proj.createdBy.displayName} (${proj.createdBy.feishuOpenId})` : '—'}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-400">文件夹</dt>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-600">文件夹</dt>
|
||||
<dd class="mt-0.5 text-surface-800">{proj.folder ? proj.folder.name : '(根)'}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-400">飞书群</dt>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-600">飞书群</dt>
|
||||
<dd class="mt-0.5 text-surface-800">
|
||||
{#if proj.binding}
|
||||
<span class="saas-badge-success mr-1">已绑定</span>
|
||||
<span class="font-mono text-xs">群 {proj.binding.chatId}</span>
|
||||
<span class="text-surface-400"> · {fmtDate(proj.binding.createdAt)}</span>
|
||||
<span class="text-surface-600"> · {fmtDate(proj.binding.createdAt)}</span>
|
||||
{:else}
|
||||
<span class="saas-badge-neutral">未绑定</span>
|
||||
{/if}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-400">创建时间</dt>
|
||||
<dt class="text-xs font-medium uppercase tracking-wide text-surface-600">创建时间</dt>
|
||||
<dd class="mt-0.5 text-surface-800">{fmtDate(proj.createdAt)}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -273,7 +273,7 @@
|
||||
<td class="font-mono text-xs">{s.provider} / {s.roleId}</td>
|
||||
<td class="font-mono text-xs">{s.model}</td>
|
||||
<td class="tabular-nums">{s.runCount}</td>
|
||||
<td class="text-surface-500">{fmtDate(s.updatedAt)}</td>
|
||||
<td class="text-surface-700">{fmtDate(s.updatedAt)}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3 border-t border-surface-100 pt-4">
|
||||
<span class="text-xs text-surface-400"
|
||||
<span class="text-xs text-surface-600"
|
||||
>{conn.updatedAt ? '更新于 ' + fmtDate(conn.updatedAt) : '尚未配置'}</span
|
||||
>
|
||||
<div class="flex-1"></div>
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
<div class="saas-card-pad border-dashed">
|
||||
<h3 class="saas-section-title mb-2">运行时解析</h3>
|
||||
<ul class="space-y-2 text-sm text-surface-500">
|
||||
<ul class="space-y-2 text-sm text-surface-700">
|
||||
<li class="flex gap-2">
|
||||
<span class="text-primary-500">•</span>
|
||||
<span>自带密钥:智能体会使用本连接的接口地址与访问令牌。</span>
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2.5">
|
||||
<span class="text-base font-semibold text-surface-900">{t.name}</span>
|
||||
<span class="font-mono text-xs text-surface-400">/{t.slug}</span>
|
||||
<span class="font-mono text-xs text-surface-600">/{t.slug}</span>
|
||||
<span class="saas-badge-neutral">{t.memberCount} 成员</span>
|
||||
<div class="ml-auto flex flex-wrap gap-2">
|
||||
<Collapsible.Trigger class="saas-btn-secondary !py-1.5 text-sm">
|
||||
@@ -167,14 +167,14 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if t.description}
|
||||
<p class="mt-1.5 text-sm text-surface-500">{t.description}</p>
|
||||
<p class="mt-1.5 text-sm text-surface-700">{t.description}</p>
|
||||
{/if}
|
||||
<p class="mt-1 text-xs text-surface-400">创建于 {fmtDate(t.createdAt)}</p>
|
||||
<p class="mt-1 text-xs text-surface-600">创建于 {fmtDate(t.createdAt)}</p>
|
||||
|
||||
<Collapsible.Content>
|
||||
<div class="mt-4 border-t border-surface-200 pt-4">
|
||||
{#if loadingMembers && expandedId === t.id}
|
||||
<p class="text-sm text-surface-400">加载中…</p>
|
||||
<p class="text-sm text-surface-600">加载中…</p>
|
||||
{:else if expandedId === t.id}
|
||||
<div class="mb-4 flex gap-2">
|
||||
<input
|
||||
@@ -188,7 +188,7 @@
|
||||
<button class="saas-btn-secondary shrink-0" onclick={() => addMember(t)}>加入</button>
|
||||
</div>
|
||||
{#if teamMembers.length === 0}
|
||||
<p class="py-3 text-center text-sm text-surface-400">团队暂无成员</p>
|
||||
<p class="py-3 text-center text-sm text-surface-600">团队暂无成员</p>
|
||||
{:else}
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
@@ -204,7 +204,7 @@
|
||||
<tr>
|
||||
<td class="font-medium">{m.displayName}</td>
|
||||
<td class="font-mono text-xs">{m.feishuOpenId}</td>
|
||||
<td class="text-surface-500">{fmtDate(m.createdAt)}</td>
|
||||
<td class="text-surface-700">{fmtDate(m.createdAt)}</td>
|
||||
<td class="text-right">
|
||||
<button class="saas-btn-danger !py-1 text-xs" onclick={() => revokeMember(t, m)}>
|
||||
移除
|
||||
@@ -222,5 +222,5 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<p class="mt-4 text-xs text-surface-400">归档团队会同步撤销其活跃的项目授权。</p>
|
||||
<p class="mt-4 text-xs text-surface-600">归档团队会同步撤销其活跃的项目授权。</p>
|
||||
{/if}
|
||||
|
||||
+216
-125
@@ -6,9 +6,22 @@
|
||||
@source './**/*.{html,js,svelte,ts}';
|
||||
@source '../lib/**/*.{html,js,svelte,ts}';
|
||||
|
||||
/* Flat industrial: zero radius, higher-contrast surfaces, CJK-first type */
|
||||
@theme {
|
||||
--font-sans: 'Inter', 'Noto Sans SC', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
--font-sans:
|
||||
'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Inter', ui-sans-serif,
|
||||
system-ui, -apple-system, 'Segoe UI', sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
|
||||
|
||||
--radius-none: 0;
|
||||
--radius-sm: 0;
|
||||
--radius-md: 0;
|
||||
--radius-lg: 0;
|
||||
--radius-xl: 0;
|
||||
--radius-2xl: 0;
|
||||
--radius-3xl: 0;
|
||||
--radius-full: 0;
|
||||
--radius: 0;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
@@ -17,47 +30,79 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-feature-settings: 'kern' 1, 'liga' 1;
|
||||
/* Prefer readable CJK metrics over Latin optical sizing */
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
font-family: var(--font-sans);
|
||||
background: var(--color-surface-50);
|
||||
color: var(--color-surface-900);
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0.01em;
|
||||
/* Slightly cooler industrial surface */
|
||||
background: var(--color-surface-100);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
font-variant-east-asian: proportional-width;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: color-mix(in oklab, var(--color-primary-500) 28%, transparent);
|
||||
/* CJK headings: no negative tracking, breathing line-height */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 600;
|
||||
line-height: 1.45;
|
||||
letter-spacing: 0;
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
/* Harder focus ring for industrial UI */
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--color-primary-500);
|
||||
outline: 2px solid var(--color-primary-600);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: color-mix(in oklab, var(--color-primary-600) 35%, transparent);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
/* Tables: high-contrast grid, flat */
|
||||
table.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
table.data-table thead th {
|
||||
padding: 0.625rem 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
color: var(--color-surface-500);
|
||||
border-bottom: 1px solid var(--color-surface-200);
|
||||
font-weight: 600;
|
||||
color: var(--color-surface-700);
|
||||
background: var(--color-surface-100);
|
||||
border-bottom: 1px solid var(--color-surface-300);
|
||||
white-space: nowrap;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
table.data-table tbody td {
|
||||
padding: 0.75rem;
|
||||
border-bottom: 1px solid var(--color-surface-100);
|
||||
border-bottom: 1px solid var(--color-surface-200);
|
||||
vertical-align: middle;
|
||||
color: var(--color-surface-900);
|
||||
}
|
||||
|
||||
table.data-table tbody tr:hover td {
|
||||
background: color-mix(in oklab, var(--color-surface-100) 70%, transparent);
|
||||
background: var(--color-surface-100);
|
||||
}
|
||||
|
||||
table.data-table tbody tr:last-child td {
|
||||
@@ -67,51 +112,56 @@
|
||||
|
||||
@layer components {
|
||||
.saas-card {
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-50);
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 0.04);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.saas-card-pad {
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-50);
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 0.04);
|
||||
box-shadow: none;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.saas-page-title {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--color-surface-900);
|
||||
font-size: 1.375rem;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
.saas-section-title {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
font-weight: 600;
|
||||
color: var(--color-surface-900);
|
||||
letter-spacing: 0;
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
.saas-muted {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-surface-500);
|
||||
line-height: 1.65;
|
||||
color: var(--color-surface-700);
|
||||
}
|
||||
|
||||
.saas-label {
|
||||
display: block;
|
||||
margin-bottom: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-surface-700);
|
||||
line-height: 1.5;
|
||||
font-weight: 600;
|
||||
color: var(--color-surface-800);
|
||||
}
|
||||
|
||||
.saas-help {
|
||||
margin-top: 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-surface-400);
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.6;
|
||||
color: var(--color-surface-600);
|
||||
}
|
||||
|
||||
.saas-toolbar {
|
||||
@@ -123,28 +173,29 @@
|
||||
}
|
||||
|
||||
.saas-stat {
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-50);
|
||||
padding: 1rem;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.saas-stat-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--color-surface-500);
|
||||
color: var(--color-surface-600);
|
||||
}
|
||||
|
||||
.saas-stat-value {
|
||||
margin-top: 0.25rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--color-surface-900);
|
||||
letter-spacing: 0;
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
.saas-empty {
|
||||
@@ -155,6 +206,7 @@
|
||||
gap: 0.5rem;
|
||||
padding: 3rem 1rem;
|
||||
text-align: center;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.saas-shell {
|
||||
@@ -169,7 +221,7 @@
|
||||
width: 16rem;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--color-surface-200);
|
||||
border-right: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-50);
|
||||
}
|
||||
|
||||
@@ -187,10 +239,11 @@
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
border-bottom: 1px solid var(--color-surface-200);
|
||||
background: color-mix(in oklab, var(--color-surface-50) 90%, transparent);
|
||||
border-bottom: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-50);
|
||||
padding: 0 1.5rem;
|
||||
backdrop-filter: blur(8px);
|
||||
/* flat: no glass */
|
||||
backdrop-filter: none;
|
||||
}
|
||||
|
||||
.saas-content {
|
||||
@@ -215,112 +268,134 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
color: var(--color-surface-600);
|
||||
transition: color 0.15s, background-color 0.15s;
|
||||
color: var(--color-surface-700);
|
||||
border-left: 2px solid transparent;
|
||||
transition: color 0.1s, background-color 0.1s, border-color 0.1s;
|
||||
}
|
||||
|
||||
.saas-nav-item:hover {
|
||||
background: var(--color-surface-100);
|
||||
color: var(--color-surface-900);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
.saas-nav-item[data-active='true'] {
|
||||
background: var(--color-primary-100);
|
||||
color: var(--color-primary-700);
|
||||
background: var(--color-primary-50, var(--color-primary-100));
|
||||
color: var(--color-primary-800);
|
||||
border-left-color: var(--color-primary-600);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.saas-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.saas-badge-neutral {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
background: var(--color-surface-200);
|
||||
color: var(--color-surface-700);
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--color-surface-300);
|
||||
background: var(--color-surface-100);
|
||||
color: var(--color-surface-800);
|
||||
}
|
||||
|
||||
.saas-badge-primary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--color-primary-300);
|
||||
background: var(--color-primary-100);
|
||||
color: var(--color-primary-700);
|
||||
color: var(--color-primary-800);
|
||||
}
|
||||
|
||||
.saas-badge-success {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--color-success-300);
|
||||
background: var(--color-success-100);
|
||||
color: var(--color-success-700);
|
||||
color: var(--color-success-800);
|
||||
}
|
||||
|
||||
.saas-badge-warning {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--color-warning-300);
|
||||
background: var(--color-warning-100);
|
||||
color: var(--color-warning-800);
|
||||
color: var(--color-warning-900);
|
||||
}
|
||||
|
||||
.saas-badge-error {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: 0;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--color-error-300);
|
||||
background: var(--color-error-100);
|
||||
color: var(--color-error-700);
|
||||
color: var(--color-error-800);
|
||||
}
|
||||
|
||||
.saas-input,
|
||||
.saas-select,
|
||||
.saas-textarea {
|
||||
width: 100%;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
background: var(--color-surface-50);
|
||||
color: var(--color-surface-900);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
line-height: 1.5;
|
||||
outline: none;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
transition: border-color 0.1s, box-shadow 0.1s;
|
||||
}
|
||||
|
||||
.saas-input::placeholder,
|
||||
.saas-textarea::placeholder {
|
||||
color: var(--color-surface-500);
|
||||
}
|
||||
|
||||
.saas-input:focus,
|
||||
.saas-select:focus,
|
||||
.saas-textarea:focus {
|
||||
border-color: var(--color-primary-400);
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary-500) 20%, transparent);
|
||||
border-color: var(--color-primary-600);
|
||||
box-shadow: inset 0 0 0 1px var(--color-primary-600);
|
||||
}
|
||||
|
||||
.saas-textarea {
|
||||
font-family: var(--font-mono);
|
||||
line-height: 1.55;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
@@ -328,23 +403,23 @@
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
background: var(--color-surface-50);
|
||||
color: var(--color-surface-900);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
line-height: 1.5;
|
||||
outline: none;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
transition: border-color 0.1s, box-shadow 0.1s;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.saas-select-trigger:focus-visible,
|
||||
.saas-select-trigger[data-state='open'] {
|
||||
border-color: var(--color-primary-400);
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary-500) 20%, transparent);
|
||||
border-color: var(--color-primary-600);
|
||||
box-shadow: inset 0 0 0 1px var(--color-primary-600);
|
||||
}
|
||||
|
||||
.saas-select-trigger:disabled,
|
||||
@@ -354,7 +429,7 @@
|
||||
}
|
||||
|
||||
.saas-select-trigger [data-placeholder] {
|
||||
color: var(--color-surface-400);
|
||||
color: var(--color-surface-500);
|
||||
}
|
||||
|
||||
.saas-select-content {
|
||||
@@ -363,33 +438,34 @@
|
||||
width: var(--bits-select-anchor-width);
|
||||
min-width: var(--bits-select-anchor-width);
|
||||
overflow: hidden;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
background: var(--color-surface-50);
|
||||
box-shadow: 0 10px 30px rgb(15 23 42 / 0.12);
|
||||
box-shadow: 4px 4px 0 rgb(15 23 42 / 0.12);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.saas-select-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 0;
|
||||
padding: 0.45rem 0.65rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
color: var(--color-surface-800);
|
||||
line-height: 1.5;
|
||||
color: var(--color-surface-900);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.saas-select-item[data-highlighted] {
|
||||
background: color-mix(in oklab, var(--color-primary-500) 12%, transparent);
|
||||
background: var(--color-primary-100);
|
||||
color: var(--color-primary-900);
|
||||
}
|
||||
|
||||
.saas-select-item[data-selected] {
|
||||
color: var(--color-primary-800);
|
||||
font-weight: 500;
|
||||
color: var(--color-primary-900);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.saas-select-item[data-disabled] {
|
||||
@@ -405,14 +481,15 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.375rem;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: 0;
|
||||
padding: 0.5rem 0.875rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
letter-spacing: 0.01em;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.15s;
|
||||
transition: background-color 0.1s, color 0.1s, border-color 0.1s, opacity 0.1s;
|
||||
}
|
||||
|
||||
.saas-btn-primary:disabled,
|
||||
@@ -424,39 +501,47 @@
|
||||
}
|
||||
|
||||
.saas-btn-primary {
|
||||
background: var(--color-primary-500);
|
||||
background: var(--color-primary-600);
|
||||
border-color: var(--color-primary-700);
|
||||
color: var(--color-primary-contrast-500, white);
|
||||
}
|
||||
|
||||
.saas-btn-primary:hover:not(:disabled) {
|
||||
background: var(--color-primary-600);
|
||||
background: var(--color-primary-700);
|
||||
border-color: var(--color-primary-800);
|
||||
}
|
||||
|
||||
.saas-btn-secondary {
|
||||
background: var(--color-secondary-100);
|
||||
color: var(--color-secondary-800);
|
||||
background: var(--color-surface-100);
|
||||
border-color: var(--color-surface-400);
|
||||
color: var(--color-surface-900);
|
||||
}
|
||||
|
||||
.saas-btn-secondary:hover:not(:disabled) {
|
||||
background: var(--color-secondary-200);
|
||||
background: var(--color-surface-200);
|
||||
border-color: var(--color-surface-500);
|
||||
}
|
||||
|
||||
.saas-btn-ghost {
|
||||
background: var(--color-surface-100);
|
||||
color: var(--color-surface-700);
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: var(--color-surface-800);
|
||||
}
|
||||
|
||||
.saas-btn-ghost:hover:not(:disabled) {
|
||||
background: var(--color-surface-200);
|
||||
color: var(--color-surface-950, var(--color-surface-900));
|
||||
}
|
||||
|
||||
.saas-btn-danger {
|
||||
background: var(--color-error-100);
|
||||
color: var(--color-error-700);
|
||||
border-color: var(--color-error-400);
|
||||
color: var(--color-error-800);
|
||||
}
|
||||
|
||||
.saas-btn-danger:hover:not(:disabled) {
|
||||
background: var(--color-error-200);
|
||||
border-color: var(--color-error-500);
|
||||
}
|
||||
|
||||
.saas-checkbox {
|
||||
@@ -466,22 +551,22 @@
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
flex-shrink: 0;
|
||||
border-radius: 0.3rem;
|
||||
border: 1px solid var(--color-surface-300);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-500);
|
||||
background: var(--color-surface-50);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
|
||||
transition: background 0.1s, border-color 0.1s;
|
||||
}
|
||||
|
||||
.saas-checkbox[data-state='checked'] {
|
||||
background: var(--color-primary-600);
|
||||
border-color: var(--color-primary-600);
|
||||
background: var(--color-primary-700);
|
||||
border-color: var(--color-primary-700);
|
||||
}
|
||||
|
||||
.saas-checkbox:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary-500) 20%, transparent);
|
||||
box-shadow: 0 0 0 2px var(--color-surface-50), 0 0 0 4px var(--color-primary-600);
|
||||
}
|
||||
|
||||
.saas-checkbox[data-disabled] {
|
||||
@@ -489,27 +574,30 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Square industrial switch (no pill) */
|
||||
.saas-switch {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 2.5rem;
|
||||
height: 1.4rem;
|
||||
height: 1.35rem;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
border-radius: 9999px;
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-500);
|
||||
background: var(--color-surface-300);
|
||||
padding: 0.125rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
transition: background 0.1s, border-color 0.1s;
|
||||
}
|
||||
|
||||
.saas-switch[data-state='checked'] {
|
||||
background: var(--color-primary-600);
|
||||
border-color: var(--color-primary-700);
|
||||
}
|
||||
|
||||
.saas-switch:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary-500) 20%, transparent);
|
||||
box-shadow: 0 0 0 2px var(--color-surface-50), 0 0 0 4px var(--color-primary-600);
|
||||
}
|
||||
|
||||
.saas-switch[data-disabled] {
|
||||
@@ -519,26 +607,28 @@
|
||||
|
||||
.saas-switch-thumb {
|
||||
display: block;
|
||||
width: 1.05rem;
|
||||
height: 1.05rem;
|
||||
border-radius: 9999px;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 0;
|
||||
background: white;
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 0.18);
|
||||
transition: transform 0.15s;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
box-shadow: none;
|
||||
transition: transform 0.1s;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.saas-switch[data-state='checked'] .saas-switch-thumb,
|
||||
.saas-switch-thumb[data-state='checked'] {
|
||||
transform: translateX(1.05rem);
|
||||
transform: translateX(1.1rem);
|
||||
border-color: var(--color-primary-800);
|
||||
}
|
||||
|
||||
.saas-modal-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
background: rgb(2 6 23 / 0.4);
|
||||
backdrop-filter: blur(2px);
|
||||
background: rgb(2 6 23 / 0.55);
|
||||
backdrop-filter: none;
|
||||
}
|
||||
|
||||
.saas-modal {
|
||||
@@ -549,11 +639,11 @@
|
||||
width: calc(100% - 2rem);
|
||||
max-width: 28rem;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
background: var(--color-surface-50);
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 20px 40px rgb(15 23 42 / 0.16);
|
||||
box-shadow: 6px 6px 0 rgb(15 23 42 / 0.15);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -570,11 +660,12 @@
|
||||
.saas-status-card {
|
||||
width: 100%;
|
||||
max-width: 28rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--color-surface-200);
|
||||
border-radius: 0;
|
||||
border: 1px solid var(--color-surface-400);
|
||||
background: var(--color-surface-50);
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 0.04);
|
||||
box-shadow: 4px 4px 0 rgb(15 23 42 / 0.1);
|
||||
line-height: 1.7;
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+23
@@ -1292,6 +1292,29 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.11.1.tgz",
|
||||
"integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.2",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.11.1.tgz",
|
||||
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz",
|
||||
|
||||
Reference in New Issue
Block a user