@waterwx/dsh-novel-forge 0.1.0 → 1.0.0-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 +162 -111
- package/cordis.patch.yml +1 -1
- package/lib/client.js +54165 -2666
- package/lib/index.js +11895 -2211
- package/lib/types/ai-scan.d.ts +36 -0
- package/lib/types/assets.d.ts +19 -4
- package/lib/types/assistant.d.ts +6 -0
- package/lib/types/author-assets.d.ts +25 -0
- package/lib/types/bookshelf.d.ts +8 -1
- package/lib/types/client/api.d.ts +235 -3
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/llmLive.d.ts +33 -0
- package/lib/types/client/locales.d.ts +101 -28
- package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
- package/lib/types/client/panel/AssetsTab.d.ts +6 -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/CreateBookView.d.ts +13 -0
- package/lib/types/client/panel/DirectorView.d.ts +7 -0
- package/lib/types/client/panel/FlowGuide.d.ts +22 -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 +5 -0
- package/lib/types/client/panel/MangaRoleLibrary.d.ts +20 -0
- package/lib/types/client/panel/MangaWorkspace.d.ts +12 -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/ModelPicker.d.ts +11 -0
- package/lib/types/client/panel/NovelPanel.d.ts +1 -1
- 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/PropLibrary.d.ts +8 -0
- package/lib/types/client/panel/ReaderView.d.ts +8 -0
- package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/client/panel/RunPanel.d.ts +5 -0
- package/lib/types/client/panel/SceneLibrary.d.ts +16 -0
- package/lib/types/client/panel/SettingsView.d.ts +16 -0
- package/lib/types/client/panel/ShelfView.d.ts +15 -0
- package/lib/types/client/panel/StoryboardTab.d.ts +19 -0
- package/lib/types/client/panel/StyleCard.d.ts +8 -0
- package/lib/types/client/panel/StyleGallery.d.ts +1 -0
- package/lib/types/client/panel/WorldTab.d.ts +10 -0
- package/lib/types/client/panel/helpers.d.ts +10 -0
- package/lib/types/client/panel/views.d.ts +77 -0
- package/lib/types/client/run-status.d.ts +19 -0
- package/lib/types/client/text.d.ts +9 -0
- package/lib/types/comic-presets.d.ts +12 -0
- package/lib/types/engine.d.ts +250 -8
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/index.d.ts +24 -2
- package/lib/types/llm-live.d.ts +44 -0
- package/lib/types/manga-genre-rules.d.ts +22 -0
- package/lib/types/market-radar-scan.d.ts +19 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/novel-context.d.ts +80 -0
- package/lib/types/prompt-utils.d.ts +35 -0
- package/lib/types/prompting.d.ts +35 -0
- package/lib/types/protocol.d.ts +1453 -5
- package/lib/types/review-policy.d.ts +97 -0
- package/lib/types/run.d.ts +32 -0
- package/lib/types/shot-language.d.ts +46 -0
- package/lib/types/src/ai-scan.d.ts +36 -0
- package/lib/types/src/assets.d.ts +37 -0
- package/lib/types/src/assistant.d.ts +45 -0
- package/lib/types/src/author-assets.d.ts +25 -0
- package/lib/types/src/bookshelf.d.ts +42 -0
- package/lib/types/src/client/api.d.ts +234 -0
- package/lib/types/src/client/docx.d.ts +15 -0
- package/lib/types/src/client/index.d.ts +14 -0
- package/lib/types/src/client/locales.d.ts +212 -0
- package/lib/types/src/client/mount.d.ts +9 -0
- package/lib/types/src/client/panel/AdaptModeView.d.ts +5 -0
- package/lib/types/src/client/panel/AssetsTab.d.ts +12 -0
- package/lib/types/src/client/panel/AssistantTab.d.ts +7 -0
- package/lib/types/src/client/panel/AuthorAssetsView.d.ts +4 -0
- package/lib/types/src/client/panel/AuthorHome.d.ts +32 -0
- package/lib/types/src/client/panel/BookshelfBar.d.ts +11 -0
- package/lib/types/src/client/panel/CreateBookView.d.ts +9 -0
- package/lib/types/src/client/panel/FlowGuide.d.ts +22 -0
- package/lib/types/src/client/panel/GlobalAssetLibraryView.d.ts +4 -0
- package/lib/types/src/client/panel/ImportModal.d.ts +7 -0
- package/lib/types/src/client/panel/MangaRoleLibrary.d.ts +20 -0
- package/lib/types/src/client/panel/MangaWorkspace.d.ts +12 -0
- package/lib/types/src/client/panel/ModelManager.d.ts +14 -0
- package/lib/types/src/client/panel/NovelPanel.d.ts +13 -0
- package/lib/types/src/client/panel/ProgressConsole.d.ts +17 -0
- package/lib/types/src/client/panel/ProgressHomeView.d.ts +5 -0
- package/lib/types/src/client/panel/PropLibrary.d.ts +8 -0
- package/lib/types/src/client/panel/ReaderView.d.ts +8 -0
- package/lib/types/src/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/src/client/panel/RunPanel.d.ts +5 -0
- package/lib/types/src/client/panel/SceneLibrary.d.ts +16 -0
- package/lib/types/src/client/panel/SettingsView.d.ts +16 -0
- package/lib/types/src/client/panel/ShelfView.d.ts +15 -0
- package/lib/types/src/client/panel/StoryboardTab.d.ts +19 -0
- package/lib/types/src/client/panel/StyleCard.d.ts +8 -0
- package/lib/types/src/client/panel/StyleGallery.d.ts +1 -0
- package/lib/types/src/client/panel/WorldTab.d.ts +10 -0
- package/lib/types/src/client/panel/controller.d.ts +19 -0
- package/lib/types/src/client/panel/helpers.d.ts +18 -0
- package/lib/types/src/client/panel/views.d.ts +77 -0
- package/lib/types/src/client/sidebar-entry.d.ts +13 -0
- package/lib/types/src/client/text.d.ts +9 -0
- package/lib/types/src/comic-presets.d.ts +12 -0
- package/lib/types/src/docx.d.ts +19 -0
- package/lib/types/src/engine.d.ts +402 -0
- package/lib/types/src/index.d.ts +79 -0
- package/lib/types/src/manga-genre-rules.d.ts +22 -0
- package/lib/types/src/novel-context.d.ts +80 -0
- package/lib/types/src/prompt-utils.d.ts +35 -0
- package/lib/types/src/protocol.d.ts +2176 -0
- package/lib/types/src/review-policy.d.ts +97 -0
- package/lib/types/src/routes.d.ts +29 -0
- package/lib/types/src/run.d.ts +32 -0
- package/lib/types/src/shot-language.d.ts +46 -0
- package/lib/types/src/story-beat-language.d.ts +35 -0
- package/lib/types/src/style-library.d.ts +33 -0
- package/lib/types/story-beat-language.d.ts +35 -0
- package/lib/types/style-library.d.ts +33 -0
- package/package.json +57 -42
- package/scripts/patch-schemastery.mjs +47 -0
- package/src/ai-scan.ts +179 -0
- package/src/assets.ts +1774 -518
- package/src/assistant.ts +915 -547
- package/src/author-assets.ts +147 -0
- package/src/bookshelf.ts +32 -2
- package/src/client/api.ts +666 -254
- package/src/client/index.ts +3 -1
- package/src/client/llmLive.ts +101 -0
- package/src/client/locales.ts +178 -31
- package/src/client/mount.tsx +1 -1
- package/src/client/panel/AdaptModeView.tsx +535 -0
- package/src/client/panel/AssetsTab.tsx +168 -51
- package/src/client/panel/AssistantTab.tsx +339 -188
- package/src/client/panel/AuthorAssetsView.tsx +208 -0
- package/src/client/panel/AuthorHome.tsx +234 -0
- package/src/client/panel/BookAnalysisView.tsx +60 -0
- package/src/client/panel/BookshelfBar.tsx +63 -9
- package/src/client/panel/CreateBookView.tsx +371 -0
- package/src/client/panel/DirectorView.tsx +200 -0
- package/src/client/panel/GlobalAssetLibraryView.tsx +172 -0
- package/src/client/panel/IdeaInspirationView.tsx +71 -0
- package/src/client/panel/ImportModal.tsx +303 -0
- package/src/client/panel/KnowledgeBaseView.tsx +73 -0
- package/src/client/panel/LiveFeedLog.tsx +67 -0
- package/src/client/panel/MarketRadarView.tsx +374 -0
- package/src/client/panel/ModelManager.tsx +205 -0
- package/src/client/panel/NovelPanel.tsx +4713 -990
- package/src/client/panel/ProgressConsole.tsx +63 -0
- package/src/client/panel/ProgressHomeView.tsx +87 -0
- package/src/client/panel/ReaderView.tsx +306 -0
- package/src/client/panel/ReasoningSection.tsx +31 -0
- package/src/client/panel/RunPanel.tsx +204 -0
- package/src/client/panel/SettingsView.tsx +265 -0
- package/src/client/panel/ShelfView.tsx +242 -0
- package/src/client/panel/WorldTab.tsx +202 -0
- package/src/client/panel/helpers.ts +50 -0
- package/src/client/panel/panel.module.css +4325 -526
- package/src/client/panel/views.tsx +346 -0
- package/src/client/run-status.ts +100 -0
- package/src/client/sidebar-entry.ts +1 -1
- package/src/client/text.ts +28 -0
- package/src/engine.ts +4230 -1019
- package/src/global-assets.ts +59 -0
- package/src/index.ts +123 -9
- package/src/llm-live.ts +68 -0
- package/src/market-radar-scan.ts +41 -0
- package/src/market-radar-sources.ts +173 -0
- package/src/novel-context.ts +287 -0
- package/src/prompt-utils.ts +73 -0
- package/src/prompting.ts +159 -0
- package/src/protocol.ts +1998 -539
- package/src/review-policy.ts +161 -0
- package/src/routes.ts +3277 -955
- package/src/run.ts +338 -0
- package/lib/client.js.map +0 -1
- package/lib/index.js.map +0 -1
|
@@ -11,21 +11,30 @@ import css from './panel.module.css'
|
|
|
11
11
|
/** Props. */
|
|
12
12
|
export interface AssetsTabProps {
|
|
13
13
|
api: NovelApi
|
|
14
|
+
/** 初始子页(左侧导航直达对应资产分类)。 */
|
|
15
|
+
initialTab?: AssetSubTab
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/** 渲染题材树(带勾选当前题材)。 */
|
|
17
19
|
function GenreTree({ node, selected, onSelect }: { node: GenreNode; selected: string; onSelect: (node: GenreNode) => void }) {
|
|
18
20
|
return (
|
|
19
21
|
<div>
|
|
20
|
-
<label style={{ display: 'flex', alignItems: 'flex-start', gap: 6, cursor: 'pointer' }}>
|
|
22
|
+
<label style={{ display: 'flex', alignItems: 'flex-start', gap: 'var(--nf-space-6)', cursor: 'pointer' }}>
|
|
21
23
|
<input type="radio" name="genre" checked={selected === node.name} onChange={() => { onSelect(node) }} />
|
|
22
24
|
<span>
|
|
23
25
|
<b>{node.name}</b>
|
|
24
|
-
{node.description !== '' &&
|
|
26
|
+
{node.description !== '' && (
|
|
27
|
+
<span
|
|
28
|
+
className={`${css.meta} ${css.genreDesc}`}
|
|
29
|
+
title={node.description}
|
|
30
|
+
>
|
|
31
|
+
— {node.description}
|
|
32
|
+
</span>
|
|
33
|
+
)}
|
|
25
34
|
</span>
|
|
26
35
|
</label>
|
|
27
36
|
{node.children.length > 0 && (
|
|
28
|
-
<div style={{ marginLeft:
|
|
37
|
+
<div style={{ marginLeft: 'var(--nf-space-24)', display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-4)' }}>
|
|
29
38
|
{node.children.map(child => (
|
|
30
39
|
<GenreTree key={child.name} node={child} selected={selected} onSelect={onSelect} />
|
|
31
40
|
))}
|
|
@@ -42,14 +51,14 @@ type AssetSubTab = 'genre' | 'progression' | 'templates' | 'rules' | 'style'
|
|
|
42
51
|
const SUB_TABS: ReadonlyArray<{ id: AssetSubTab; label: string }> = [
|
|
43
52
|
{ id: 'genre', label: '题材基底' },
|
|
44
53
|
{ id: 'progression', label: '推进模式' },
|
|
45
|
-
{ id: 'templates', label: '
|
|
46
|
-
{ id: 'rules', label: '
|
|
47
|
-
{ id: 'style', label: '
|
|
54
|
+
{ id: 'templates', label: '笔法帖' },
|
|
55
|
+
{ id: 'rules', label: '文戒' },
|
|
56
|
+
{ id: 'style', label: '心法' },
|
|
48
57
|
]
|
|
49
58
|
|
|
50
59
|
/** 写作资产页签。 */
|
|
51
|
-
export function AssetsTab({ api }: AssetsTabProps) {
|
|
52
|
-
const [assetTab, setAssetTab] = useState<AssetSubTab>(
|
|
60
|
+
export function AssetsTab({ api, initialTab = 'genre' }: AssetsTabProps) {
|
|
61
|
+
const [assetTab, setAssetTab] = useState<AssetSubTab>(initialTab)
|
|
53
62
|
const [data, setData] = useState<AssetsResponse | null>(null)
|
|
54
63
|
const [busy, setBusy] = useState(false)
|
|
55
64
|
const [error, setError] = useState('')
|
|
@@ -58,6 +67,8 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
58
67
|
const [styleName, setStyleName] = useState('')
|
|
59
68
|
const [newRule, setNewRule] = useState('')
|
|
60
69
|
const [newProgression, setNewProgression] = useState('')
|
|
70
|
+
/** 正在行内编辑的自定义反 AI 规则(下标 + 草稿字段)。 */
|
|
71
|
+
const [editingRule, setEditingRule] = useState<{ index: number; name: string; avoid: string; fix: string } | null>(null)
|
|
61
72
|
const loadId = useRef(0)
|
|
62
73
|
|
|
63
74
|
/** Load assets (or reset from a new call). */
|
|
@@ -135,6 +146,39 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
135
146
|
setNewRule('')
|
|
136
147
|
}
|
|
137
148
|
|
|
149
|
+
/** 保存行内编辑的自定义规则。 */
|
|
150
|
+
const handleSaveRuleEdit = async (): Promise<void> => {
|
|
151
|
+
if (editingRule === null) return
|
|
152
|
+
const rules = [...(data?.projectAssets.antiAiRules ?? [])]
|
|
153
|
+
if (editingRule.index < 0 || editingRule.index >= rules.length) return
|
|
154
|
+
const name = editingRule.name.trim() || rules[editingRule.index]!.name
|
|
155
|
+
if (editingRule.avoid.trim() === '' && editingRule.fix.trim() === '') return
|
|
156
|
+
rules[editingRule.index] = { name, avoid: editingRule.avoid.trim(), fix: editingRule.fix.trim() }
|
|
157
|
+
await patch({ antiAiRules: rules })
|
|
158
|
+
setEditingRule(null)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 删除一条自定义规则。 */
|
|
162
|
+
const handleRemoveRule = async (index: number): Promise<void> => {
|
|
163
|
+
const rules = [...(data?.projectAssets.antiAiRules ?? [])]
|
|
164
|
+
rules.splice(index, 1)
|
|
165
|
+
setEditingRule(null)
|
|
166
|
+
await patch({ antiAiRules: rules })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 把内置规则复制为自定义副本(同名覆盖生效),并打开行内编辑。 */
|
|
170
|
+
const handleOverrideBuiltin = (rule: AntiAiRule): void => {
|
|
171
|
+
const rules = data?.projectAssets.antiAiRules ?? []
|
|
172
|
+
const existing = rules.findIndex(r => r.name === rule.name)
|
|
173
|
+
if (existing >= 0) {
|
|
174
|
+
setEditingRule({ index: existing, name: rules[existing]!.name, avoid: rules[existing]!.avoid, fix: rules[existing]!.fix ?? '' })
|
|
175
|
+
} else {
|
|
176
|
+
const next = [...rules, { ...rule, fix: rule.fix ?? '' }]
|
|
177
|
+
setEditingRule({ index: next.length - 1, name: rule.name, avoid: rule.avoid, fix: rule.fix ?? '' })
|
|
178
|
+
void patch({ antiAiRules: next })
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
138
182
|
/** 设置题材。 */
|
|
139
183
|
const handleSelectGenre = (node: GenreNode): void => {
|
|
140
184
|
void patch({ genre: node })
|
|
@@ -161,23 +205,51 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
161
205
|
const genreLibrary = data.genreLibrary
|
|
162
206
|
|
|
163
207
|
return (
|
|
164
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
208
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-12)', flex: 1, minHeight: 0 }}>
|
|
209
|
+
<div className={css.row} style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
|
210
|
+
<span className={css.cardTitle} style={{ fontSize: 'var(--nf-fs-16)', fontWeight: 700 }}>🧰 创作资产</span>
|
|
211
|
+
<span className={css.meta}>题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法</span>
|
|
212
|
+
</div>
|
|
165
213
|
{error !== '' && <div className={css.card} style={{ borderColor: 'var(--nf-error)' }}><span style={{ color: 'var(--nf-error)' }}>{tt('common.error')}: {error}</span></div>}
|
|
166
214
|
{notice !== '' && <div className={css.card}><span style={{ color: 'var(--nf-success)' }}>{notice}</span></div>}
|
|
167
215
|
|
|
168
|
-
{/*
|
|
169
|
-
<div className={css.
|
|
170
|
-
{
|
|
216
|
+
{/* 资产状态总览(参照 AI-Novel-Writing-Assistant 状态网格) */}
|
|
217
|
+
<div className={css.assetGrid}>
|
|
218
|
+
<div className={css.assetStat}>
|
|
219
|
+
<span className={css.assetStatLabel}>当前题材</span>
|
|
220
|
+
<span className={css.assetStatValue}>{assets.genre?.name ?? '未设置'}</span>
|
|
221
|
+
{assets.genre !== undefined && <span className={css.assetStatDetail} title={assets.genre.description}>{assets.genre.description}</span>}
|
|
222
|
+
</div>
|
|
223
|
+
<div className={css.assetStat}>
|
|
224
|
+
<span className={css.assetStatLabel}>主推进模式</span>
|
|
225
|
+
<span className={css.assetStatValue}>{assets.primaryProgression?.name ?? '未设置'}</span>
|
|
226
|
+
{assets.primaryProgression !== undefined && <span className={css.assetStatDetail} title={assets.primaryProgression.driver}>{assets.primaryProgression.driver}</span>}
|
|
227
|
+
</div>
|
|
228
|
+
<div className={css.assetStat}>
|
|
229
|
+
<span className={css.assetStatLabel}>已绑定写法</span>
|
|
230
|
+
<span className={css.assetStatValue}>{assets.styleAssets?.length ?? 0} 套</span>
|
|
231
|
+
<span className={css.assetStatDetail} title={(assets.styleAssets ?? []).map(s => s.name).join('、')}>
|
|
232
|
+
{(assets.styleAssets ?? []).map(s => s.name).join('、') || '未绑定(可在「笔法帖」一键选用)'}
|
|
233
|
+
</span>
|
|
234
|
+
</div>
|
|
235
|
+
<div className={css.assetStat}>
|
|
236
|
+
<span className={css.assetStatLabel}>文戒</span>
|
|
237
|
+
<span className={css.assetStatValue}>{builtinRules.length} 内置 + {(assets.antiAiRules ?? []).length} 自定义</span>
|
|
238
|
+
<span className={css.assetStatDetail}>全部生效于生成与审稿提示词</span>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
{/* 子页签:题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法 */}
|
|
243
|
+
<div className={css.row} style={{ flexWrap: 'wrap', gap: 'var(--nf-space-6)' }}>
|
|
244
|
+
{SUB_TABS.map(t => (
|
|
171
245
|
<button
|
|
172
|
-
key={
|
|
246
|
+
key={t.id}
|
|
173
247
|
type="button"
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
className={css.tab}
|
|
178
|
-
onClick={() => { setAssetTab(tab.id) }}
|
|
248
|
+
className={`${css.button} ${assetTab === t.id ? css.buttonPrimary : ''}`}
|
|
249
|
+
style={{ fontSize: 'var(--nf-fs-14)', flex: 1 }}
|
|
250
|
+
onClick={() => { setAssetTab(t.id) }}
|
|
179
251
|
>
|
|
180
|
-
{
|
|
252
|
+
{t.label}
|
|
181
253
|
</button>
|
|
182
254
|
))}
|
|
183
255
|
</div>
|
|
@@ -188,11 +260,12 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
188
260
|
<span className={css.cardTitle}>题材基底库</span>
|
|
189
261
|
<span className={css.meta}>这本书属于哪个阅读市场?题材定位会注入章节生成与审稿提示词。</span>
|
|
190
262
|
{assets.genre !== undefined && (
|
|
191
|
-
<div style={{ border: '1px solid var(--nf-border)', borderRadius: 6, padding: '
|
|
263
|
+
<div style={{ border: '1px solid var(--nf-border)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
192
264
|
<b>当前题材:{assets.genre.name}</b> — {assets.genre.description}
|
|
193
265
|
</div>
|
|
194
266
|
)}
|
|
195
|
-
|
|
267
|
+
{/* 题材基底库:自然展开,不套内部滚动条(描述两行截断,悬停看全文) */}
|
|
268
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-8)' }}>
|
|
196
269
|
{genreLibrary.map(root => <GenreTree key={root.name} node={root} selected={assets.genre?.name ?? ''} onSelect={handleSelectGenre} />)}
|
|
197
270
|
</div>
|
|
198
271
|
</div>
|
|
@@ -200,21 +273,21 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
200
273
|
|
|
201
274
|
{/* 推进模式库 */}
|
|
202
275
|
{assetTab === 'progression' && (
|
|
203
|
-
<div className={css.card}>
|
|
276
|
+
<div className={css.card} style={{ flex: 1, minHeight: 0 }}>
|
|
204
277
|
<span className={css.cardTitle}>推进模式库</span>
|
|
205
278
|
<span className={css.meta}>读者为什么继续看下一章?主模式 + 辅助模式注入卷规划与章节生成。</span>
|
|
206
279
|
{assets.primaryProgression !== undefined && (
|
|
207
|
-
<div style={{ border: '1px solid var(--nf-accent)', borderRadius: 6, padding: '
|
|
280
|
+
<div style={{ border: '1px solid var(--nf-accent)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)', color: 'var(--nf-accent)' }}>
|
|
208
281
|
<b>主推进:{assets.primaryProgression.name}</b> — {assets.primaryProgression.driver}
|
|
209
282
|
</div>
|
|
210
283
|
)}
|
|
211
284
|
{assets.auxiliaryProgressions.map(mode => (
|
|
212
|
-
<div key={mode.name} style={{ border: '1px solid var(--nf-border)', borderRadius: 6, padding: '
|
|
285
|
+
<div key={mode.name} style={{ border: '1px solid var(--nf-border)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
213
286
|
<b>{mode.name}</b> — {mode.driver}
|
|
214
287
|
</div>
|
|
215
288
|
))}
|
|
216
289
|
<span className={css.meta}>从内置推进模式库选择添加(第一个设为主推进):</span>
|
|
217
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 6,
|
|
290
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)', flex: 1, minHeight: 0, overflowY: 'auto' }}>
|
|
218
291
|
{data.progressionLibrary.map(mode => {
|
|
219
292
|
const alreadyPrimary = assets.primaryProgression?.name === mode.name
|
|
220
293
|
const alreadyAux = assets.auxiliaryProgressions.some(m => m.name === mode.name)
|
|
@@ -229,17 +302,17 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
229
302
|
</div>
|
|
230
303
|
)}
|
|
231
304
|
|
|
232
|
-
{/*
|
|
305
|
+
{/* 笔法帖(一键绑定) */}
|
|
233
306
|
{assetTab === 'templates' && (
|
|
234
|
-
<div className={css.card}>
|
|
235
|
-
<span className={css.cardTitle}
|
|
307
|
+
<div className={css.card} style={{ flex: 1, minHeight: 0 }}>
|
|
308
|
+
<span className={css.cardTitle}>笔法帖</span>
|
|
236
309
|
<span className={css.meta}>从内置 8 套叙事风格模板中一键选用(来自 AI-Novel-Writing-Assistant 写法引擎),无需样本文本;绑定后生成与润色都遵循该风格。</span>
|
|
237
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8,
|
|
310
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-8)', flex: 1, minHeight: 0, overflowY: 'auto' }}>
|
|
238
311
|
{data.styleTemplates.map(template => {
|
|
239
312
|
const bound = assets.styleAssets.some(s => s.name === template.name)
|
|
240
313
|
return (
|
|
241
|
-
<div key={template.key} style={{ border: `1px solid ${bound ? 'var(--nf-accent)' : 'var(--nf-border)'}`, borderRadius: 6, padding: '
|
|
242
|
-
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
|
314
|
+
<div key={template.key} style={{ border: `1px solid ${bound ? 'var(--nf-accent)' : 'var(--nf-border)'}`, borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-8) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
315
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--nf-space-8)' }}>
|
|
243
316
|
<span><b>{template.name}</b> <span className={css.badge} style={{ borderColor: 'var(--nf-text-3)', color: 'var(--nf-text-3)' }}>{template.category}</span></span>
|
|
244
317
|
<button
|
|
245
318
|
className={`${css.button} ${css.buttonSmall} ${bound ? '' : css.buttonPrimary}`}
|
|
@@ -269,24 +342,68 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
269
342
|
</div>
|
|
270
343
|
)}
|
|
271
344
|
|
|
272
|
-
{/*
|
|
345
|
+
{/* 文戒 */}
|
|
273
346
|
{assetTab === 'rules' && (
|
|
274
|
-
<div className={css.card}>
|
|
275
|
-
<span className={css.cardTitle}
|
|
276
|
-
<span className={css.meta}>写作时必须遵守的表达边界(内置全局 +
|
|
277
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 6,
|
|
278
|
-
{builtinRules.map(rule =>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<div
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
347
|
+
<div className={css.card} style={{ flex: 1, minHeight: 0 }}>
|
|
348
|
+
<span className={css.cardTitle}>文戒</span>
|
|
349
|
+
<span className={css.meta}>写作时必须遵守的表达边界(内置全局 + 项目自定义),生成与审稿都会检查。内置规则可用「覆盖编辑」复制为自定义版本调整。</span>
|
|
350
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)', flex: 1, minHeight: 0, overflowY: 'auto' }}>
|
|
351
|
+
{builtinRules.map(rule => {
|
|
352
|
+
const overridden = customRules.some(r => r.name === rule.name)
|
|
353
|
+
return (
|
|
354
|
+
<div key={rule.name} style={{ border: '1px solid var(--nf-border)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
355
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--nf-space-8)' }}>
|
|
356
|
+
<span>
|
|
357
|
+
<b>{rule.name}</b>{' '}
|
|
358
|
+
<span className={css.badge} style={{ borderColor: overridden ? 'var(--nf-accent)' : 'var(--nf-text-3)', color: overridden ? 'var(--nf-accent)' : 'var(--nf-text-3)' }}>
|
|
359
|
+
{overridden ? '自定义覆盖中' : '内置'}
|
|
360
|
+
</span>
|
|
361
|
+
</span>
|
|
362
|
+
<button className={`${css.button} ${css.buttonSmall}`} disabled={busy} onClick={() => { handleOverrideBuiltin(rule) }}>
|
|
363
|
+
{overridden ? '✎ 编辑覆盖' : '+ 覆盖编辑'}
|
|
364
|
+
</button>
|
|
365
|
+
</div>
|
|
366
|
+
<div className={css.meta}>避免:{rule.avoid}</div>
|
|
367
|
+
<div className={css.meta}>修正:{rule.fix}</div>
|
|
368
|
+
</div>
|
|
369
|
+
)
|
|
370
|
+
})}
|
|
371
|
+
{customRules.map((rule, index) => (
|
|
372
|
+
<div key={`${rule.name}-${index}`} style={{ border: '1px solid var(--nf-accent)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
373
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--nf-space-8)' }}>
|
|
374
|
+
<span>
|
|
375
|
+
<b>{rule.name}</b>{' '}
|
|
376
|
+
<span className={css.badge} style={{ borderColor: 'var(--nf-accent)', color: 'var(--nf-accent)' }}>自定义</span>
|
|
377
|
+
</span>
|
|
378
|
+
<span style={{ display: 'flex', gap: 'var(--nf-space-6)' }}>
|
|
379
|
+
<button className={`${css.button} ${css.buttonSmall}`} disabled={busy} onClick={() => { setEditingRule({ index, name: rule.name, avoid: rule.avoid, fix: rule.fix ?? '' }) }}>
|
|
380
|
+
编辑
|
|
381
|
+
</button>
|
|
382
|
+
<button className={`${css.button} ${css.buttonSmall}`} disabled={busy} onClick={() => { void handleRemoveRule(index) }}>
|
|
383
|
+
删除
|
|
384
|
+
</button>
|
|
385
|
+
</span>
|
|
386
|
+
</div>
|
|
387
|
+
{editingRule !== null && editingRule.index === index ? (
|
|
388
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--nf-space-6)', marginTop: 'var(--nf-space-6)' }}>
|
|
389
|
+
<input className={css.input} placeholder="规则名" value={editingRule.name} onChange={e => { setEditingRule({ ...editingRule, name: e.target.value }) }} />
|
|
390
|
+
<input className={css.input} placeholder="避免(要杜绝的表达)" value={editingRule.avoid} onChange={e => { setEditingRule({ ...editingRule, avoid: e.target.value }) }} />
|
|
391
|
+
<input className={css.input} placeholder="修正(改写方向,可留空)" value={editingRule.fix} onChange={e => { setEditingRule({ ...editingRule, fix: e.target.value }) }} />
|
|
392
|
+
<div className={css.row}>
|
|
393
|
+
<button className={`${css.button} ${css.buttonSmall} ${css.buttonPrimary}`} disabled={busy || (editingRule.avoid.trim() === '' && editingRule.fix.trim() === '')} onClick={() => { void handleSaveRuleEdit() }}>
|
|
394
|
+
保存
|
|
395
|
+
</button>
|
|
396
|
+
<button className={`${css.button} ${css.buttonSmall}`} onClick={() => { setEditingRule(null) }}>
|
|
397
|
+
取消
|
|
398
|
+
</button>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
) : (
|
|
402
|
+
<>
|
|
403
|
+
<div className={css.meta}>避免:{rule.avoid}</div>
|
|
404
|
+
{rule.fix !== '' && <div className={css.meta}>修正:{rule.fix}</div>}
|
|
405
|
+
</>
|
|
406
|
+
)}
|
|
290
407
|
</div>
|
|
291
408
|
))}
|
|
292
409
|
</div>
|
|
@@ -303,13 +420,13 @@ export function AssetsTab({ api }: AssetsTabProps) {
|
|
|
303
420
|
</div>
|
|
304
421
|
)}
|
|
305
422
|
|
|
306
|
-
{/*
|
|
423
|
+
{/* 心法(自定义写法引擎) */}
|
|
307
424
|
{assetTab === 'style' && (
|
|
308
425
|
<div className={css.card}>
|
|
309
|
-
<span className={css.cardTitle}
|
|
426
|
+
<span className={css.cardTitle}>心法</span>
|
|
310
427
|
<span className={css.meta}>粘贴一段你喜欢的样本文本,AI 提取叙事风格规则并绑定到本书,后续章节保持同一味道。</span>
|
|
311
428
|
{assets.styleAssets.map(style => (
|
|
312
|
-
<div key={style.name} style={{ border: '1px solid var(--nf-border)', borderRadius: 6, padding: '
|
|
429
|
+
<div key={style.name} style={{ border: '1px solid var(--nf-border)', borderRadius: 'var(--nf-radius-6)', padding: 'var(--nf-space-6) var(--nf-space-10)', fontSize: 'var(--nf-fs-12)' }}>
|
|
313
430
|
<b>{style.name}</b>
|
|
314
431
|
<div className={css.meta}>叙述:{style.proseRules.slice(0, 3).join(';')}</div>
|
|
315
432
|
{style.dialogueRules.length > 0 && <div className={css.meta}>台词:{style.dialogueRules.slice(0, 2).join(';')}</div>}
|