@waterwx/dsh-novel-forge 2.1.1-alpha → 2.1.1
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.
- package/README.md +368 -472
- package/lib/client.js +19578 -21391
- package/lib/index.js +4640 -3932
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +0 -6
- package/lib/types/assets.d.ts +7 -9
- package/lib/types/assistant.d.ts +25 -0
- package/lib/types/author-assets.d.ts +1 -3
- package/lib/types/bookshelf.d.ts +13 -3
- package/lib/types/client/api.d.ts +49 -29
- package/lib/types/client/chip-prefs.d.ts +36 -0
- package/lib/types/client/docx.d.ts +2 -2
- package/lib/types/client/llmLive.d.ts +12 -14
- package/lib/types/client/locales.d.ts +12 -67
- package/lib/types/client/mount.d.ts +0 -2
- package/lib/types/client/panel/AdaptModeView.d.ts +1 -1
- package/lib/types/client/panel/AiTag.d.ts +1 -0
- package/lib/types/client/panel/AssetsTab.d.ts +1 -1
- package/lib/types/client/panel/AssistantTab.d.ts +4 -1
- package/lib/types/client/panel/AuthorAssetsView.d.ts +1 -1
- package/lib/types/client/panel/AuthorHome.d.ts +12 -12
- package/lib/types/client/panel/CmdPalette.d.ts +12 -0
- package/lib/types/client/panel/CreateBookView.d.ts +1 -1
- package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +1 -1
- package/lib/types/client/panel/ImportModal.d.ts +1 -1
- package/lib/types/client/panel/LiveFeedLog.d.ts +8 -0
- package/lib/types/client/panel/ModelManager.d.ts +2 -2
- package/lib/types/client/panel/NovelPanel.d.ts +7 -2
- package/lib/types/client/panel/PageHeader.d.ts +18 -0
- package/lib/types/client/panel/ProgressConsole.d.ts +1 -1
- package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
- package/lib/types/client/panel/ReaderView.d.ts +1 -1
- package/lib/types/client/panel/RunPanel.d.ts +1 -1
- package/lib/types/client/panel/SettingsView.d.ts +13 -10
- package/lib/types/client/panel/ShelfView.d.ts +11 -2
- package/lib/types/client/panel/SubPage.d.ts +27 -0
- package/lib/types/client/panel/TensionView.d.ts +18 -0
- package/lib/types/client/panel/TimelineView.d.ts +18 -0
- package/lib/types/client/panel/WorldTab.d.ts +1 -1
- package/lib/types/client/panel/helpers.d.ts +5 -3
- package/lib/types/client/panel/market-radar-state.d.ts +56 -0
- package/lib/types/client/panel/outline-markdown.d.ts +112 -0
- package/lib/types/client/panel/status-badge.d.ts +13 -0
- package/lib/types/client/panel/storage-parse.d.ts +46 -0
- package/lib/types/client/panel/toast.d.ts +2 -0
- package/lib/types/client/panel/usePersistentState.d.ts +17 -0
- package/lib/types/client/panel/views.d.ts +21 -35
- package/lib/types/client/run-status.d.ts +21 -0
- package/lib/types/client/sidebar-entry.d.ts +0 -2
- package/lib/types/digest-parse.d.ts +29 -0
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +122 -103
- package/lib/types/home.d.ts +11 -0
- package/lib/types/index.d.ts +21 -13
- package/lib/types/llm-live.d.ts +62 -36
- package/lib/types/llm-retry.d.ts +38 -0
- package/lib/types/market-radar-scan.d.ts +0 -1
- package/lib/types/market-radar-sources.d.ts +1 -1
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/model-capability.d.ts +119 -0
- package/lib/types/model-request.d.ts +36 -0
- package/lib/types/output-budget.d.ts +86 -0
- package/lib/types/plotline-align.d.ts +35 -0
- package/lib/types/prompt-slots.d.ts +57 -0
- package/lib/types/prompting.d.ts +2 -27
- package/lib/types/protocol.d.ts +479 -595
- package/lib/types/revision.d.ts +76 -0
- package/lib/types/routes.d.ts +9 -3
- package/lib/types/run-selection.d.ts +51 -0
- package/lib/types/run.d.ts +31 -0
- package/lib/types/settings-persist.d.ts +59 -0
- package/lib/types/snapshots.d.ts +46 -0
- package/lib/types/stage-contract.d.ts +58 -0
- package/lib/types/stream-fallback.d.ts +60 -0
- package/lib/types/strip-headings.d.ts +33 -0
- package/lib/types/tension.d.ts +53 -0
- package/lib/types/timeline.d.ts +37 -0
- package/package.json +41 -28
- package/assets/styles/arcane-thick-3d.webp +0 -0
- package/assets/styles/chinese-ink.webp +0 -0
- package/assets/styles/clay-stopmotion-3d.webp +0 -0
- package/assets/styles/dark-ghibli.webp +0 -0
- package/assets/styles/dark-gothic-3d.webp +0 -0
- package/assets/styles/film-photo.webp +0 -0
- package/assets/styles/guofeng-heavy.webp +0 -0
- package/assets/styles/guoman-2d-gufeng.webp +0 -0
- package/assets/styles/guoman-3d-tianyuan.webp +0 -0
- package/assets/styles/guoman-3d-xuanhuan.webp +0 -0
- package/assets/styles/horror-game-3d.webp +0 -0
- package/assets/styles/hyperreal-3d.webp +0 -0
- package/assets/styles/japan-cel-dark.webp +0 -0
- package/assets/styles/korean-webtoon.webp +0 -0
- package/assets/styles/low-cyberpunk-3d.webp +0 -0
- package/assets/styles/nextgen-cg.webp +0 -0
- package/assets/styles/noir-film.webp +0 -0
- package/assets/styles/openworld-survival.webp +0 -0
- package/assets/styles/paper-cut-3d.webp +0 -0
- package/assets/styles/pixar-adult-3d.webp +0 -0
- package/assets/styles/pop-marvel-2d.webp +0 -0
- package/assets/styles/sandiao-qban.webp +0 -0
- package/assets/styles/shinkai-anime.webp +0 -0
- package/assets/styles/tactical-stealth.webp +0 -0
- package/assets/styles/urban-lightreal-3d.webp +0 -0
- package/assets/styles/vaporwave-3d.webp +0 -0
- package/assets/styles/vhs90.webp +0 -0
- package/lib/types/client/panel/BookshelfBar.d.ts +0 -11
- package/lib/types/client/panel/FlowGuide.d.ts +0 -22
- package/lib/types/client/panel/MangaRoleLibrary.d.ts +0 -20
- package/lib/types/client/panel/MangaWorkspace.d.ts +0 -12
- package/lib/types/client/panel/ModelPicker.d.ts +0 -11
- package/lib/types/client/panel/ProgressHomeView.d.ts +0 -5
- package/lib/types/client/panel/PropLibrary.d.ts +0 -8
- package/lib/types/client/panel/SceneLibrary.d.ts +0 -16
- package/lib/types/client/panel/StoryboardTab.d.ts +0 -19
- package/lib/types/client/panel/StyleCard.d.ts +0 -8
- package/lib/types/client/panel/StyleGallery.d.ts +0 -1
- package/lib/types/comic-presets.d.ts +0 -12
- package/lib/types/manga-genre-rules.d.ts +0 -22
- package/lib/types/prompt-utils.d.ts +0 -35
- package/lib/types/review-policy.d.ts +0 -97
- package/lib/types/shot-language.d.ts +0 -46
- package/lib/types/src/ai-scan.d.ts +0 -36
- package/lib/types/src/assets.d.ts +0 -37
- package/lib/types/src/assistant.d.ts +0 -45
- package/lib/types/src/author-assets.d.ts +0 -25
- package/lib/types/src/bookshelf.d.ts +0 -42
- package/lib/types/src/client/api.d.ts +0 -234
- package/lib/types/src/client/docx.d.ts +0 -15
- package/lib/types/src/client/index.d.ts +0 -14
- package/lib/types/src/client/locales.d.ts +0 -212
- package/lib/types/src/client/mount.d.ts +0 -9
- package/lib/types/src/client/panel/AdaptModeView.d.ts +0 -5
- package/lib/types/src/client/panel/AssetsTab.d.ts +0 -12
- package/lib/types/src/client/panel/AssistantTab.d.ts +0 -7
- package/lib/types/src/client/panel/AuthorAssetsView.d.ts +0 -4
- package/lib/types/src/client/panel/AuthorHome.d.ts +0 -32
- package/lib/types/src/client/panel/BookshelfBar.d.ts +0 -11
- package/lib/types/src/client/panel/CreateBookView.d.ts +0 -9
- package/lib/types/src/client/panel/FlowGuide.d.ts +0 -22
- package/lib/types/src/client/panel/GlobalAssetLibraryView.d.ts +0 -4
- package/lib/types/src/client/panel/ImportModal.d.ts +0 -7
- package/lib/types/src/client/panel/MangaRoleLibrary.d.ts +0 -20
- package/lib/types/src/client/panel/MangaWorkspace.d.ts +0 -12
- package/lib/types/src/client/panel/ModelManager.d.ts +0 -14
- package/lib/types/src/client/panel/NovelPanel.d.ts +0 -13
- package/lib/types/src/client/panel/ProgressConsole.d.ts +0 -17
- package/lib/types/src/client/panel/ProgressHomeView.d.ts +0 -5
- package/lib/types/src/client/panel/PropLibrary.d.ts +0 -8
- package/lib/types/src/client/panel/ReaderView.d.ts +0 -8
- package/lib/types/src/client/panel/ReasoningSection.d.ts +0 -10
- package/lib/types/src/client/panel/RunPanel.d.ts +0 -5
- package/lib/types/src/client/panel/SceneLibrary.d.ts +0 -16
- package/lib/types/src/client/panel/SettingsView.d.ts +0 -16
- package/lib/types/src/client/panel/ShelfView.d.ts +0 -15
- package/lib/types/src/client/panel/StoryboardTab.d.ts +0 -19
- package/lib/types/src/client/panel/StyleCard.d.ts +0 -8
- package/lib/types/src/client/panel/StyleGallery.d.ts +0 -1
- package/lib/types/src/client/panel/WorldTab.d.ts +0 -10
- package/lib/types/src/client/panel/controller.d.ts +0 -19
- package/lib/types/src/client/panel/helpers.d.ts +0 -18
- package/lib/types/src/client/panel/views.d.ts +0 -77
- package/lib/types/src/client/sidebar-entry.d.ts +0 -13
- package/lib/types/src/client/text.d.ts +0 -9
- package/lib/types/src/comic-presets.d.ts +0 -12
- package/lib/types/src/docx.d.ts +0 -19
- package/lib/types/src/engine.d.ts +0 -402
- package/lib/types/src/index.d.ts +0 -79
- package/lib/types/src/manga-genre-rules.d.ts +0 -22
- package/lib/types/src/novel-context.d.ts +0 -80
- package/lib/types/src/prompt-utils.d.ts +0 -35
- package/lib/types/src/protocol.d.ts +0 -2176
- package/lib/types/src/review-policy.d.ts +0 -97
- package/lib/types/src/routes.d.ts +0 -29
- package/lib/types/src/run.d.ts +0 -32
- package/lib/types/src/shot-language.d.ts +0 -46
- package/lib/types/src/story-beat-language.d.ts +0 -35
- package/lib/types/src/style-library.d.ts +0 -33
- package/lib/types/story-beat-language.d.ts +0 -35
- package/lib/types/style-library.d.ts +0 -33
- package/src/ai-scan.ts +0 -179
- package/src/assets.ts +0 -1774
- package/src/assistant.ts +0 -915
- package/src/author-assets.ts +0 -147
- package/src/bookshelf.ts +0 -167
- package/src/client/api.ts +0 -716
- package/src/client/css-modules.d.ts +0 -8
- package/src/client/docx.ts +0 -69
- package/src/client/index.ts +0 -34
- package/src/client/llmLive.ts +0 -101
- package/src/client/locales.ts +0 -418
- package/src/client/mount.tsx +0 -97
- package/src/client/mount.tsx._new +0 -97
- package/src/client/mount.tsx._nf +0 -97
- package/src/client/mount.tsx._tmp +0 -97
- package/src/client/panel/AdaptModeView.tsx +0 -535
- package/src/client/panel/AssetsTab.tsx +0 -458
- package/src/client/panel/AssistantTab.tsx +0 -339
- package/src/client/panel/AuthorAssetsView.tsx +0 -208
- package/src/client/panel/AuthorHome.tsx +0 -234
- package/src/client/panel/BookAnalysisView.tsx +0 -60
- package/src/client/panel/BookshelfBar.tsx +0 -170
- package/src/client/panel/CreateBookView.tsx +0 -371
- package/src/client/panel/DirectorView.tsx +0 -200
- package/src/client/panel/FlowGuide.tsx +0 -84
- package/src/client/panel/GlobalAssetLibraryView.tsx +0 -172
- package/src/client/panel/IdeaInspirationView.tsx +0 -71
- package/src/client/panel/ImportModal.tsx +0 -303
- package/src/client/panel/KnowledgeBaseView.tsx +0 -73
- package/src/client/panel/LiveFeedLog.tsx +0 -76
- package/src/client/panel/MangaRoleLibrary.tsx +0 -1002
- package/src/client/panel/MangaWorkspace.tsx +0 -715
- package/src/client/panel/MarketRadarView.tsx +0 -374
- package/src/client/panel/ModelManager.tsx +0 -205
- package/src/client/panel/NovelPanel.tsx +0 -4721
- package/src/client/panel/ProgressConsole.tsx +0 -63
- package/src/client/panel/ProgressHomeView.tsx +0 -87
- package/src/client/panel/PropLibrary.tsx +0 -175
- package/src/client/panel/ReaderView.tsx +0 -306
- package/src/client/panel/ReasoningSection.tsx +0 -31
- package/src/client/panel/RunPanel.tsx +0 -204
- package/src/client/panel/SceneLibrary.tsx +0 -453
- package/src/client/panel/SettingsView.tsx +0 -289
- package/src/client/panel/ShelfView.tsx +0 -242
- package/src/client/panel/StoryboardTab.tsx +0 -472
- package/src/client/panel/StyleCard.tsx +0 -90
- package/src/client/panel/StyleGallery.tsx +0 -58
- package/src/client/panel/WorldTab.tsx +0 -202
- package/src/client/panel/controller.ts +0 -45
- package/src/client/panel/helpers.ts +0 -67
- package/src/client/panel/panel.module.css +0 -4359
- package/src/client/panel/views.tsx +0 -314
- package/src/client/sidebar-entry.ts +0 -122
- package/src/client/text.ts +0 -28
- package/src/comic-presets.ts +0 -67
- package/src/docx.ts +0 -83
- package/src/engine.ts +0 -5636
- package/src/global-assets.ts +0 -59
- package/src/index.ts +0 -346
- package/src/llm-live.ts +0 -68
- package/src/manga-genre-rules.ts +0 -87
- package/src/market-radar-scan.ts +0 -41
- package/src/market-radar-sources.ts +0 -173
- package/src/novel-context.ts +0 -287
- package/src/prompt-utils.ts +0 -73
- package/src/prompting.ts +0 -159
- package/src/protocol.ts +0 -2565
- package/src/review-policy.ts +0 -161
- package/src/routes.ts +0 -3939
- package/src/run.ts +0 -338
- package/src/shot-language.ts +0 -181
- package/src/story-beat-language.ts +0 -130
- package/src/style-library.ts +0 -84
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function AiTag(): JSX.Element;
|
|
@@ -8,5 +8,5 @@ export interface AssetsTabProps {
|
|
|
8
8
|
/** 写作资产子页签。 */
|
|
9
9
|
type AssetSubTab = 'genre' | 'progression' | 'templates' | 'rules' | 'style';
|
|
10
10
|
/** 写作资产页签。 */
|
|
11
|
-
export declare function AssetsTab({ api, initialTab }: AssetsTabProps):
|
|
11
|
+
export declare function AssetsTab({ api, initialTab }: AssetsTabProps): import("react").JSX.Element;
|
|
12
12
|
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
|
+
import type { StageInfo } from '../../protocol.ts';
|
|
2
3
|
/** Props. */
|
|
3
4
|
export interface AssistantTabProps {
|
|
4
5
|
api: NovelApi;
|
|
6
|
+
/** 宿主算出的创作阶段(/status.stage):让作者在同一屏看到「现在该做什么」。 */
|
|
7
|
+
stage?: StageInfo | null;
|
|
5
8
|
}
|
|
6
9
|
/** The assistant conversation tab. */
|
|
7
|
-
export declare function AssistantTab({ api }: AssistantTabProps):
|
|
10
|
+
export declare function AssistantTab({ api, stage }: AssistantTabProps): import("react").JSX.Element;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
2
|
import type { BookshelfSnapshot } from '../../protocol.ts';
|
|
3
3
|
import { type ProgressLine } from './ProgressConsole.tsx';
|
|
4
|
-
export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook,
|
|
4
|
+
export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onBackground, progress, busy, busyLabel, liveBar, onClearProgress, onUseIdea, themeMode, themeDensity, onToggleMode, onChangeThemeMode, onChangeThemeDensity }: {
|
|
5
|
+
/** 当前显示模式(顶栏「墨纸 / 纸白」切换用)。 */
|
|
6
|
+
themeMode?: 'system' | 'light' | 'dark';
|
|
7
|
+
/** 当前界面密度。 */
|
|
8
|
+
themeDensity?: 'comfort' | 'compact' | 'spacious';
|
|
9
|
+
/** 切换墨纸 / 纸白(由 NovelPanel 落 localStorage)。 */
|
|
10
|
+
onToggleMode?: () => void;
|
|
11
|
+
/** 改变全局显示模式(联动整个面板)。 */
|
|
12
|
+
onChangeThemeMode: (m: 'system' | 'light' | 'dark') => void;
|
|
13
|
+
/** 改变全局界面密度(联动整个面板)。 */
|
|
14
|
+
onChangeThemeDensity: (d: 'comfort' | 'compact' | 'spacious') => void;
|
|
5
15
|
api: NovelApi;
|
|
6
16
|
shelf: BookshelfSnapshot;
|
|
7
17
|
onOpenBook: (id: string) => void;
|
|
@@ -10,18 +20,8 @@ export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBo
|
|
|
10
20
|
onImportBook: () => void;
|
|
11
21
|
/** 创意灵感 → 采纳某个灵感带入开书向导。 */
|
|
12
22
|
onUseIdea?: (idea: import('../../protocol.ts').IdeaInspirationResult['ideas'][number]) => void;
|
|
13
|
-
/** 兼容旧入口:首页设置现为独立设置页,此回调保留但不再使用。 */
|
|
14
|
-
onOpenSettings?: () => void;
|
|
15
|
-
/** 主题/模式/密度变化回调(供面板根容器实时生效)。 */
|
|
16
|
-
onTheme?: (theme: 'liquid' | 'neumorph' | 'macos' | 'clay' | 'endfield', mode: 'system' | 'light' | 'dark', density: 'comfort' | 'compact' | 'spacious') => void;
|
|
17
23
|
/** 自定义背景变化回调。 */
|
|
18
24
|
onBackground?: (bg: string | undefined, blur: number) => void;
|
|
19
|
-
/** 玻璃透明度变化回调。 */
|
|
20
|
-
onOpacity?: (n: number) => void;
|
|
21
|
-
/** 终末地强调色变化回调。 */
|
|
22
|
-
onEndfieldAccent?: (accent: 'valley' | 'wuling') => void;
|
|
23
|
-
/** 是否启用改编模式(默认 false=隐藏入口)。 */
|
|
24
|
-
adaptEnabled?: boolean;
|
|
25
25
|
/** AI 进度实时状态(与书内共享同一份)。 */
|
|
26
26
|
progress?: ProgressLine[];
|
|
27
27
|
busy?: boolean;
|
|
@@ -31,4 +31,4 @@ export declare function AuthorHome({ api, shelf, onOpenBook, onReadBook, onAddBo
|
|
|
31
31
|
ratio?: number;
|
|
32
32
|
} | null;
|
|
33
33
|
onClearProgress?: () => void;
|
|
34
|
-
}):
|
|
34
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
export interface CmdAction {
|
|
3
|
+
icon: string;
|
|
4
|
+
label: string;
|
|
5
|
+
hint?: string;
|
|
6
|
+
run: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function CmdPalette({ open, onClose, actions }: {
|
|
9
|
+
open: boolean;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
actions: CmdAction[];
|
|
12
|
+
}): ReactElement | null;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 「实时调用 / LIVE」—— 内嵌到 AI 进度窗的下半区(详情层),不再独立浮窗。
|
|
3
3
|
* 连接 /llm-live/stream,按 LLM 调用分组展示模型实时输出。
|
|
4
|
+
*
|
|
5
|
+
* 除实时输出外,这里还承担两件与「花了多少」有关的事:
|
|
6
|
+
* 1) 每次调用显示 token 用量(输入↑ / 输出↓ / 思考)与耗时、首字耗时;
|
|
7
|
+
* 2) 「查看 Prompt」按 sessionId 拉取这次调用**实际发送**的 system / user。
|
|
8
|
+
* 顶部另有一条「本次运行」累计条(进程内,重启归零)。
|
|
9
|
+
*
|
|
10
|
+
* 状态色全部走 panel.module.css 的 .lfPill / .lfDot(主题令牌 + --nf-streaming),
|
|
11
|
+
* 不再内联硬编码色;布局性 inline style 保留(均为令牌引用或动态值)。
|
|
4
12
|
*/
|
|
5
13
|
export default function LiveFeedLog(): JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
2
|
import type { SavedModel } from '../../protocol.ts';
|
|
3
|
-
/**
|
|
4
|
-
export declare const FALLBACK_MODEL_PRESETS: readonly ["deepseek-
|
|
3
|
+
/** 目录不可用时回退的内置预设(对齐当前模型目录)。 */
|
|
4
|
+
export declare const FALLBACK_MODEL_PRESETS: readonly ["deepseek-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"];
|
|
5
5
|
export interface ModelManagerProps {
|
|
6
6
|
api: NovelApi;
|
|
7
7
|
provider: string;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
2
|
import type { PanelController } from './controller.ts';
|
|
3
|
+
/**
|
|
4
|
+
* 面板宽度低于此值时自动折叠左侧导航。
|
|
5
|
+
* 依据:.panelNav 占 216px,内容区留到 ~680px 才不至于把正文挤成竖排。
|
|
6
|
+
* 宿主中列宽度 ≠ 视口宽度(用户可拖拽侧边栏),所以必须用实测宽度而非媒体查询。
|
|
7
|
+
*/
|
|
3
8
|
/** The panel's tab identifiers. */
|
|
4
|
-
export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'director' | 'knowledge' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle' | 'run' | '
|
|
9
|
+
export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'director' | 'knowledge' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle' | 'run' | 'book' | 'assets';
|
|
5
10
|
/** Panel shell props. */
|
|
6
11
|
export interface NovelPanelProps {
|
|
7
12
|
/** The panel state owner (open/close/toggle). */
|
|
@@ -10,4 +15,4 @@ export interface NovelPanelProps {
|
|
|
10
15
|
api: NovelApi;
|
|
11
16
|
}
|
|
12
17
|
/** The novel-forge panel. */
|
|
13
|
-
export declare function NovelPanel({ controller, api }: NovelPanelProps):
|
|
18
|
+
export declare function NovelPanel({ controller, api }: NovelPanelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** 墨案统一页头:eyebrow(11 大写窄) + 衬线大标题 + 一句说明 + 主/次动作。
|
|
2
|
+
* 每个视图只用一个 PageHeader,消灭"各自为政"的页面标题。
|
|
3
|
+
* 图标用 lucide 16 (单色),标题旁禁止 emoji;标题用衬线 Display(书卷感)。 */
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
export declare function PageHeader({ eyebrow, title, icon, sub, actions, right }: {
|
|
6
|
+
/** 页眉小标(大写窄,可选,如 "BOOKSHELF")。 */
|
|
7
|
+
eyebrow?: string;
|
|
8
|
+
/** 页面大标题(衬线 Display)。 */
|
|
9
|
+
title: string;
|
|
10
|
+
/** lucide 图标(单色,16px),可选。 */
|
|
11
|
+
icon?: ReactNode;
|
|
12
|
+
/** 一句话辅助说明(meta)。 */
|
|
13
|
+
sub?: string;
|
|
14
|
+
/** 右侧主/次按钮组。 */
|
|
15
|
+
actions?: ReactNode;
|
|
16
|
+
/** 标题右上(如 AI 微签)。 */
|
|
17
|
+
right?: ReactNode;
|
|
18
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 提示词槽位:给作者留几个**安全可改**的位置来微调本书表达偏好。
|
|
3
|
+
*
|
|
4
|
+
* 边界写在界面上:槽位只**追加偏好**,不能覆盖道藏、写作红线、九条内容合规红线、
|
|
5
|
+
* 反 AI 规则与阶段契约 —— 这些由宿主锁定。清空即恢复默认。
|
|
6
|
+
*/
|
|
7
|
+
import type { NovelApi } from '../api.ts';
|
|
8
|
+
export interface PromptSlotsViewProps {
|
|
9
|
+
api: NovelApi;
|
|
10
|
+
}
|
|
11
|
+
export default function PromptSlotsView({ api }: PromptSlotsViewProps): JSX.Element;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
|
-
type SettingsTab = 'model' | '
|
|
3
|
-
|
|
4
|
-
type ThemeName = 'liquid' | 'neumorph' | 'macos' | 'clay' | 'endfield';
|
|
5
|
-
type EndfieldAccent = 'valley' | 'wuling';
|
|
6
|
-
type ThemeDensity = 'comfort' | 'compact' | 'spacious';
|
|
7
|
-
export declare function SettingsView({ api, onTheme, onSettingsTab, onEditorFontSize, onBackground, onOpacity, onEndfieldAccent }: {
|
|
2
|
+
type SettingsTab = 'model' | 'files' | 'appearance';
|
|
3
|
+
export declare function SettingsView({ api, variant, onSettingsTab, onEditorFontSize, onBackground, themeMode, themeDensity, onChangeThemeMode, onChangeThemeDensity }: {
|
|
8
4
|
api: NovelApi;
|
|
9
|
-
|
|
5
|
+
/** page = 全屏 SubPage(保留给旧入口);drawer = 右滑抽屉内容(无壳)。 */
|
|
6
|
+
variant?: 'page' | 'drawer';
|
|
10
7
|
onSettingsTab?: (tab: SettingsTab) => void;
|
|
11
8
|
onEditorFontSize?: (n: number) => void;
|
|
12
9
|
onBackground?: (bg: string | undefined, blur: number) => void;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
/** 全局显示模式(联动整个面板,NovelPanel 落 localStorage)。 */
|
|
11
|
+
themeMode: 'system' | 'light' | 'dark';
|
|
12
|
+
/** 全局界面密度。 */
|
|
13
|
+
themeDensity: 'comfort' | 'compact' | 'spacious';
|
|
14
|
+
/** 改变全局显示模式。 */
|
|
15
|
+
onChangeThemeMode: (m: 'system' | 'light' | 'dark') => void;
|
|
16
|
+
/** 改变全局界面密度。 */
|
|
17
|
+
onChangeThemeDensity: (d: 'comfort' | 'compact' | 'spacious') => void;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
16
19
|
export {};
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 书架首页视图:书卡网格(封面/书名/简介/进度)+ 搜索筛选 + 开书入口。
|
|
3
|
+
* 进入小说工坊默认展示;点击书卡进入该书工作台,+ 进入开书向导页。
|
|
4
|
+
*/
|
|
5
|
+
import { type ReactNode } from 'react';
|
|
1
6
|
import type { NovelApi } from '../api.ts';
|
|
2
7
|
import type { BookshelfSnapshot } from '../../protocol.ts';
|
|
3
8
|
/** 书架首页。 */
|
|
4
|
-
export declare function ShelfView({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, }: {
|
|
9
|
+
export declare function ShelfView({ api, shelf, onOpenBook, onReadBook, onAddBook, onImportBook, onOpenAdapt, toolbarExtra, }: {
|
|
5
10
|
api: NovelApi;
|
|
6
11
|
shelf: BookshelfSnapshot;
|
|
12
|
+
/** 书架工具行右端插槽:组入口按钮由 AuthorHome 下放(创作 / 灵感策划 / 资产)。 */
|
|
13
|
+
toolbarExtra?: ReactNode;
|
|
7
14
|
/** 点击书卡:激活该书并进入工作台。 */
|
|
8
15
|
onOpenBook: (id: string) => void;
|
|
9
16
|
/** 点击「阅读」:激活该书并进入沉浸式阅读页。 */
|
|
@@ -12,4 +19,6 @@ export declare function ShelfView({ api, shelf, onOpenBook, onReadBook, onAddBoo
|
|
|
12
19
|
onAddBook: () => void;
|
|
13
20
|
/** 点击「导入」:打开导入弹窗(已有项目目录 / txt/md 全本)。 */
|
|
14
21
|
onImportBook: () => void;
|
|
15
|
-
|
|
22
|
+
/** 点击「改编模式」:进入改编六步向导(入口收在书架页头,与导入并排)。 */
|
|
23
|
+
onOpenAdapt?: () => void;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一子页骨架(1:1 对齐 demo-subpages.html 的 .subpage / .subpage-head / .subpage-body)。
|
|
3
|
+
*
|
|
4
|
+
* 所有作者级与书内子页都应套这层壳:
|
|
5
|
+
* - head:返回钮(可选)+ 大标题 + meta + 右侧操作区
|
|
6
|
+
* - body:页面内容(内部按 demo 用 .card / .box / .grid / .row 等块)
|
|
7
|
+
* 业务逻辑继续留在各自的 View 组件里,这里只负责「壳」。
|
|
8
|
+
*/
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
export declare function SubPage(props: {
|
|
11
|
+
/** 页面大标题(衬线 display 档)。 */
|
|
12
|
+
title: string;
|
|
13
|
+
/** 标题右侧的说明行(如「4 本书 · 当前书《X》」)。 */
|
|
14
|
+
meta?: ReactNode;
|
|
15
|
+
/** 返回钮:作者级返回书架,书内返回总编台。 */
|
|
16
|
+
back?: {
|
|
17
|
+
label: string;
|
|
18
|
+
onClick: () => void;
|
|
19
|
+
};
|
|
20
|
+
/** 页头右侧操作区(主 CTA 放最右)。 */
|
|
21
|
+
actions?: ReactNode;
|
|
22
|
+
/** 主题系统局部模式(透传到根 div 的 data-nf-mode,供设置页等预览明暗)。 */
|
|
23
|
+
dataNfMode?: 'light' | 'dark';
|
|
24
|
+
/** 主题系统局部密度(透传到根 div 的 data-nf-density)。 */
|
|
25
|
+
dataNfDensity?: 'comfort' | 'compact' | 'spacious';
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 张力曲线:把「每章该有多紧」画出来并可逐章调整。
|
|
3
|
+
*
|
|
4
|
+
* 图上是三条线:
|
|
5
|
+
* 参考(虚线,形状预设) / 目标(实线,作者或规划设定) / 实际(散点,审稿打分)。
|
|
6
|
+
* 下方列出规则核对发现的曲线级问题(连续同值、高位不回落、长期低位、实际偏离目标)。
|
|
7
|
+
*
|
|
8
|
+
* 张力只影响写作提示词里的"松紧"指引,不改变任何校验规则。
|
|
9
|
+
*/
|
|
10
|
+
import type { NovelApi } from '../api.ts';
|
|
11
|
+
export interface TensionViewProps {
|
|
12
|
+
api: NovelApi;
|
|
13
|
+
/** 一键按建议修订(合并审稿/张力/时间线后改这一章;由父级统一发起)。 */
|
|
14
|
+
onRevise?: (chapterNo: number) => void;
|
|
15
|
+
/** 外部忙碌态(修订进行中时禁用按钮)。 */
|
|
16
|
+
busy?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export default function TensionView({ api, onRevise, busy: outerBusy }: TensionViewProps): JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 故事时间线:把逐章抽取的「时间点 / 地点 / 在场角色 / 事件」按章节顺序排开,
|
|
3
|
+
* 带规则初筛 + AI 复核的矛盾清单,并支持就地修正(手改后标记为 manual)。
|
|
4
|
+
*
|
|
5
|
+
* 与「编年录」的分工:编年录记事实状态,这里记**顺序**——专门抓时间倒流、
|
|
6
|
+
* 地点瞬移、跨度不合理这类长篇最容易崩的地方。
|
|
7
|
+
*/
|
|
8
|
+
import type { NovelApi } from '../api.ts';
|
|
9
|
+
export interface TimelineViewProps {
|
|
10
|
+
api: NovelApi;
|
|
11
|
+
/** 章节号列表(用于「抽取本章」下拉)。 */
|
|
12
|
+
chapters: number[];
|
|
13
|
+
/** 一键按建议修订(合并审稿/时间线/张力后改这一章;由父级统一发起)。 */
|
|
14
|
+
onRevise?: (chapterNo: number) => void;
|
|
15
|
+
/** 外部忙碌态(修订进行中时禁用按钮)。 */
|
|
16
|
+
busy?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export default function TimelineView({ api, chapters, onRevise, busy: outerBusy }: TimelineViewProps): JSX.Element;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tiny translation helper for the panel
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Tiny translation helper for the panel.
|
|
3
|
+
*
|
|
4
|
+
* 面板是中文单语界面:文案只在 `locales.ts` 定义一次,这里按类型化键位取值
|
|
5
|
+
* (键位不匹配会在编译期报错),保持客户端 bundle 自包含、不引入运行时语言服务。
|
|
6
|
+
* 需要多语言时再接入 DSH 的 locale 服务,届时只需替换本函数实现。
|
|
5
7
|
*/
|
|
6
8
|
import { type NovelKey } from '../locales.ts';
|
|
7
9
|
/** Translate one key with optional {placeholder} substitution. */
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 热门题材雷达的界面状态规则。
|
|
3
|
+
*
|
|
4
|
+
* 职责边界:本文件只回答「界面上此刻能不能点」,不持有任何数据。抽成纯函数的
|
|
5
|
+
* 理由是它可以离线测——组件渲染不好测,判定逻辑好测。
|
|
6
|
+
*
|
|
7
|
+
* 下面先用**独立表述的验收条件**写清楚要什么,再由两个函数实现;这样即使日后
|
|
8
|
+
* 需求变化,改动方向也是照着条件走,而不是照着某个外部实现的形状走。
|
|
9
|
+
*
|
|
10
|
+
* R1 扫榜或分析进行中,不得改动作品勾选(否则分析输入与界面不一致);
|
|
11
|
+
* R2 未勾选任何作品时不能开始分析;
|
|
12
|
+
* R3 已经有报告之后,仍允许改选作品并**再次**分析 —— 不得退化成"只能查看";
|
|
13
|
+
* R4 信号勾选只在确实换了一份报告时复位;同一份报告的重渲染不得冲掉作者手选。
|
|
14
|
+
*/
|
|
15
|
+
/** 雷达三个入口此刻的可用性,以及分析按钮该显示哪种文案。 */
|
|
16
|
+
export interface RadarControls {
|
|
17
|
+
/** 扫榜按钮是否可点。 */
|
|
18
|
+
canScan: boolean;
|
|
19
|
+
/** 作品勾选(含全选 / 清空)是否可改。 */
|
|
20
|
+
canSelectCandidates: boolean;
|
|
21
|
+
/** 分析按钮是否可点。 */
|
|
22
|
+
canAnalyze: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 分析按钮的文案标识:
|
|
25
|
+
* - `wait`:还在扫榜,先等榜单;
|
|
26
|
+
* - `pick`:还没勾选作品;
|
|
27
|
+
* - `first`:首次分析;
|
|
28
|
+
* - `again`:已有报告,这次是再出一份。
|
|
29
|
+
*/
|
|
30
|
+
analyzeLabel: 'wait' | 'pick' | 'first' | 'again';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 实现 R1-R3:判定三个入口的可用性。
|
|
34
|
+
* @param state - 当前界面状态。
|
|
35
|
+
* @returns 各入口可用性与分析按钮文案标识。
|
|
36
|
+
*/
|
|
37
|
+
export declare function radarControls(state: {
|
|
38
|
+
/** 正在抓榜单。 */
|
|
39
|
+
scanning: boolean;
|
|
40
|
+
/** 正在做 AI 分析。 */
|
|
41
|
+
analyzing: boolean;
|
|
42
|
+
/** 已勾选的作品数。 */
|
|
43
|
+
pickedCount: number;
|
|
44
|
+
/** 界面上是否已有可查看的报告。 */
|
|
45
|
+
hasReport: boolean;
|
|
46
|
+
}): RadarControls;
|
|
47
|
+
/**
|
|
48
|
+
* 实现 R4:本次报告是否**不是**勾选当前所依据的那一份。
|
|
49
|
+
*
|
|
50
|
+
* 传入的是上一次已经据以复位过的报告标识:相同 → 不动作者勾选;不同 → 勾选所依据
|
|
51
|
+
* 的信号集合已经变了,需要按新报告重新给出推荐勾选。
|
|
52
|
+
* @param selectionBasis - 勾选当前依据的报告标识(空串表示尚未复位过)。
|
|
53
|
+
* @param incoming - 本次拿到的报告标识(空串表示旧版产物没有该标识)。
|
|
54
|
+
* @returns 需要按新报告重置勾选时为 true。
|
|
55
|
+
*/
|
|
56
|
+
export declare function isDifferentReport(selectionBasis: string, incoming: string): boolean;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 大纲 / 正文的轻量 Markdown 解析(零依赖、可离线单测)。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么不用现成的库
|
|
5
|
+
*
|
|
6
|
+
* 插件不引第三方依赖(`package.json` 只有宿主 peer),而大纲是**受控文本**:
|
|
7
|
+
* 内容由本插件自己的 prompt 产出或作者手写,不是任意的用户 markdown。
|
|
8
|
+
* 拉一个 markdown 库进来(marked / markdown-it ≈ 100KB+)只为一个只读预览,
|
|
9
|
+
* 不划算;而自己写又必须**只实现真实用到的语法**,否则就是自造半吊子。
|
|
10
|
+
*
|
|
11
|
+
* ## 实际要支持什么(由真实产物倒推,不是凭想象)
|
|
12
|
+
*
|
|
13
|
+
* 看 `H:/novels/还债疯了/_outline.md` 的真实形态:
|
|
14
|
+
* - 大量**中文序号小标题**独立成行:「一、书名与题材定位」「二、金手指与核心机制」。
|
|
15
|
+
* 旧解析器只认 `#`,这些全被塞进一个 `<p>`,整份大纲在页面上是一堵墙。
|
|
16
|
+
* - 顶格书名独占一行(《还债疯了》)后接空行。
|
|
17
|
+
* - 冒号开头的**定义式条目**:「人情债与识海:救人即触发…」。
|
|
18
|
+
* - LLM 被明确要求**不输出换行符**,所以段落内会挤很长的句子
|
|
19
|
+
* (实测最长 700+ 字),必须靠标点断句才读得下去。
|
|
20
|
+
*
|
|
21
|
+
* 加上作者手写时会用的:`- / * / 1.` 列表、`**加粗**`、`*斜体*`、`` `代码` ``、
|
|
22
|
+
* `> 引用`、`|` 表格、`---` 分隔、` ``` ` 代码块。
|
|
23
|
+
*
|
|
24
|
+
* ## 设计约束
|
|
25
|
+
*
|
|
26
|
+
* 1. **永不抛错**:任何看不懂的输入都退化为普通段落,渲染器不能因为一个畸形
|
|
27
|
+
* 字符整页白屏。
|
|
28
|
+
* 2. **纯函数、无 React**:解析结果只含数据,渲染交给调用方。这样解析规则可以
|
|
29
|
+
* 完全离线验证,不必拉起整个面板。
|
|
30
|
+
* 3. **行内标记只做切分,不生成 HTML**:返回「片段数组」(普通文本 / 加粗 /
|
|
31
|
+
* 斜体 / 行内代码),由 React 渲染成元素——绝不用 dangerouslySetInnerHTML。
|
|
32
|
+
*/
|
|
33
|
+
/** 行内片段:把一段文本按 `**粗**` / `*斜*` / `` `码` `` 切分。 */
|
|
34
|
+
export type InlineSpan = {
|
|
35
|
+
kind: 'text';
|
|
36
|
+
text: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'strong';
|
|
39
|
+
text: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'em';
|
|
42
|
+
text: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'code';
|
|
45
|
+
text: string;
|
|
46
|
+
};
|
|
47
|
+
/** 块级节点类型。 */
|
|
48
|
+
export type MdBlock = {
|
|
49
|
+
type: 'heading';
|
|
50
|
+
level: 1 | 2 | 3 | 4;
|
|
51
|
+
spans: InlineSpan[];
|
|
52
|
+
id: string;
|
|
53
|
+
} | {
|
|
54
|
+
type: 'para';
|
|
55
|
+
spans: InlineSpan[];
|
|
56
|
+
id: string;
|
|
57
|
+
} | {
|
|
58
|
+
type: 'list';
|
|
59
|
+
ordered: boolean;
|
|
60
|
+
items: MdListItem[];
|
|
61
|
+
id: string;
|
|
62
|
+
} | {
|
|
63
|
+
type: 'quote';
|
|
64
|
+
spans: InlineSpan[];
|
|
65
|
+
id: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'code';
|
|
68
|
+
lang: string;
|
|
69
|
+
text: string;
|
|
70
|
+
id: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'table';
|
|
73
|
+
header: string[];
|
|
74
|
+
rows: string[][];
|
|
75
|
+
id: string;
|
|
76
|
+
} | {
|
|
77
|
+
type: 'hr';
|
|
78
|
+
id: string;
|
|
79
|
+
};
|
|
80
|
+
/** 列表项:支持一层嵌套(`- 外层` / ` - 内层`),再深就压平。 */
|
|
81
|
+
export interface MdListItem {
|
|
82
|
+
/** 主行内容(已解析行内标记)。 */
|
|
83
|
+
spans: InlineSpan[];
|
|
84
|
+
/** 嵌套子项。 */
|
|
85
|
+
children: MdListItem[];
|
|
86
|
+
}
|
|
87
|
+
/** 解析结果。 */
|
|
88
|
+
export interface MdDoc {
|
|
89
|
+
blocks: MdBlock[];
|
|
90
|
+
/** 目录项(heading level<=3)。 */
|
|
91
|
+
toc: Array<{
|
|
92
|
+
level: number;
|
|
93
|
+
text: string;
|
|
94
|
+
id: string;
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/** 去掉 markdown 标记后的纯文本(用于生成目录文字与 slug)。 */
|
|
98
|
+
export declare function stripInline(text: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* 解析行内标记为片段数组。
|
|
101
|
+
*
|
|
102
|
+
* 顺序有讲究:先剥行内代码(其内容不该再被解析),再剥加粗,最后剥斜体。
|
|
103
|
+
* 未闭合的标记按普通文本处理——宁可少强调,不可渲染出残缺标签。
|
|
104
|
+
*/
|
|
105
|
+
export declare function parseInline(text: string): InlineSpan[];
|
|
106
|
+
/**
|
|
107
|
+
* 解析一段 Markdown 为块序列。
|
|
108
|
+
*
|
|
109
|
+
* @param text - 原始文本。
|
|
110
|
+
* @returns 块序列 + 目录。
|
|
111
|
+
*/
|
|
112
|
+
export declare function parseMarkdown(text: string): MdDoc;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** 徽章的语义色调——决定颜色,与具体 CSS 类名解耦。 */
|
|
2
|
+
export type StatusTone = 'pending' | 'active' | 'written' | 'done' | 'rejected' | 'error';
|
|
3
|
+
/** 色调 → CSS module 类名(两处界面共用同一份,不再各写一遍)。 */
|
|
4
|
+
export declare const BADGE_CLASS: Record<StatusTone, string>;
|
|
5
|
+
/**
|
|
6
|
+
* 取某章状态的色调。
|
|
7
|
+
*
|
|
8
|
+
* 未知状态一律按 `error` 处理:显式失败优于静默伪装成"正常/待写"。
|
|
9
|
+
* 状态集合由 protocol 定义并可能扩展,所以这里不能写死穷尽分支。
|
|
10
|
+
*/
|
|
11
|
+
export declare function statusTone(status: string): StatusTone;
|
|
12
|
+
/** 取某章状态的徽章类名。 */
|
|
13
|
+
export declare function statusBadgeClass(status: string): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* localStorage 值的解析 / 校验(纯函数,零依赖,可离线单测)。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么单独成文件
|
|
5
|
+
*
|
|
6
|
+
* `usePersistentState.ts` 需要 React,而本插件的测试环境是 node
|
|
7
|
+
* (vitest.config.ts `environment: 'node'`,无 jsdom),import React 会直接失败。
|
|
8
|
+
* 但这个模块真正容易出错的地方**不在 hook 本身**(`useState` + `useCallback`
|
|
9
|
+
* 是标准写法),而在**校验逻辑**:一旦校验写漏,脏数据会被写进 localStorage,
|
|
10
|
+
* 之后每次启动都读到它,且没有恢复路径——作者只能手动清缓存。
|
|
11
|
+
*
|
|
12
|
+
* 所以把校验做成零依赖的纯函数,让「最容易错的部分」可以完整验证。
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* 受枚举约束的字符串。
|
|
16
|
+
*
|
|
17
|
+
* @param raw - localStorage 里的原始字符串。
|
|
18
|
+
* @param allowed - 合法值集合。
|
|
19
|
+
* @returns 合法则返回该值;否则 undefined(调用方应回落默认值)。
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseEnum<T extends string>(raw: string, allowed: readonly T[]): T | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* 受上下界约束的整数。
|
|
24
|
+
*
|
|
25
|
+
* 先 `Number.isFinite` 再四舍五入——顺序很关键:先取整的话
|
|
26
|
+
* `NaN` 会被 `Math.round` 变成 `NaN` 之外的怪值,或被 `||` 当成 0 误收。
|
|
27
|
+
*
|
|
28
|
+
* @param raw - localStorage 里的原始字符串。
|
|
29
|
+
* @param min - 下界(含)。
|
|
30
|
+
* @param max - 上界(含)。
|
|
31
|
+
* @returns 合法则返回整数;否则 undefined。
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseIntInRange(raw: string, min: number, max: number): number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* 解析一个 JSON 对象,字段逐个校验,缺失/错类型回落默认值。
|
|
36
|
+
*
|
|
37
|
+
* 用于悬浮窗位置尺寸这类**结构固定但字段可能残缺**的数据。之所以不直接
|
|
38
|
+
* `JSON.parse` 后强转(`as {x?: unknown}`),是因为那样 `NaN` / 字符串 / `null`
|
|
39
|
+
* 都会被放过去,最终变成一个 `left: NaNpx` 的悬浮窗——不报错,只是不见了。
|
|
40
|
+
*
|
|
41
|
+
* @param raw - localStorage 里的原始字符串。
|
|
42
|
+
* @param keys - 需要的字段名。
|
|
43
|
+
* @param fallback - 各字段的默认值(与 keys 一一对应)。
|
|
44
|
+
* @returns 校验通过的完整对象;解析失败返回 undefined。
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseNumberRecord<K extends string>(raw: string, keys: readonly K[], fallback: Record<K, number>): Record<K, number> | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface PersistentStateOptions<T> {
|
|
2
|
+
/** localStorage 键。 */
|
|
3
|
+
key: string;
|
|
4
|
+
/** 读取时的解析与校验;返回 undefined 表示「数据不可用」,用默认值。 */
|
|
5
|
+
parse: (raw: string) => T | undefined;
|
|
6
|
+
/** 写入时的序列化。 */
|
|
7
|
+
serialize: (value: T) => string;
|
|
8
|
+
/** 读取失败 / 无数据 / 校验不过时的默认值。 */
|
|
9
|
+
fallback: T;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 一个与 localStorage 同步的 state。
|
|
13
|
+
*
|
|
14
|
+
* @returns `[当前值, 设置函数]`。设置函数会同步写回 localStorage。
|
|
15
|
+
*/
|
|
16
|
+
export declare function usePersistentState<T>(options: PersistentStateOptions<T>): [T, (next: T | ((prev: T) => T)) => void];
|
|
17
|
+
export { parseEnum, parseIntInRange } from './storage-parse.ts';
|