fix(filelib-web): 补回迁移丢失的共享组件样式层与图标集

第一版迁移只把 uiTheme.ts 的 @theme 颜色令牌搬了过来,155 行里约 90
行的组件类(.btn/.panel/.input/.select/table.list/.tag/.switch/
.link-danger/.quiet 等)被丢掉,于是每个组件各自内联重述按钮、输入框、
面板的样式 —— 正是旧代码的重复问题被原样复刻,后台观感明显退化。

现在 app.css 是设计系统的唯一去处:@theme 管令牌,@layer components
管组件类。组件只带布局工具类,不重述组件样式。

图标集同样是丢的:旧面板有 13 个内联 SVG,新版一个不剩,只有纯文字的
「+」「删除」—— 这是"简陋"最直接的来源。提成 Icon.svelte 共享。
Group 节点沿用两人剪影而非文件夹图标:MemberGroup 与文件库的
FOLDER/PROJECT 是两套无关层级,图标不应混淆(ADR-0028/0021)。

顺手修 FilesPanel 的 uploadInput:bind:this 的目标要用 $state,
否则 Svelte 5 下不保证更新。
This commit is contained in:
2026-07-26 20:19:10 +08:00
parent a7f90f387d
commit 325b4fc137
5 changed files with 309 additions and 37 deletions
+23 -21
View File
@@ -4,6 +4,7 @@
import { selectedFilePath, filesVersion } from "./browser.js";
import type { FileEntry, NodeDetail } from "./types.js";
import Modal from "./Modal.svelte";
import Icon from "./Icon.svelte";
let { node }: { node: NodeDetail } = $props();
@@ -12,7 +13,8 @@
let showNewFile = $state(false);
let newPath = $state("");
let newContent = $state("");
let uploadInput: HTMLInputElement;
// bind:this 的目标要用 $state,否则 svelte 5 warn 不会正确更新。
let uploadInput = $state<HTMLInputElement | null>(null);
const canEdit = $derived(node.role !== "VIEW");
@@ -84,34 +86,34 @@
}
</script>
<div class="rounded-xl border border-line-soft bg-panel p-5">
<div class="mb-2 flex items-center justify-between">
<div class="text-[13px] font-semibold">项目文件({files?.length ?? 0})</div>
<div class="panel">
<div class="mb-1.5 flex items-center justify-between">
<div class="section-title">项目文件({files?.length ?? 0})</div>
{#if canEdit}
<div class="flex gap-2">
<button class="rounded-lg border border-line bg-panel px-3 py-1.5 text-[12.5px] font-medium text-ink transition hover:bg-hover" onclick={() => (showNewFile = true)}>+ 新建文件</button>
<button class="rounded-lg bg-accent px-3 py-1.5 text-[12.5px] font-medium text-white transition hover:bg-accent-hover" onclick={() => uploadInput.click()}>上传文件</button>
<div class="flex gap-1.5">
<button class="btn" onclick={() => (showNewFile = true)}><Icon name="plus" size={13} /> 新建文件</button>
<button class="btn btn-primary" onclick={() => uploadInput?.click()}>上传文件</button>
<input bind:this={uploadInput} type="file" class="hidden" onchange={doUpload} />
</div>
{/if}
</div>
{#if files === null && loadError === null}
<div class="py-5 text-center text-xs text-ink-3">加载中…</div>
<div class="quiet py-5 text-center">加载中…</div>
{:else if loadError}
<div class="py-5 text-center text-xs text-danger">{loadError}</div>
{:else if files && files.length === 0}
<div class="py-5 text-center text-xs text-ink-3">空仓库 · 可新建或上传文件</div>
<div class="quiet py-5 text-center">空仓库 · 可新建或上传文件</div>
{:else if files}
<table class="w-full border-collapse text-[13px]">
<table class="list">
<tbody>
{#each files as f (f.path)}
<tr
class="cursor-pointer border-t border-line-soft first:border-t-0 {$selectedFilePath === f.path ? 'bg-selected' : 'hover:bg-hover'}"
class="cursor-pointer {$selectedFilePath === f.path ? 'bg-selected' : 'hover:bg-hover'}"
onclick={() => selectedFilePath.set(f.path)}
>
<td class="py-2 font-mono text-[12.5px] text-ink">{f.path}</td>
<td class="py-2 text-right font-mono text-[11px] text-ink-3">{f.size} B</td>
<td class="font-mono text-[12.5px] text-ink">{f.path}</td>
<td class="file-meta text-right">{f.size} B</td>
</tr>
{/each}
</tbody>
@@ -121,17 +123,17 @@
{#if showNewFile}
<Modal title="新建文件" onclose={() => (showNewFile = false)}>
<div class="mb-3">
<label class="mb-1 block text-[11.5px] text-ink-3" for="nf-path">路径</label>
<input id="nf-path" class="w-full rounded-lg border border-line bg-panel px-3 py-2 font-mono text-[13px] outline-none focus:border-accent" bind:value={newPath} placeholder="docs/intro.md" />
<div class="form-row">
<label class="form-label" for="nf-path">路径</label>
<input id="nf-path" class="input font-mono" bind:value={newPath} placeholder="docs/intro.md" />
</div>
<div class="mb-3">
<label class="mb-1 block text-[11.5px] text-ink-3" for="nf-content">内容</label>
<textarea id="nf-content" rows="8" class="w-full rounded-lg border border-line bg-panel px-3 py-2 font-mono text-xs outline-none focus:border-accent" bind:value={newContent} placeholder="内容…"></textarea>
<div class="form-row">
<label class="form-label" for="nf-content">内容</label>
<textarea id="nf-content" rows="8" class="textarea" bind:value={newContent} placeholder="内容…"></textarea>
</div>
<div class="mt-4 flex justify-end gap-2">
<button class="rounded-lg border border-line bg-panel px-3.5 py-1.5 text-[12.5px] font-medium text-ink transition hover:bg-hover" onclick={() => (showNewFile = false)}>取消</button>
<button class="rounded-lg bg-accent px-3.5 py-1.5 text-[12.5px] font-medium text-white transition hover:bg-accent-hover" onclick={submitNewFile}>创建</button>
<button class="btn" onclick={() => (showNewFile = false)}>取消</button>
<button class="btn btn-primary" onclick={submitNewFile}>创建</button>
</div>
</Modal>
{/if}