-
-
-
Zotero 文献库
-
{error ? '未连接' : loading ? '…' : items.length}
- {offline && !error ? (
-
- ) : null}
-
+ const activePath = useEditorStore((s) => s.activePath);
+ const { items, loading, error, offline, selected, detail, detailLoading, detailError } = library;
+ const query = filter.trim().toLocaleLowerCase();
+ const shown = items.filter((item) => [item.title, item.authors, item.year, item.citekey].some((value) => value.toLocaleLowerCase().includes(query)));
+ return
+
+ Zotero 文献库
+ {error ? '未连接' : loading ? '加载中…' : items.length}
+ {offline && !error ? : null}
+
+
+ {error ? {error}
连接 Zotero 与 Better BibTeX,或在设置中配置文献库。
: <>
+
+
+
+ {!loading && shown.length === 0 ? - {items.length === 0 ? '库为空' : '无匹配'}
: null}
+ {shown.map((item) => )}
+
+
+ {selected ? <>
+
+
{selected.title || selected.citekey}
{[selected.authors, selected.year].filter(Boolean).join(' · ')}
+
- 引用键
- {selected.citekey}
+ {text(detail?.['container-title']) ? <>- 出版物
- {text(detail?.['container-title'])}
> : null}
+ {text(detail?.DOI) ? <>- DOI
- {text(detail?.DOI)}
> : null}
+
+ {detailLoading ?
加载详情…
: detailError ?
详情暂不可用:{detailError}
: null}
+ {text(detail?.abstract) ?
{text(detail?.abstract)}
: null}
+
+
+
+ {activePath ? '插入到当前文稿的光标位置' : '先打开或新建文稿,再插入引用'}
+
+ > :
选择一篇文献查看详情
}
+
- {error ? (
- {error}
- ) : (
- <>
- {items.length > 0 ? (
- setFilter(e.target.value)}
- placeholder="过滤文献…"
- className="mx-2 mb-1 rounded-[4px] border border-[var(--background-modifier-border)] bg-[var(--background-primary)] px-1.5 py-0.5 text-[12px] text-[var(--text-normal)] outline-none focus:border-[var(--accent)]"
- />
- ) : null}
-
- {shown.length === 0 ? (
- -
- {items.length === 0 ? '库为空' : '无匹配'}
-
- ) : (
- shown.map((it) => (
- -
-
-
- ))
- )}
-
- >
- )}
-
- );
+ >}
+ ;
}
diff --git a/src/components/workbench/layoutPatch.test.ts b/src/components/workbench/layoutPatch.test.ts
index 3493bac..fa918a3 100644
--- a/src/components/workbench/layoutPatch.test.ts
+++ b/src/components/workbench/layoutPatch.test.ts
@@ -60,7 +60,8 @@ describe('buildLayoutPatch (UAT #6 面板独立性)', () => {
it.each([0, -1, 199, 481, Number.NaN, Number.POSITIVE_INFINITY])('ignores an expanded sidebar measured at %s without losing its remembered width', (width) => {
const patch = buildLayoutPatch(fakePanel(false, width), fakePanel(false, 320), false);
expect(patch).toEqual({ rightPanelCollapsed: false, rightPanelWidth: 320 });
- expect({ ...DEFAULT_LAYOUT, ...patch }.sidebarWidth).toBe(280);
+ // An existing user's chosen width is independent of the new-session default.
+ expect({ ...DEFAULT_LAYOUT, sidebarWidth: 292, ...patch }.sidebarWidth).toBe(292);
});
it.each([0, -1, 239, 561, Number.NaN, Number.POSITIVE_INFINITY])('ignores an expanded tool panel measured at %s', (width) => {
@@ -76,7 +77,7 @@ describe('buildLayoutPatch (UAT #6 面板独立性)', () => {
describe('layout persistence requires a current user resize', () => {
function setup() {
- const context = { compact: false, blocked: false, projectId: 'first', mode: 'academic', layout: { ...DEFAULT_LAYOUT }, viewportWidth: 1440 };
+ const context = { compact: false, blocked: false, projectId: 'first', mode: 'academic', layout: { ...DEFAULT_LAYOUT, sidebarWidth: 280 }, viewportWidth: 1440 };
const save = vi.fn();
const writeback = createLayoutWriteback(() => context, save);
return { context, save, writeback };
@@ -116,7 +117,7 @@ describe('layout persistence requires a current user resize', () => {
const { context, save, writeback } = setup();
writeback.begin();
writeback.cancel();
- context.layout = { ...DEFAULT_LAYOUT };
+ context.layout = { ...DEFAULT_LAYOUT, sidebarWidth: 280 };
writeback.commit(fakePanel(false, 280), fakePanel(false, 560));
writeback.commit(fakePanel(false, 280), fakePanel(false, 320));
expect(save).not.toHaveBeenCalled();
diff --git a/src/components/workbench/workbench.css b/src/components/workbench/workbench.css
index eb96c0f..fba763b 100644
--- a/src/components/workbench/workbench.css
+++ b/src/components/workbench/workbench.css
@@ -27,3 +27,70 @@
inset: 0 -0.5px;
background: var(--accent);
}
+
+.workspace-navigation { display: flex; flex-shrink: 0; gap: 22px; min-height: 47px; padding: 0 24px; border-bottom: 1px solid var(--background-modifier-border); }
+.workspace-navigation button { position: relative; padding: 12px 0; color: var(--text-muted); font-size: 13px; white-space: nowrap; }
+.workspace-navigation button[aria-selected='true'] { color: var(--text-normal); font-weight: 600; }
+.workspace-navigation button[aria-selected='true']::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: var(--accent); }
+.workspace-destination { min-height: 0; flex: 1; overflow: auto; container-type: inline-size; }
+.workspace-caption { font-size: 12px; color: var(--text-muted); }
+.project-overview, .project-versions { max-width: 860px; margin: 0 auto; padding: 36px clamp(22px, 5cqi, 64px); }
+.project-overview header, .project-versions header { display: flex; align-items: center; gap: 25px; }
+.project-overview header > div { min-width: 0; }
+.project-overview h1, .project-versions h1 { font-family: var(--font-editor); font-size: 28px; font-weight: 500; line-height: 1.4; margin: 6px 0 12px; overflow-wrap: anywhere; }
+.project-overview p, .project-versions p { margin: 12px 0; color: var(--text-muted); font-size: 12px; line-height: 1.85; overflow-wrap: anywhere; }
+.project-overview .project-cover-large { width: 66px; height: 94px; margin: 0; }
+.project-overview-metrics { display: flex; padding: 22px 0; margin: 29px 0; border-block: 1px solid var(--background-modifier-border); }
+.project-overview-metrics > div { flex: 1; }
+.project-overview-metrics dt { color: var(--text-muted); font-size: 12px; }
+.project-overview-metrics dd { margin-top: 5px; font-size: 26px; font-weight: 350; font-variant-numeric: tabular-nums; }
+.project-overview h2 { font-size: 15px; font-weight: 550; margin: 24px 0 14px; }
+.overview-continue { display: flex; align-items: center; gap: 15px; width: 100%; padding: 18px 15px; border: 1px solid var(--background-modifier-border); border-radius: 8px; text-align: left; }
+.overview-continue > span { display: flex; flex: 1; flex-direction: column; min-width: 0; gap: 4px; }
+.overview-continue strong { font-weight: 550; overflow: hidden; text-overflow: ellipsis; }
+.overview-continue small { color: var(--text-muted); }
+.overview-continue:hover { background: var(--background-modifier-hover); }
+.overview-new { display: inline-flex; align-items: center; gap: 8px; margin-top: 20px; padding: 8px 0; color: var(--text-muted); }
+.project-versions header { justify-content: space-between; flex-wrap: wrap; }
+.version-timeline { margin: 28px 0; padding-left: 15px; border-left: 1px solid var(--background-modifier-border); list-style: none; }
+.version-timeline li { position: relative; padding-bottom: 12px; }
+.version-timeline li::before { content: ''; position: absolute; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); left: -19px; top: 16px; }
+.version-timeline button { display: flex; flex-direction: column; gap: 5px; width: 100%; padding: 10px 15px; text-align: left; border-radius: 6px; }
+.version-timeline button:hover { background: var(--background-modifier-hover); }
+.version-timeline time, .version-timeline small { font-size: 11px; color: var(--text-muted); }
+.version-timeline strong { font-size: 14px; font-weight: 500; overflow-wrap: anywhere; }
+.reference-library { min-height: 0; height: 100%; display: flex; flex-direction: column; font-size: 12px; }
+.reference-library-heading { display: flex; align-items: center; gap: 7px; padding: 14px 14px 9px; flex-shrink: 0; }
+.reference-library-heading > span:nth-of-type(2) { color: var(--text-muted); font-variant-numeric: tabular-nums; }
+.reference-library-heading button { margin-left: auto; padding: 6px; border-radius: 5px; }
+.reference-library-heading button:hover { background: var(--background-modifier-hover); }
+.reference-filter { display: block; padding: 5px 14px 14px; flex-shrink: 0; }
+.reference-filter input { width: 100%; padding: 8px 10px; border: 1px solid var(--background-modifier-border); border-radius: 6px; background: var(--background-primary); }
+.reference-browser { display: flex; flex-direction: column; min-height: 0; flex: 1; }
+.reference-list { list-style: none; min-height: 90px; flex: 1; overflow: auto; margin: 0; padding: 0 7px 12px; }
+.reference-list button { display: flex; flex-direction: column; width: 100%; padding: 12px 10px; gap: 5px; text-align: left; border-radius: 5px; border-left: 2px solid transparent; }
+.reference-list button:hover { background: var(--background-modifier-hover); }
+.reference-list button[aria-pressed='true'] { background: var(--background-modifier-active); border-left-color: var(--accent); }
+.reference-list button > span { font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }
+.reference-list small { font-size: 11px; color: var(--text-muted); }
+.reference-detail { display: flex; flex-direction: column; flex: 1; min-height: 180px; border-top: 1px solid var(--background-modifier-border); padding: 20px 17px; overflow: hidden; overflow-wrap: anywhere; }
+.reference-detail-body { min-height: 0; flex: 1; overflow: auto; }
+.reference-detail-action { flex-shrink: 0; padding-top: 14px; }
+.reference-detail h2 { font-family: var(--font-editor); font-size: 19px; font-weight: 500; line-height: 1.6; margin-bottom: 12px; }
+.reference-detail p { color: var(--text-muted); font-size: 12px; line-height: 1.8; margin-block: 10px; }
+.reference-detail dl { margin: 18px 0; font-size: 12px; }
+.reference-detail dt { color: var(--text-muted); margin-top: 12px; }
+.reference-detail dd { margin: 4px 0; user-select: text; }
+.reference-detail-action > small { display: block; color: var(--text-muted); margin-top: 10px; }
+.reference-insert { width: 100%; padding: 9px 12px; border-radius: 5px; border: 1px solid var(--background-modifier-border); font-weight: 550; background: var(--background-secondary); }
+.reference-insert:hover:not(:disabled) { background: var(--background-modifier-active); }
+.reference-insert:disabled { opacity: .45; }
+.reference-empty { padding: 20px 14px; color: var(--text-muted); line-height: 1.8; }
+.reference-empty p { margin: 0 0 14px; overflow-wrap: anywhere; }
+.reference-workspace .reference-library-heading { padding: 24px 24px 12px; font-size: 15px; }
+.reference-workspace .reference-filter { padding-inline: 24px; }
+@container (min-width: 580px) {
+ .reference-workspace .reference-browser { flex-direction: row; }
+ .reference-workspace .reference-list { flex: 1.05; padding-left: 14px; }
+ .reference-workspace .reference-detail { flex: 1; border-top: 0; border-left: 1px solid var(--background-modifier-border); padding: 22px; }
+}
diff --git a/src/data/changelog.ts b/src/data/changelog.ts
index e1f1268..69cc5eb 100644
--- a/src/data/changelog.ts
+++ b/src/data/changelog.ts
@@ -21,6 +21,20 @@ export interface ChangelogEntry {
}
export const CHANGELOG: readonly ChangelogEntry[] = [
+ {
+ version: '2.1.0',
+ date: '2026-09-13',
+ title: 'v2.1.0 · 多栏工作台与文献浏览',
+ level: 'minor',
+ highlights: [
+ '工作区导航:新增概览、文稿、文献和版本视图;查看资料或历史后返回文稿,保留正文、光标、选区和撤销记录。',
+ '项目快捷切换:左侧项目轨展示多个项目与封面,切回恢复各自文稿;目标目录不可用时显示错误并保留原项目。',
+ '文献浏览:选择条目先查看详情,点击「插入所选引用」才写入文稿;插入可单独撤销,长详情滚动时操作按钮仍可见。',
+ '键盘与布局:工作区、文稿和工具标签支持方向键及 Home/End;两侧面板独立折叠,缩放保留已调整的栏宽。',
+ '阅读排版:统一纸白、石墨灰与低饱和灰绿,正文采用宋体风格;优化工具密度、暗色标记和原生滚动条。',
+ '体验修复:修复状态栏裁剪模式菜单、退出简易模式后创作工具空白的问题,并为设置开关补充可识别名称。',
+ ],
+ },
{
version: '2.0.0',
date: '2026-09-13',
diff --git a/src/editor/academicActions.ts b/src/editor/academicActions.ts
index 3ed0167..944ed2b 100644
--- a/src/editor/academicActions.ts
+++ b/src/editor/academicActions.ts
@@ -1,4 +1,5 @@
import type { EditorView } from '@codemirror/view';
+import { isolateHistory } from '@codemirror/commands';
import { zoteroCayw } from '../ipc/zotero';
import { useEditorStore } from '../stores/useEditorStore';
import { showToast } from '../stores/useToastStore';
@@ -69,12 +70,12 @@ function insertCitationText(view: EditorView, cite: string): void {
const path = useEditorStore.getState().activePath ?? '';
const text = formatCitationFor(cite, languageFromDoc(view.state.doc.toString(), path));
const { from, to } = view.state.selection.main;
- view.dispatch({ changes: { from, to, insert: text }, selection: { anchor: from + text.length }, scrollIntoView: true });
+ view.dispatch({ changes: { from, to, insert: text }, selection: { anchor: from + text.length }, scrollIntoView: true, annotations: isolateHistory.of('full') });
view.focus();
}
/**
- * 直接插入指定 citekey 的引用(ACAD-01 Sidebar 库点击):不弹 CAYW,按文档语言重排后插光标处。
+ * 明确插入指定 citekey:不弹 CAYW,按文档语言重排,在独立撤销步骤中插入。
*/
export function insertCitekey(citekey: string): void {
runWritableCommand((view) => insertCitationText(view, `[@${citekey}]`));
diff --git a/src/editor/fileOpenFlow.ts b/src/editor/fileOpenFlow.ts
index 977dad2..e859edf 100644
--- a/src/editor/fileOpenFlow.ts
+++ b/src/editor/fileOpenFlow.ts
@@ -5,6 +5,7 @@ import { readFile } from '../ipc/files';
import { showToast } from '../stores/useToastStore';
import { useEditorStore } from '../stores/useEditorStore';
import { useVaultStore } from '../stores/useVaultStore';
+import { useWorkbenchStore } from '../stores/useWorkbenchStore';
import type { TreeNode } from '../types/vault';
import { baseExtensions } from './extensions';
import { openFile, snapshotBeforeSwitch, switchToTab } from './editorState';
@@ -27,6 +28,8 @@ function readFailure(error: unknown): string {
export async function openFileInEditor(view: EditorView, node: TreeNode, request = beginDocumentNavigation()): Promise
{
const vault = useVaultStore.getState().vault;
if (!vault || node.isDir) return;
+ const workspace = useWorkbenchStore.getState();
+ if (['projectOverview', 'references', 'projectVersions'].includes(workspace.centralView)) workspace.setCentralView('editor');
const active = useEditorStore.getState().activePath;
if (active) snapshotBeforeSwitch(view, active);
try {
diff --git a/src/stores/effectiveCentralView.ts b/src/stores/effectiveCentralView.ts
index e18e770..c295f80 100644
--- a/src/stores/effectiveCentralView.ts
+++ b/src/stores/effectiveCentralView.ts
@@ -8,7 +8,7 @@ export function effectiveCentralView(
if (view === 'bookshelf' && !capabilities.bookshelfEnabled) return 'editor';
if (
capabilities.simpleMode &&
- (view === 'gitGraph' || view === 'graph' || view === 'mergeResolve' || view === 'multibuffer')
+ (view === 'references' || view === 'projectVersions' || view === 'gitGraph' || view === 'graph' || view === 'mergeResolve' || view === 'multibuffer')
) {
return 'editor';
}
diff --git a/src/stores/useWorkbenchStore.ts b/src/stores/useWorkbenchStore.ts
index 4ab4a61..315ed35 100644
--- a/src/stores/useWorkbenchStore.ts
+++ b/src/stores/useWorkbenchStore.ts
@@ -6,6 +6,9 @@ import { DEFAULT_LAYOUT, type ModeLayout, type TabId } from '../types/workbench'
/** 中央区视图:编辑器 / git-graph / 知识图谱 / 合并冲突解决器 / 阅读模式 / 书架 / 全库搜索(multibuffer)整页接管。 */
export type CentralView =
| 'editor'
+ | 'projectOverview'
+ | 'projectVersions'
+ | 'references'
| 'gitGraph'
| 'graph'
| 'mergeResolve'
diff --git a/src/styles/base.css b/src/styles/base.css
index 49ee9da..87b481a 100644
--- a/src/styles/base.css
+++ b/src/styles/base.css
@@ -11,10 +11,10 @@
--font-ui:
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei UI',
'Hiragino Sans GB', 'Noto Sans CJK SC', sans-serif;
- /* 编辑器正文栈(R5 §2.2,中文优先阅读向):正文用 YaHei 文档变体,思源黑体作未预装时礼貌回退。 */
+ /* 正文采用原型的阅读向宋体;界面与代码仍保留各自系统栈。 */
--font-editor:
- -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei',
- 'Hiragino Sans GB', 'Source Han Sans SC', 'Noto Sans CJK SC', sans-serif;
+ 'Iowan Old Style', 'Palatino Linotype', 'Noto Serif CJK SC', 'Source Han Serif SC',
+ 'Songti SC', 'SimSun', serif;
/* 代码字体栈(R5 §2.2):Cascadia Code(含连字,Windows 自带)/ JetBrains Mono 覆盖西文,
中文回退 Microsoft YaHei(非等宽但远好于系统默认方框)。 */
--font-mono:
@@ -24,7 +24,7 @@
--editor-max-width 是版心列宽:经 .cm-scroller 的 paddingInline 居中(Zettlr 式,extensions.ts),
不再压 .cm-content 盒模型;横向留白下限内联在 scroller 公式(1.5rem),故无独立 padding-x token。 */
--editor-font-size: 16px;
- --editor-line-height: 1.7;
+ --editor-line-height: 2.05;
--editor-max-width: 46rem;
/* 标题上下边距(R5 §3.1,inlinePlugin 行级 cm-ink-hN 消费)。 */
--h-margin-top: 1.4em;
@@ -51,3 +51,6 @@ body {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
+
+/* The active writing line follows the neutral surface palette in all modes. */
+.cm-editor .cm-activeLine { background-color: var(--background-modifier-hover); }
diff --git a/src/styles/material.css b/src/styles/material.css
index 4628fb6..4dfb113 100644
--- a/src/styles/material.css
+++ b/src/styles/material.css
@@ -2,8 +2,8 @@
:root {
--workbench-header-height: 44px;
--workbench-status-height: 30px;
- --project-rail-width: 64px;
- --shell-frame: color-mix(in srgb, var(--background-secondary) 84%, #809889);
+ --project-rail-width: 100px;
+ --shell-frame: color-mix(in srgb, var(--background-secondary) 96%, var(--archive-sage));
--shell-edge: color-mix(in srgb, var(--text-normal) 13%, transparent);
--surface-paper: var(--background-primary);
--surface-tool: color-mix(in srgb, var(--background-secondary) 84%, transparent);
@@ -38,7 +38,7 @@ html[data-theme='dark'] {
.workbench-tool-tabs button { padding-left: 10px; padding-right: 10px; font-size: 12px; border-radius: 7px 7px 0 0; }
.workbench-tool-tabs button[aria-selected='true'] { background: color-mix(in srgb, var(--background-primary) 68%, transparent); border-bottom-width: 1px; }
.material-eyebrow { font-size: 9px; line-height: 1.5; font-weight: 650; letter-spacing: .18em; color: var(--text-muted); }
-.project-navigation-heading { min-height: 74px; padding: 17px 17px 12px; display: flex; flex-direction: column; gap: 5px; }
+.project-navigation-heading { min-height: 52px; padding: 16px 17px 12px; display: flex; flex-direction: column; gap: 5px; }
.project-navigation-heading > span:last-child { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 15px; font-weight: 550; letter-spacing: .015em; }
.project-navigation-tabs { display: flex; margin: 0 12px 10px; padding: 3px; gap: 2px; border: 1px solid var(--shell-edge); border-radius: 9px; background: color-mix(in srgb, var(--shell-frame) 68%, transparent); }
.project-navigation-tabs button { flex: 1; min-height: 29px; font-size: 12px; border-radius: 6px; color: var(--text-muted); }
@@ -55,9 +55,10 @@ html[data-theme='dark'] {
.workbench-status-brand { margin-right: 14px; flex-shrink: 0; font-size: 8px; letter-spacing: .15em; opacity: .65; }
.snapshot-retry { color: var(--color-error); padding: 0 8px; white-space: nowrap; }
.snapshot-saving { padding: 0 8px; white-space: nowrap; }
-.project-rail { position: relative; width: var(--project-rail-width); flex-shrink: 0; display: flex; align-items: center; flex-direction: column; padding: 13px 6px 8px; gap: 18px; }
+.project-rail { position: relative; width: var(--project-rail-width); flex-shrink: 0; display: flex; align-items: center; flex-direction: column; padding: 13px 8px 8px; gap: 14px; }
.inkstream-mark { display: block; width: 38px; height: 38px; object-fit: contain; flex-shrink: 0; }
-.project-rail-archive { position: relative; display: flex; flex-direction: column; align-items: center; gap: 11px; padding: 0 4px 11px; width: 49px; border-radius: 9px; }
+html[data-theme='dark'] .inkstream-mark { filter: invert(.8) hue-rotate(180deg); }
+.project-rail-archive { position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 9px 4px; width: 76px; border-radius: 9px; }
.project-rail-archive:hover, .project-rail-archive[aria-expanded='true'] { background: var(--background-modifier-hover); }
.project-rail-spine { width: 34px; height: 160px; position: relative; border: 1px solid var(--material-highlight); border-radius: 8px; background: linear-gradient(110deg, var(--archive-card), color-mix(in srgb, var(--archive-surface) 55%, transparent)); box-shadow: 3px 3px 9px var(--material-shadow), inset 3px 0 3px var(--material-highlight); animation: project-spine-enter var(--archive-switch-duration) var(--material-ease) both; overflow: hidden; }
.project-rail-spine > span { display: block; padding: 12px 7px; writing-mode: vertical-rl; text-orientation: mixed; height: 100%; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; line-height: 17px; letter-spacing: .06em; }
@@ -67,6 +68,17 @@ html[data-theme='dark'] {
.project-rail-actions button { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 8px; color: var(--text-muted); }
.project-rail-actions button:hover { color: var(--text-normal); background: var(--background-modifier-hover); }
.project-rail button:disabled { opacity: .4; cursor: default; }
+.project-rail-projects { width: 100%; min-height: 0; flex: 1; display: flex; flex-direction: column; gap: 8px; overflow: auto; }
+.project-rail-project { width: 100%; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 9px; padding: 12px 5px; border-radius: 8px; }
+.project-rail-project:hover { background: var(--background-modifier-hover); }
+.project-rail-project[aria-current='true'] { background: var(--background-modifier-active); }
+.project-rail-project > span:last-child { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; width: 100%; font-size: 11px; line-height: 1.6; text-align: center; overflow-wrap: anywhere; }
+.project-rail-project .project-cover { width: 37px; height: 52px; }
+.project-rail-empty { margin-top: 15px; text-align: center; font-size: 11px; color: var(--text-muted); }
+.project-rail-actions button[aria-pressed='true'] { background: var(--background-modifier-active); color: var(--text-normal); }
+.workbench-statusbar { white-space: nowrap; }
+.workbench-statusbar [data-testid='status-bar-right'] { overflow-x: auto; max-width: 80%; scrollbar-width: none; }
+.workbench-statusbar [data-testid='status-bar-right'] > * { flex-shrink: 0; }
.project-archive-overlay {
--text-normal: var(--archive-ink);
--text-muted: var(--archive-muted);
@@ -176,10 +188,10 @@ html[data-theme='dark'] {
.workbench-status-brand { display: none; }
}
@media (max-width: 760px) {
- :root { --project-rail-width: 52px; }
+ :root { --project-rail-width: 68px; }
.workbench-stage { padding-right: 7px; padding-top: 6px; }
.project-rail { padding-left: 3px; padding-right: 3px; }
- .project-rail-archive { width: 44px; }
+ .project-rail-archive { width: 58px; }
.workbench-group > #sidebar[data-panel] { position: absolute !important; left: 0; top: 0; bottom: 0; z-index: 13; width: min(var(--navigation-width, 232px), 60vw) !important; flex: none !important; max-width: none !important; box-shadow: 10px 0 25px var(--material-shadow); }
.workbench-group > #right-panel[data-panel] { width: min(var(--tools-width, 304px), 70vw) !important; }
.workbench-window-title { display: none; }
diff --git a/src/styles/theme.css b/src/styles/theme.css
index c068092..fbd12aa 100644
--- a/src/styles/theme.css
+++ b/src/styles/theme.css
@@ -18,10 +18,11 @@
/* ── 层 1 base:中性变量挂 data-theme(UI-SPEC 总表逐行照抄) ───────────── */
html[data-theme='light'] {
- --background-primary: hsl(230, 1%, 98%);
- --background-primary-alt: hsl(230, 1%, 95%);
- --background-secondary: hsl(230, 1%, 94%);
- --background-secondary-alt: hsl(230, 1%, 92%);
+ color-scheme: light;
+ --background-primary: hsl(90, 6%, 98%);
+ --background-primary-alt: hsl(90, 4%, 95%);
+ --background-secondary: hsl(100, 4%, 94%);
+ --background-secondary-alt: hsl(100, 4%, 92%);
--background-modifier-border: hsl(230, 4%, 86%);
--background-modifier-hover: hsl(230, 8%, 24%, 0.06);
--background-modifier-active: hsl(230, 8%, 24%, 0.1);
@@ -52,9 +53,9 @@ html[data-theme='light'] {
/* 弹出层阴影(UI-SPEC:阴影仅弹出层使用,禁止卡片化滥用) */
--shadow-popup: 0 4px 16px hsl(230 8% 24% / 0.12);
/* 模式身份静态色点(与各模式 accent 同源,Plan 05 模式菜单消费) */
- --mode-dot-standard: hsl(220, 10%, 50%);
- --mode-dot-academic: hsl(220, 70%, 45%);
- --mode-dot-creative: hsl(355, 65%, 50%);
+ --mode-dot-standard: hsl(145, 12%, 38%);
+ --mode-dot-academic: hsl(156, 17%, 35%);
+ --mode-dot-creative: hsl(130, 10%, 38%);
/* CodeMirror 语法高亮 token(UI-SPEC §Color A,取值同源 Atom one-light)。
仅随 data-theme 变,不吃模式 accent;highlightTheme.ts 经 var(--cm-*) 消费。 */
@@ -88,10 +89,11 @@ html[data-theme='light'] {
--cm-table-header-bg: hsl(230, 8%, 94%);
--cm-hr: hsl(230, 6%, 82%);
--cm-checkbox-checked: hsl(var(--accent-hsl));
- --cm-image-loading-bg: hsl(230, 1%, 95%);
+ --cm-image-loading-bg: hsl(90, 4%, 95%);
}
html[data-theme='dark'] {
+ color-scheme: dark;
--background-primary: hsl(220, 13%, 18%);
--background-primary-alt: hsl(220, 13%, 16%);
--background-secondary: hsl(220, 13%, 15%);
@@ -99,11 +101,11 @@ html[data-theme='dark'] {
--background-modifier-border: hsl(222, 13%, 11%);
--background-modifier-hover: hsl(220, 14%, 71%, 0.08);
--background-modifier-active: hsl(220, 14%, 71%, 0.12);
- --text-normal: hsl(220, 14%, 71%);
+ --text-normal: hsl(145, 13%, 69%);
--text-muted: hsl(220, 9%, 55%);
--text-faint: hsl(220, 10%, 40%);
--text-on-accent: hsl(220, 13%, 18%);
- --color-error: hsl(355, 65%, 65%);
+ --color-error: hsl(130, 12%, 69%);
--archive-canvas: hsl(150, 5%, 14%);
--archive-ink: hsl(120, 5%, 90%);
--archive-muted: hsl(140, 5%, 68%);
@@ -123,19 +125,19 @@ html[data-theme='dark'] {
--crea-progress-track: hsl(220, 14%, 71%, 0.12);
--cm-codex-mention: hsl(265, 48%, 70%);
--shadow-popup: 0 4px 16px hsl(0 0% 0% / 0.18);
- --mode-dot-standard: hsl(220, 14%, 71%);
- --mode-dot-academic: hsl(207, 82%, 66%);
- --mode-dot-creative: hsl(355, 65%, 65%);
+ --mode-dot-standard: hsl(145, 13%, 69%);
+ --mode-dot-academic: hsl(156, 18%, 69%);
+ --mode-dot-creative: hsl(130, 12%, 69%);
/* CodeMirror 语法高亮 token(UI-SPEC §Color A,取值同源 Atom one-dark)。 */
--cm-keyword: hsl(286, 60%, 67%);
--cm-string: hsl(95, 38%, 62%);
--cm-comment: hsl(220, 10%, 40%);
--cm-number: hsl(29, 54%, 61%);
- --cm-function: hsl(207, 82%, 66%);
+ --cm-function: hsl(156, 18%, 69%);
--cm-type: hsl(39, 67%, 69%);
- --cm-variable: hsl(355, 65%, 65%);
- --cm-operator: hsl(220, 14%, 71%);
+ --cm-variable: hsl(130, 12%, 69%);
+ --cm-operator: hsl(145, 13%, 69%);
/* R5-typography D-2:标题去红改正文色;对 background-primary(18% L) 对比 ≈ 6.55:1,超 4.5。 */
--cm-heading: var(--text-normal);
--cm-emphasis: hsl(286, 60%, 67%);
@@ -143,7 +145,7 @@ html[data-theme='dark'] {
/* R5-typography §3.2:暗态链接微提到 70% L 保暗背景对比 ≈ 6.63:1。 */
--cm-link: hsl(207, 82%, 70%);
--cm-meta: hsl(220, 9%, 55%);
- --cm-invalid: hsl(355, 65%, 65%);
+ --cm-invalid: hsl(130, 12%, 69%);
/* Live Preview 装饰专用 token(Phase 3,UI-SPEC §Color 新增表,dark 镜像同源 Atom one-dark)。 */
/* 光标行标记淡显不透明度(UAT #4):dark 下略提一档保暗背景上仍可辨。 */
@@ -163,12 +165,12 @@ html[data-theme='dark'] {
/* ── 层 2 mode:6 组合只覆写 --accent-hsl(3 模式 x 2 亮暗,D-14 一次定型) ── */
-html[data-mode='standard'][data-theme='light'] { --accent-hsl: 220 10% 50%; }
-html[data-mode='standard'][data-theme='dark'] { --accent-hsl: 220 14% 71%; }
-html[data-mode='academic'][data-theme='light'] { --accent-hsl: 220 70% 45%; }
-html[data-mode='academic'][data-theme='dark'] { --accent-hsl: 207 82% 66%; }
-html[data-mode='creative'][data-theme='light'] { --accent-hsl: 355 65% 50%; }
-html[data-mode='creative'][data-theme='dark'] { --accent-hsl: 355 65% 65%; }
+html[data-mode='standard'][data-theme='light'] { --accent-hsl: 145 12% 38%; }
+html[data-mode='standard'][data-theme='dark'] { --accent-hsl: 145 13% 69%; }
+html[data-mode='academic'][data-theme='light'] { --accent-hsl: 156 17% 35%; }
+html[data-mode='academic'][data-theme='dark'] { --accent-hsl: 156 18% 69%; }
+html[data-mode='creative'][data-theme='light'] { --accent-hsl: 130 10% 38%; }
+html[data-mode='creative'][data-theme='dark'] { --accent-hsl: 130 12% 69%; }
/* ── 层 3 派生 + 动效 token(JS 永不注入色值,只切 data 属性) ──────────── */
diff --git a/src/styles/theme.test.ts b/src/styles/theme.test.ts
index 35ec24e..2c3c826 100644
--- a/src/styles/theme.test.ts
+++ b/src/styles/theme.test.ts
@@ -79,9 +79,9 @@ const NEUTRAL_VARS = [
];
const ACCENTS: Record = {
- standard: { light: '220 10% 50%', dark: '220 14% 71%' },
- academic: { light: '220 70% 45%', dark: '207 82% 66%' },
- creative: { light: '355 65% 50%', dark: '355 65% 65%' },
+ standard: { light: '145 12% 38%', dark: '145 13% 69%' },
+ academic: { light: '156 17% 35%', dark: '156 18% 69%' },
+ creative: { light: '130 10% 38%', dark: '130 12% 69%' },
};
describe('theme.css 变量架构(D-14 立约)', () => {
diff --git a/src/types/workbench.ts b/src/types/workbench.ts
index e5d939a..5c16ffe 100644
--- a/src/types/workbench.ts
+++ b/src/types/workbench.ts
@@ -29,10 +29,10 @@ export interface ModePreset {
label: string;
}
-/** 默认布局(UI-SPEC Layout Contract:Sidebar 280 / RightPanel 320,均展开)。 */
+/** 原型日常写作比例;仅新会话和显式重置采用,已有项目保留记忆宽度。 */
export const DEFAULT_LAYOUT: ModeLayout = {
- sidebarWidth: 280,
- rightPanelWidth: 320,
+ sidebarWidth: 220,
+ rightPanelWidth: 260,
sidebarCollapsed: false,
rightPanelCollapsed: false,
};