@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
package/src/client/docx.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser-side docx outline extraction: a .docx is a zip whose
|
|
3
|
-
* word/document.xml holds the body text in <w:t> runs inside <w:p> paragraphs.
|
|
4
|
-
* Uses fflate (inlined into the client bundle) so the user can pick or drag a
|
|
5
|
-
* docx without any server upload.
|
|
6
|
-
*
|
|
7
|
-
* Import from 'fflate/browser' (not 'fflate'): the default entry resolves to
|
|
8
|
-
* the Node build (esm/index.mjs), which calls module.createRequire() for the
|
|
9
|
-
* optional worker_threads path — inlining that into the browser bundle leaves
|
|
10
|
-
* a bare require("module") the client-modules table cannot answer.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { unzipSync, strFromU8 } from 'fflate/browser'
|
|
14
|
-
|
|
15
|
-
/** Decode the handful of XML entities docx bodies actually use. */
|
|
16
|
-
function decodeEntities(text: string): string {
|
|
17
|
-
return text
|
|
18
|
-
.replace(/</g, '<')
|
|
19
|
-
.replace(/>/g, '>')
|
|
20
|
-
.replace(/"/g, '"')
|
|
21
|
-
.replace(/'/g, "'")
|
|
22
|
-
.replace(/&/g, '&')
|
|
23
|
-
.replace(/ /g, ' ')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Extract plain text from a docx buffer: one line per <w:p> paragraph. */
|
|
27
|
-
export function extractDocxTextFromBuffer(buffer: ArrayBuffer | Uint8Array): string {
|
|
28
|
-
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer)
|
|
29
|
-
let files: ReturnType<typeof unzipSync>
|
|
30
|
-
try {
|
|
31
|
-
files = unzipSync(bytes)
|
|
32
|
-
} catch (error) {
|
|
33
|
-
throw new Error(`不是有效的 docx(zip 解压失败):${(error as Error).message}`)
|
|
34
|
-
}
|
|
35
|
-
const document = files['word/document.xml']
|
|
36
|
-
if (document === undefined) {
|
|
37
|
-
throw new Error('不是有效的 docx(缺少 word/document.xml)')
|
|
38
|
-
}
|
|
39
|
-
const xml = strFromU8(document)
|
|
40
|
-
|
|
41
|
-
const paragraphs: string[] = []
|
|
42
|
-
const parts = xml.split(/<w:p\b[^>]*>/)
|
|
43
|
-
for (let i = 1; i < parts.length; i++) {
|
|
44
|
-
const segment = parts[i]!
|
|
45
|
-
const runs: string[] = []
|
|
46
|
-
const runRe = /<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>|<w:tab\b[^>]*\/>|<w:br\b[^>]*\/>/g
|
|
47
|
-
for (const match of segment.matchAll(runRe)) {
|
|
48
|
-
if (match[0].startsWith('<w:tab')) {
|
|
49
|
-
runs.push('\t')
|
|
50
|
-
} else if (match[0].startsWith('<w:br')) {
|
|
51
|
-
runs.push('\n')
|
|
52
|
-
} else {
|
|
53
|
-
runs.push(decodeEntities(match[1] ?? ''))
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
paragraphs.push(runs.join('').replace(/\u00a0/g, ' ').trimEnd())
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const text = paragraphs.join('\n').replace(/\n{3,}/g, '\n\n').trim()
|
|
60
|
-
if (text.length === 0) {
|
|
61
|
-
throw new Error('docx 中没有可提取的文本')
|
|
62
|
-
}
|
|
63
|
-
return text
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Read a File as ArrayBuffer. */
|
|
67
|
-
export function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
|
|
68
|
-
return file.arrayBuffer()
|
|
69
|
-
}
|
package/src/client/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser-half entry for the dsh-novel-forge plugin — runs inside the dsh web
|
|
3
|
-
* GUI. Registers the sidebar entry row and the workbench panel. DOM mounting
|
|
4
|
-
* problems are logged, never thrown — the web shell fails the whole boot when
|
|
5
|
-
* a plugin apply throws.
|
|
6
|
-
*/
|
|
7
|
-
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
8
|
-
import { NovelApi } from './api.ts'
|
|
9
|
-
import { mountPanel } from './mount.tsx'
|
|
10
|
-
import { PanelController } from './panel/controller.ts'
|
|
11
|
-
import { mountSidebarEntry } from './sidebar-entry.ts'
|
|
12
|
-
|
|
13
|
-
/** Required services (fiber inject waiting). */
|
|
14
|
-
export const inject = ['slots', 'locale']
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Mount the novel-forge workbench.
|
|
18
|
-
* @param ctx - client root context.
|
|
19
|
-
*/
|
|
20
|
-
export function apply(ctx: ClientContext): void {
|
|
21
|
-
const controller = new PanelController()
|
|
22
|
-
const api = new NovelApi()
|
|
23
|
-
const disposers: Array<() => void> = []
|
|
24
|
-
try {
|
|
25
|
-
disposers.push(mountSidebarEntry(controller))
|
|
26
|
-
disposers.push(mountPanel(controller, api))
|
|
27
|
-
} catch (error) {
|
|
28
|
-
// DOM failures degrade the panel, never the GUI.
|
|
29
|
-
console.warn('[dsh-novel-forge] mount failed:', error)
|
|
30
|
-
}
|
|
31
|
-
ctx.effect(() => () => {
|
|
32
|
-
for (const dispose of disposers.splice(0)) dispose()
|
|
33
|
-
}, 'dsh-novel-forge: ui mounts')
|
|
34
|
-
}
|
package/src/client/llmLive.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 浏览器端「AI 创作实况」钩子:连接 /llm-live/stream (SSE),按 LLM 调用分组会话。
|
|
3
|
-
* 轻量实现,不持久化缓存(对齐上游 useLlmLiveFeed 的核心行为)。
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
7
|
-
import { NOVEL_API } from '../protocol.ts'
|
|
8
|
-
|
|
9
|
-
export interface LlmLiveFrame {
|
|
10
|
-
type: 'session_started' | 'output_delta' | 'reasoning_delta' | 'phase_changed' | 'session_completed'
|
|
11
|
-
sessionId: string
|
|
12
|
-
label?: string
|
|
13
|
-
model?: string
|
|
14
|
-
at: string
|
|
15
|
-
content?: string
|
|
16
|
-
totalChars?: number
|
|
17
|
-
totalReasoningChars?: number
|
|
18
|
-
phase?: 'requesting' | 'streaming' | 'completed' | 'failed'
|
|
19
|
-
phaseMessage?: string
|
|
20
|
-
preview?: string
|
|
21
|
-
error?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface LlmLiveSession {
|
|
25
|
-
sessionId: string
|
|
26
|
-
label: string
|
|
27
|
-
model?: string
|
|
28
|
-
phase: 'requesting' | 'streaming' | 'completed' | 'failed'
|
|
29
|
-
phaseMessage: string
|
|
30
|
-
preview: string
|
|
31
|
-
totalChars: number
|
|
32
|
-
startedAt: string
|
|
33
|
-
updatedAt: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const MAX_PREVIEW_CHARS = 20_000
|
|
37
|
-
|
|
38
|
-
function applyFrame(current: Record<string, LlmLiveSession>, frame: LlmLiveFrame): Record<string, LlmLiveSession> {
|
|
39
|
-
const next = { ...current }
|
|
40
|
-
const id = frame.sessionId
|
|
41
|
-
let session = next[id]
|
|
42
|
-
if (frame.type === 'session_started') {
|
|
43
|
-
session = {
|
|
44
|
-
sessionId: id,
|
|
45
|
-
label: frame.label ?? 'LLM 调用',
|
|
46
|
-
model: frame.model,
|
|
47
|
-
phase: 'requesting',
|
|
48
|
-
phaseMessage: '正在连接模型',
|
|
49
|
-
preview: '',
|
|
50
|
-
totalChars: 0,
|
|
51
|
-
startedAt: frame.at,
|
|
52
|
-
updatedAt: frame.at,
|
|
53
|
-
}
|
|
54
|
-
next[id] = session
|
|
55
|
-
return next
|
|
56
|
-
}
|
|
57
|
-
if (session === undefined) return next
|
|
58
|
-
if (frame.type === 'output_delta') {
|
|
59
|
-
const preview = (session.preview + (frame.content ?? '')).slice(-MAX_PREVIEW_CHARS)
|
|
60
|
-
session = { ...session, phase: session.phase === 'requesting' ? 'streaming' : session.phase, phaseMessage: session.phase === 'requesting' ? '模型正在返回内容' : session.phaseMessage, preview, totalChars: frame.totalChars ?? session.totalChars + (frame.content?.length ?? 0), updatedAt: frame.at }
|
|
61
|
-
next[id] = session
|
|
62
|
-
} else if (frame.type === 'reasoning_delta') {
|
|
63
|
-
session = { ...session, phase: session.phase === 'requesting' ? 'streaming' : session.phase, updatedAt: frame.at }
|
|
64
|
-
next[id] = session
|
|
65
|
-
} else if (frame.type === 'phase_changed') {
|
|
66
|
-
session = { ...session, phase: frame.phase ?? session.phase, phaseMessage: frame.phaseMessage ?? session.phaseMessage, updatedAt: frame.at }
|
|
67
|
-
next[id] = session
|
|
68
|
-
} else if (frame.type === 'session_completed') {
|
|
69
|
-
session = { ...session, phase: frame.phase ?? 'completed', phaseMessage: frame.phase === 'failed' ? (frame.error ?? '调用失败') : '模型结果已准备完成', totalChars: frame.totalChars ?? session.totalChars, preview: session.preview || (frame.preview ?? ''), updatedAt: frame.at }
|
|
70
|
-
next[id] = session
|
|
71
|
-
}
|
|
72
|
-
return next
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function useLlmLiveFeed(enabled = true): { connected: boolean; sessions: LlmLiveSession[] } {
|
|
76
|
-
const [sessions, setSessions] = useState<Record<string, LlmLiveSession>>({})
|
|
77
|
-
const [connected, setConnected] = useState(false)
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (!enabled) {
|
|
80
|
-
setSessions({})
|
|
81
|
-
setConnected(false)
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
const es = new EventSource(NOVEL_API.llmLive)
|
|
85
|
-
es.onopen = () => setConnected(true)
|
|
86
|
-
es.onmessage = (ev) => {
|
|
87
|
-
try {
|
|
88
|
-
const frame = JSON.parse(ev.data as string) as LlmLiveFrame
|
|
89
|
-
setSessions(prev => applyFrame(prev, frame))
|
|
90
|
-
} catch { /* ignore malformed frame */ }
|
|
91
|
-
}
|
|
92
|
-
// EventSource 断线会自动重连;这里只在断线瞬间标记未连接,重连成功后会再次 onopen。
|
|
93
|
-
es.onerror = () => setConnected(false)
|
|
94
|
-
return () => {
|
|
95
|
-
es.close()
|
|
96
|
-
setConnected(false)
|
|
97
|
-
}
|
|
98
|
-
}, [enabled])
|
|
99
|
-
const list = useMemo(() => Object.values(sessions).sort((a, b) => a.startedAt.localeCompare(b.startedAt)), [sessions])
|
|
100
|
-
return { connected, sessions: list }
|
|
101
|
-
}
|
package/src/client/locales.ts
DELETED
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-novel-forge — locale dictionaries (zh / en).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** zh dictionary. */
|
|
6
|
-
export const zh = {
|
|
7
|
-
'entry.label': '小说工坊',
|
|
8
|
-
'entry.tooltip': 'AI 编译小说工作台:总纲 → 道藏 → 卷计划 → 章节计划 → 逐章生成+审稿',
|
|
9
|
-
'panel.title': '小说工坊',
|
|
10
|
-
'common.close': '关闭',
|
|
11
|
-
'common.loading': '加载中…',
|
|
12
|
-
'common.save': '保存',
|
|
13
|
-
'common.error': '错误',
|
|
14
|
-
'common.success': '成功',
|
|
15
|
-
'common.generating': '生成中…',
|
|
16
|
-
'common.chars': '字',
|
|
17
|
-
'tab.workflow': '工作台',
|
|
18
|
-
'tab.overview': '总纲',
|
|
19
|
-
'tab.plan': '章节',
|
|
20
|
-
'tab.bible': '道藏',
|
|
21
|
-
'tab.foreshadow': '暗线',
|
|
22
|
-
'tab.assistant': 'AI 编辑 Agent',
|
|
23
|
-
'tab.settings': '设置',
|
|
24
|
-
'workflow.title': '创作工作台',
|
|
25
|
-
'workflow.step1': '① 加载总纲',
|
|
26
|
-
'workflow.step2': '② 提炼道藏',
|
|
27
|
-
'workflow.step3': '③ 规划卷',
|
|
28
|
-
'workflow.step4': '④ 生成章节计划',
|
|
29
|
-
'workflow.step5': '⑤ 逐章写作 + AI 审稿',
|
|
30
|
-
'workflow.step6': '⑥ 润色 / 导出',
|
|
31
|
-
'workflow.loadOutline': '读取总纲',
|
|
32
|
-
'workflow.genBible': '提炼道藏',
|
|
33
|
-
'workflow.genVolumes': '生成卷计划',
|
|
34
|
-
'workflow.genPlan': '生成章节计划',
|
|
35
|
-
'workflow.done': '已完成',
|
|
36
|
-
'workflow.todo': '待办',
|
|
37
|
-
'workflow.bibleDone': '道藏已生成({n} 条规则 / {c} 个人物 / {r} 条红线)',
|
|
38
|
-
'workflow.volumesDone': '卷计划已生成({n} 卷)',
|
|
39
|
-
'workflow.planDone': '章节计划已生成({n} 章)',
|
|
40
|
-
'workflow.progress': '进度:总纲 ✓ · 道藏 {bible} · 卷 {volumes} · 计划 {plan} · 已完成 {done}/{total} 章',
|
|
41
|
-
'overview.loadDocx': '从 docx 读取总纲',
|
|
42
|
-
'overview.loadDocxDefault': '读取默认总纲',
|
|
43
|
-
'overview.loadingOutline': '正在解析 docx…',
|
|
44
|
-
'overview.outlineHint': '总纲文本(可编辑)',
|
|
45
|
-
'overview.outlineChars': '总纲字数',
|
|
46
|
-
'overview.saveOutline': '保存总纲',
|
|
47
|
-
'overview.saved': '总纲已保存',
|
|
48
|
-
'overview.bookName': '书名',
|
|
49
|
-
'overview.loadCustom': '指定 docx 路径',
|
|
50
|
-
'overview.loadCustomHint': '绝对路径,留空使用默认',
|
|
51
|
-
'plan.generate': '生成章节计划',
|
|
52
|
-
'plan.generateHint': '章节数量',
|
|
53
|
-
'plan.count': '章',
|
|
54
|
-
'plan.empty': '暂无章节计划,请先生成',
|
|
55
|
-
'plan.chapter': '章',
|
|
56
|
-
'plan.pending': '待生成',
|
|
57
|
-
'plan.generating': '生成中',
|
|
58
|
-
'plan.written': '待审稿',
|
|
59
|
-
'plan.reviewing': '审稿中',
|
|
60
|
-
'plan.approved': '已通过',
|
|
61
|
-
'plan.rejected': '待修订',
|
|
62
|
-
'plan.error': '失败',
|
|
63
|
-
'plan.write': '生成本章',
|
|
64
|
-
'plan.rewrite': '修订',
|
|
65
|
-
'plan.review': '审稿',
|
|
66
|
-
'plan.polish': '去AI味',
|
|
67
|
-
'plan.writeAll': '批量生成全部',
|
|
68
|
-
'plan.writeAllPending': '批量生成剩余',
|
|
69
|
-
'plan.generated': '已生成',
|
|
70
|
-
'plan.progress': '进度',
|
|
71
|
-
'plan.beats': '剧情要点',
|
|
72
|
-
'plan.reviewReport': '审稿报告',
|
|
73
|
-
'plan.reviewScore': '评分',
|
|
74
|
-
'plan.reviewVerdict': '总评',
|
|
75
|
-
'plan.reviewIssues': '问题清单',
|
|
76
|
-
'plan.reviewPass': '通过',
|
|
77
|
-
'plan.reviewFail': '未通过',
|
|
78
|
-
'plan.approve': '手动通过',
|
|
79
|
-
'plan.summary': '章节摘要',
|
|
80
|
-
'plan.volumes': '卷',
|
|
81
|
-
'plan.noVolume': '未分卷',
|
|
82
|
-
'bible.title': '道藏',
|
|
83
|
-
'bible.gen': 'AI 提炼道藏',
|
|
84
|
-
'bible.genre': '题材基调',
|
|
85
|
-
'bible.worldRules': '世界规则',
|
|
86
|
-
'bible.characters': '人物志',
|
|
87
|
-
'bible.redLines': '写作红线',
|
|
88
|
-
'bible.style': '风格要求',
|
|
89
|
-
'bible.none': '尚未生成道藏。生成后写作会严格遵守人设与金手指规则,审稿也会按红线检查。',
|
|
90
|
-
'foreshadow.title': '暗线管理',
|
|
91
|
-
'foreshadow.suggest': 'AI 建议暗线',
|
|
92
|
-
'foreshadow.none': '暂无暗线',
|
|
93
|
-
'foreshadow.status': '状态',
|
|
94
|
-
'foreshadow.planned': '计划中',
|
|
95
|
-
'foreshadow.planted': '已埋设',
|
|
96
|
-
'foreshadow.progressing': '推进中',
|
|
97
|
-
'foreshadow.resolved': '已回收',
|
|
98
|
-
'foreshadow.abandoned': '已放弃',
|
|
99
|
-
'foreshadow.target': '预计回收',
|
|
100
|
-
'foreshadow.plantedAt': '埋设于',
|
|
101
|
-
'foreshadow.setPlanted': '标记已埋设',
|
|
102
|
-
'foreshadow.setResolved': '标记已回收',
|
|
103
|
-
'tab.facts': '编年录',
|
|
104
|
-
'tab.plotlines': '剧情线',
|
|
105
|
-
'plotlines.hint': '主线/支线/人物线/悬念线的推进管理:章节生成时会注入未完结的线,要求每章至少推进一条;关联章节记录推进节点。',
|
|
106
|
-
'plotlines.empty': '还没有剧情线——长篇小说建议先建主线与几条支线。',
|
|
107
|
-
'plotlines.new': '+ 新建剧情线',
|
|
108
|
-
'plotlines.name': '线名',
|
|
109
|
-
'plotlines.kind': '类型',
|
|
110
|
-
'plotlines.kindMain': '主线',
|
|
111
|
-
'plotlines.kindBranch': '支线',
|
|
112
|
-
'plotlines.kindCharacter': '人物线',
|
|
113
|
-
'plotlines.kindMystery': '悬念线',
|
|
114
|
-
'plotlines.goal': '目标/终点',
|
|
115
|
-
'plotlines.progress': '当前进度',
|
|
116
|
-
'plotlines.status': '状态',
|
|
117
|
-
'plotlines.statusActive': '推进中',
|
|
118
|
-
'plotlines.statusPaused': '暂停',
|
|
119
|
-
'plotlines.statusResolved': '已完结',
|
|
120
|
-
'plotlines.statusAbandoned': '已废弃',
|
|
121
|
-
'plotlines.save': '保存',
|
|
122
|
-
'plotlines.cancel': '取消',
|
|
123
|
-
'plotlines.edit': '编辑',
|
|
124
|
-
'plotlines.remove': '删除',
|
|
125
|
-
'plotlines.linkChapter': '关联本章',
|
|
126
|
-
'plotlines.chapters': '已关联',
|
|
127
|
-
'plotlines.workflowTitle': '剧情线进度',
|
|
128
|
-
'plotlines.workflowEmpty': '未建剧情线(去「剧情线」页创建)',
|
|
129
|
-
'sensitive.title': '敏感词检查',
|
|
130
|
-
'sensitive.scanAll': '🔞 全书敏感词检查',
|
|
131
|
-
'sensitive.hint': '内置违禁词库硬匹配(政治/擦边/暴力/辱骂/广告/其他),发布前建议检查;只做提示,是否修改由你判断。',
|
|
132
|
-
'sensitive.clean': '✅ 未命中任何违禁词(扫描 {n} 章)',
|
|
133
|
-
'sensitive.hits': '命中 {n} 处({chapters} 章受影响)',
|
|
134
|
-
'sensitive.goFix': '去修订',
|
|
135
|
-
'sensitive.fixPrefill': '请删除或替换正文中的违禁表达「{word}」(类别:{category}),用含蓄/间接的写法替代。',
|
|
136
|
-
'facts.title': '编年录 / 时间线({n} 条)',
|
|
137
|
-
'facts.hint': '每章生成后自动抽取「已确立事实」(人物状态/境界资源/关系变化/伏笔落地),最近 20 条注入后续章节生成,保证长期一致。',
|
|
138
|
-
'facts.backfill': '回填历史章节事实',
|
|
139
|
-
'facts.backfilled': '已从历史章节回填 {n} 条事实',
|
|
140
|
-
'settings.title': '设置',
|
|
141
|
-
'settings.outlinePath': '默认大纲路径',
|
|
142
|
-
'settings.outputDir': '输出目录',
|
|
143
|
-
'settings.provider': '模型提供商',
|
|
144
|
-
'settings.model': '模型',
|
|
145
|
-
'settings.modelCustom': '自定义…',
|
|
146
|
-
'settings.customProvider': '自定义提供商…',
|
|
147
|
-
'settings.edit': '编辑',
|
|
148
|
-
'settings.delete': '删除',
|
|
149
|
-
'settings.modelCustomPlaceholder': '输入任意模型 id',
|
|
150
|
-
'settings.savedModels': '我的模型',
|
|
151
|
-
'settings.modelCards': '模型卡片(内置 + 已添加)',
|
|
152
|
-
'settings.savedModelName': '名称',
|
|
153
|
-
'settings.addModel': '添加模型',
|
|
154
|
-
'settings.catalogRefresh': '刷新厂商',
|
|
155
|
-
'settings.refreshModels': '刷新模型',
|
|
156
|
-
'settings.savedModelsHint': '手动添加常用模型(provider + 模型 id),点击即可切换当前模型;厂商模式会写入 DSH 凭据并注册路由。',
|
|
157
|
-
'settings.testConnection': '测试连通',
|
|
158
|
-
'settings.testRunning': '测试中',
|
|
159
|
-
'settings.testOk': '连通',
|
|
160
|
-
'settings.testFail': '失败',
|
|
161
|
-
'settings.testHint': '真实发送一次最小调用(约几十 token),验证 Key / 端点 / 模型可用。',
|
|
162
|
-
'settings.reasoningSection': '推理强度',
|
|
163
|
-
'settings.reasoningEffort': '写作类任务思考强度 (reasoningEffort)',
|
|
164
|
-
'settings.reasoningHint': '章节生成/润色/重写/审稿等写作任务使用;关掉更省更快。',
|
|
165
|
-
'settings.analysisReasoning': '分析类任务思考强度(提炼/拆书/反推大纲)',
|
|
166
|
-
'settings.analysisReasoningHint': '角色/场景/道藏提炼、拆书、反推大纲等一次性分析任务默认 low,保证质量,不受上面写作档位影响。',
|
|
167
|
-
'settings.reasoning.off': 'off · 关闭思考(推荐日常写文)',
|
|
168
|
-
'settings.reasoning.low': 'low · 低思考',
|
|
169
|
-
'settings.reasoning.high': 'high · 高思考',
|
|
170
|
-
'settings.reasoning.max': 'max · 最高思考(质检/推演)',
|
|
171
|
-
'settings.theme': '主题',
|
|
172
|
-
'settings.themeLiquid': 'iOS 液态玻璃(绿)',
|
|
173
|
-
'settings.themeNeumorph': '新拟物(双阴影)',
|
|
174
|
-
'settings.themeHint': '存于浏览器本地,即时生效',
|
|
175
|
-
'settings.chapterChars': '每章目标字数',
|
|
176
|
-
'settings.maxTokens': '单章最大输出 tokens',
|
|
177
|
-
'settings.reviewPassScore': '审稿通过分数(0-100)',
|
|
178
|
-
'settings.editorFontSize': '编辑器字号(仅编辑页显示)',
|
|
179
|
-
'settings.editorFontSizeHint': '存于浏览器本地,不影响其他设备',
|
|
180
|
-
'settings.autoReview': '生成后自动审稿',
|
|
181
|
-
'settings.autoAuthorReview': '生成后自动作者复盘',
|
|
182
|
-
'settings.autoAuthorReviewHint': '复盘钩子兑现/结尾钩子/剧情线推进/连续性/节奏趋势,每章约 2000 token',
|
|
183
|
-
'settings.autoReviewAfterRevise': '修订后自动审查',
|
|
184
|
-
'settings.autoReviewAfterReviseHint': '按意见修订/润色产出草稿后自动附带一次 AI 审查,直接显示新稿评分与剩余问题(每章约 2-3k token,可关)',
|
|
185
|
-
'settings.save': '保存设置',
|
|
186
|
-
'settings.saved': '设置已保存',
|
|
187
|
-
'settings.openFolder': '打开输出文件夹',
|
|
188
|
-
'settings.export': '导出',
|
|
189
|
-
'settings.exportTxt': '导出 TXT',
|
|
190
|
-
'settings.exportMd': '导出 Markdown',
|
|
191
|
-
'settings.exported': '已导出:{file}({chars} 字,{chapters} 章)',
|
|
192
|
-
'progress.generating': '正在生成第 {no} 章《{title}》…',
|
|
193
|
-
'progress.done': '第 {no} 章完成({chars} 字)→ {file}',
|
|
194
|
-
'progress.reviewed': '第 {no} 章审稿:{score} 分 — {verdict}',
|
|
195
|
-
'progress.error': '第 {no} 章失败:{message}',
|
|
196
|
-
'progress.rewriting': '正在修订第 {no} 章…',
|
|
197
|
-
'progress.polishing': '正在润色第 {no} 章…',
|
|
198
|
-
'progress.empty': '生成/审稿进度将显示在这里',
|
|
199
|
-
'assistant.hint': '和 AI 编辑 Agent 讨论剧情、人设、暗线;达成一致后可让它直接修改总纲、道藏、章节内容。',
|
|
200
|
-
'assistant.placeholder': '例如:我想让第 2 章结尾加一个悬念——墟境里传来爷爷的声音…',
|
|
201
|
-
'assistant.send': '发送',
|
|
202
|
-
'assistant.toolStart': '⚙ 执行操作:{name}…',
|
|
203
|
-
'assistant.toolDone': '✓ {name} 完成:{detail}',
|
|
204
|
-
'assistant.toolError': '✗ {name} 失败:{detail}',
|
|
205
|
-
'assistant.empty': '还没有对话。和 AI 编辑 Agent 聊聊剧情吧。',
|
|
206
|
-
'status.projectNone': '输出目录中还没有项目。请先加载大纲。',
|
|
207
|
-
'status.files': '已生成文件',
|
|
208
|
-
'api.error': '请求失败',
|
|
209
|
-
} as const
|
|
210
|
-
|
|
211
|
-
/** en dictionary (fallback). */
|
|
212
|
-
export const en: Record<string, string> = {
|
|
213
|
-
'entry.label': 'Novel Forge',
|
|
214
|
-
'entry.tooltip': 'AI novel workbench: outline → bible → volumes → plan → write + review',
|
|
215
|
-
'panel.title': 'Novel Forge',
|
|
216
|
-
'common.close': 'Close',
|
|
217
|
-
'common.loading': 'Loading…',
|
|
218
|
-
'common.save': 'Save',
|
|
219
|
-
'common.error': 'Error',
|
|
220
|
-
'common.success': 'Success',
|
|
221
|
-
'common.generating': 'Generating…',
|
|
222
|
-
'common.chars': ' chars',
|
|
223
|
-
'tab.workflow': 'Workbench',
|
|
224
|
-
'tab.overview': 'Outline',
|
|
225
|
-
'tab.plan': 'Chapters',
|
|
226
|
-
'tab.bible': 'Bible',
|
|
227
|
-
'tab.foreshadow': 'Foreshadow',
|
|
228
|
-
'tab.settings': 'Settings',
|
|
229
|
-
'workflow.title': 'Writing workbench',
|
|
230
|
-
'workflow.step1': '① Load outline',
|
|
231
|
-
'workflow.step2': '② Extract story bible',
|
|
232
|
-
'workflow.step3': '③ Plan volumes',
|
|
233
|
-
'workflow.step4': '④ Plan chapters',
|
|
234
|
-
'workflow.step5': '⑤ Write + AI review',
|
|
235
|
-
'workflow.step6': '⑥ Polish / export',
|
|
236
|
-
'workflow.loadOutline': 'Load outline',
|
|
237
|
-
'workflow.genBible': 'Extract bible',
|
|
238
|
-
'workflow.genVolumes': 'Plan volumes',
|
|
239
|
-
'workflow.genPlan': 'Plan chapters',
|
|
240
|
-
'workflow.done': 'done',
|
|
241
|
-
'workflow.todo': 'todo',
|
|
242
|
-
'workflow.bibleDone': 'Bible ready ({n} rules / {c} characters / {r} red lines)',
|
|
243
|
-
'workflow.volumesDone': 'Volumes ready ({n})',
|
|
244
|
-
'workflow.planDone': 'Plan ready ({n} chapters)',
|
|
245
|
-
'workflow.progress': 'Outline ✓ · bible {bible} · volumes {volumes} · plan {plan} · {done}/{total} chapters',
|
|
246
|
-
'overview.loadDocx': 'Load outline from docx',
|
|
247
|
-
'overview.loadDocxDefault': 'Load default outline',
|
|
248
|
-
'overview.loadingOutline': 'Parsing docx…',
|
|
249
|
-
'overview.outlineHint': 'Outline text (editable)',
|
|
250
|
-
'overview.outlineChars': 'Outline length',
|
|
251
|
-
'overview.saveOutline': 'Save outline',
|
|
252
|
-
'overview.saved': 'Outline saved',
|
|
253
|
-
'overview.bookName': 'Book',
|
|
254
|
-
'overview.loadCustom': 'Custom docx path',
|
|
255
|
-
'overview.loadCustomHint': 'Absolute path; empty = default',
|
|
256
|
-
'plan.generate': 'Plan chapters',
|
|
257
|
-
'plan.generateHint': 'Chapter count',
|
|
258
|
-
'plan.count': ' chapters',
|
|
259
|
-
'plan.empty': 'No plan yet — generate one first',
|
|
260
|
-
'plan.chapter': 'Ch.',
|
|
261
|
-
'plan.pending': 'pending',
|
|
262
|
-
'plan.generating': 'writing',
|
|
263
|
-
'plan.written': 'to review',
|
|
264
|
-
'plan.reviewing': 'reviewing',
|
|
265
|
-
'plan.approved': 'approved',
|
|
266
|
-
'plan.rejected': 'to revise',
|
|
267
|
-
'plan.error': 'failed',
|
|
268
|
-
'plan.write': 'Write',
|
|
269
|
-
'plan.rewrite': 'Revise',
|
|
270
|
-
'plan.review': 'Review',
|
|
271
|
-
'plan.polish': 'De-AI',
|
|
272
|
-
'plan.writeAll': 'Write all',
|
|
273
|
-
'plan.writeAllPending': 'Write remaining',
|
|
274
|
-
'plan.generated': 'generated',
|
|
275
|
-
'plan.progress': 'progress',
|
|
276
|
-
'plan.beats': 'Beats',
|
|
277
|
-
'plan.reviewReport': 'Review report',
|
|
278
|
-
'plan.reviewScore': 'Score',
|
|
279
|
-
'plan.reviewVerdict': 'Verdict',
|
|
280
|
-
'plan.reviewIssues': 'Issues',
|
|
281
|
-
'plan.reviewPass': 'Passed',
|
|
282
|
-
'plan.reviewFail': 'Failed',
|
|
283
|
-
'plan.approve': 'Approve',
|
|
284
|
-
'plan.summary': 'Summary',
|
|
285
|
-
'plan.volumes': 'Volumes',
|
|
286
|
-
'plan.noVolume': 'No volume',
|
|
287
|
-
'bible.title': 'Story bible',
|
|
288
|
-
'bible.gen': 'Extract bible with AI',
|
|
289
|
-
'bible.genre': 'Genre',
|
|
290
|
-
'bible.worldRules': 'World rules',
|
|
291
|
-
'bible.characters': 'Characters',
|
|
292
|
-
'bible.redLines': 'Red lines',
|
|
293
|
-
'bible.style': 'Style',
|
|
294
|
-
'bible.none': 'No bible yet. Generation and review follow it strictly once extracted.',
|
|
295
|
-
'foreshadow.title': 'Foreshadowing',
|
|
296
|
-
'foreshadow.suggest': 'Suggest with AI',
|
|
297
|
-
'foreshadow.none': 'No foreshadows',
|
|
298
|
-
'foreshadow.status': 'Status',
|
|
299
|
-
'foreshadow.planned': 'planned',
|
|
300
|
-
'foreshadow.planted': 'planted',
|
|
301
|
-
'foreshadow.progressing': 'progressing',
|
|
302
|
-
'foreshadow.resolved': 'resolved',
|
|
303
|
-
'foreshadow.abandoned': 'abandoned',
|
|
304
|
-
'foreshadow.target': 'target',
|
|
305
|
-
'foreshadow.plantedAt': 'planted at',
|
|
306
|
-
'foreshadow.setPlanted': 'Mark planted',
|
|
307
|
-
'foreshadow.setResolved': 'Mark resolved',
|
|
308
|
-
'settings.title': 'Settings',
|
|
309
|
-
'settings.outlinePath': 'Default outline path',
|
|
310
|
-
'settings.outputDir': 'Output directory',
|
|
311
|
-
'settings.provider': 'Provider',
|
|
312
|
-
'settings.model': 'Model',
|
|
313
|
-
'settings.modelCustom': 'Custom…',
|
|
314
|
-
'settings.customProvider': 'Custom provider…',
|
|
315
|
-
'settings.edit': 'Edit',
|
|
316
|
-
'settings.delete': 'Delete',
|
|
317
|
-
'settings.modelCustomPlaceholder': 'Type any model id',
|
|
318
|
-
'settings.savedModels': 'My Models',
|
|
319
|
-
'settings.modelCards': 'Model cards (built-in + added)',
|
|
320
|
-
'settings.savedModelName': 'Name',
|
|
321
|
-
'settings.addModel': 'Add model',
|
|
322
|
-
'settings.catalogRefresh': 'Refresh vendors',
|
|
323
|
-
'settings.refreshModels': 'Refresh models',
|
|
324
|
-
'settings.savedModelsHint': 'Manually add a model (provider + model id); click to switch. Vendor mode writes the key into DSH credentials and registers the route.',
|
|
325
|
-
'settings.testConnection': 'Test',
|
|
326
|
-
'settings.testRunning': 'Testing',
|
|
327
|
-
'settings.testOk': 'OK',
|
|
328
|
-
'settings.testFail': 'Failed',
|
|
329
|
-
'settings.testHint': 'Sends one minimal real call (~dozens of tokens) to verify key / endpoint / model.',
|
|
330
|
-
'settings.reasoningSection': 'Reasoning',
|
|
331
|
-
'settings.reasoningEffort': 'Writing-task reasoning effort',
|
|
332
|
-
'settings.analysisReasoning': 'Analysis-task reasoning (extract / breakdown / reverse outline)',
|
|
333
|
-
'settings.analysisReasoningHint': 'One-off analysis tasks (roles/scenes/bible extract, breakdown, reverse outline) default to low; not affected by the writing-level switch.',
|
|
334
|
-
'settings.reasoning.off': 'off · no thinking (recommended)',
|
|
335
|
-
'settings.reasoning.low': 'low',
|
|
336
|
-
'settings.reasoning.high': 'high',
|
|
337
|
-
'settings.reasoning.max': 'max (audit/reasoning)',
|
|
338
|
-
'settings.reasoningHint': 'off is fastest/cheapest; high/max are slower and stronger',
|
|
339
|
-
'settings.theme': 'Theme',
|
|
340
|
-
'settings.themeLiquid': 'iOS Liquid Glass (green)',
|
|
341
|
-
'settings.themeClassic': 'Classic frosted (blue)',
|
|
342
|
-
'settings.themeNeumorph': 'Neumorphism (dual-shadow)',
|
|
343
|
-
'settings.themeHint': 'Stored in browser localStorage; applies instantly',
|
|
344
|
-
'settings.chapterChars': 'Chars per chapter',
|
|
345
|
-
'settings.maxTokens': 'Max output tokens',
|
|
346
|
-
'settings.reviewPassScore': 'Review pass score (0-100)',
|
|
347
|
-
'settings.editorFontSize': 'Editor font size (workspace only)',
|
|
348
|
-
'settings.editorFontSizeHint': 'Stored in browser localStorage; display-only',
|
|
349
|
-
'settings.autoReview': 'Auto-review after writing',
|
|
350
|
-
'settings.autoAuthorReview': 'Auto author review after writing',
|
|
351
|
-
'settings.autoAuthorReviewHint': 'Reviews hook payoff / ending hook / plotline progress / continuity / pacing trend (~2000 tokens per chapter)',
|
|
352
|
-
'settings.autoReviewAfterRevise': 'Auto review after revise',
|
|
353
|
-
'settings.autoReviewAfterReviseHint': 'Automatically runs one AI review on the produced draft after revise/polish, showing the new score and remaining issues (~2-3k tokens, can be disabled)',
|
|
354
|
-
'settings.save': 'Save settings',
|
|
355
|
-
'settings.saved': 'Settings saved',
|
|
356
|
-
'settings.openFolder': 'Open output folder',
|
|
357
|
-
'settings.export': 'Export',
|
|
358
|
-
'settings.exportTxt': 'Export TXT',
|
|
359
|
-
'settings.exportMd': 'Export Markdown',
|
|
360
|
-
'settings.exported': 'Exported: {file} ({chars} chars, {chapters} chapters)',
|
|
361
|
-
'tab.facts': 'Chronicle',
|
|
362
|
-
'tab.plotlines': 'Plotlines',
|
|
363
|
-
'plotlines.hint': 'Track main/branch/character/mystery arcs: unfinished arcs are injected into chapter writing so each chapter advances at least one; linked chapters record progress nodes.',
|
|
364
|
-
'plotlines.empty': 'No plotlines yet — consider creating the main arc and a few branches.',
|
|
365
|
-
'plotlines.new': '+ New plotline',
|
|
366
|
-
'plotlines.name': 'Name',
|
|
367
|
-
'plotlines.kind': 'Type',
|
|
368
|
-
'plotlines.kindMain': 'Main',
|
|
369
|
-
'plotlines.kindBranch': 'Branch',
|
|
370
|
-
'plotlines.kindCharacter': 'Character',
|
|
371
|
-
'plotlines.kindMystery': 'Mystery',
|
|
372
|
-
'plotlines.goal': 'Goal',
|
|
373
|
-
'plotlines.progress': 'Current progress',
|
|
374
|
-
'plotlines.status': 'Status',
|
|
375
|
-
'plotlines.statusActive': 'Active',
|
|
376
|
-
'plotlines.statusPaused': 'Paused',
|
|
377
|
-
'plotlines.statusResolved': 'Resolved',
|
|
378
|
-
'plotlines.statusAbandoned': 'Abandoned',
|
|
379
|
-
'plotlines.save': 'Save',
|
|
380
|
-
'plotlines.cancel': 'Cancel',
|
|
381
|
-
'plotlines.edit': 'Edit',
|
|
382
|
-
'plotlines.remove': 'Remove',
|
|
383
|
-
'plotlines.linkChapter': 'Link chapter',
|
|
384
|
-
'plotlines.chapters': 'Linked',
|
|
385
|
-
'plotlines.workflowTitle': 'Plotline progress',
|
|
386
|
-
'plotlines.workflowEmpty': 'No plotlines yet (create them in the Plotlines tab)',
|
|
387
|
-
'sensitive.title': 'Sensitive-word check',
|
|
388
|
-
'sensitive.scanAll': '🔞 Scan all chapters',
|
|
389
|
-
'sensitive.hint': 'Built-in banned-word library (politics/erotica/violence/abuse/ads/other). Run before publishing; suggestions only.',
|
|
390
|
-
'sensitive.clean': '✅ No banned words found ({n} chapters scanned)',
|
|
391
|
-
'sensitive.hits': '{n} hits across {chapters} chapters',
|
|
392
|
-
'sensitive.goFix': 'Fix',
|
|
393
|
-
'sensitive.fixPrefill': 'Remove or rephrase the banned expression "{word}" (category: {category}) with an indirect alternative.',
|
|
394
|
-
'facts.title': 'Chronicle / timeline ({n} facts)',
|
|
395
|
-
'facts.hint': 'Auto-extracted established facts per chapter (state/resources/relations/foreshadow landings); the latest 20 are injected into later chapters for consistency.',
|
|
396
|
-
'facts.backfill': 'Backfill facts from historical chapters',
|
|
397
|
-
'facts.backfilled': 'Backfilled {n} facts from historical chapters',
|
|
398
|
-
'progress.generating': 'Writing chapter {no} “{title}”…',
|
|
399
|
-
'progress.done': 'Chapter {no} done ({chars} chars) → {file}',
|
|
400
|
-
'progress.reviewed': 'Chapter {no} review: {score} — {verdict}',
|
|
401
|
-
'progress.error': 'Chapter {no} failed: {message}',
|
|
402
|
-
'progress.rewriting': 'Revising chapter {no}…',
|
|
403
|
-
'progress.polishing': 'Polishing chapter {no}…',
|
|
404
|
-
'progress.empty': 'Generation/review progress appears here',
|
|
405
|
-
'assistant.hint': 'Discuss plot, characters, foreshadowing with the AI Editor Agent; once agreed, let it edit the outline, bible, or chapters directly.',
|
|
406
|
-
'assistant.placeholder': 'e.g. Add a hook at the end of chapter 2…',
|
|
407
|
-
'assistant.send': 'Send',
|
|
408
|
-
'assistant.toolStart': '⚙ Running {name}…',
|
|
409
|
-
'assistant.toolDone': '✓ {name} done: {detail}',
|
|
410
|
-
'assistant.toolError': '✗ {name} failed: {detail}',
|
|
411
|
-
'assistant.empty': 'No conversation yet. Chat with the AI editor.',
|
|
412
|
-
'status.projectNone': 'No project in the output directory yet. Load an outline first.',
|
|
413
|
-
'status.files': 'Generated files',
|
|
414
|
-
'api.error': 'Request failed',
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/** Keys type for the zh dictionary. */
|
|
418
|
-
export type NovelKey = keyof typeof zh
|