@waterwx/dsh-novel-forge 1.3.2 → 1.3.3-alpha
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 +288 -279
- package/lib/client.js +56288 -12061
- package/lib/index.js +14655 -5573
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +30 -0
- package/lib/types/assets.d.ts +22 -9
- package/lib/types/assistant.d.ts +29 -0
- package/lib/types/author-assets.d.ts +23 -0
- package/lib/types/bookshelf.d.ts +18 -3
- package/lib/types/client/api.d.ts +187 -10
- package/lib/types/client/chip-prefs.d.ts +36 -0
- package/lib/types/client/docx.d.ts +2 -2
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/llmLive.d.ts +31 -0
- package/lib/types/client/locales.d.ts +29 -67
- package/lib/types/client/mount.d.ts +0 -2
- package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
- package/lib/types/client/panel/AiTag.d.ts +1 -0
- package/lib/types/client/panel/AssistantTab.d.ts +4 -1
- package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
- package/lib/types/client/panel/AuthorHome.d.ts +34 -0
- package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
- package/lib/types/client/panel/CmdPalette.d.ts +12 -0
- package/lib/types/client/panel/CreateBookView.d.ts +5 -1
- package/lib/types/client/panel/DirectorView.d.ts +7 -0
- package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
- package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
- package/lib/types/client/panel/ImportModal.d.ts +7 -0
- package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
- package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
- package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +14 -0
- package/lib/types/client/panel/NovelPanel.d.ts +6 -1
- package/lib/types/client/panel/PageHeader.d.ts +18 -0
- package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
- package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
- package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
- package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/client/panel/SettingsView.d.ts +19 -0
- package/lib/types/client/panel/ShelfView.d.ts +12 -1
- 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/helpers.d.ts +5 -3
- package/lib/types/client/panel/market-radar-state.d.ts +56 -0
- package/lib/types/client/panel/toast.d.ts +2 -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/client/text.d.ts +9 -0
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +186 -45
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/home.d.ts +11 -0
- package/lib/types/index.d.ts +36 -10
- package/lib/types/llm-live.d.ts +70 -0
- package/lib/types/llm-retry.d.ts +38 -0
- package/lib/types/market-radar-scan.d.ts +18 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/model-capability.d.ts +93 -0
- package/lib/types/novel-context.d.ts +80 -0
- package/lib/types/output-budget.d.ts +86 -0
- package/lib/types/prompt-slots.d.ts +57 -0
- package/lib/types/prompt-utils.d.ts +35 -0
- package/lib/types/prompting.d.ts +10 -0
- package/lib/types/protocol.d.ts +1378 -166
- package/lib/types/review-policy.d.ts +97 -0
- package/lib/types/revision.d.ts +76 -0
- package/lib/types/routes.d.ts +9 -3
- package/lib/types/run.d.ts +23 -1
- 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/tension.d.ts +53 -0
- package/lib/types/timeline.d.ts +37 -0
- package/package.json +54 -28
- package/src/action-guard.ts +127 -0
- package/src/ai-scan.ts +155 -0
- package/src/assets.ts +1801 -520
- package/src/assistant.ts +1098 -770
- package/src/author-assets.ts +131 -0
- package/src/bookshelf.ts +193 -151
- package/src/client/api.ts +380 -23
- package/src/client/chip-prefs.ts +121 -0
- package/src/client/docx.ts +32 -69
- package/src/client/index.ts +3 -1
- package/src/client/llmLive.ts +111 -0
- package/src/client/locales.ts +29 -253
- package/src/client/mount.tsx +4 -3
- package/src/client/panel/AdaptModeView.tsx +535 -0
- package/src/client/panel/AiTag.tsx +7 -0
- package/src/client/panel/AssetsTab.tsx +448 -458
- package/src/client/panel/AssistantTab.tsx +355 -301
- package/src/client/panel/AuthorAssetsView.tsx +206 -0
- package/src/client/panel/AuthorHome.tsx +309 -0
- package/src/client/panel/BookAnalysisView.tsx +63 -0
- package/src/client/panel/CmdPalette.tsx +102 -0
- package/src/client/panel/CreateBookView.tsx +366 -348
- package/src/client/panel/DirectorView.tsx +200 -0
- package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
- package/src/client/panel/IdeaInspirationView.tsx +76 -0
- package/src/client/panel/ImportModal.tsx +303 -0
- package/src/client/panel/KnowledgeBaseView.tsx +125 -0
- package/src/client/panel/LiveFeedLog.tsx +204 -0
- package/src/client/panel/MarketRadarView.tsx +415 -0
- package/src/client/panel/ModelManager.tsx +205 -0
- package/src/client/panel/NovelPanel.tsx +3013 -2500
- package/src/client/panel/PageHeader.tsx +37 -0
- package/src/client/panel/ProgressConsole.tsx +63 -0
- package/src/client/panel/PromptSlotsView.tsx +97 -0
- package/src/client/panel/ReaderView.tsx +306 -306
- package/src/client/panel/ReasoningSection.tsx +31 -0
- package/src/client/panel/RunPanel.tsx +64 -32
- package/src/client/panel/SettingsView.tsx +355 -0
- package/src/client/panel/ShelfView.tsx +266 -236
- package/src/client/panel/SubPage.tsx +42 -0
- package/src/client/panel/TensionView.tsx +212 -0
- package/src/client/panel/TimelineView.tsx +208 -0
- package/src/client/panel/WorldTab.tsx +202 -202
- package/src/client/panel/helpers.ts +7 -5
- package/src/client/panel/market-radar-state.ts +79 -0
- package/src/client/panel/panel.module.css +4481 -1655
- package/src/client/panel/toast.ts +51 -0
- package/src/client/panel/views.tsx +316 -314
- package/src/client/run-status.ts +171 -0
- package/src/client/sidebar-entry.ts +4 -4
- package/src/client/text.ts +28 -0
- package/src/docx-core.ts +53 -0
- package/src/docx.ts +44 -83
- package/src/engine.ts +4759 -2943
- package/src/global-assets.ts +58 -0
- package/src/home.ts +33 -0
- package/src/index.ts +206 -65
- package/src/llm-live.ts +245 -0
- package/src/llm-retry.ts +77 -0
- package/src/market-radar-scan.ts +35 -0
- package/src/market-radar-sources.ts +173 -0
- package/src/migrate-output.ts +76 -0
- package/src/model-capability.ts +215 -0
- package/src/novel-context.ts +286 -0
- package/src/output-budget.ts +147 -0
- package/src/prompt-slots.ts +125 -0
- package/src/prompting.ts +26 -0
- package/src/protocol.ts +1405 -158
- package/src/revision.ts +283 -0
- package/src/routes.ts +1959 -436
- package/src/run.ts +118 -21
- package/src/settings-persist.ts +114 -0
- package/src/snapshots.ts +174 -0
- package/src/stage-contract.ts +186 -0
- package/src/tension.ts +251 -0
- package/src/timeline.ts +159 -0
- package/lib/client.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types/comic-presets.d.ts +0 -12
- package/scripts/assistant-frames.txt +0 -8
- package/scripts/diagnose-adapter.mts +0 -81
- package/scripts/diagnose-assistant.mjs +0 -58
- package/scripts/diagnose-bible.mjs +0 -72
- package/scripts/diagnose-harness.mts +0 -75
- package/scripts/novel-forge-restart.log +0 -10
- package/scripts/novel-forge-web.stderr.log +0 -2
- package/scripts/novel-forge-web.stdout.log +0 -1
- package/scripts/outline-sample.txt +0 -449
- package/scripts/probe.mjs +0 -6
- package/scripts/restart-web.ps1 +0 -88
- package/scripts/smoke.mts +0 -72
- package/scripts/upload-github.ps1 +0 -51
- package/src/client/panel/BookshelfBar.tsx +0 -170
- package/src/comic-presets.ts +0 -67
package/src/client/docx.ts
CHANGED
|
@@ -1,69 +1,32 @@
|
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
}
|
|
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
|
+
* XML 解析共享核心在 ../docx-core.ts(与服务端同源)。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { unzipSync, strFromU8 } from 'fflate/browser'
|
|
16
|
+
import { extractDocxTextFromXml } from '../docx-core.ts'
|
|
17
|
+
|
|
18
|
+
/** Extract plain text from a docx buffer: one line per <w:p> paragraph. */
|
|
19
|
+
export function extractDocxTextFromBuffer(buffer: ArrayBuffer | Uint8Array): string {
|
|
20
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer)
|
|
21
|
+
let files: ReturnType<typeof unzipSync>
|
|
22
|
+
try {
|
|
23
|
+
files = unzipSync(bytes)
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(`不是有效的 docx(zip 解压失败):${(error as Error).message}`)
|
|
26
|
+
}
|
|
27
|
+
const document = files['word/document.xml']
|
|
28
|
+
if (document === undefined) {
|
|
29
|
+
throw new Error('不是有效的 docx(缺少 word/document.xml)')
|
|
30
|
+
}
|
|
31
|
+
return extractDocxTextFromXml(strFromU8(document))
|
|
32
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* problems are logged, never thrown — the web shell fails the whole boot when
|
|
5
5
|
* a plugin apply throws.
|
|
6
6
|
*/
|
|
7
|
-
import type { ClientContext } from '@deepseek-ai/
|
|
7
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
8
8
|
import { NovelApi } from './api.ts'
|
|
9
9
|
import { mountPanel } from './mount.tsx'
|
|
10
10
|
import { PanelController } from './panel/controller.ts'
|
|
11
11
|
import { mountSidebarEntry } from './sidebar-entry.ts'
|
|
12
|
+
import { mountRunStatus } from './run-status.ts'
|
|
12
13
|
|
|
13
14
|
/** Required services (fiber inject waiting). */
|
|
14
15
|
export const inject = ['slots', 'locale']
|
|
@@ -24,6 +25,7 @@ export function apply(ctx: ClientContext): void {
|
|
|
24
25
|
try {
|
|
25
26
|
disposers.push(mountSidebarEntry(controller))
|
|
26
27
|
disposers.push(mountPanel(controller, api))
|
|
28
|
+
disposers.push(mountRunStatus(controller, api))
|
|
27
29
|
} catch (error) {
|
|
28
30
|
// DOM failures degrade the panel, never the GUI.
|
|
29
31
|
console.warn('[dsh-novel-forge] mount failed:', error)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器端「AI 创作实况」钩子:连接 /llm-live/stream (SSE),按 LLM 调用分组会话。
|
|
3
|
+
* 轻量实现,不持久化缓存(对齐上游 useLlmLiveFeed 的核心行为)。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
7
|
+
import { NOVEL_API, type LlmLiveFrame, type LlmTokenUsage } from '../protocol.ts'
|
|
8
|
+
|
|
9
|
+
// 帧结构定义在 protocol.ts(宿主与浏览器共用同一份线上形状),此处只再导出。
|
|
10
|
+
export type { LlmLiveFrame }
|
|
11
|
+
|
|
12
|
+
export interface LlmLiveSession {
|
|
13
|
+
sessionId: string
|
|
14
|
+
label: string
|
|
15
|
+
model?: string
|
|
16
|
+
phase: 'requesting' | 'streaming' | 'completed' | 'failed'
|
|
17
|
+
phaseMessage: string
|
|
18
|
+
preview: string
|
|
19
|
+
totalChars: number
|
|
20
|
+
startedAt: string
|
|
21
|
+
updatedAt: string
|
|
22
|
+
/** 本次调用的 token 用量(供应商未上报时缺省)。 */
|
|
23
|
+
usage?: LlmTokenUsage
|
|
24
|
+
/** 本次调用耗时(毫秒)。 */
|
|
25
|
+
elapsedMs?: number
|
|
26
|
+
/** 首字耗时(毫秒)。 */
|
|
27
|
+
firstTokenMs?: number
|
|
28
|
+
/** 是否保存了可查看的 Prompt。 */
|
|
29
|
+
hasPrompt?: boolean
|
|
30
|
+
/** Prompt 字符数(system + user)。 */
|
|
31
|
+
promptChars?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const MAX_PREVIEW_CHARS = 20_000
|
|
35
|
+
|
|
36
|
+
function applyFrame(current: Record<string, LlmLiveSession>, frame: LlmLiveFrame): Record<string, LlmLiveSession> {
|
|
37
|
+
const next = { ...current }
|
|
38
|
+
const id = frame.sessionId
|
|
39
|
+
let session = next[id]
|
|
40
|
+
if (frame.type === 'session_started') {
|
|
41
|
+
session = {
|
|
42
|
+
sessionId: id,
|
|
43
|
+
label: frame.label ?? 'LLM 调用',
|
|
44
|
+
model: frame.model,
|
|
45
|
+
phase: 'requesting',
|
|
46
|
+
phaseMessage: '正在连接模型',
|
|
47
|
+
preview: '',
|
|
48
|
+
totalChars: 0,
|
|
49
|
+
startedAt: frame.at,
|
|
50
|
+
updatedAt: frame.at,
|
|
51
|
+
hasPrompt: frame.hasPrompt,
|
|
52
|
+
promptChars: frame.promptChars,
|
|
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 = {
|
|
70
|
+
...session,
|
|
71
|
+
phase: frame.phase ?? 'completed',
|
|
72
|
+
phaseMessage: frame.phase === 'failed' ? (frame.error ?? '调用失败') : '模型结果已准备完成',
|
|
73
|
+
totalChars: frame.totalChars ?? session.totalChars,
|
|
74
|
+
preview: session.preview || (frame.preview ?? ''),
|
|
75
|
+
updatedAt: frame.at,
|
|
76
|
+
usage: frame.usage ?? session.usage,
|
|
77
|
+
elapsedMs: frame.elapsedMs ?? session.elapsedMs,
|
|
78
|
+
firstTokenMs: frame.firstTokenMs ?? session.firstTokenMs,
|
|
79
|
+
}
|
|
80
|
+
next[id] = session
|
|
81
|
+
}
|
|
82
|
+
return next
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function useLlmLiveFeed(enabled = true): { connected: boolean; sessions: LlmLiveSession[] } {
|
|
86
|
+
const [sessions, setSessions] = useState<Record<string, LlmLiveSession>>({})
|
|
87
|
+
const [connected, setConnected] = useState(false)
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!enabled) {
|
|
90
|
+
setSessions({})
|
|
91
|
+
setConnected(false)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
const es = new EventSource(NOVEL_API.llmLive)
|
|
95
|
+
es.onopen = () => setConnected(true)
|
|
96
|
+
es.onmessage = (ev) => {
|
|
97
|
+
try {
|
|
98
|
+
const frame = JSON.parse(ev.data as string) as LlmLiveFrame
|
|
99
|
+
setSessions(prev => applyFrame(prev, frame))
|
|
100
|
+
} catch { /* ignore malformed frame */ }
|
|
101
|
+
}
|
|
102
|
+
// EventSource 断线会自动重连;这里只在断线瞬间标记未连接,重连成功后会再次 onopen。
|
|
103
|
+
es.onerror = () => setConnected(false)
|
|
104
|
+
return () => {
|
|
105
|
+
es.close()
|
|
106
|
+
setConnected(false)
|
|
107
|
+
}
|
|
108
|
+
}, [enabled])
|
|
109
|
+
const list = useMemo(() => Object.values(sessions).sort((a, b) => a.startedAt.localeCompare(b.startedAt)), [sessions])
|
|
110
|
+
return { connected, sessions: list }
|
|
111
|
+
}
|
package/src/client/locales.ts
CHANGED
|
@@ -1,58 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dsh-novel-forge —
|
|
2
|
+
* dsh-novel-forge — 面板文案词典(简体中文单语)。
|
|
3
|
+
*
|
|
4
|
+
* 定位说明:小说工坊面板是**中文单语界面**——这里就是全部用户可见文案的唯一定义处,
|
|
5
|
+
* 键位即类型(`NovelKey`),新增文案请同时在此登记,不要散落到组件里硬编码。
|
|
6
|
+
* 文档层(README / docs / CHANGELOG)仍提供英文对照,那是给人读的,不是界面语言。
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
|
-
/**
|
|
9
|
+
/** 面板文案(简体中文)。 */
|
|
6
10
|
export const zh = {
|
|
7
11
|
'entry.label': '小说工坊',
|
|
8
12
|
'entry.tooltip': 'AI 编译小说工作台:总纲 → 道藏 → 卷计划 → 章节计划 → 逐章生成+审稿',
|
|
9
|
-
'panel.title': '小说工坊',
|
|
10
13
|
'common.close': '关闭',
|
|
11
14
|
'common.loading': '加载中…',
|
|
12
|
-
'common.save': '保存',
|
|
13
15
|
'common.error': '错误',
|
|
14
|
-
'common.success': '成功',
|
|
15
|
-
'common.generating': '生成中…',
|
|
16
16
|
'common.chars': '字',
|
|
17
17
|
'tab.workflow': '工作台',
|
|
18
18
|
'tab.overview': '总纲',
|
|
19
19
|
'tab.plan': '章节',
|
|
20
|
-
'tab.
|
|
21
|
-
'tab.foreshadow': '暗线',
|
|
22
|
-
'tab.assistant': 'AI 助手',
|
|
20
|
+
'tab.assistant': 'AI 编辑 Agent',
|
|
23
21
|
'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
22
|
'workflow.genVolumes': '生成卷计划',
|
|
34
|
-
'workflow.genPlan': '生成章节计划',
|
|
35
|
-
'workflow.done': '已完成',
|
|
36
|
-
'workflow.todo': '待办',
|
|
37
23
|
'workflow.bibleDone': '道藏已生成({n} 条规则 / {c} 个人物 / {r} 条红线)',
|
|
38
24
|
'workflow.volumesDone': '卷计划已生成({n} 卷)',
|
|
39
25
|
'workflow.planDone': '章节计划已生成({n} 章)',
|
|
40
|
-
'workflow.progress': '进度:总纲 ✓ · 道藏 {bible} · 卷 {volumes} · 计划 {plan} · 已完成 {done}/{total} 章',
|
|
41
|
-
'overview.loadDocx': '从 docx 读取总纲',
|
|
42
|
-
'overview.loadDocxDefault': '读取默认总纲',
|
|
43
26
|
'overview.loadingOutline': '正在解析 docx…',
|
|
44
27
|
'overview.outlineHint': '总纲文本(可编辑)',
|
|
45
28
|
'overview.outlineChars': '总纲字数',
|
|
46
29
|
'overview.saveOutline': '保存总纲',
|
|
47
30
|
'overview.saved': '总纲已保存',
|
|
48
31
|
'overview.bookName': '书名',
|
|
49
|
-
'overview.loadCustom': '指定 docx 路径',
|
|
50
|
-
'overview.loadCustomHint': '绝对路径,留空使用默认',
|
|
51
32
|
'plan.generate': '生成章节计划',
|
|
52
33
|
'plan.generateHint': '章节数量',
|
|
53
34
|
'plan.count': '章',
|
|
54
|
-
'plan.empty': '暂无章节计划,请先生成',
|
|
55
|
-
'plan.chapter': '章',
|
|
56
35
|
'plan.pending': '待生成',
|
|
57
36
|
'plan.generating': '生成中',
|
|
58
37
|
'plan.written': '待审稿',
|
|
@@ -64,23 +43,17 @@ export const zh = {
|
|
|
64
43
|
'plan.rewrite': '修订',
|
|
65
44
|
'plan.review': '审稿',
|
|
66
45
|
'plan.polish': '去AI味',
|
|
67
|
-
'plan.writeAll': '批量生成全部',
|
|
68
46
|
'plan.writeAllPending': '批量生成剩余',
|
|
69
|
-
'plan.generated': '已生成',
|
|
70
|
-
'plan.progress': '进度',
|
|
71
47
|
'plan.beats': '剧情要点',
|
|
72
48
|
'plan.reviewReport': '审稿报告',
|
|
73
49
|
'plan.reviewScore': '评分',
|
|
74
50
|
'plan.reviewVerdict': '总评',
|
|
75
|
-
'plan.reviewIssues': '问题清单',
|
|
76
51
|
'plan.reviewPass': '通过',
|
|
77
52
|
'plan.reviewFail': '未通过',
|
|
78
|
-
'plan.approve': '手动通过',
|
|
79
53
|
'plan.summary': '章节摘要',
|
|
80
54
|
'plan.volumes': '卷',
|
|
81
|
-
'plan.noVolume': '未分卷',
|
|
82
55
|
'bible.title': '道藏',
|
|
83
|
-
'bible.gen': '
|
|
56
|
+
'bible.gen': '提炼道藏',
|
|
84
57
|
'bible.genre': '题材基调',
|
|
85
58
|
'bible.worldRules': '世界规则',
|
|
86
59
|
'bible.characters': '人物志',
|
|
@@ -88,9 +61,8 @@ export const zh = {
|
|
|
88
61
|
'bible.style': '风格要求',
|
|
89
62
|
'bible.none': '尚未生成道藏。生成后写作会严格遵守人设与金手指规则,审稿也会按红线检查。',
|
|
90
63
|
'foreshadow.title': '暗线管理',
|
|
91
|
-
'foreshadow.suggest': '
|
|
64
|
+
'foreshadow.suggest': '建议暗线',
|
|
92
65
|
'foreshadow.none': '暂无暗线',
|
|
93
|
-
'foreshadow.status': '状态',
|
|
94
66
|
'foreshadow.planned': '计划中',
|
|
95
67
|
'foreshadow.planted': '已埋设',
|
|
96
68
|
'foreshadow.progressing': '推进中',
|
|
@@ -100,7 +72,6 @@ export const zh = {
|
|
|
100
72
|
'foreshadow.plantedAt': '埋设于',
|
|
101
73
|
'foreshadow.setPlanted': '标记已埋设',
|
|
102
74
|
'foreshadow.setResolved': '标记已回收',
|
|
103
|
-
'tab.facts': '编年录',
|
|
104
75
|
'tab.plotlines': '剧情线',
|
|
105
76
|
'plotlines.hint': '主线/支线/人物线/悬念线的推进管理:章节生成时会注入未完结的线,要求每章至少推进一条;关联章节记录推进节点。',
|
|
106
77
|
'plotlines.empty': '还没有剧情线——长篇小说建议先建主线与几条支线。',
|
|
@@ -122,44 +93,42 @@ export const zh = {
|
|
|
122
93
|
'plotlines.cancel': '取消',
|
|
123
94
|
'plotlines.edit': '编辑',
|
|
124
95
|
'plotlines.remove': '删除',
|
|
125
|
-
'plotlines.linkChapter': '关联本章',
|
|
126
96
|
'plotlines.chapters': '已关联',
|
|
127
|
-
'
|
|
128
|
-
'plotlines.workflowEmpty': '未建剧情线(去「剧情线」页创建)',
|
|
129
|
-
'sensitive.title': '敏感词检查',
|
|
130
|
-
'sensitive.scanAll': '🔞 全书敏感词检查',
|
|
97
|
+
'sensitive.scanAll': ' 全书敏感词检查',
|
|
131
98
|
'sensitive.hint': '内置违禁词库硬匹配(政治/擦边/暴力/辱骂/广告/其他),发布前建议检查;只做提示,是否修改由你判断。',
|
|
132
|
-
'sensitive.clean': '
|
|
99
|
+
'sensitive.clean': ' 未命中任何违禁词(扫描 {n} 章)',
|
|
133
100
|
'sensitive.hits': '命中 {n} 处({chapters} 章受影响)',
|
|
134
101
|
'sensitive.goFix': '去修订',
|
|
135
102
|
'sensitive.fixPrefill': '请删除或替换正文中的违禁表达「{word}」(类别:{category}),用含蓄/间接的写法替代。',
|
|
136
103
|
'facts.title': '编年录 / 时间线({n} 条)',
|
|
137
104
|
'facts.hint': '每章生成后自动抽取「已确立事实」(人物状态/境界资源/关系变化/伏笔落地),最近 20 条注入后续章节生成,保证长期一致。',
|
|
138
105
|
'facts.backfill': '回填历史章节事实',
|
|
139
|
-
'facts.backfilled': '已从历史章节回填 {n} 条事实',
|
|
140
|
-
'settings.title': '设置',
|
|
141
|
-
'settings.outlinePath': '默认大纲路径',
|
|
142
106
|
'settings.outputDir': '输出目录',
|
|
143
107
|
'settings.provider': '模型提供商',
|
|
144
108
|
'settings.model': '模型',
|
|
145
109
|
'settings.modelCustom': '自定义…',
|
|
110
|
+
'settings.customProvider': '自定义提供商…',
|
|
111
|
+
'settings.edit': '编辑',
|
|
112
|
+
'settings.delete': '删除',
|
|
146
113
|
'settings.modelCustomPlaceholder': '输入任意模型 id',
|
|
147
|
-
'settings.
|
|
114
|
+
'settings.refreshModels': '刷新模型',
|
|
115
|
+
'settings.testConnection': '测试连通',
|
|
116
|
+
'settings.testRunning': '测试中',
|
|
117
|
+
'settings.testOk': '连通',
|
|
118
|
+
'settings.testFail': '失败',
|
|
119
|
+
'settings.testHint': '真实发送一次最小调用(约几十 token),验证 Key / 端点 / 模型可用。',
|
|
120
|
+
'settings.reasoningSection': '推理强度',
|
|
121
|
+
'settings.reasoningEffort': '写作类任务思考强度 (reasoningEffort)',
|
|
122
|
+
'settings.reasoningHint': '章节生成/润色/重写/审稿等写作任务使用;关掉更省更快。',
|
|
123
|
+
'settings.analysisReasoning': '分析类任务思考强度(提炼/拆书/反推大纲)',
|
|
124
|
+
'settings.analysisReasoningHint': '角色/场景/道藏提炼、拆书、反推大纲等一次性分析任务默认 low,保证质量,不受上面写作档位影响。',
|
|
148
125
|
'settings.reasoning.off': 'off · 关闭思考(推荐日常写文)',
|
|
149
126
|
'settings.reasoning.low': 'low · 低思考',
|
|
150
127
|
'settings.reasoning.high': 'high · 高思考',
|
|
151
128
|
'settings.reasoning.max': 'max · 最高思考(质检/推演)',
|
|
152
|
-
'settings.reasoningHint': 'off 最快最省;high/max 更慢更贵但推理更强',
|
|
153
|
-
'settings.theme': '主题',
|
|
154
|
-
'settings.themeLiquid': 'iOS 液态玻璃(绿)',
|
|
155
|
-
'settings.themeClassic': '经典毛玻璃(蓝)',
|
|
156
|
-
'settings.themeNeumorph': '新拟物(双阴影)',
|
|
157
|
-
'settings.themeHint': '存于浏览器本地,即时生效',
|
|
158
129
|
'settings.chapterChars': '每章目标字数',
|
|
159
130
|
'settings.maxTokens': '单章最大输出 tokens',
|
|
160
131
|
'settings.reviewPassScore': '审稿通过分数(0-100)',
|
|
161
|
-
'settings.editorFontSize': '编辑器字号(仅编辑页显示)',
|
|
162
|
-
'settings.editorFontSizeHint': '存于浏览器本地,不影响其他设备',
|
|
163
132
|
'settings.autoReview': '生成后自动审稿',
|
|
164
133
|
'settings.autoAuthorReview': '生成后自动作者复盘',
|
|
165
134
|
'settings.autoAuthorReviewHint': '复盘钩子兑现/结尾钩子/剧情线推进/连续性/节奏趋势,每章约 2000 token',
|
|
@@ -167,8 +136,8 @@ export const zh = {
|
|
|
167
136
|
'settings.autoReviewAfterReviseHint': '按意见修订/润色产出草稿后自动附带一次 AI 审查,直接显示新稿评分与剩余问题(每章约 2-3k token,可关)',
|
|
168
137
|
'settings.save': '保存设置',
|
|
169
138
|
'settings.saved': '设置已保存',
|
|
139
|
+
'settings.savedUnpersisted': '设置已生效,但未能持久化(宿主未登记本插件的配置段),重启后会回到原值。',
|
|
170
140
|
'settings.openFolder': '打开输出文件夹',
|
|
171
|
-
'settings.export': '导出',
|
|
172
141
|
'settings.exportTxt': '导出 TXT',
|
|
173
142
|
'settings.exportMd': '导出 Markdown',
|
|
174
143
|
'settings.exported': '已导出:{file}({chars} 字,{chapters} 章)',
|
|
@@ -178,206 +147,13 @@ export const zh = {
|
|
|
178
147
|
'progress.error': '第 {no} 章失败:{message}',
|
|
179
148
|
'progress.rewriting': '正在修订第 {no} 章…',
|
|
180
149
|
'progress.polishing': '正在润色第 {no} 章…',
|
|
181
|
-
'
|
|
182
|
-
'assistant.hint': '和 AI 编辑讨论剧情、人设、暗线;达成一致后可让它直接修改总纲、道藏、章节内容。',
|
|
150
|
+
'assistant.hint': '和 AI 编辑 Agent 讨论剧情、人设、暗线;达成一致后可让它直接修改总纲、道藏、章节内容。',
|
|
183
151
|
'assistant.placeholder': '例如:我想让第 2 章结尾加一个悬念——墟境里传来爷爷的声音…',
|
|
184
152
|
'assistant.send': '发送',
|
|
185
|
-
'assistant.
|
|
186
|
-
'assistant.toolDone': '✓ {name} 完成:{detail}',
|
|
187
|
-
'assistant.toolError': '✗ {name} 失败:{detail}',
|
|
188
|
-
'assistant.empty': '还没有对话。和 AI 编辑聊聊剧情吧。',
|
|
153
|
+
'assistant.empty': '还没有对话。和 AI 编辑 Agent 聊聊剧情吧。',
|
|
189
154
|
'status.projectNone': '输出目录中还没有项目。请先加载大纲。',
|
|
190
155
|
'status.files': '已生成文件',
|
|
191
|
-
'api.error': '请求失败',
|
|
192
156
|
} as const
|
|
193
157
|
|
|
194
|
-
/**
|
|
195
|
-
export const en: Record<string, string> = {
|
|
196
|
-
'entry.label': 'Novel Forge',
|
|
197
|
-
'entry.tooltip': 'AI novel workbench: outline → bible → volumes → plan → write + review',
|
|
198
|
-
'panel.title': 'Novel Forge',
|
|
199
|
-
'common.close': 'Close',
|
|
200
|
-
'common.loading': 'Loading…',
|
|
201
|
-
'common.save': 'Save',
|
|
202
|
-
'common.error': 'Error',
|
|
203
|
-
'common.success': 'Success',
|
|
204
|
-
'common.generating': 'Generating…',
|
|
205
|
-
'common.chars': ' chars',
|
|
206
|
-
'tab.workflow': 'Workbench',
|
|
207
|
-
'tab.overview': 'Outline',
|
|
208
|
-
'tab.plan': 'Chapters',
|
|
209
|
-
'tab.bible': 'Bible',
|
|
210
|
-
'tab.foreshadow': 'Foreshadow',
|
|
211
|
-
'tab.settings': 'Settings',
|
|
212
|
-
'workflow.title': 'Writing workbench',
|
|
213
|
-
'workflow.step1': '① Load outline',
|
|
214
|
-
'workflow.step2': '② Extract story bible',
|
|
215
|
-
'workflow.step3': '③ Plan volumes',
|
|
216
|
-
'workflow.step4': '④ Plan chapters',
|
|
217
|
-
'workflow.step5': '⑤ Write + AI review',
|
|
218
|
-
'workflow.step6': '⑥ Polish / export',
|
|
219
|
-
'workflow.loadOutline': 'Load outline',
|
|
220
|
-
'workflow.genBible': 'Extract bible',
|
|
221
|
-
'workflow.genVolumes': 'Plan volumes',
|
|
222
|
-
'workflow.genPlan': 'Plan chapters',
|
|
223
|
-
'workflow.done': 'done',
|
|
224
|
-
'workflow.todo': 'todo',
|
|
225
|
-
'workflow.bibleDone': 'Bible ready ({n} rules / {c} characters / {r} red lines)',
|
|
226
|
-
'workflow.volumesDone': 'Volumes ready ({n})',
|
|
227
|
-
'workflow.planDone': 'Plan ready ({n} chapters)',
|
|
228
|
-
'workflow.progress': 'Outline ✓ · bible {bible} · volumes {volumes} · plan {plan} · {done}/{total} chapters',
|
|
229
|
-
'overview.loadDocx': 'Load outline from docx',
|
|
230
|
-
'overview.loadDocxDefault': 'Load default outline',
|
|
231
|
-
'overview.loadingOutline': 'Parsing docx…',
|
|
232
|
-
'overview.outlineHint': 'Outline text (editable)',
|
|
233
|
-
'overview.outlineChars': 'Outline length',
|
|
234
|
-
'overview.saveOutline': 'Save outline',
|
|
235
|
-
'overview.saved': 'Outline saved',
|
|
236
|
-
'overview.bookName': 'Book',
|
|
237
|
-
'overview.loadCustom': 'Custom docx path',
|
|
238
|
-
'overview.loadCustomHint': 'Absolute path; empty = default',
|
|
239
|
-
'plan.generate': 'Plan chapters',
|
|
240
|
-
'plan.generateHint': 'Chapter count',
|
|
241
|
-
'plan.count': ' chapters',
|
|
242
|
-
'plan.empty': 'No plan yet — generate one first',
|
|
243
|
-
'plan.chapter': 'Ch.',
|
|
244
|
-
'plan.pending': 'pending',
|
|
245
|
-
'plan.generating': 'writing',
|
|
246
|
-
'plan.written': 'to review',
|
|
247
|
-
'plan.reviewing': 'reviewing',
|
|
248
|
-
'plan.approved': 'approved',
|
|
249
|
-
'plan.rejected': 'to revise',
|
|
250
|
-
'plan.error': 'failed',
|
|
251
|
-
'plan.write': 'Write',
|
|
252
|
-
'plan.rewrite': 'Revise',
|
|
253
|
-
'plan.review': 'Review',
|
|
254
|
-
'plan.polish': 'De-AI',
|
|
255
|
-
'plan.writeAll': 'Write all',
|
|
256
|
-
'plan.writeAllPending': 'Write remaining',
|
|
257
|
-
'plan.generated': 'generated',
|
|
258
|
-
'plan.progress': 'progress',
|
|
259
|
-
'plan.beats': 'Beats',
|
|
260
|
-
'plan.reviewReport': 'Review report',
|
|
261
|
-
'plan.reviewScore': 'Score',
|
|
262
|
-
'plan.reviewVerdict': 'Verdict',
|
|
263
|
-
'plan.reviewIssues': 'Issues',
|
|
264
|
-
'plan.reviewPass': 'Passed',
|
|
265
|
-
'plan.reviewFail': 'Failed',
|
|
266
|
-
'plan.approve': 'Approve',
|
|
267
|
-
'plan.summary': 'Summary',
|
|
268
|
-
'plan.volumes': 'Volumes',
|
|
269
|
-
'plan.noVolume': 'No volume',
|
|
270
|
-
'bible.title': 'Story bible',
|
|
271
|
-
'bible.gen': 'Extract bible with AI',
|
|
272
|
-
'bible.genre': 'Genre',
|
|
273
|
-
'bible.worldRules': 'World rules',
|
|
274
|
-
'bible.characters': 'Characters',
|
|
275
|
-
'bible.redLines': 'Red lines',
|
|
276
|
-
'bible.style': 'Style',
|
|
277
|
-
'bible.none': 'No bible yet. Generation and review follow it strictly once extracted.',
|
|
278
|
-
'foreshadow.title': 'Foreshadowing',
|
|
279
|
-
'foreshadow.suggest': 'Suggest with AI',
|
|
280
|
-
'foreshadow.none': 'No foreshadows',
|
|
281
|
-
'foreshadow.status': 'Status',
|
|
282
|
-
'foreshadow.planned': 'planned',
|
|
283
|
-
'foreshadow.planted': 'planted',
|
|
284
|
-
'foreshadow.progressing': 'progressing',
|
|
285
|
-
'foreshadow.resolved': 'resolved',
|
|
286
|
-
'foreshadow.abandoned': 'abandoned',
|
|
287
|
-
'foreshadow.target': 'target',
|
|
288
|
-
'foreshadow.plantedAt': 'planted at',
|
|
289
|
-
'foreshadow.setPlanted': 'Mark planted',
|
|
290
|
-
'foreshadow.setResolved': 'Mark resolved',
|
|
291
|
-
'settings.title': 'Settings',
|
|
292
|
-
'settings.outlinePath': 'Default outline path',
|
|
293
|
-
'settings.outputDir': 'Output directory',
|
|
294
|
-
'settings.provider': 'Provider',
|
|
295
|
-
'settings.model': 'Model',
|
|
296
|
-
'settings.modelCustom': 'Custom…',
|
|
297
|
-
'settings.modelCustomPlaceholder': 'Type any model id',
|
|
298
|
-
'settings.reasoningEffort': 'Reasoning effort',
|
|
299
|
-
'settings.reasoning.off': 'off · no thinking (recommended)',
|
|
300
|
-
'settings.reasoning.low': 'low',
|
|
301
|
-
'settings.reasoning.high': 'high',
|
|
302
|
-
'settings.reasoning.max': 'max (audit/reasoning)',
|
|
303
|
-
'settings.reasoningHint': 'off is fastest/cheapest; high/max are slower and stronger',
|
|
304
|
-
'settings.theme': 'Theme',
|
|
305
|
-
'settings.themeLiquid': 'iOS Liquid Glass (green)',
|
|
306
|
-
'settings.themeClassic': 'Classic frosted (blue)',
|
|
307
|
-
'settings.themeNeumorph': 'Neumorphism (dual-shadow)',
|
|
308
|
-
'settings.themeHint': 'Stored in browser localStorage; applies instantly',
|
|
309
|
-
'settings.chapterChars': 'Chars per chapter',
|
|
310
|
-
'settings.maxTokens': 'Max output tokens',
|
|
311
|
-
'settings.reviewPassScore': 'Review pass score (0-100)',
|
|
312
|
-
'settings.editorFontSize': 'Editor font size (workspace only)',
|
|
313
|
-
'settings.editorFontSizeHint': 'Stored in browser localStorage; display-only',
|
|
314
|
-
'settings.autoReview': 'Auto-review after writing',
|
|
315
|
-
'settings.autoAuthorReview': 'Auto author review after writing',
|
|
316
|
-
'settings.autoAuthorReviewHint': 'Reviews hook payoff / ending hook / plotline progress / continuity / pacing trend (~2000 tokens per chapter)',
|
|
317
|
-
'settings.autoReviewAfterRevise': 'Auto review after revise',
|
|
318
|
-
'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)',
|
|
319
|
-
'settings.save': 'Save settings',
|
|
320
|
-
'settings.saved': 'Settings saved',
|
|
321
|
-
'settings.openFolder': 'Open output folder',
|
|
322
|
-
'settings.export': 'Export',
|
|
323
|
-
'settings.exportTxt': 'Export TXT',
|
|
324
|
-
'settings.exportMd': 'Export Markdown',
|
|
325
|
-
'settings.exported': 'Exported: {file} ({chars} chars, {chapters} chapters)',
|
|
326
|
-
'tab.facts': 'Chronicle',
|
|
327
|
-
'tab.plotlines': 'Plotlines',
|
|
328
|
-
'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.',
|
|
329
|
-
'plotlines.empty': 'No plotlines yet — consider creating the main arc and a few branches.',
|
|
330
|
-
'plotlines.new': '+ New plotline',
|
|
331
|
-
'plotlines.name': 'Name',
|
|
332
|
-
'plotlines.kind': 'Type',
|
|
333
|
-
'plotlines.kindMain': 'Main',
|
|
334
|
-
'plotlines.kindBranch': 'Branch',
|
|
335
|
-
'plotlines.kindCharacter': 'Character',
|
|
336
|
-
'plotlines.kindMystery': 'Mystery',
|
|
337
|
-
'plotlines.goal': 'Goal',
|
|
338
|
-
'plotlines.progress': 'Current progress',
|
|
339
|
-
'plotlines.status': 'Status',
|
|
340
|
-
'plotlines.statusActive': 'Active',
|
|
341
|
-
'plotlines.statusPaused': 'Paused',
|
|
342
|
-
'plotlines.statusResolved': 'Resolved',
|
|
343
|
-
'plotlines.statusAbandoned': 'Abandoned',
|
|
344
|
-
'plotlines.save': 'Save',
|
|
345
|
-
'plotlines.cancel': 'Cancel',
|
|
346
|
-
'plotlines.edit': 'Edit',
|
|
347
|
-
'plotlines.remove': 'Remove',
|
|
348
|
-
'plotlines.linkChapter': 'Link chapter',
|
|
349
|
-
'plotlines.chapters': 'Linked',
|
|
350
|
-
'plotlines.workflowTitle': 'Plotline progress',
|
|
351
|
-
'plotlines.workflowEmpty': 'No plotlines yet (create them in the Plotlines tab)',
|
|
352
|
-
'sensitive.title': 'Sensitive-word check',
|
|
353
|
-
'sensitive.scanAll': '🔞 Scan all chapters',
|
|
354
|
-
'sensitive.hint': 'Built-in banned-word library (politics/erotica/violence/abuse/ads/other). Run before publishing; suggestions only.',
|
|
355
|
-
'sensitive.clean': '✅ No banned words found ({n} chapters scanned)',
|
|
356
|
-
'sensitive.hits': '{n} hits across {chapters} chapters',
|
|
357
|
-
'sensitive.goFix': 'Fix',
|
|
358
|
-
'sensitive.fixPrefill': 'Remove or rephrase the banned expression "{word}" (category: {category}) with an indirect alternative.',
|
|
359
|
-
'facts.title': 'Chronicle / timeline ({n} facts)',
|
|
360
|
-
'facts.hint': 'Auto-extracted established facts per chapter (state/resources/relations/foreshadow landings); the latest 20 are injected into later chapters for consistency.',
|
|
361
|
-
'facts.backfill': 'Backfill facts from historical chapters',
|
|
362
|
-
'facts.backfilled': 'Backfilled {n} facts from historical chapters',
|
|
363
|
-
'progress.generating': 'Writing chapter {no} “{title}”…',
|
|
364
|
-
'progress.done': 'Chapter {no} done ({chars} chars) → {file}',
|
|
365
|
-
'progress.reviewed': 'Chapter {no} review: {score} — {verdict}',
|
|
366
|
-
'progress.error': 'Chapter {no} failed: {message}',
|
|
367
|
-
'progress.rewriting': 'Revising chapter {no}…',
|
|
368
|
-
'progress.polishing': 'Polishing chapter {no}…',
|
|
369
|
-
'progress.empty': 'Generation/review progress appears here',
|
|
370
|
-
'assistant.hint': 'Discuss plot, characters, foreshadowing with the AI editor; once agreed, let it edit the outline, bible, or chapters directly.',
|
|
371
|
-
'assistant.placeholder': 'e.g. Add a hook at the end of chapter 2…',
|
|
372
|
-
'assistant.send': 'Send',
|
|
373
|
-
'assistant.toolStart': '⚙ Running {name}…',
|
|
374
|
-
'assistant.toolDone': '✓ {name} done: {detail}',
|
|
375
|
-
'assistant.toolError': '✗ {name} failed: {detail}',
|
|
376
|
-
'assistant.empty': 'No conversation yet. Chat with the AI editor.',
|
|
377
|
-
'status.projectNone': 'No project in the output directory yet. Load an outline first.',
|
|
378
|
-
'status.files': 'Generated files',
|
|
379
|
-
'api.error': 'Request failed',
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/** Keys type for the zh dictionary. */
|
|
158
|
+
/** 面板文案键位(= zh 词典的键)。 */
|
|
383
159
|
export type NovelKey = keyof typeof zh
|
package/src/client/mount.tsx
CHANGED
|
@@ -10,9 +10,9 @@ import { NovelPanel } from './panel/NovelPanel.tsx'
|
|
|
10
10
|
import css from './panel/panel.module.css'
|
|
11
11
|
|
|
12
12
|
/** The injected panel container. */
|
|
13
|
-
|
|
13
|
+
const PANEL_VIEW_SELECTOR = '[data-dsh-novelforge-view]'
|
|
14
14
|
|
|
15
|
-
const CONVERSATION_COLUMN_SELECTOR = '[data-pane="conversation"]'
|
|
15
|
+
const CONVERSATION_COLUMN_SELECTOR = '[data-pane="conversation"], [class*="centerCol"]'
|
|
16
16
|
const ACTIVE_ATTR = 'data-dsh-novelforge-active'
|
|
17
17
|
/** Sibling panels' activation attributes (evicted when this panel opens). */
|
|
18
18
|
const OTHER_ACTIVE_ATTRS = ['data-dsh-taskboard-active', 'data-dsh-ssh-active']
|
|
@@ -44,7 +44,8 @@ export function mountPanel(controller: PanelController, api: NovelApi): () => vo
|
|
|
44
44
|
const column = conversationColumn()
|
|
45
45
|
if (column === undefined) return
|
|
46
46
|
container = document.createElement('div')
|
|
47
|
-
|
|
47
|
+
// 归属标记只在这里落地;取值与 PANEL_VIEW_SELECTOR 同源,字符串不写第二遍。
|
|
48
|
+
container.setAttribute(PANEL_VIEW_SELECTOR.slice(1, -1), 'true')
|
|
48
49
|
container.className = css.view
|
|
49
50
|
column.appendChild(container)
|
|
50
51
|
root = createRoot(container)
|