From 011bd6d4ca2548540570d33d51171e9f2cb393b0 Mon Sep 17 00:00:00 2001 From: kun1h Date: Mon, 6 Jul 2026 10:39:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E7=AC=94=E4=B8=B4=20?= =?UTF-8?q?P0/P1=20Electron=20=E6=A1=8C=E9=9D=A2=E5=BA=94=E7=94=A8=20v0.1.?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 交付写作核心(TipTap、自动保存、分卷分章)、Onboarding、7 主题与双语 i18n、快捷键设置;主进程使用 node:sqlite;补全 Vitest 与 Playwright E2E;统一 design/ 目录并移除 desigin 拼写错误路径。 Co-authored-by: Cursor --- .npmrc | 3 + README.md | 26 + {desigin => design}/readme.md | 10 +- {desigin => design}/ui_pc.html | 730 +- .../plans/2026-07-05-bilin-p0-p1.md | 1332 +++ e2e/global-setup.ts | 10 + e2e/onboarding.spec.ts | 79 + e2e/settings.spec.ts | 87 + e2e/shortcuts.spec.ts | 82 + e2e/writing.spec.ts | 75 + electron.vite.config.ts | 46 + package-lock.json | 8447 +++++++++++++++++ package.json | 54 + playwright.config.ts | 11 + public/locales/en/translation.json | 87 + public/locales/zh-CN/translation.json | 87 + src/main/db/connection.ts | 25 + src/main/db/migrate.ts | 22 + src/main/db/repositories/chapter.repo.ts | 91 + src/main/db/repositories/volume.repo.ts | 52 + src/main/db/schema-v1.sql | 28 + src/main/db/types.ts | 3 + src/main/index.ts | 79 + src/main/ipc/handlers/book.handler.ts | 39 + src/main/ipc/handlers/chapter.handler.ts | 96 + src/main/ipc/handlers/settings.handler.ts | 11 + src/main/ipc/handlers/shortcut.handler.ts | 18 + src/main/ipc/register.ts | 30 + src/main/ipc/result.ts | 18 + src/main/services/book-registry.ts | 136 + src/main/services/global-settings.ts | 50 + src/main/services/word-count.ts | 6 + src/main/shortcuts/manager.ts | 64 + src/main/vite-env.d.ts | 4 + src/preload/index.ts | 78 + src/renderer/App.tsx | 107 + src/renderer/atoms/editorAtoms.ts | 6 + .../components/editor/TipTapEditor.tsx | 146 + src/renderer/components/home/HomePage.tsx | 138 + .../components/layout/EditorLayout.tsx | 149 + src/renderer/components/layout/RightPanel.tsx | 40 + src/renderer/components/layout/TopBar.tsx | 77 + .../onboarding/OnboardingWizard.tsx | 90 + .../components/settings/SettingsPage.tsx | 103 + .../components/settings/ShortcutEditor.tsx | 133 + src/renderer/i18n/index.ts | 16 + src/renderer/index.html | 12 + src/renderer/lib/ipc-client.ts | 17 + src/renderer/lib/theme.ts | 9 + src/renderer/main.tsx | 9 + src/renderer/stores/useAppStore.ts | 30 + src/renderer/stores/useBookStore.ts | 66 + src/renderer/stores/useSettingsStore.ts | 33 + src/renderer/stores/useTabStore.ts | 58 + src/renderer/styles/globals.css | 64 + src/renderer/styles/layout.css | 515 + src/renderer/styles/themes.css | 119 + src/renderer/vite-env.d.ts | 1 + src/shared/default-shortcuts.ts | 15 + src/shared/electron-api.d.ts | 61 + src/shared/ipc-channels.ts | 22 + src/shared/types.ts | 102 + tests/main/book-registry.test.ts | 39 + tests/main/chapter.repo.test.ts | 28 + tests/main/global-settings.test.ts | 32 + tests/main/word-count.test.ts | 12 + tsconfig.json | 7 + tsconfig.node.json | 21 + tsconfig.web.json | 22 + vitest.config.ts | 14 + 70 files changed, 14245 insertions(+), 84 deletions(-) create mode 100644 .npmrc create mode 100644 README.md rename {desigin => design}/readme.md (98%) rename {desigin => design}/ui_pc.html (82%) create mode 100644 docs/superpowers/plans/2026-07-05-bilin-p0-p1.md create mode 100644 e2e/global-setup.ts create mode 100644 e2e/onboarding.spec.ts create mode 100644 e2e/settings.spec.ts create mode 100644 e2e/shortcuts.spec.ts create mode 100644 e2e/writing.spec.ts create mode 100644 electron.vite.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 public/locales/en/translation.json create mode 100644 public/locales/zh-CN/translation.json create mode 100644 src/main/db/connection.ts create mode 100644 src/main/db/migrate.ts create mode 100644 src/main/db/repositories/chapter.repo.ts create mode 100644 src/main/db/repositories/volume.repo.ts create mode 100644 src/main/db/schema-v1.sql create mode 100644 src/main/db/types.ts create mode 100644 src/main/index.ts create mode 100644 src/main/ipc/handlers/book.handler.ts create mode 100644 src/main/ipc/handlers/chapter.handler.ts create mode 100644 src/main/ipc/handlers/settings.handler.ts create mode 100644 src/main/ipc/handlers/shortcut.handler.ts create mode 100644 src/main/ipc/register.ts create mode 100644 src/main/ipc/result.ts create mode 100644 src/main/services/book-registry.ts create mode 100644 src/main/services/global-settings.ts create mode 100644 src/main/services/word-count.ts create mode 100644 src/main/shortcuts/manager.ts create mode 100644 src/main/vite-env.d.ts create mode 100644 src/preload/index.ts create mode 100644 src/renderer/App.tsx create mode 100644 src/renderer/atoms/editorAtoms.ts create mode 100644 src/renderer/components/editor/TipTapEditor.tsx create mode 100644 src/renderer/components/home/HomePage.tsx create mode 100644 src/renderer/components/layout/EditorLayout.tsx create mode 100644 src/renderer/components/layout/RightPanel.tsx create mode 100644 src/renderer/components/layout/TopBar.tsx create mode 100644 src/renderer/components/onboarding/OnboardingWizard.tsx create mode 100644 src/renderer/components/settings/SettingsPage.tsx create mode 100644 src/renderer/components/settings/ShortcutEditor.tsx create mode 100644 src/renderer/i18n/index.ts create mode 100644 src/renderer/index.html create mode 100644 src/renderer/lib/ipc-client.ts create mode 100644 src/renderer/lib/theme.ts create mode 100644 src/renderer/main.tsx create mode 100644 src/renderer/stores/useAppStore.ts create mode 100644 src/renderer/stores/useBookStore.ts create mode 100644 src/renderer/stores/useSettingsStore.ts create mode 100644 src/renderer/stores/useTabStore.ts create mode 100644 src/renderer/styles/globals.css create mode 100644 src/renderer/styles/layout.css create mode 100644 src/renderer/styles/themes.css create mode 100644 src/renderer/vite-env.d.ts create mode 100644 src/shared/default-shortcuts.ts create mode 100644 src/shared/electron-api.d.ts create mode 100644 src/shared/ipc-channels.ts create mode 100644 src/shared/types.ts create mode 100644 tests/main/book-registry.test.ts create mode 100644 tests/main/chapter.repo.test.ts create mode 100644 tests/main/global-settings.test.ts create mode 100644 tests/main/word-count.test.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 tsconfig.web.json create mode 100644 vitest.config.ts diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..9291011 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmmirror.com +electron_mirror=https://npmmirror.com/mirrors/electron/ +electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2779fc --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# 笔临 (Bilin) + +长篇创作智能协作平台 — Electron 桌面客户端 v0.1.0(P0/P1) + +## 开发 + +```bash +npm install --legacy-peer-deps +npm run dev +``` + +数据库使用 Node.js 内置 `node:sqlite`(无需原生模块编译)。 + +## 测试 + +```bash +npm run test # 单元测试 6/6 +npm run test:e2e # E2E 测试(需先 build) +npm run build +``` + +## 文档 + +- 设计规格:`docs/superpowers/specs/2026-07-05-bilin-p0-p1-design.md` +- 实现计划:`docs/superpowers/plans/2026-07-05-bilin-p0-p1.md` +- UI 原型:`design/ui_pc.html` diff --git a/desigin/readme.md b/design/readme.md similarity index 98% rename from desigin/readme.md rename to design/readme.md index d1dd7bb..ab6abd2 100644 --- a/desigin/readme.md +++ b/design/readme.md @@ -315,7 +315,10 @@ #### 5.2.1 章节列表 -- **树状展示**:卷→章,可折叠;支持拖拽排序(同卷及跨卷移动)。拖拽移动后若章节绑定大纲,询问是否更新大纲关联。 +- **树状展示**:卷→章,可折叠;点击卷标题▶箭头可单独折叠/展开;支持拖拽排序(同卷及跨卷移动)。拖拽移动后若章节绑定大纲,询问是否更新大纲关联。 +- **分卷记忆**:点击分卷标题时,自动展开该卷并选中该卷**上次打开的章节**;若该卷尚无打开记录,则选中第一章。当前分卷高亮显示。 +- **按卷新建章节**:「新章」按钮在**当前选中分卷**内创建章节并立即在编辑器中打开。 +- **双击改标题**:章节名支持双击原地编辑;修改后同步更新编辑器上方标题栏。 - **章节标记系统**:用户可创建自定义标签(如"高潮""需大修""女主主场"),并给章节打标。列表支持按标签筛选和颜色标识。 - **章节预览**:鼠标悬停显示首段内容及自定义摘要。 - **章节状态**:可手动标注"草稿/润色中/定稿"等状态,不同状态图标区分。 @@ -476,6 +479,11 @@ ### 5.5 大纲、设定与灵感管理 +- **统一编辑体验**:章节、大纲、设定、灵感条目均在中央内容编辑器中直接编辑;切换条目时自动保存当前内容。 +- **侧栏新建入口**:大纲 / 设定 / 灵感面板底部提供「新建」按钮;章节面板底部保留「新章」按钮。 +- **新建即打开**:新建章节、大纲、设定或灵感后,立即在编辑器中打开该条目,光标定位至文末,可直接输入。 +- **双击改标题**:侧栏章节名、大纲/设定/灵感标题,以及编辑器上方文档标题,均支持**双击原地变为输入框**修改(无弹窗);Enter 确认、Esc 取消、点击其他区域自动保存。 + #### 5.5.1 大纲系统 - **层级大纲编辑器**:支持无限层级的树状大纲,可拖拽排序、缩进/提升层级。 diff --git a/desigin/ui_pc.html b/design/ui_pc.html similarity index 82% rename from desigin/ui_pc.html rename to design/ui_pc.html index 6c5d6cf..f114055 100644 --- a/desigin/ui_pc.html +++ b/design/ui_pc.html @@ -505,7 +505,12 @@ } .vol-header:hover { background: var(--bg-hover); - color: #fff; + color: var(--text-primary); + } + .vol-header.active { + background: var(--bg-active); + color: var(--text-primary); + border-left: 3px solid var(--accent); } .vol-header .vol-arrow { font-size: 9px; @@ -554,6 +559,74 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + cursor: text; + } + .chapter-item .ch-title:hover, + .setting-entry .entry-name:hover, + .outline-item .item-title:hover, + .inspiration-item .item-title:hover { + color: var(--accent-light); + } + .inline-title-input { + width: 100%; + min-width: 0; + padding: 2px 6px; + border-radius: 3px; + border: 1px solid var(--accent); + background: var(--bg-surface); + color: var(--text-primary); + font-size: inherit; + font-family: inherit; + font-weight: inherit; + outline: none; + box-shadow: 0 0 0 2px var(--accent-glow); + } + #editor-toolbar .ch-label { + font-size: 11px; + color: var(--text-muted); + margin-left: 8px; + font-weight: 500; + cursor: text; + max-width: 280px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + #editor-toolbar .ch-label:hover { color: var(--text-primary); } + #editor-toolbar .inline-title-input.editor-label-input { + max-width: 240px; + font-size: 11px; + font-weight: 500; + } + .outline-item .item-prefix { + flex-shrink: 0; + margin-right: 4px; + } + .outline-item .item-title { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: text; + } + .outline-item .item-meta { + flex-shrink: 0; + font-size: 10px; + color: var(--text-muted); + margin-left: 4px; + } + .outline-item[data-status="writing"] .item-title { color: var(--accent-light); } + .outline-item[data-status="done"] .item-title { color: var(--green); } + .inspiration-item .item-title { + display: block; + cursor: text; + } + .setting-entry .entry-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: text; } .chapter-item .ch-badge { font-size: 9px; @@ -657,12 +730,6 @@ background: rgba(91, 156, 245, 0.2); color: var(--blue); } - .setting-entry .entry-name { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } .setting-entry .entry-count { font-size: 10px; color: var(--text-muted); @@ -711,12 +778,6 @@ background: var(--border); margin: 0 6px; } - #editor-toolbar .ch-label { - font-size: 11px; - color: var(--text-muted); - margin-left: 8px; - font-weight: 500; - } #editor-toolbar .word-count-display { font-size: 11px; color: var(--accent-light); @@ -1069,10 +1130,10 @@ } .settings-sidebar .settings-nav-item:hover { background: var(--bg-hover); - color: #fff; + color: var(--text-primary); } .settings-sidebar .settings-nav-item.active { - color: #fff; + color: var(--text-primary); background: var(--bg-tertiary); border-left-color: var(--accent); font-weight: 600; @@ -1091,7 +1152,7 @@ } .settings-content h2 { font-size: 20px; - color: #fff; + color: var(--text-primary); margin-bottom: 20px; font-weight: 700; } @@ -1115,6 +1176,7 @@ margin-bottom: 6px; font-size: 13px; gap: 12px; + color: var(--text-secondary); } .settings-content .setting-item .setting-label { flex: 1; @@ -1126,12 +1188,13 @@ } .settings-content input[type="text"], .settings-content input[type="number"], + .settings-content input[type="password"], .settings-content select { padding: 6px 10px; border-radius: 4px; border: 1px solid var(--border); background: var(--bg-surface); - color: #fff; + color: var(--text-primary); font-size: 12px; font-family: var(--font-sans); } @@ -1501,7 +1564,7 @@ border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--bg-tertiary); - color: #fff; + color: var(--text-primary); font-size: 13px; font-family: var(--font-sans); outline: none; @@ -1535,7 +1598,8 @@ #batchToolbar button { padding:3px 8px;border-radius:3px;border:1px solid rgba(255,255,255,0.2);background:transparent;color:#fff;cursor:pointer;font-size:10px;transition:var(--transition); } #batchToolbar button:hover { background:rgba(255,255,255,0.15); } .outline-item { display:flex;align-items:center;padding:6px 8px 6px 18px;cursor:pointer;border-radius:4px;font-size:12px;color:var(--text-secondary);border-left:3px solid transparent;margin:1px 4px;gap:6px;transition:var(--transition); } - .outline-item:hover { background:var(--bg-hover);color:#fff; } + .outline-item:hover { background:var(--bg-hover);color:var(--text-primary); } + .outline-item.active { background:var(--bg-active);color:var(--text-primary);font-weight:600; } .outline-item.uncovered { border-left-color:var(--orange); } .outline-item.uncovered::after { content:'\26A0';font-size:10px;color:var(--orange);margin-left:auto; } #editor-toolbar .tool-btn.active { color:var(--accent-light);background:var(--bg-active); } @@ -1625,6 +1689,36 @@ .vol-header:hover .vol-menu-btn { opacity:1; } .vol-header .vol-menu-btn:hover { background:var(--bg-active);color:#fff; } .setting-cat .add-btn { color:var(--accent-light);cursor:pointer;font-weight:400; } + .setting-entry.active { + background: var(--bg-active); + border-left: 3px solid var(--accent); + } + .setting-entry.active .entry-name { + color: var(--text-primary); + } + .inspiration-item { + padding: 6px 8px; + cursor: pointer; + border-radius: 4px; + font-size: 12px; + color: var(--text-secondary); + background: var(--bg-tertiary); + margin: 2px 0; + transition: var(--transition); + border-left: 3px solid transparent; + } + .inspiration-item:hover { background: var(--bg-hover); color: var(--text-primary); } + .inspiration-item.active { background: var(--bg-active); color: var(--text-primary); border-left-color: var(--accent); font-weight: 600; } + .panel-list-body { flex: 1; overflow-y: auto; min-height: 0; } + #left-sidebar .sidebar-footer button.primary-new { + flex: 1; + color: var(--accent-light); + border-color: var(--accent-dark); + } + #left-sidebar .sidebar-footer button.primary-new:hover { + background: var(--accent-glow); + color: var(--accent-light); + } .retry-btn { display:none;padding:4px 10px;border-radius:10px;border:1px solid var(--orange);background:rgba(232,144,80,0.15);color:var(--orange);cursor:pointer;font-size:10px; } .retry-btn.show { display:inline-block; } .settings-content .shortcut-key { display:inline-block;padding:2px 8px;border-radius:3px;background:var(--bg-active);font-family:var(--font-mono);font-size:11px;border:1px solid var(--border);cursor:pointer;min-width:80px;text-align:center; } @@ -1740,45 +1834,45 @@
-
-
- 第一卷·青云之始 +
+
+ 第一卷·青云之始 12章 · 3.8万字
- 第1章山村少年定稿 + 第1章山村少年定稿
- 第2章意外觉醒定稿 + 第2章意外觉醒定稿
- 第3章入门考验AI初稿 + 第3章入门考验AI初稿
- 第4章灵根测试草稿 + 第4章灵根测试草稿
- 第5章初次修炼需修 + 第5章初次修炼需修
-
-
- 第二卷·宗门风云 +
+
+ 第二卷·宗门风云 8章 · 2.4万字
@@ -1800,52 +1894,66 @@
-
大纲树(拖拽排序 · 点击关联章节)
-
⚠ 第一卷核心剧情线
-
⚠ 林远觉醒三纹灵根
-
▶ 入门考验(写作中 — 第3章)
-
✓ 拜入青云宗(第13章 · 2,100字)
-
右键大纲条目 → "生成章节骨架"
+
+
大纲树(拖拽排序 · 点击编辑)
+
第一卷核心剧情线
+
林远觉醒三纹灵根
+
入门考验(写作中 — 第3章)
+
拜入青云宗(第13章 · 2,100字)
+
+