@waterwx/dsh-novel-forge 2.1.2-alpha → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +278 -368
  2. package/lib/client.js +19568 -21381
  3. package/lib/index.js +5158 -3958
  4. package/lib/types/action-guard.d.ts +61 -0
  5. package/lib/types/ai-scan.d.ts +80 -12
  6. package/lib/types/assets.d.ts +7 -9
  7. package/lib/types/assistant.d.ts +25 -0
  8. package/lib/types/author-assets.d.ts +1 -3
  9. package/lib/types/bookshelf.d.ts +13 -3
  10. package/lib/types/client/api.d.ts +49 -29
  11. package/lib/types/client/chip-prefs.d.ts +36 -0
  12. package/lib/types/client/docx.d.ts +2 -2
  13. package/lib/types/client/llmLive.d.ts +12 -14
  14. package/lib/types/client/locales.d.ts +12 -67
  15. package/lib/types/client/mount.d.ts +0 -2
  16. package/lib/types/client/panel/AdaptModeView.d.ts +1 -1
  17. package/lib/types/client/panel/AiTag.d.ts +1 -0
  18. package/lib/types/client/panel/AssetsTab.d.ts +1 -1
  19. package/lib/types/client/panel/AssistantTab.d.ts +4 -1
  20. package/lib/types/client/panel/AuthorAssetsView.d.ts +1 -1
  21. package/lib/types/client/panel/AuthorHome.d.ts +12 -12
  22. package/lib/types/client/panel/CmdPalette.d.ts +12 -0
  23. package/lib/types/client/panel/CreateBookView.d.ts +1 -1
  24. package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +1 -1
  25. package/lib/types/client/panel/ImportModal.d.ts +1 -1
  26. package/lib/types/client/panel/LiveFeedLog.d.ts +8 -0
  27. package/lib/types/client/panel/ModelManager.d.ts +2 -2
  28. package/lib/types/client/panel/NovelPanel.d.ts +7 -2
  29. package/lib/types/client/panel/PageHeader.d.ts +18 -0
  30. package/lib/types/client/panel/ProgressConsole.d.ts +1 -1
  31. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  32. package/lib/types/client/panel/ReaderView.d.ts +1 -1
  33. package/lib/types/client/panel/RunPanel.d.ts +1 -1
  34. package/lib/types/client/panel/SettingsView.d.ts +13 -10
  35. package/lib/types/client/panel/ShelfView.d.ts +11 -2
  36. package/lib/types/client/panel/SubPage.d.ts +27 -0
  37. package/lib/types/client/panel/TensionView.d.ts +18 -0
  38. package/lib/types/client/panel/TimelineView.d.ts +18 -0
  39. package/lib/types/client/panel/WorldTab.d.ts +1 -1
  40. package/lib/types/client/panel/helpers.d.ts +5 -3
  41. package/lib/types/client/panel/market-radar-state.d.ts +56 -0
  42. package/lib/types/client/panel/outline-markdown.d.ts +112 -0
  43. package/lib/types/client/panel/status-badge.d.ts +13 -0
  44. package/lib/types/client/panel/storage-parse.d.ts +46 -0
  45. package/lib/types/client/panel/toast.d.ts +2 -0
  46. package/lib/types/client/panel/usePersistentState.d.ts +17 -0
  47. package/lib/types/client/panel/views.d.ts +21 -35
  48. package/lib/types/client/run-status.d.ts +21 -0
  49. package/lib/types/client/sidebar-entry.d.ts +0 -2
  50. package/lib/types/digest-parse.d.ts +29 -0
  51. package/lib/types/docx-core.d.ts +16 -0
  52. package/lib/types/docx.d.ts +2 -3
  53. package/lib/types/engine.d.ts +122 -103
  54. package/lib/types/home.d.ts +11 -0
  55. package/lib/types/index.d.ts +21 -13
  56. package/lib/types/llm-live.d.ts +62 -36
  57. package/lib/types/llm-retry.d.ts +38 -0
  58. package/lib/types/market-radar-scan.d.ts +0 -1
  59. package/lib/types/market-radar-sources.d.ts +1 -1
  60. package/lib/types/migrate-output.d.ts +25 -0
  61. package/lib/types/model-capability.d.ts +119 -0
  62. package/lib/types/model-request.d.ts +36 -0
  63. package/lib/types/output-budget.d.ts +86 -0
  64. package/lib/types/plotline-align.d.ts +35 -0
  65. package/lib/types/prompt-slots.d.ts +57 -0
  66. package/lib/types/prompting.d.ts +2 -27
  67. package/lib/types/protocol.d.ts +479 -595
  68. package/lib/types/revision.d.ts +76 -0
  69. package/lib/types/routes.d.ts +9 -3
  70. package/lib/types/run-selection.d.ts +51 -0
  71. package/lib/types/run.d.ts +31 -0
  72. package/lib/types/settings-persist.d.ts +59 -0
  73. package/lib/types/snapshots.d.ts +46 -0
  74. package/lib/types/stage-contract.d.ts +58 -0
  75. package/lib/types/stream-fallback.d.ts +60 -0
  76. package/lib/types/strip-headings.d.ts +33 -0
  77. package/lib/types/style-fingerprint.d.ts +78 -0
  78. package/lib/types/tension.d.ts +53 -0
  79. package/lib/types/timeline.d.ts +37 -0
  80. package/package.json +41 -28
  81. package/assets/styles/arcane-thick-3d.webp +0 -0
  82. package/assets/styles/chinese-ink.webp +0 -0
  83. package/assets/styles/clay-stopmotion-3d.webp +0 -0
  84. package/assets/styles/dark-ghibli.webp +0 -0
  85. package/assets/styles/dark-gothic-3d.webp +0 -0
  86. package/assets/styles/film-photo.webp +0 -0
  87. package/assets/styles/guofeng-heavy.webp +0 -0
  88. package/assets/styles/guoman-2d-gufeng.webp +0 -0
  89. package/assets/styles/guoman-3d-tianyuan.webp +0 -0
  90. package/assets/styles/guoman-3d-xuanhuan.webp +0 -0
  91. package/assets/styles/horror-game-3d.webp +0 -0
  92. package/assets/styles/hyperreal-3d.webp +0 -0
  93. package/assets/styles/japan-cel-dark.webp +0 -0
  94. package/assets/styles/korean-webtoon.webp +0 -0
  95. package/assets/styles/low-cyberpunk-3d.webp +0 -0
  96. package/assets/styles/nextgen-cg.webp +0 -0
  97. package/assets/styles/noir-film.webp +0 -0
  98. package/assets/styles/openworld-survival.webp +0 -0
  99. package/assets/styles/paper-cut-3d.webp +0 -0
  100. package/assets/styles/pixar-adult-3d.webp +0 -0
  101. package/assets/styles/pop-marvel-2d.webp +0 -0
  102. package/assets/styles/sandiao-qban.webp +0 -0
  103. package/assets/styles/shinkai-anime.webp +0 -0
  104. package/assets/styles/tactical-stealth.webp +0 -0
  105. package/assets/styles/urban-lightreal-3d.webp +0 -0
  106. package/assets/styles/vaporwave-3d.webp +0 -0
  107. package/assets/styles/vhs90.webp +0 -0
  108. package/lib/types/client/panel/BookshelfBar.d.ts +0 -11
  109. package/lib/types/client/panel/FlowGuide.d.ts +0 -22
  110. package/lib/types/client/panel/MangaRoleLibrary.d.ts +0 -20
  111. package/lib/types/client/panel/MangaWorkspace.d.ts +0 -12
  112. package/lib/types/client/panel/ModelPicker.d.ts +0 -11
  113. package/lib/types/client/panel/ProgressHomeView.d.ts +0 -5
  114. package/lib/types/client/panel/PropLibrary.d.ts +0 -8
  115. package/lib/types/client/panel/SceneLibrary.d.ts +0 -16
  116. package/lib/types/client/panel/StoryboardTab.d.ts +0 -19
  117. package/lib/types/client/panel/StyleCard.d.ts +0 -8
  118. package/lib/types/client/panel/StyleGallery.d.ts +0 -1
  119. package/lib/types/comic-presets.d.ts +0 -12
  120. package/lib/types/manga-genre-rules.d.ts +0 -22
  121. package/lib/types/prompt-utils.d.ts +0 -35
  122. package/lib/types/review-policy.d.ts +0 -97
  123. package/lib/types/shot-language.d.ts +0 -46
  124. package/lib/types/src/ai-scan.d.ts +0 -36
  125. package/lib/types/src/assets.d.ts +0 -37
  126. package/lib/types/src/assistant.d.ts +0 -45
  127. package/lib/types/src/author-assets.d.ts +0 -25
  128. package/lib/types/src/bookshelf.d.ts +0 -42
  129. package/lib/types/src/client/api.d.ts +0 -234
  130. package/lib/types/src/client/docx.d.ts +0 -15
  131. package/lib/types/src/client/index.d.ts +0 -14
  132. package/lib/types/src/client/locales.d.ts +0 -212
  133. package/lib/types/src/client/mount.d.ts +0 -9
  134. package/lib/types/src/client/panel/AdaptModeView.d.ts +0 -5
  135. package/lib/types/src/client/panel/AssetsTab.d.ts +0 -12
  136. package/lib/types/src/client/panel/AssistantTab.d.ts +0 -7
  137. package/lib/types/src/client/panel/AuthorAssetsView.d.ts +0 -4
  138. package/lib/types/src/client/panel/AuthorHome.d.ts +0 -32
  139. package/lib/types/src/client/panel/BookshelfBar.d.ts +0 -11
  140. package/lib/types/src/client/panel/CreateBookView.d.ts +0 -9
  141. package/lib/types/src/client/panel/FlowGuide.d.ts +0 -22
  142. package/lib/types/src/client/panel/GlobalAssetLibraryView.d.ts +0 -4
  143. package/lib/types/src/client/panel/ImportModal.d.ts +0 -7
  144. package/lib/types/src/client/panel/MangaRoleLibrary.d.ts +0 -20
  145. package/lib/types/src/client/panel/MangaWorkspace.d.ts +0 -12
  146. package/lib/types/src/client/panel/ModelManager.d.ts +0 -14
  147. package/lib/types/src/client/panel/NovelPanel.d.ts +0 -13
  148. package/lib/types/src/client/panel/ProgressConsole.d.ts +0 -17
  149. package/lib/types/src/client/panel/ProgressHomeView.d.ts +0 -5
  150. package/lib/types/src/client/panel/PropLibrary.d.ts +0 -8
  151. package/lib/types/src/client/panel/ReaderView.d.ts +0 -8
  152. package/lib/types/src/client/panel/ReasoningSection.d.ts +0 -10
  153. package/lib/types/src/client/panel/RunPanel.d.ts +0 -5
  154. package/lib/types/src/client/panel/SceneLibrary.d.ts +0 -16
  155. package/lib/types/src/client/panel/SettingsView.d.ts +0 -16
  156. package/lib/types/src/client/panel/ShelfView.d.ts +0 -15
  157. package/lib/types/src/client/panel/StoryboardTab.d.ts +0 -19
  158. package/lib/types/src/client/panel/StyleCard.d.ts +0 -8
  159. package/lib/types/src/client/panel/StyleGallery.d.ts +0 -1
  160. package/lib/types/src/client/panel/WorldTab.d.ts +0 -10
  161. package/lib/types/src/client/panel/controller.d.ts +0 -19
  162. package/lib/types/src/client/panel/helpers.d.ts +0 -18
  163. package/lib/types/src/client/panel/views.d.ts +0 -77
  164. package/lib/types/src/client/sidebar-entry.d.ts +0 -13
  165. package/lib/types/src/client/text.d.ts +0 -9
  166. package/lib/types/src/comic-presets.d.ts +0 -12
  167. package/lib/types/src/docx.d.ts +0 -19
  168. package/lib/types/src/engine.d.ts +0 -402
  169. package/lib/types/src/index.d.ts +0 -79
  170. package/lib/types/src/manga-genre-rules.d.ts +0 -22
  171. package/lib/types/src/novel-context.d.ts +0 -80
  172. package/lib/types/src/prompt-utils.d.ts +0 -35
  173. package/lib/types/src/protocol.d.ts +0 -2176
  174. package/lib/types/src/review-policy.d.ts +0 -97
  175. package/lib/types/src/routes.d.ts +0 -29
  176. package/lib/types/src/run.d.ts +0 -32
  177. package/lib/types/src/shot-language.d.ts +0 -46
  178. package/lib/types/src/story-beat-language.d.ts +0 -35
  179. package/lib/types/src/style-library.d.ts +0 -33
  180. package/lib/types/story-beat-language.d.ts +0 -35
  181. package/lib/types/style-library.d.ts +0 -33
  182. package/src/ai-scan.ts +0 -179
  183. package/src/assets.ts +0 -1774
  184. package/src/assistant.ts +0 -915
  185. package/src/author-assets.ts +0 -147
  186. package/src/bookshelf.ts +0 -167
  187. package/src/client/api.ts +0 -716
  188. package/src/client/css-modules.d.ts +0 -8
  189. package/src/client/docx.ts +0 -69
  190. package/src/client/index.ts +0 -34
  191. package/src/client/llmLive.ts +0 -101
  192. package/src/client/locales.ts +0 -418
  193. package/src/client/mount.tsx +0 -97
  194. package/src/client/mount.tsx._new +0 -97
  195. package/src/client/mount.tsx._nf +0 -97
  196. package/src/client/mount.tsx._tmp +0 -97
  197. package/src/client/panel/AdaptModeView.tsx +0 -535
  198. package/src/client/panel/AssetsTab.tsx +0 -458
  199. package/src/client/panel/AssistantTab.tsx +0 -339
  200. package/src/client/panel/AuthorAssetsView.tsx +0 -208
  201. package/src/client/panel/AuthorHome.tsx +0 -234
  202. package/src/client/panel/BookAnalysisView.tsx +0 -60
  203. package/src/client/panel/BookshelfBar.tsx +0 -170
  204. package/src/client/panel/CreateBookView.tsx +0 -371
  205. package/src/client/panel/DirectorView.tsx +0 -200
  206. package/src/client/panel/FlowGuide.tsx +0 -84
  207. package/src/client/panel/GlobalAssetLibraryView.tsx +0 -172
  208. package/src/client/panel/IdeaInspirationView.tsx +0 -71
  209. package/src/client/panel/ImportModal.tsx +0 -303
  210. package/src/client/panel/KnowledgeBaseView.tsx +0 -73
  211. package/src/client/panel/LiveFeedLog.tsx +0 -76
  212. package/src/client/panel/MangaRoleLibrary.tsx +0 -1002
  213. package/src/client/panel/MangaWorkspace.tsx +0 -715
  214. package/src/client/panel/MarketRadarView.tsx +0 -374
  215. package/src/client/panel/ModelManager.tsx +0 -205
  216. package/src/client/panel/NovelPanel.tsx +0 -4721
  217. package/src/client/panel/ProgressConsole.tsx +0 -63
  218. package/src/client/panel/ProgressHomeView.tsx +0 -87
  219. package/src/client/panel/PropLibrary.tsx +0 -175
  220. package/src/client/panel/ReaderView.tsx +0 -306
  221. package/src/client/panel/ReasoningSection.tsx +0 -31
  222. package/src/client/panel/RunPanel.tsx +0 -204
  223. package/src/client/panel/SceneLibrary.tsx +0 -453
  224. package/src/client/panel/SettingsView.tsx +0 -289
  225. package/src/client/panel/ShelfView.tsx +0 -242
  226. package/src/client/panel/StoryboardTab.tsx +0 -472
  227. package/src/client/panel/StyleCard.tsx +0 -90
  228. package/src/client/panel/StyleGallery.tsx +0 -58
  229. package/src/client/panel/WorldTab.tsx +0 -202
  230. package/src/client/panel/controller.ts +0 -45
  231. package/src/client/panel/helpers.ts +0 -67
  232. package/src/client/panel/panel.module.css +0 -4359
  233. package/src/client/panel/views.tsx +0 -314
  234. package/src/client/sidebar-entry.ts +0 -122
  235. package/src/client/text.ts +0 -28
  236. package/src/comic-presets.ts +0 -67
  237. package/src/docx.ts +0 -83
  238. package/src/engine.ts +0 -5636
  239. package/src/global-assets.ts +0 -59
  240. package/src/index.ts +0 -346
  241. package/src/llm-live.ts +0 -68
  242. package/src/manga-genre-rules.ts +0 -87
  243. package/src/market-radar-scan.ts +0 -41
  244. package/src/market-radar-sources.ts +0 -173
  245. package/src/novel-context.ts +0 -287
  246. package/src/prompt-utils.ts +0 -73
  247. package/src/prompting.ts +0 -159
  248. package/src/protocol.ts +0 -2565
  249. package/src/review-policy.ts +0 -161
  250. package/src/routes.ts +0 -3939
  251. package/src/run.ts +0 -338
  252. package/src/shot-language.ts +0 -181
  253. package/src/story-beat-language.ts +0 -130
  254. package/src/style-library.ts +0 -84
@@ -1,234 +0,0 @@
1
- /**
2
- * 作者级首页容器:书架页左侧导航 + 内容区。
3
- * 导航复用工作区 .panelNav 玻璃卡片样式(同款),导航项为作者级:书架/改编/资产库/全局资产库/AI 进度/设置。
4
- */
5
- import { useState, useRef, useEffect } from 'react'
6
- import { Library, Wand2, Boxes, Brush, Radar, FileSearch, Lightbulb, Settings } from 'lucide-react'
7
- import type { NovelApi } from '../api.ts'
8
- import type { BookshelfSnapshot } from '../../protocol.ts'
9
- import { ShelfView } from './ShelfView.tsx'
10
- import { AdaptModeView } from './AdaptModeView.tsx'
11
- import { AuthorAssetsView } from './AuthorAssetsView.tsx'
12
- import { GlobalAssetLibraryView } from './GlobalAssetLibraryView.tsx'
13
- import { ProgressConsole, type ProgressLine } from './ProgressConsole.tsx'
14
- import { SettingsView } from './SettingsView.tsx'
15
- import LiveFeedLog from './LiveFeedLog.tsx'
16
- import MarketRadarView from './MarketRadarView.tsx'
17
- import BookAnalysisView from './BookAnalysisView.tsx'
18
- import IdeaInspirationView from './IdeaInspirationView.tsx'
19
- import css from './panel.module.css'
20
-
21
- /** 构建时注入的插件版本(tsdown define 替换为字符串字面量)。 */
22
- declare const __NOVEL_FORGE_VERSION__: string | undefined
23
- const PLUGIN_VERSION: string = typeof __NOVEL_FORGE_VERSION__ !== 'undefined' ? __NOVEL_FORGE_VERSION__ : '0.0.0'
24
- /** GitHub 仓库地址(关于区块点击跳转)。 */
25
- const REPO_URL = 'https://github.com/watersxya/dsh-novel-forge'
26
-
27
- type AuthorNav = 'shelf' | 'adapt' | 'assets' | 'library' | 'marketRadar' | 'bookAnalysis' | 'ideaInspiration' | 'settings';
28
-
29
- type AuthorNavGroup = { label: string; items: Array<{ id: Exclude<AuthorNav, 'settings'>; label: string; icon: JSX.Element; hint: string }> };
30
-
31
- const NAV_GROUPS: AuthorNavGroup[] = [
32
- {
33
- label: '创作',
34
- items: [
35
- { id: 'shelf', label: '书架', icon: <Library size={18} />, hint: '我的书' },
36
- { id: 'adapt', label: '改编模式', icon: <Wand2 size={18} />, hint: '上传全文→可改范围' },
37
- ],
38
- },
39
- {
40
- label: '灵感 · 策划',
41
- items: [
42
- { id: 'marketRadar', label: '题材雷达', icon: <Radar size={18} />, hint: '热门题材雷达' },
43
- { id: 'ideaInspiration', label: '创意灵感', icon: <Lightbulb size={18} />, hint: '多方向开书灵感' },
44
- { id: 'bookAnalysis', label: '书分析', icon: <FileSearch size={18} />, hint: '拆书/卖点/可借鉴' },
45
- ],
46
- },
47
- {
48
- label: '资产',
49
- items: [
50
- { id: 'assets', label: '作者资产库', icon: <Boxes size={18} />, hint: '跨书总数据' },
51
- { id: 'library', label: '全局写作资产库', icon: <Brush size={18} />, hint: '内置题材/规则/模板' },
52
- ],
53
- },
54
- ];
55
-
56
-
57
- export function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onOpenSettings, onTheme, onBackground, onOpacity, onEndfieldAccent, adaptEnabled, progress, busy, busyLabel, liveBar, onClearProgress, onUseIdea }: {
58
- api: NovelApi; shelf: BookshelfSnapshot;
59
- onOpenBook: (id: string) => void; onReadBook: (id: string) => void; onAddBook: () => void; onImportBook: () => void;
60
- /** 创意灵感 → 采纳某个灵感带入开书向导。 */
61
- onUseIdea?: (idea: import('../../protocol.ts').IdeaInspirationResult['ideas'][number]) => void;
62
- /** 兼容旧入口:首页设置现为独立设置页,此回调保留但不再使用。 */
63
- onOpenSettings?: () => void;
64
- /** 主题/模式/密度变化回调(供面板根容器实时生效)。 */
65
- onTheme?: (theme: 'liquid' | 'neumorph' | 'macos' | 'clay' | 'endfield', mode: 'system' | 'light' | 'dark', density: 'comfort' | 'compact' | 'spacious') => void;
66
- /** 自定义背景变化回调。 */
67
- onBackground?: (bg: string | undefined, blur: number) => void;
68
- /** 玻璃透明度变化回调。 */
69
- onOpacity?: (n: number) => void;
70
- /** 终末地强调色变化回调。 */
71
- onEndfieldAccent?: (accent: 'valley' | 'wuling') => void;
72
- /** 是否启用改编模式(默认 false=隐藏入口)。 */
73
- adaptEnabled?: boolean;
74
- /** AI 进度实时状态(与书内共享同一份)。 */
75
- progress?: ProgressLine[];
76
- busy?: boolean;
77
- busyLabel?: string;
78
- liveBar?: { text: string; ratio?: number } | null;
79
- onClearProgress?: () => void;
80
- }) {
81
- const [nav, setNav] = useState<AuthorNav>('shelf');
82
- const [updating, setUpdating] = useState(false);
83
- const [progressOpen, setProgressOpen] = useState(false);
84
- const [progressPos, setProgressPos] = useState(() => {
85
- try {
86
- const raw = window.localStorage.getItem('dsh-novel-forge.home.progress.float')
87
- if (raw !== null) { const p = JSON.parse(raw) as { x?: unknown; y?: unknown }; return { x: typeof p.x === 'number' ? p.x : 60, y: typeof p.y === 'number' ? p.y : 120 } }
88
- } catch { /* ignore */ }
89
- return { x: 60, y: 120 }
90
- });
91
- const [progressSize, setProgressSize] = useState(() => {
92
- try {
93
- const raw = window.localStorage.getItem('dsh-novel-forge.home.progress.size')
94
- if (raw !== null) { const p = JSON.parse(raw) as { w?: unknown; h?: unknown }; return { w: typeof p.w === 'number' ? p.w : 460, h: typeof p.h === 'number' ? p.h : 420 } }
95
- } catch { /* ignore */ }
96
- return { w: 460, h: 420 }
97
- });
98
- const dragState = useRef<{ type: 'move' | 'resize'; startX: number; startY: number; origX: number; origY: number; origW: number; origH: number } | null>(null);
99
-
100
- useEffect(() => {
101
- try {
102
- window.localStorage.setItem('dsh-novel-forge.home.progress.float', JSON.stringify(progressPos))
103
- window.localStorage.setItem('dsh-novel-forge.home.progress.size', JSON.stringify(progressSize))
104
- } catch { /* ignore */ }
105
- }, [progressPos, progressSize]);
106
-
107
- useEffect(() => {
108
- const onMove = (e: MouseEvent): void => {
109
- const s = dragState.current
110
- if (s === null) return
111
- if (s.type === 'move') {
112
- setProgressPos({ x: Math.max(-340, Math.min(s.origX + e.clientX - s.startX, 3000)), y: Math.max(0, Math.min(s.origY + e.clientY - s.startY, 3000)) })
113
- } else {
114
- setProgressSize({ w: Math.max(320, Math.min(s.origW + e.clientX - s.startX, 1400)), h: Math.max(220, Math.min(s.origH + e.clientY - s.startY, 1200)) })
115
- }
116
- }
117
- const onUp = (): void => { dragState.current = null }
118
- window.addEventListener('mousemove', onMove)
119
- window.addEventListener('mouseup', onUp)
120
- return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp) }
121
- }, []);
122
-
123
- /** npm 最新版本(更新检测;null = 未检测/检测失败)。 */
124
- const [npmLatest, setNpmLatest] = useState<string | null>(null)
125
- useEffect(() => {
126
- let cancelled = false
127
- void (async () => {
128
- try {
129
- const r = await fetch('https://registry.npmjs.org/@waterwx%2Fdsh-novel-forge/latest')
130
- if (!r.ok) return
131
- const data = (await r.json()) as { version?: unknown }
132
- if (!cancelled && typeof data.version === 'string') setNpmLatest(data.version)
133
- } catch { /* best-effort */ }
134
- })()
135
- return () => { cancelled = true }
136
- }, [])
137
-
138
- const doUpdate = async (): Promise<void> => {
139
- setUpdating(true)
140
- try {
141
- const res = await api.pluginUpdate()
142
- window.alert(res.message)
143
- } catch (err) {
144
- window.alert('更新失败:' + ((err as Error).message ?? String(err)))
145
- } finally {
146
- setUpdating(false)
147
- }
148
- };
149
-
150
- return (
151
- <div className={css.authorHome}>
152
- <nav className={css.panelNav} aria-label="作者级导航">
153
- <div className={css.navTitle}>
154
- <div className={css.navTitleLogo}>书</div>
155
- <div style={{ minWidth: 0 }}>
156
- <div className={css.navTitleName}>小说工坊</div>
157
- <div className={css.navTitleBook}>作者级 · 跨书</div>
158
- </div>
159
- </div>
160
- {NAV_GROUPS.map(group => (
161
- <div className={css.navGroup} key={group.label}>
162
- <div className={css.navGroupLabel}>{group.label}</div>
163
- {group.items.filter(item => item.id !== 'adapt' || adaptEnabled).map(item => (
164
- <button key={item.id} type="button" role="tab" aria-selected={nav === item.id} data-active={nav === item.id ? '' : undefined} className={css.navTab} title={item.hint} onClick={() => { setNav(item.id) }}>
165
- <span className={css.navTabIcon}>{item.icon}</span>
166
- <span className={css.navTabLabel}>{item.label}</span>
167
- </button>
168
- ))}
169
- </div>
170
- ))}
171
- <div className={css.navSpacer} />
172
- <button type="button" role="tab" aria-selected={nav === 'settings'} data-active={nav === 'settings' ? '' : undefined} className={css.navTab} title="设置" onClick={() => setNav('settings')}>
173
- <span className={css.navTabIcon}><Settings size={18} /></span>
174
- <span className={css.navTabLabel}>设置</span>
175
- </button>
176
- <div className={css.navAbout}>
177
- <button type="button" className={css.navAboutRow} title="打开 GitHub 仓库" onClick={() => { window.open(REPO_URL, '_blank', 'noopener') }}>
178
- <span>ℹ️ v{PLUGIN_VERSION}</span>
179
- <span className={css.meta}>GitHub ↗</span>
180
- </button>
181
- {npmLatest !== null && npmLatest !== PLUGIN_VERSION && (
182
- <button type="button" className={css.navAboutUpdate} title="点击自动更新(下载最新 npm 版,完成后请重启 DSH)" disabled={updating} onClick={() => { void doUpdate() }}>
183
- {updating ? '🔄 更新中…' : `🔄 更新到 v${npmLatest}`}
184
- </button>
185
- )}
186
- </div>
187
- </nav>
188
- <div className={css.authorContent}>
189
- {nav === 'shelf' && <ShelfView api={api} shelf={shelf} onOpenBook={onOpenBook} onReadBook={onReadBook} onAddBook={onAddBook} onImportBook={onImportBook} />}
190
- {nav === 'adapt' && <AdaptModeView api={api} onOpenBook={onOpenBook} />}
191
- {nav === 'assets' && <AuthorAssetsView api={api} />}
192
- {nav === 'library' && <GlobalAssetLibraryView api={api} />}
193
- {nav === 'marketRadar' && <MarketRadarView api={api} bookId={shelf?.activeBookId ?? undefined} />}
194
- {nav === 'bookAnalysis' && <BookAnalysisView api={api} />}
195
- {nav === 'ideaInspiration' && <IdeaInspirationView api={api} onUseIdea={onUseIdea} />}
196
- {nav === 'settings' && <SettingsView api={api} onTheme={onTheme} onBackground={onBackground} onOpacity={onOpacity} onEndfieldAccent={onEndfieldAccent} />}
197
- </div>
198
- {progressOpen && (
199
- <div className={css.assistantFloat} style={{ left: progressPos.x, top: progressPos.y, width: progressSize.w, height: progressSize.h }}>
200
- <div
201
- className={css.assistantFloatHeader}
202
- onMouseDown={e => {
203
- e.preventDefault()
204
- dragState.current = { type: 'move', startX: e.clientX, startY: e.clientY, origX: progressPos.x, origY: progressPos.y, origW: progressSize.w, origH: progressSize.h }
205
- }}
206
- >
207
- <span>
208
- 📊 AI 进度
209
- {busy && <span style={{ color: 'var(--nf-accent)' }}> 🟢 任务进行中</span>}
210
- <span className={css.meta}>(拖动标题栏移动 · 右下角拉大小)</span>
211
- </span>
212
- <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--nf-space-4)' }}>
213
- {(progress ?? []).length > 0 && <button type="button" className={`${css.button} ${css.buttonSmall}`} onClick={onClearProgress} title="清空活动记录">清空</button>}
214
- <button type="button" className={css.iconButton} title="关闭" aria-label="关闭AI进度" onClick={() => setProgressOpen(false)}>×</button>
215
- </span>
216
- </div>
217
- <div className={css.assistantFloatBody} style={{ padding: 'var(--nf-space-10)', display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-8)', overflow: 'hidden', flex: 1, minHeight: 0 }}>
218
- <div style={{ flex: '0 2 auto', minHeight: 0, maxHeight: '42%', display: 'flex', flexDirection: 'column' }}>
219
- <ProgressConsole progress={progress ?? []} busy={busy ?? false} busyLabel={busyLabel ?? ''} liveBar={liveBar ?? null} onClear={() => onClearProgress?.()} />
220
- </div>
221
- <LiveFeedLog />
222
- </div>
223
- <div
224
- style={{ width: 14, height: 14, position: 'absolute', right: 0, bottom: 0, cursor: 'nwse-resize' }}
225
- onMouseDown={e => {
226
- e.preventDefault()
227
- dragState.current = { type: 'resize', startX: e.clientX, startY: e.clientY, origX: progressPos.x, origY: progressPos.y, origW: progressSize.w, origH: progressSize.h }
228
- }}
229
- />
230
- </div>
231
- )}
232
- </div>
233
- );
234
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * 书分析/拆书:输入一本书/章节文本 → 卖点/结构/可借鉴点/风险。
3
- */
4
- import { useState } from 'react'
5
- import type { NovelApi } from '../api.ts'
6
- import type { BookAnalysisResult } from '../../protocol.ts'
7
- import css from './panel.module.css'
8
-
9
- const SECTIONS: Array<{ key: keyof BookAnalysisResult; label: string; color: string }> = [
10
- { key: 'sellingPoints', label: '卖点', color: 'var(--nf-success)' },
11
- { key: 'structure', label: '结构', color: 'var(--nf-info)' },
12
- { key: 'lessons', label: '可借鉴', color: 'var(--nf-accent)' },
13
- { key: 'risks', label: '风险', color: 'var(--nf-error)' },
14
- ]
15
-
16
- export default function BookAnalysisView({ api }: { api: NovelApi }): JSX.Element {
17
- const [text, setText] = useState('')
18
- const [busy, setBusy] = useState(false)
19
- const [error, setError] = useState('')
20
- const [result, setResult] = useState<BookAnalysisResult | null>(null)
21
-
22
- const run = async (): Promise<void> => {
23
- if (text.trim().length < 50) { setError('文本过短(<50 字符),请粘贴一段书/章节内容'); return }
24
- setBusy(true); setError('')
25
- try {
26
- const r = await api.bookAnalysis(text.trim())
27
- setResult(r.result)
28
- } catch (e) { setError((e as Error).message) } finally { setBusy(false) }
29
- }
30
-
31
- return (
32
- <div className={css.authorPageBody}>
33
- <div className={`${css.card} ${css.settingsCard}`} style={{ gap: 'var(--nf-space-10)' }}>
34
- <span className={css.cardTitle}>🔍 书分析 / 拆书</span>
35
- <span className={css.meta}>粘贴一本书/章节文本 → 提炼卖点、结构、可借鉴点与风险(不照搬具体作品)。</span>
36
- <textarea className={css.input} style={{ minHeight: 180, resize: 'vertical' }} value={text} onChange={e => setText(e.target.value)} placeholder="粘贴要分析的书/章节正文…" />
37
- <div>
38
- <button type="button" className={`${css.button} ${css.buttonPrimary}`} disabled={busy} onClick={() => { void run() }}>
39
- {busy ? '分析中…' : '开始分析'}
40
- </button>
41
- </div>
42
- </div>
43
-
44
- {error !== '' && <div style={{ color: 'var(--nf-error)', fontSize: 13 }}>⚠ {error}</div>}
45
-
46
- {result !== null && (
47
- <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 'var(--nf-space-10)' }}>
48
- {SECTIONS.map(s => (
49
- <div key={s.key} className={`${css.card} ${css.settingsCard}`} style={{ gap: 'var(--nf-space-8)' }}>
50
- <div style={{ fontSize: 13, fontWeight: 600, color: s.color }}>{s.label}({(result[s.key] ?? []).length})</div>
51
- <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
52
- {(result[s.key] ?? []).length === 0 ? <span style={{ color: 'var(--nf-text-2)', fontSize: 12 }}>无</span> : (result[s.key] ?? []).map((x, i) => <div key={i} style={{ fontSize: 12, borderLeft: '3px solid ' + s.color, paddingLeft: 6 }}>{x}</div>)}
53
- </div>
54
- </div>
55
- ))}
56
- </div>
57
- )}
58
- </div>
59
- )
60
- }
@@ -1,170 +0,0 @@
1
- /**
2
- * 书架条:显示所有书,点击切换当前书(继续编译),+ 新建。
3
- * 新建 = 开书向导:书名 + 大纲(拖拽/选择 docx 或粘贴文本),创建即建项目。
4
- */
5
- import { useRef, useState } from 'react'
6
- import type { NovelApi } from '../api.ts'
7
- import { extractDocxTextFromBuffer } from '../docx.ts'
8
- import type { BookshelfSnapshot } from '../../protocol.ts'
9
- import css from './panel.module.css'
10
-
11
- /** Props. */
12
- export interface BookshelfBarProps {
13
- api: NovelApi
14
- shelf: BookshelfSnapshot
15
- /** 刷新整个面板(切换书后重新拉状态)。 */
16
- onSwitch: () => void
17
- }
18
-
19
- /** 书架条。 */
20
- export function BookshelfBar({ api, shelf, onSwitch }: BookshelfBarProps) {
21
- const [creating, setCreating] = useState(false)
22
- const [name, setName] = useState('')
23
- const [outlineText, setOutlineText] = useState('')
24
- const [outlineName, setOutlineName] = useState('')
25
- const [busy, setBusy] = useState(false)
26
- const [error, setError] = useState('')
27
- const outlineFileRef = useRef<HTMLInputElement | null>(null)
28
-
29
- const handlePickOutlineFile = async (file: File | undefined): Promise<void> => {
30
- if (file === undefined) return
31
- try {
32
- const buffer = await file.arrayBuffer()
33
- const text = extractDocxTextFromBuffer(buffer)
34
- if (text.length < 50) {
35
- setError('大纲内容过短(<50 字符),请检查文件')
36
- return
37
- }
38
- setOutlineText(text)
39
- setOutlineName(file.name)
40
- setError('')
41
- } catch (err) {
42
- setError(`读取大纲失败:${(err as Error).message}`)
43
- }
44
- }
45
-
46
- const handleCreate = async (): Promise<void> => {
47
- const bookName = name.trim()
48
- if (bookName === '') return
49
- setBusy(true)
50
- setError('')
51
- try {
52
- await api.bookCreate(bookName, undefined, outlineText.trim() !== '' ? outlineText.trim() : undefined)
53
- setCreating(false)
54
- setName('')
55
- setOutlineText('')
56
- setOutlineName('')
57
- onSwitch()
58
- } catch (err) {
59
- setError((err as Error).message)
60
- } finally {
61
- setBusy(false)
62
- }
63
- }
64
-
65
- const handleActivate = async (id: string): Promise<void> => {
66
- if (id === shelf.activeBookId) return
67
- setBusy(true)
68
- try {
69
- await api.bookActivate(id)
70
- onSwitch()
71
- } catch (err) {
72
- setError((err as Error).message)
73
- } finally {
74
- setBusy(false)
75
- }
76
- }
77
-
78
- const handleRemove = async (id: string): Promise<void> => {
79
- setBusy(true)
80
- try {
81
- await api.bookRemove(id)
82
- onSwitch()
83
- } catch (err) {
84
- setError((err as Error).message)
85
- } finally {
86
- setBusy(false)
87
- }
88
- }
89
-
90
- return (
91
- <div className={css.bookshelf}>
92
- <span className={css.bookshelfLabel}>书架</span>
93
- <div className={css.bookshelfList}>
94
- {shelf.books.map(book => (
95
- <div
96
- key={book.id}
97
- className={`${css.bookChip} ${book.id === shelf.activeBookId ? css.bookChipActive : ''}`}
98
- onClick={() => { void handleActivate(book.id) }}
99
- title={book.outputDir}
100
- >
101
- <span className={css.bookChipName}>{book.bookName}</span>
102
- <span className={css.bookChipMeta}>{book.hasProject ? `${book.done}/${book.total} 章` : '未开书'}</span>
103
- <button
104
- type="button"
105
- className={css.bookChipRemove}
106
- title="从书架移除"
107
- onClick={(e) => { e.stopPropagation(); void handleRemove(book.id) }}
108
- >
109
- ×
110
- </button>
111
- </div>
112
- ))}
113
- {!creating ? (
114
- <button type="button" className={css.bookAdd} onClick={() => { setCreating(true) }}>
115
- + 新书
116
- </button>
117
- ) : (
118
- <div className={css.bookCreateForm}>
119
- <input
120
- className={css.input}
121
- style={{ width: 140 }}
122
- placeholder="书名"
123
- value={name}
124
- onChange={e => { setName(e.target.value) }}
125
- onKeyDown={e => { if (e.key === 'Enter') void handleCreate() }}
126
- autoFocus
127
- />
128
- {/* 开书向导:大纲(可选,推荐) */}
129
- <div className={css.bookCreateOutline}>
130
- <button
131
- type="button"
132
- className={`${css.button} ${css.buttonSmall}`}
133
- onClick={() => { outlineFileRef.current?.click() }}
134
- >
135
- {outlineName !== '' ? `✓ ${outlineName}` : '📄 选择大纲 docx'}
136
- </button>
137
- <input
138
- ref={outlineFileRef}
139
- type="file"
140
- accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
141
- style={{ display: 'none' }}
142
- onChange={e => {
143
- void handlePickOutlineFile(e.target.files?.[0])
144
- e.target.value = ''
145
- }}
146
- />
147
- <textarea
148
- className={css.input}
149
- style={{ width: 280, minHeight: 64, resize: 'vertical' }}
150
- placeholder="或直接粘贴大纲文本(提供大纲后开书即建立项目,书名自动从大纲首行识别)"
151
- value={outlineText}
152
- onChange={e => { setOutlineText(e.target.value) }}
153
- />
154
- </div>
155
- <div className={css.row}>
156
- <button type="button" className={`${css.button} ${css.buttonSmall} ${css.buttonPrimary}`} disabled={busy || name.trim() === ''} onClick={() => { void handleCreate() }}>
157
- 开书
158
- </button>
159
- <button type="button" className={`${css.button} ${css.buttonSmall}`} onClick={() => { setCreating(false) }}>
160
- 取消
161
- </button>
162
- <span className={css.meta}>{outlineText.length > 0 ? `大纲 ${outlineText.length} 字` : '未提供大纲(稍后在大纲页导入)'}</span>
163
- </div>
164
- </div>
165
- )}
166
- </div>
167
- {error !== '' && <span style={{ color: 'var(--nf-error)', fontSize: 'var(--nf-fs-12)' }}>{error}</span>}
168
- </div>
169
- )
170
- }