@waterwx/dsh-novel-forge 2.0.1-alpha → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/README.md +368 -362
  2. package/lib/client.js +20340 -19622
  3. package/lib/index.js +11313 -7285
  4. package/lib/types/action-guard.d.ts +61 -0
  5. package/lib/types/ai-scan.d.ts +0 -6
  6. package/lib/types/assets.d.ts +20 -9
  7. package/lib/types/assistant.d.ts +29 -0
  8. package/lib/types/author-assets.d.ts +1 -3
  9. package/lib/types/bookshelf.d.ts +13 -3
  10. package/lib/types/client/api.d.ts +136 -27
  11. package/lib/types/client/chip-prefs.d.ts +36 -0
  12. package/lib/types/client/docx.d.ts +2 -2
  13. package/lib/types/client/llmLive.d.ts +31 -0
  14. package/lib/types/client/locales.d.ts +15 -70
  15. package/lib/types/client/mount.d.ts +0 -2
  16. package/lib/types/client/panel/AiTag.d.ts +1 -0
  17. package/lib/types/client/panel/AssistantTab.d.ts +4 -1
  18. package/lib/types/client/panel/AuthorHome.d.ts +13 -11
  19. package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
  20. package/lib/types/client/panel/CmdPalette.d.ts +12 -0
  21. package/lib/types/client/panel/CreateBookView.d.ts +5 -1
  22. package/lib/types/client/panel/DirectorView.d.ts +7 -0
  23. package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
  24. package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
  25. package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
  26. package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
  27. package/lib/types/client/panel/ModelManager.d.ts +2 -2
  28. package/lib/types/client/panel/NovelPanel.d.ts +6 -1
  29. package/lib/types/client/panel/PageHeader.d.ts +18 -0
  30. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  31. package/lib/types/client/panel/SettingsView.d.ts +12 -9
  32. package/lib/types/client/panel/ShelfView.d.ts +10 -1
  33. package/lib/types/client/panel/SubPage.d.ts +27 -0
  34. package/lib/types/client/panel/TensionView.d.ts +18 -0
  35. package/lib/types/client/panel/TimelineView.d.ts +18 -0
  36. package/lib/types/client/panel/helpers.d.ts +5 -3
  37. package/lib/types/client/panel/market-radar-state.d.ts +56 -0
  38. package/lib/types/client/panel/outline-markdown.d.ts +112 -0
  39. package/lib/types/client/panel/status-badge.d.ts +13 -0
  40. package/lib/types/client/panel/storage-parse.d.ts +46 -0
  41. package/lib/types/client/panel/toast.d.ts +2 -0
  42. package/lib/types/client/panel/usePersistentState.d.ts +17 -0
  43. package/lib/types/client/panel/views.d.ts +21 -35
  44. package/lib/types/client/run-status.d.ts +21 -0
  45. package/lib/types/client/sidebar-entry.d.ts +0 -2
  46. package/lib/types/digest-parse.d.ts +29 -0
  47. package/lib/types/docx-core.d.ts +16 -0
  48. package/lib/types/docx.d.ts +2 -3
  49. package/lib/types/engine.d.ts +150 -103
  50. package/lib/types/global-assets.d.ts +6 -0
  51. package/lib/types/home.d.ts +11 -0
  52. package/lib/types/index.d.ts +26 -12
  53. package/lib/types/llm-live.d.ts +70 -0
  54. package/lib/types/llm-retry.d.ts +38 -0
  55. package/lib/types/market-radar-scan.d.ts +18 -0
  56. package/lib/types/market-radar-sources.d.ts +31 -0
  57. package/lib/types/migrate-output.d.ts +25 -0
  58. package/lib/types/model-capability.d.ts +119 -0
  59. package/lib/types/model-request.d.ts +36 -0
  60. package/lib/types/output-budget.d.ts +86 -0
  61. package/lib/types/plotline-align.d.ts +35 -0
  62. package/lib/types/prompt-slots.d.ts +57 -0
  63. package/lib/types/prompting.d.ts +10 -0
  64. package/lib/types/protocol.d.ts +841 -601
  65. package/lib/types/revision.d.ts +76 -0
  66. package/lib/types/routes.d.ts +9 -3
  67. package/lib/types/run-selection.d.ts +51 -0
  68. package/lib/types/run.d.ts +32 -1
  69. package/lib/types/settings-persist.d.ts +59 -0
  70. package/lib/types/snapshots.d.ts +46 -0
  71. package/lib/types/stage-contract.d.ts +58 -0
  72. package/lib/types/stream-fallback.d.ts +60 -0
  73. package/lib/types/strip-headings.d.ts +33 -0
  74. package/lib/types/tension.d.ts +53 -0
  75. package/lib/types/timeline.d.ts +37 -0
  76. package/package.json +50 -27
  77. package/assets/styles/arcane-thick-3d.webp +0 -0
  78. package/assets/styles/chinese-ink.webp +0 -0
  79. package/assets/styles/clay-stopmotion-3d.webp +0 -0
  80. package/assets/styles/dark-ghibli.webp +0 -0
  81. package/assets/styles/dark-gothic-3d.webp +0 -0
  82. package/assets/styles/film-photo.webp +0 -0
  83. package/assets/styles/guofeng-heavy.webp +0 -0
  84. package/assets/styles/guoman-2d-gufeng.webp +0 -0
  85. package/assets/styles/guoman-3d-tianyuan.webp +0 -0
  86. package/assets/styles/guoman-3d-xuanhuan.webp +0 -0
  87. package/assets/styles/horror-game-3d.webp +0 -0
  88. package/assets/styles/hyperreal-3d.webp +0 -0
  89. package/assets/styles/japan-cel-dark.webp +0 -0
  90. package/assets/styles/korean-webtoon.webp +0 -0
  91. package/assets/styles/low-cyberpunk-3d.webp +0 -0
  92. package/assets/styles/nextgen-cg.webp +0 -0
  93. package/assets/styles/noir-film.webp +0 -0
  94. package/assets/styles/openworld-survival.webp +0 -0
  95. package/assets/styles/paper-cut-3d.webp +0 -0
  96. package/assets/styles/pixar-adult-3d.webp +0 -0
  97. package/assets/styles/pop-marvel-2d.webp +0 -0
  98. package/assets/styles/sandiao-qban.webp +0 -0
  99. package/assets/styles/shinkai-anime.webp +0 -0
  100. package/assets/styles/tactical-stealth.webp +0 -0
  101. package/assets/styles/urban-lightreal-3d.webp +0 -0
  102. package/assets/styles/vaporwave-3d.webp +0 -0
  103. package/assets/styles/vhs90.webp +0 -0
  104. package/lib/types/client/panel/BookshelfBar.d.ts +0 -11
  105. package/lib/types/client/panel/FlowGuide.d.ts +0 -22
  106. package/lib/types/client/panel/MangaRoleLibrary.d.ts +0 -20
  107. package/lib/types/client/panel/MangaWorkspace.d.ts +0 -12
  108. package/lib/types/client/panel/ModelPicker.d.ts +0 -11
  109. package/lib/types/client/panel/ProgressHomeView.d.ts +0 -5
  110. package/lib/types/client/panel/PropLibrary.d.ts +0 -8
  111. package/lib/types/client/panel/SceneLibrary.d.ts +0 -16
  112. package/lib/types/client/panel/StoryboardTab.d.ts +0 -19
  113. package/lib/types/client/panel/StyleCard.d.ts +0 -8
  114. package/lib/types/client/panel/StyleGallery.d.ts +0 -1
  115. package/lib/types/comic-presets.d.ts +0 -12
  116. package/lib/types/manga-genre-rules.d.ts +0 -22
  117. package/lib/types/prompt-utils.d.ts +0 -35
  118. package/lib/types/review-policy.d.ts +0 -97
  119. package/lib/types/shot-language.d.ts +0 -46
  120. package/lib/types/story-beat-language.d.ts +0 -35
  121. package/lib/types/style-library.d.ts +0 -33
  122. package/skills/chapter-batch/SKILL.md +0 -81
  123. package/src/ai-scan.ts +0 -179
  124. package/src/assets.ts +0 -879
  125. package/src/assistant.ts +0 -770
  126. package/src/author-assets.ts +0 -147
  127. package/src/bookshelf.ts +0 -167
  128. package/src/client/api.ts +0 -617
  129. package/src/client/css-modules.d.ts +0 -8
  130. package/src/client/docx.ts +0 -69
  131. package/src/client/index.ts +0 -34
  132. package/src/client/locales.ts +0 -418
  133. package/src/client/mount.tsx +0 -97
  134. package/src/client/mount.tsx._new +0 -97
  135. package/src/client/mount.tsx._nf +0 -97
  136. package/src/client/mount.tsx._tmp +0 -97
  137. package/src/client/panel/AdaptModeView.tsx +0 -520
  138. package/src/client/panel/AssetsTab.tsx +0 -458
  139. package/src/client/panel/AssistantTab.tsx +0 -301
  140. package/src/client/panel/AuthorAssetsView.tsx +0 -208
  141. package/src/client/panel/AuthorHome.tsx +0 -204
  142. package/src/client/panel/BookshelfBar.tsx +0 -170
  143. package/src/client/panel/CreateBookView.tsx +0 -352
  144. package/src/client/panel/FlowGuide.tsx +0 -84
  145. package/src/client/panel/GlobalAssetLibraryView.tsx +0 -172
  146. package/src/client/panel/ImportModal.tsx +0 -303
  147. package/src/client/panel/MangaRoleLibrary.tsx +0 -1002
  148. package/src/client/panel/MangaWorkspace.tsx +0 -715
  149. package/src/client/panel/ModelManager.tsx +0 -205
  150. package/src/client/panel/NovelPanel.tsx +0 -4620
  151. package/src/client/panel/ProgressConsole.tsx +0 -64
  152. package/src/client/panel/ProgressHomeView.tsx +0 -87
  153. package/src/client/panel/PropLibrary.tsx +0 -175
  154. package/src/client/panel/ReaderView.tsx +0 -306
  155. package/src/client/panel/ReasoningSection.tsx +0 -31
  156. package/src/client/panel/RunPanel.tsx +0 -204
  157. package/src/client/panel/SceneLibrary.tsx +0 -453
  158. package/src/client/panel/SettingsView.tsx +0 -286
  159. package/src/client/panel/ShelfView.tsx +0 -242
  160. package/src/client/panel/StoryboardTab.tsx +0 -472
  161. package/src/client/panel/StyleCard.tsx +0 -90
  162. package/src/client/panel/StyleGallery.tsx +0 -58
  163. package/src/client/panel/WorldTab.tsx +0 -202
  164. package/src/client/panel/controller.ts +0 -45
  165. package/src/client/panel/helpers.ts +0 -67
  166. package/src/client/panel/panel.module.css +0 -4360
  167. package/src/client/panel/views.tsx +0 -314
  168. package/src/client/sidebar-entry.ts +0 -122
  169. package/src/client/text.ts +0 -28
  170. package/src/comic-presets.ts +0 -67
  171. package/src/docx.ts +0 -83
  172. package/src/engine.ts +0 -5200
  173. package/src/index.ts +0 -329
  174. package/src/manga-genre-rules.ts +0 -87
  175. package/src/novel-context.ts +0 -287
  176. package/src/prompt-utils.ts +0 -73
  177. package/src/protocol.ts +0 -2233
  178. package/src/review-policy.ts +0 -161
  179. package/src/routes.ts +0 -3419
  180. package/src/run.ts +0 -331
  181. package/src/shot-language.ts +0 -181
  182. package/src/story-beat-language.ts +0 -130
  183. package/src/style-library.ts +0 -84
@@ -1,202 +0,0 @@
1
- /**
2
- * 大世界页签:境界体系 / 地理区域 / 势力分布 的结构化编辑 + AI 提炼。
3
- * 数据注入每章生成与审稿提示词(renderWorld),保证设定不写飞。
4
- */
5
- import { useEffect, useState } from 'react'
6
- import type { NovelApi } from '../api.ts'
7
- import type { WorldFaction, WorldRealm, WorldRegion, WorldState } from '../../protocol.ts'
8
- import css from './panel.module.css'
9
-
10
- const EMPTY: WorldState = { realms: [], regions: [], factions: [] }
11
-
12
- /** 一个可编辑条目行。 */
13
- function EditableRow({
14
- name,
15
- detail,
16
- onName,
17
- onDetail,
18
- onRemove,
19
- namePlaceholder,
20
- detailPlaceholder,
21
- }: {
22
- name: string
23
- detail: string
24
- onName: (v: string) => void
25
- onDetail: (v: string) => void
26
- onRemove: () => void
27
- namePlaceholder: string
28
- detailPlaceholder: string
29
- }) {
30
- return (
31
- <div style={{ display: 'flex', gap: 'var(--nf-space-6)', alignItems: 'flex-start' }}>
32
- <input className={css.input} style={{ flex: 2, minWidth: 0 }} placeholder={namePlaceholder} value={name} onChange={e => { onName(e.target.value) }} />
33
- <input className={css.input} style={{ flex: 3, minWidth: 0 }} placeholder={detailPlaceholder} value={detail} onChange={e => { onDetail(e.target.value) }} />
34
- <button type="button" className={`${css.button} ${css.buttonSmall}`} title="删除" onClick={onRemove}>×</button>
35
- </div>
36
- )
37
- }
38
-
39
- /** 大世界页签。 */
40
- export function WorldTab({
41
- api,
42
- world,
43
- onChanged,
44
- }: {
45
- api: NovelApi
46
- /** 当前项目的大世界数据(可能为空)。 */
47
- world: WorldState | undefined
48
- /** 保存/提炼成功后由父组件刷新项目状态。 */
49
- onChanged: (world: WorldState) => void
50
- }) {
51
- const [draft, setDraft] = useState<WorldState>(() => world ?? EMPTY)
52
- const [busy, setBusy] = useState(false)
53
- const [error, setError] = useState('')
54
-
55
- // 外部数据变化时同步草稿(提炼/保存/切换书后)。
56
- useEffect(() => {
57
- setDraft(world ?? EMPTY)
58
- }, [world])
59
-
60
- const handleGenerate = async (): Promise<void> => {
61
- setBusy(true)
62
- setError('')
63
- try {
64
- const result = await api.world('generate')
65
- setDraft(result.world)
66
- onChanged(result.world)
67
- } catch (err) {
68
- setError((err as Error).message)
69
- } finally {
70
- setBusy(false)
71
- }
72
- }
73
-
74
- const handleSave = async (): Promise<void> => {
75
- setBusy(true)
76
- setError('')
77
- try {
78
- const result = await api.world('save', draft)
79
- onChanged(result.world)
80
- } catch (err) {
81
- setError((err as Error).message)
82
- } finally {
83
- setBusy(false)
84
- }
85
- }
86
-
87
- const setRealms = (realms: WorldRealm[]): void => setDraft(prev => ({ ...prev, realms }))
88
- const setRegions = (regions: WorldRegion[]): void => setDraft(prev => ({ ...prev, regions }))
89
- const setFactions = (factions: WorldFaction[]): void => setDraft(prev => ({ ...prev, factions }))
90
-
91
- return (
92
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-12)' }}>
93
- {error !== '' && <div className={css.card} style={{ borderColor: 'var(--nf-error)' }}><span style={{ color: 'var(--nf-error)' }}>{error}</span></div>}
94
-
95
- <div className={css.card}>
96
- <div className={css.row} style={{ justifyContent: 'space-between' }}>
97
- <span className={css.cardTitle}>大世界</span>
98
- <div className={css.row}>
99
- <button type="button" className={`${css.button} ${css.buttonSmall} ${css.buttonPrimary}`} disabled={busy} onClick={() => { void handleGenerate() }}>
100
- ✨ AI 提炼
101
- </button>
102
- <button type="button" className={`${css.button} ${css.buttonSmall}`} disabled={busy} onClick={() => { void handleSave() }}>
103
- 💾 保存
104
- </button>
105
- </div>
106
- </div>
107
- <span className={css.meta}>
108
- 境界体系按由低到高排序注入章节生成提示词,模型不得随意跳级或自创境界;区域与势力约束地理/势力设定。AI 提炼不满意可逐条编辑后保存。
109
- </span>
110
- </div>
111
-
112
- {/* 境界体系 */}
113
- <div className={css.card}>
114
- <div className={css.row} style={{ justifyContent: 'space-between' }}>
115
- <span className={css.cardTitle}>境界体系({draft.realms.length})</span>
116
- <button type="button" className={`${css.button} ${css.buttonSmall}`} onClick={() => { setRealms([...draft.realms, { name: '', description: '' }]) }}>
117
- + 新增境界
118
- </button>
119
- </div>
120
- {draft.realms.length === 0 ? (
121
- <span className={css.meta}>暂无境界体系 — 点击 ✨AI 提炼 或手动添加(由低到高)。</span>
122
- ) : (
123
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)' }}>
124
- {draft.realms.map((realm, i) => (
125
- <EditableRow
126
- key={i}
127
- name={realm.name}
128
- detail={realm.description}
129
- onName={v => { const next = [...draft.realms]; next[i] = { ...next[i]!, name: v }; setRealms(next) }}
130
- onDetail={v => { const next = [...draft.realms]; next[i] = { ...next[i]!, description: v }; setRealms(next) }}
131
- onRemove={() => { setRealms(draft.realms.filter((_, idx) => idx !== i)) }}
132
- namePlaceholder={`第 ${i + 1} 阶境界名`}
133
- detailPlaceholder="突破条件 / 寿命 / 标志…"
134
- />
135
- ))}
136
- </div>
137
- )}
138
- </div>
139
-
140
- {/* 地理区域 */}
141
- <div className={css.card}>
142
- <div className={css.row} style={{ justifyContent: 'space-between' }}>
143
- <span className={css.cardTitle}>地理区域({draft.regions.length})</span>
144
- <button type="button" className={`${css.button} ${css.buttonSmall}`} onClick={() => { setRegions([...draft.regions, { name: '', description: '' }]) }}>
145
- + 新增区域
146
- </button>
147
- </div>
148
- {draft.regions.length === 0 ? (
149
- <span className={css.meta}>暂无地理区域 — 大陆 / 海域 / 秘境 / 遗迹…</span>
150
- ) : (
151
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)' }}>
152
- {draft.regions.map((region, i) => (
153
- <EditableRow
154
- key={i}
155
- name={region.name}
156
- detail={region.description}
157
- onName={v => { const next = [...draft.regions]; next[i] = { ...next[i]!, name: v }; setRegions(next) }}
158
- onDetail={v => { const next = [...draft.regions]; next[i] = { ...next[i]!, description: v }; setRegions(next) }}
159
- onRemove={() => { setRegions(draft.regions.filter((_, idx) => idx !== i)) }}
160
- namePlaceholder="区域名(如大荒 / 青云山脉)"
161
- detailPlaceholder="描述…"
162
- />
163
- ))}
164
- </div>
165
- )}
166
- </div>
167
-
168
- {/* 势力分布 */}
169
- <div className={css.card}>
170
- <div className={css.row} style={{ justifyContent: 'space-between' }}>
171
- <span className={css.cardTitle}>势力分布({draft.factions.length})</span>
172
- <button type="button" className={`${css.button} ${css.buttonSmall}`} onClick={() => { setFactions([...draft.factions, { name: '', kind: '宗门', description: '' }]) }}>
173
- + 新增势力
174
- </button>
175
- </div>
176
- {draft.factions.length === 0 ? (
177
- <span className={css.meta}>暂无势力 — 宗门 / 家族 / 王朝 / 组织…</span>
178
- ) : (
179
- <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)' }}>
180
- {draft.factions.map((faction, i) => (
181
- <EditableRow
182
- key={i}
183
- name={`${faction.name}(${faction.kind})`}
184
- detail={faction.description}
185
- onName={v => {
186
- const next = [...draft.factions]
187
- const match = /^(.*)((.*))$/.exec(v)
188
- next[i] = { ...next[i]!, name: (match?.[1] ?? v).trim(), kind: (match?.[2] ?? next[i]!.kind).trim() || '宗门' }
189
- setFactions(next)
190
- }}
191
- onDetail={v => { const next = [...draft.factions]; next[i] = { ...next[i]!, description: v }; setFactions(next) }}
192
- onRemove={() => { setFactions(draft.factions.filter((_, idx) => idx !== i)) }}
193
- namePlaceholder="势力名(类型)如:青云宗(宗门)"
194
- detailPlaceholder="描述…"
195
- />
196
- ))}
197
- </div>
198
- )}
199
- </div>
200
- </div>
201
- )
202
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Novel-forge panel controller: the single owner of the panel's open/closed
3
- * state (framework-free, mirroring the family plugins' controllers).
4
- */
5
-
6
- /** Immutable controller snapshot for UI subscriptions. */
7
- export interface PanelControllerSnapshot {
8
- panelOpen: boolean
9
- }
10
-
11
- /** The panel state owner the sidebar entry toggles and the view renders from. */
12
- export class PanelController {
13
- private panelOpen = false
14
- private listeners = new Set<() => void>()
15
-
16
- getSnapshot(): PanelControllerSnapshot {
17
- return { panelOpen: this.panelOpen }
18
- }
19
-
20
- subscribe(fn: () => void): () => void {
21
- this.listeners.add(fn)
22
- return () => { this.listeners.delete(fn) }
23
- }
24
-
25
- open(): void {
26
- if (this.panelOpen) return
27
- this.panelOpen = true
28
- this.notify()
29
- }
30
-
31
- close(): void {
32
- if (!this.panelOpen) return
33
- this.panelOpen = false
34
- this.notify()
35
- }
36
-
37
- toggle(): void {
38
- if (this.panelOpen) this.close()
39
- else this.open()
40
- }
41
-
42
- private notify(): void {
43
- for (const fn of [...this.listeners]) fn()
44
- }
45
- }
@@ -1,67 +0,0 @@
1
- /**
2
- * Tiny translation helper for the panel: reads the zh dict with the en dict
3
- * as fallback (the family plugins use a full locale registry; the panel keeps
4
- * a dependency-free helper so the client bundle stays self-contained).
5
- */
6
- import { zh, en, type NovelKey } from '../locales.ts'
7
-
8
- /** Translate one key with optional {placeholder} substitution. */
9
- export function tt(key: NovelKey, params?: Record<string, string | number>): string {
10
- let text: string = zh[key] ?? en[key] ?? key
11
- if (params !== undefined) {
12
- for (const [name, value] of Object.entries(params)) {
13
- text = text.replaceAll(`{${name}}`, String(value))
14
- }
15
- }
16
- return text
17
- }
18
-
19
- // ------------------------------------------------------------ shared constants
20
-
21
- /** 角色定位中文名(角色库/提炼候选共用,收敛自多份复制)。 */
22
- export const ROLE_LABELS: Record<string, string> = {
23
- protagonist: '主角',
24
- female_lead: '女主',
25
- female_support: '女配',
26
- support: '配角',
27
- antagonist: '反派',
28
- extra: '路人',
29
- }
30
-
31
- /** 角色定位徽章颜色。 */
32
- export function roleColor(label: string): string {
33
- if (label === 'protagonist') return 'var(--nf-success)'
34
- if (label === 'female_lead') return 'var(--nf-accent)'
35
- if (label === 'antagonist') return 'var(--nf-error)'
36
- return 'var(--nf-text-3)'
37
- }
38
-
39
- /** 剧情线类型中文名(与 locale 对齐)。 */
40
- export function kindLabel(kind: string): string {
41
- switch (kind) {
42
- case 'main': return tt('plotlines.kindMain')
43
- case 'branch': return tt('plotlines.kindBranch')
44
- case 'character': return tt('plotlines.kindCharacter')
45
- case 'mystery': return tt('plotlines.kindMystery')
46
- default: return kind
47
- }
48
- }
49
-
50
- /** 剧情线状态中文名。 */
51
- export function plotlineStatusLabel(status: string): string {
52
- switch (status) {
53
- case 'active': return tt('plotlines.statusActive')
54
- case 'paused': return tt('plotlines.statusPaused')
55
- case 'resolved': return tt('plotlines.statusResolved')
56
- case 'abandoned': return tt('plotlines.statusAbandoned')
57
- default: return status
58
- }
59
- }
60
-
61
- /** 剧情线状态颜色。 */
62
- export function plotlineStatusColor(status: string): string {
63
- if (status === 'resolved') return 'var(--nf-success)'
64
- if (status === 'abandoned') return 'var(--nf-text-3)'
65
- if (status === 'paused') return 'var(--nf-warn)'
66
- return 'var(--nf-accent)'
67
- }