@waterwx/dsh-novel-forge 1.2.1-alpha → 1.3.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.
Files changed (52) hide show
  1. package/README.md +4 -5
  2. package/lib/client.js +2470 -858
  3. package/lib/index.js +2155 -388
  4. package/lib/types/action-guard.d.ts +1 -1
  5. package/lib/types/assets.d.ts +7 -7
  6. package/lib/types/client/api.d.ts +31 -1
  7. package/lib/types/client/llmLive.d.ts +12 -14
  8. package/lib/types/client/panel/LiveFeedLog.d.ts +5 -0
  9. package/lib/types/client/panel/ModelManager.d.ts +2 -2
  10. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  11. package/lib/types/client/panel/TensionView.d.ts +14 -0
  12. package/lib/types/client/panel/TimelineView.d.ts +14 -0
  13. package/lib/types/engine.d.ts +40 -2
  14. package/lib/types/index.d.ts +4 -0
  15. package/lib/types/llm-live.d.ts +65 -34
  16. package/lib/types/llm-retry.d.ts +38 -0
  17. package/lib/types/market-radar-sources.d.ts +1 -1
  18. package/lib/types/prompt-slots.d.ts +57 -0
  19. package/lib/types/prompting.d.ts +2 -2
  20. package/lib/types/protocol.d.ts +283 -3
  21. package/lib/types/snapshots.d.ts +46 -0
  22. package/lib/types/stage-contract.d.ts +1 -1
  23. package/lib/types/tension.d.ts +65 -0
  24. package/lib/types/timeline.d.ts +37 -0
  25. package/package.json +3 -2
  26. package/src/action-guard.ts +1 -1
  27. package/src/assets.ts +300 -268
  28. package/src/assistant.ts +45 -29
  29. package/src/client/api.ts +76 -2
  30. package/src/client/llmLive.ts +26 -16
  31. package/src/client/panel/AssetsTab.tsx +2 -2
  32. package/src/client/panel/LiveFeedLog.tsx +204 -67
  33. package/src/client/panel/ModelManager.tsx +2 -2
  34. package/src/client/panel/NovelPanel.tsx +127 -13
  35. package/src/client/panel/PromptSlotsView.tsx +97 -0
  36. package/src/client/panel/SettingsView.tsx +35 -0
  37. package/src/client/panel/TensionView.tsx +196 -0
  38. package/src/client/panel/TimelineView.tsx +192 -0
  39. package/src/engine.ts +549 -108
  40. package/src/index.ts +12 -0
  41. package/src/llm-live.ts +260 -68
  42. package/src/llm-retry.ts +77 -0
  43. package/src/market-radar-sources.ts +173 -173
  44. package/src/prompt-slots.ts +125 -0
  45. package/src/prompting.ts +8 -8
  46. package/src/protocol.ts +314 -3
  47. package/src/routes.ts +325 -6
  48. package/src/run.ts +4 -0
  49. package/src/snapshots.ts +174 -0
  50. package/src/stage-contract.ts +189 -189
  51. package/src/tension.ts +240 -0
  52. package/src/timeline.ts +159 -0
package/lib/client.js CHANGED
@@ -36,6 +36,18 @@ window.__ModuleLoader__.load({
36
36
  director: "/api/dsh-novel-forge/director",
37
37
  directorTodos: "/api/dsh-novel-forge/director/todos",
38
38
  llmLive: "/api/dsh-novel-forge/llm-live/stream",
39
+ /** 查看某次调用实际发送的 Prompt(按 sessionId)。 */
40
+ llmPrompt: "/api/dsh-novel-forge/llm-live/prompt",
41
+ /** 清零本次运行的用量账本。 */
42
+ usageReset: "/api/dsh-novel-forge/usage/reset",
43
+ /** 章节历史版本(快照)。 */
44
+ snapshot: "/api/dsh-novel-forge/snapshot",
45
+ /** 张力曲线(GET 数据 / POST 设置)。 */
46
+ tension: "/api/dsh-novel-forge/tension",
47
+ /** 提示词槽位(GET 列表 / POST 写入)。 */
48
+ promptSlots: "/api/dsh-novel-forge/prompt-slots",
49
+ /** 故事时间线(GET 列表 / POST 操作)。 */
50
+ timeline: "/api/dsh-novel-forge/timeline",
39
51
  marketRadar: "/api/dsh-novel-forge/market-radar",
40
52
  marketRadarScan: "/api/dsh-novel-forge/market-radar/scan",
41
53
  marketRadarApply: "/api/dsh-novel-forge/market-radar/apply",
@@ -220,6 +232,24 @@ window.__ModuleLoader__.load({
220
232
  ]
221
233
  }
222
234
  ];
235
+ /** 导出范围全集(宿主校验用;顺序即 UI 展示顺序)。 */
236
+ const EXPORT_SCOPE_VALUES = [
237
+ "book",
238
+ "settings",
239
+ "plan",
240
+ "characters",
241
+ "review",
242
+ "project"
243
+ ];
244
+ /** 导出范围的中文标签。 */
245
+ const EXPORT_SCOPE_LABELS = {
246
+ book: "整本正文",
247
+ settings: "项目设定",
248
+ plan: "规划(卷/章节)",
249
+ characters: "角色",
250
+ review: "质检记录",
251
+ project: "项目备份 JSON"
252
+ };
223
253
  //#endregion
224
254
  //#region src/client/api.ts
225
255
  /**
@@ -333,8 +363,11 @@ window.__ModuleLoader__.load({
333
363
  async foreshadow(req) {
334
364
  return postJson(NOVEL_API.foreshadow, req);
335
365
  }
336
- async exportBook(format) {
337
- return postJson(NOVEL_API.exportBook, { format });
366
+ async exportBook(format, scope = "book") {
367
+ return postJson(NOVEL_API.exportBook, {
368
+ format,
369
+ scope
370
+ });
338
371
  }
339
372
  async chapter(no) {
340
373
  return readJson(await fetch(withBookId(`${NOVEL_API.chapter}?no=${no}`)));
@@ -750,6 +783,90 @@ window.__ModuleLoader__.load({
750
783
  }
751
784
  }
752
785
  }
786
+ /** 张力曲线:数据 + 规则核对。 */
787
+ async tension() {
788
+ return readJson(await fetch(withBookId(NOVEL_API.tension)));
789
+ }
790
+ /** 设置张力曲线预设。 */
791
+ async tensionPreset(preset) {
792
+ await postJson(NOVEL_API.tension, {
793
+ op: "preset",
794
+ preset
795
+ });
796
+ }
797
+ /** 设置/清除某一章的目标张力。 */
798
+ async tensionSet(chapterNo, tension) {
799
+ await postJson(NOVEL_API.tension, {
800
+ op: "set",
801
+ chapterNo,
802
+ tension
803
+ });
804
+ }
805
+ /** 提示词槽位:列表。 */
806
+ async promptSlots() {
807
+ return readJson(await fetch(withBookId(NOVEL_API.promptSlots)));
808
+ }
809
+ /** 写入一个提示词槽位(空串 = 清空)。 */
810
+ async promptSlotSet(id, value) {
811
+ await postJson(NOVEL_API.promptSlots, {
812
+ id,
813
+ value
814
+ });
815
+ }
816
+ /** 故事时间线:列表(含规则初筛问题)。 */
817
+ async timeline() {
818
+ return readJson(await fetch(withBookId(NOVEL_API.timeline)));
819
+ }
820
+ /** 抽取某一章的时间线事件。 */
821
+ async timelineExtract(chapterNo) {
822
+ return postJson(NOVEL_API.timeline, {
823
+ op: "extract",
824
+ chapterNo
825
+ });
826
+ }
827
+ /** 检查时间线矛盾(规则初筛 + AI 复核)。 */
828
+ async timelineCheck() {
829
+ return postJson(NOVEL_API.timeline, { op: "check" });
830
+ }
831
+ /** 手工修正一条时间线事件。 */
832
+ async timelineUpdate(event) {
833
+ await postJson(NOVEL_API.timeline, {
834
+ op: "update",
835
+ event
836
+ });
837
+ }
838
+ /** 删除一条时间线事件。 */
839
+ async timelineRemove(id) {
840
+ await postJson(NOVEL_API.timeline, {
841
+ op: "remove",
842
+ id
843
+ });
844
+ }
845
+ /** 章节历史版本列表。 */
846
+ async snapshots() {
847
+ return readJson(await fetch(withBookId(NOVEL_API.snapshot)));
848
+ }
849
+ /** 回滚到某个历史版本。 */
850
+ async snapshotRestore(id) {
851
+ await postJson(NOVEL_API.snapshot, {
852
+ op: "restore",
853
+ id
854
+ });
855
+ }
856
+ /** 给某章手工存档。 */
857
+ async snapshotCreate(chapterNo) {
858
+ await postJson(NOVEL_API.snapshot, {
859
+ op: "create",
860
+ chapterNo
861
+ });
862
+ }
863
+ /** 删除一个历史版本。 */
864
+ async snapshotRemove(id) {
865
+ await postJson(NOVEL_API.snapshot, {
866
+ op: "remove",
867
+ id
868
+ });
869
+ }
753
870
  /** Load the persisted assistant conversation. */
754
871
  async assistantHistory() {
755
872
  return (await readJson(await fetch(withBookId(NOVEL_API.assistantHistory)))).messages;
@@ -36466,491 +36583,491 @@ window.__ModuleLoader__.load({
36466
36583
  document.head.appendChild(tag);
36467
36584
  }
36468
36585
  var panel_module_css_default = {
36469
- "chatBubbleUser": "jbF1yq_chatBubbleUser",
36470
- "assistantStatus": "jbF1yq_assistantStatus",
36471
- "wfMetric": "jbF1yq_wfMetric",
36472
- "outlineEdit": "jbF1yq_outlineEdit",
36586
+ "readerSeg": "jbF1yq_readerSeg",
36587
+ "chWorkAsideBody": "jbF1yq_chWorkAsideBody",
36588
+ "wfColB": "jbF1yq_wfColB",
36589
+ "subPageBack": "jbF1yq_subPageBack",
36590
+ "wfProgRest": "jbF1yq_wfProgRest",
36591
+ "bookCardCoverFallback": "jbF1yq_bookCardCoverFallback",
36592
+ "dropzoneActive": "jbF1yq_dropzoneActive",
36593
+ "pillNavPill": "jbF1yq_pillNavPill",
36594
+ "wfAct": "jbF1yq_wfAct",
36595
+ "diffTagOld": "jbF1yq_diffTagOld",
36596
+ "panelHead": "jbF1yq_panelHead",
36597
+ "importField": "jbF1yq_importField",
36598
+ "coverPreview": "jbF1yq_coverPreview",
36599
+ "ideaCandidate": "jbF1yq_ideaCandidate",
36600
+ "drawerHead": "jbF1yq_drawerHead",
36601
+ "coverPlaceholder": "jbF1yq_coverPlaceholder",
36602
+ "planTree": "jbF1yq_planTree",
36603
+ "progressLine": "jbF1yq_progressLine",
36604
+ "authorNavBtn": "jbF1yq_authorNavBtn",
36473
36605
  "planBeats": "jbF1yq_planBeats",
36474
- "wfStFix": "jbF1yq_wfStFix",
36475
- "chWorkSideHead": "jbF1yq_chWorkSideHead",
36476
- "wfAccentAmber": "jbF1yq_wfAccentAmber",
36477
- "headChip": "jbF1yq_headChip",
36478
- "lfDot": "jbF1yq_lfDot",
36479
- "dropzone": "jbF1yq_dropzone",
36480
- "navGroup": "jbF1yq_navGroup",
36481
- "readerText": "jbF1yq_readerText",
36606
+ "nfFadeIn": "jbF1yq_nfFadeIn",
36607
+ "nfSkeletonShimmer": "jbF1yq_nfSkeletonShimmer",
36608
+ "switch": "jbF1yq_switch",
36609
+ "bookCard": "jbF1yq_bookCard",
36482
36610
  "progress": "jbF1yq_progress",
36483
- "wfBox": "jbF1yq_wfBox",
36484
- "wfDl": "jbF1yq_wfDl",
36485
- "wfSep": "jbF1yq_wfSep",
36486
- "shelfStatGrid": "jbF1yq_shelfStatGrid",
36487
- "outlineH1": "jbF1yq_outlineH1",
36488
- "wfDlDim": "jbF1yq_wfDlDim",
36489
- "assetCardContent": "jbF1yq_assetCardContent",
36490
- "outlineH2": "jbF1yq_outlineH2",
36491
- "adaptRuleRow": "jbF1yq_adaptRuleRow",
36492
- "wfCmd": "jbF1yq_wfCmd",
36493
- "adaptProposal": "jbF1yq_adaptProposal",
36494
- "nfToastTick": "jbF1yq_nfToastTick",
36495
- "planTree": "jbF1yq_planTree",
36496
- "adaptOutlinePre": "jbF1yq_adaptOutlinePre",
36497
- "wsPreviewText": "jbF1yq_wsPreviewText",
36498
- "planTreeVol": "jbF1yq_planTreeVol",
36499
- "wfRsName": "jbF1yq_wfRsName",
36611
+ "readerGroupCount": "jbF1yq_readerGroupCount",
36612
+ "assetCard": "jbF1yq_assetCard",
36613
+ "t2RailBtn": "jbF1yq_t2RailBtn",
36614
+ "badgePending": "jbF1yq_badgePending",
36615
+ "adaptImpacts": "jbF1yq_adaptImpacts",
36616
+ "dropzoneIcon": "jbF1yq_dropzoneIcon",
36617
+ "wfStRev": "jbF1yq_wfStRev",
36618
+ "navGroupLabel": "jbF1yq_navGroupLabel",
36500
36619
  "contentDrawer": "jbF1yq_contentDrawer",
36501
- "diffTagNew": "jbF1yq_diffTagNew",
36502
- "nfViewIn": "jbF1yq_nfViewIn",
36503
- "textarea": "jbF1yq_textarea",
36620
+ "chatRole": "jbF1yq_chatRole",
36621
+ "chapterPreview": "jbF1yq_chapterPreview",
36622
+ "readerItemTitle": "jbF1yq_readerItemTitle",
36623
+ "importHint": "jbF1yq_importHint",
36624
+ "planMaster": "jbF1yq_planMaster",
36625
+ "planTreeRowNo": "jbF1yq_planTreeRowNo",
36626
+ "navTab": "jbF1yq_navTab",
36627
+ "settingsDrawer": "jbF1yq_settingsDrawer",
36504
36628
  "readerItemBadge": "jbF1yq_readerItemBadge",
36505
- "wfCard2": "jbF1yq_wfCard2",
36506
- "outlineHr": "jbF1yq_outlineHr",
36629
+ "readerPara": "jbF1yq_readerPara",
36630
+ "diffText": "jbF1yq_diffText",
36631
+ "outlineVolSummary": "jbF1yq_outlineVolSummary",
36632
+ "wfTool": "jbF1yq_wfTool",
36633
+ "wfFeedList": "jbF1yq_wfFeedList",
36634
+ "subPageHead": "jbF1yq_subPageHead",
36635
+ "iconButton": "jbF1yq_iconButton",
36636
+ "wfDot": "jbF1yq_wfDot",
36637
+ "drawerTitle": "jbF1yq_drawerTitle",
36638
+ "brandText": "jbF1yq_brandText",
36639
+ "wfOpsBtnMain": "jbF1yq_wfOpsBtnMain",
36640
+ "onlyChanges": "jbF1yq_onlyChanges",
36641
+ "wsPreviewText": "jbF1yq_wsPreviewText",
36507
36642
  "readerItemNo": "jbF1yq_readerItemNo",
36508
- "planTreeSearch": "jbF1yq_planTreeSearch",
36509
- "entry": "jbF1yq_entry",
36510
- "cmdItem": "jbF1yq_cmdItem",
36511
- "chWorkDraftBar": "jbF1yq_chWorkDraftBar",
36643
+ "nfToastOut": "jbF1yq_nfToastOut",
36644
+ "cmdEmpty": "jbF1yq_cmdEmpty",
36645
+ "navAboutRow": "jbF1yq_navAboutRow",
36646
+ "assistantFloatBody": "jbF1yq_assistantFloatBody",
36647
+ "ideaToggle": "jbF1yq_ideaToggle",
36648
+ "bookCardActions": "jbF1yq_bookCardActions",
36649
+ "wfPreview": "jbF1yq_wfPreview",
36650
+ "importFileInfo": "jbF1yq_importFileInfo",
36651
+ "chWorkText": "jbF1yq_chWorkText",
36652
+ "chWorkSideHead": "jbF1yq_chWorkSideHead",
36653
+ "bookCardMetaRow": "jbF1yq_bookCardMetaRow",
36654
+ "diffNew": "jbF1yq_diffNew",
36655
+ "importModalActions": "jbF1yq_importModalActions",
36656
+ "adaptProposal": "jbF1yq_adaptProposal",
36657
+ "badgeError": "jbF1yq_badgeError",
36512
36658
  "emptyStateTitle": "jbF1yq_emptyStateTitle",
36513
- "shelfToolbar": "jbF1yq_shelfToolbar",
36514
- "tab": "jbF1yq_tab",
36515
- "input": "jbF1yq_input",
36516
- "toolStepName": "jbF1yq_toolStepName",
36517
- "wsSelected": "jbF1yq_wsSelected",
36518
- "panelNavCollapsed": "jbF1yq_panelNavCollapsed",
36519
- "aiTag": "jbF1yq_aiTag",
36520
- "authorHome": "jbF1yq_authorHome",
36521
- "chWorkAside": "jbF1yq_chWorkAside",
36522
- "buttonSmall": "jbF1yq_buttonSmall",
36523
- "readerHeader": "jbF1yq_readerHeader",
36524
- "authorTopNav": "jbF1yq_authorTopNav",
36525
- "authorTopTabs": "jbF1yq_authorTopTabs",
36526
- "chapterFocusPulse": "jbF1yq_chapterFocusPulse",
36527
- "toolStep": "jbF1yq_toolStep",
36528
- "authorContent": "jbF1yq_authorContent",
36659
+ "runLogTime": "jbF1yq_runLogTime",
36660
+ "assetCardName": "jbF1yq_assetCardName",
36529
36661
  "iconBtn": "jbF1yq_iconBtn",
36530
- "shelfEmpty": "jbF1yq_shelfEmpty",
36531
- "pageHeaderActions": "jbF1yq_pageHeaderActions",
36532
- "wfFdot": "jbF1yq_wfFdot",
36533
- "chWorkText": "jbF1yq_chWorkText",
36662
+ "subPageActions": "jbF1yq_subPageActions",
36663
+ "busyRow": "jbF1yq_busyRow",
36664
+ "bookCardBody": "jbF1yq_bookCardBody",
36665
+ "assistantStatusBusy": "jbF1yq_assistantStatusBusy",
36666
+ "outlineH1": "jbF1yq_outlineH1",
36667
+ "outlineTocL2": "jbF1yq_outlineTocL2",
36668
+ "pageHeaderSub": "jbF1yq_pageHeaderSub",
36669
+ "toolLive": "jbF1yq_toolLive",
36534
36670
  "chatBubbleAssistant": "jbF1yq_chatBubbleAssistant",
36535
- "buttonPrimary": "jbF1yq_buttonPrimary",
36536
- "legendOld": "jbF1yq_legendOld",
36537
- "outlineTocL3": "jbF1yq_outlineTocL3",
36538
- "fileList": "jbF1yq_fileList",
36539
- "outlineH": "jbF1yq_outlineH",
36671
+ "wfEmpty": "jbF1yq_wfEmpty",
36672
+ "importModalTabs": "jbF1yq_importModalTabs",
36673
+ "wfStFix": "jbF1yq_wfStFix",
36674
+ "nfToast": "jbF1yq_nfToast",
36675
+ "importError": "jbF1yq_importError",
36676
+ "assistantResize": "jbF1yq_assistantResize",
36677
+ "panelBody": "jbF1yq_panelBody",
36678
+ "nfToastHost": "jbF1yq_nfToastHost",
36679
+ "wfBook": "jbF1yq_wfBook",
36680
+ "progressLineError": "jbF1yq_progressLineError",
36681
+ "wfSig": "jbF1yq_wfSig",
36682
+ "outlineHr": "jbF1yq_outlineHr",
36683
+ "importModalOverlay": "jbF1yq_importModalOverlay",
36684
+ "wfSep": "jbF1yq_wfSep",
36685
+ "wfMetrics": "jbF1yq_wfMetrics",
36686
+ "toolStepStatus": "jbF1yq_toolStepStatus",
36687
+ "outlineTocL1": "jbF1yq_outlineTocL1",
36688
+ "diffTagNew": "jbF1yq_diffTagNew",
36689
+ "readerMain": "jbF1yq_readerMain",
36690
+ "wfConOk": "jbF1yq_wfConOk",
36691
+ "adaptTable": "jbF1yq_adaptTable",
36540
36692
  "progressBarFill": "jbF1yq_progressBarFill",
36541
- "assetGroupHeader": "jbF1yq_assetGroupHeader",
36542
- "docsSurface": "jbF1yq_docsSurface",
36543
- "planTreeRowTitle": "jbF1yq_planTreeRowTitle",
36544
- "runLogLine": "jbF1yq_runLogLine",
36545
- "wfRs": "jbF1yq_wfRs",
36546
- "wfCoord": "jbF1yq_wfCoord",
36547
- "topNavSpacer": "jbF1yq_topNavSpacer",
36548
- "outlineDoc": "jbF1yq_outlineDoc",
36549
- "onlyChanges": "jbF1yq_onlyChanges",
36550
- "progressTotalBar": "jbF1yq_progressTotalBar",
36551
- "dropzoneActive": "jbF1yq_dropzoneActive",
36552
- "adaptMatrix": "jbF1yq_adaptMatrix",
36553
- "diffSame": "jbF1yq_diffSame",
36554
- "diffTagOld": "jbF1yq_diffTagOld",
36555
- "assetEditorRow": "jbF1yq_assetEditorRow",
36693
+ "assetCardContent": "jbF1yq_assetCardContent",
36694
+ "toolStepActive": "jbF1yq_toolStepActive",
36695
+ "wfPl": "jbF1yq_wfPl",
36556
36696
  "wfConsoleNote": "jbF1yq_wfConsoleNote",
36557
- "navTitleBook": "jbF1yq_navTitleBook",
36558
- "badge": "jbF1yq_badge",
36559
- "dimCardSelected": "jbF1yq_dimCardSelected",
36560
- "emptyStateIcon": "jbF1yq_emptyStateIcon",
36561
- "importError": "jbF1yq_importError",
36562
- "importFileInfo": "jbF1yq_importFileInfo",
36563
- "chWorkAsideBody": "jbF1yq_chWorkAsideBody",
36564
- "assetLabel": "jbF1yq_assetLabel",
36565
- "topNavBtn": "jbF1yq_topNavBtn",
36566
- "nfSlideInRight": "jbF1yq_nfSlideInRight",
36567
- "wfRsNo": "jbF1yq_wfRsNo",
36568
- "headSpacer": "jbF1yq_headSpacer",
36569
- "planDetailBar": "jbF1yq_planDetailBar",
36570
- "wfConTxt": "jbF1yq_wfConTxt",
36571
- "wfPill": "jbF1yq_wfPill",
36572
- "readerItemTitle": "jbF1yq_readerItemTitle",
36573
- "wfFdotErr": "jbF1yq_wfFdotErr",
36574
- "chatRole": "jbF1yq_chatRole",
36697
+ "topNav": "jbF1yq_topNav",
36698
+ "assistantFloat": "jbF1yq_assistantFloat",
36699
+ "adaptRules": "jbF1yq_adaptRules",
36700
+ "wfEmpty2": "jbF1yq_wfEmpty2",
36575
36701
  "bookCardCoverTitle": "jbF1yq_bookCardCoverTitle",
36576
- "fieldLabel": "jbF1yq_fieldLabel",
36577
- "badgeRejected": "jbF1yq_badgeRejected",
36578
- "diffOld": "jbF1yq_diffOld",
36579
- "wfSw": "jbF1yq_wfSw",
36580
- "wfOpsBtn": "jbF1yq_wfOpsBtn",
36581
- "chWorkBar": "jbF1yq_chWorkBar",
36582
- "chWorkPickBtns": "jbF1yq_chWorkPickBtns",
36583
- "planDetail": "jbF1yq_planDetail",
36584
- "wfSigGood": "jbF1yq_wfSigGood",
36585
- "chWorkFootRow": "jbF1yq_chWorkFootRow",
36586
- "bookshelf": "jbF1yq_bookshelf",
36587
- "assetGrid": "jbF1yq_assetGrid",
36588
- "wfStage": "jbF1yq_wfStage",
36589
- "authorPageBodyFlow": "jbF1yq_authorPageBodyFlow",
36590
- "brandSub": "jbF1yq_brandSub",
36591
- "panelTop": "jbF1yq_panelTop",
36702
+ "wfLiveChip": "jbF1yq_wfLiveChip",
36703
+ "wfConTxt": "jbF1yq_wfConTxt",
36704
+ "wbIndexRow": "jbF1yq_wbIndexRow",
36705
+ "assistantStatus": "jbF1yq_assistantStatus",
36706
+ "cmdMask": "jbF1yq_cmdMask",
36707
+ "pageHeaderActions": "jbF1yq_pageHeaderActions",
36708
+ "outlineH2": "jbF1yq_outlineH2",
36709
+ "authorTopTabs": "jbF1yq_authorTopTabs",
36710
+ "panelTitle": "jbF1yq_panelTitle",
36711
+ "drawerSpacer": "jbF1yq_drawerSpacer",
36712
+ "authorPageHeader": "jbF1yq_authorPageHeader",
36713
+ "dimCardSelected": "jbF1yq_dimCardSelected",
36714
+ "importModal": "jbF1yq_importModal",
36715
+ "shelfToolbarExtra": "jbF1yq_shelfToolbarExtra",
36716
+ "tnum": "jbF1yq_tnum",
36717
+ "buttonDanger": "jbF1yq_buttonDanger",
36718
+ "diffChange": "jbF1yq_diffChange",
36719
+ "planDetailBody": "jbF1yq_planDetailBody",
36720
+ "row": "jbF1yq_row",
36721
+ "t2RailHead": "jbF1yq_t2RailHead",
36722
+ "readerFoot": "jbF1yq_readerFoot",
36723
+ "entryLabel": "jbF1yq_entryLabel",
36724
+ "noticeSuccess": "jbF1yq_noticeSuccess",
36592
36725
  "wsColumns": "jbF1yq_wsColumns",
36593
- "wfPreviewActs": "jbF1yq_wfPreviewActs",
36594
- "wsPreview": "jbF1yq_wsPreview",
36595
- "wfSmallFoot": "jbF1yq_wfSmallFoot",
36596
- "field": "jbF1yq_field",
36597
- "runChip": "jbF1yq_runChip",
36598
- "outlineVolSummary": "jbF1yq_outlineVolSummary",
36599
- "brand": "jbF1yq_brand",
36600
- "wfColA": "jbF1yq_wfColA",
36601
- "t2RailSep": "jbF1yq_t2RailSep",
36726
+ "wbIndexTitle": "jbF1yq_wbIndexTitle",
36727
+ "readerTitle": "jbF1yq_readerTitle",
36728
+ "cmdItem": "jbF1yq_cmdItem",
36729
+ "chWorkActions": "jbF1yq_chWorkActions",
36730
+ "nfToastTick": "jbF1yq_nfToastTick",
36602
36731
  "chapter": "jbF1yq_chapter",
36603
- "wfPreviewName": "jbF1yq_wfPreviewName",
36604
- "wfDs": "jbF1yq_wfDs",
36605
- "wbIndexVol": "jbF1yq_wbIndexVol",
36606
- "card": "jbF1yq_card",
36607
- "nfChipPulse": "jbF1yq_nfChipPulse",
36608
- "row": "jbF1yq_row",
36609
- "t2Master": "jbF1yq_t2Master",
36610
- "adaptInputCard": "jbF1yq_adaptInputCard",
36611
- "ideaToggle": "jbF1yq_ideaToggle",
36612
- "chWorkPickBar": "jbF1yq_chWorkPickBar",
36613
- "nfFadeIn": "jbF1yq_nfFadeIn",
36614
- "adaptTable": "jbF1yq_adaptTable",
36732
+ "aiTag": "jbF1yq_aiTag",
36733
+ "wfPillDot": "jbF1yq_wfPillDot",
36734
+ "wfDrawerBox": "jbF1yq_wfDrawerBox",
36735
+ "pillNavSlider": "jbF1yq_pillNavSlider",
36736
+ "panelContent": "jbF1yq_panelContent",
36737
+ "fileList": "jbF1yq_fileList",
36738
+ "settingsDrawerOverlay": "jbF1yq_settingsDrawerOverlay",
36739
+ "wfToolBlue": "jbF1yq_wfToolBlue",
36740
+ "topNavLabel": "jbF1yq_topNavLabel",
36741
+ "assetGroupHeader": "jbF1yq_assetGroupHeader",
36742
+ "adaptTableScroll": "jbF1yq_adaptTableScroll",
36743
+ "readerGroupTitle": "jbF1yq_readerGroupTitle",
36744
+ "titleBlock": "jbF1yq_titleBlock",
36745
+ "wfCn": "jbF1yq_wfCn",
36746
+ "readerText": "jbF1yq_readerText",
36747
+ "planDot": "jbF1yq_planDot",
36748
+ "outlineP": "jbF1yq_outlineP",
36749
+ "navTitle": "jbF1yq_navTitle",
36750
+ "wfDrawerToggle": "jbF1yq_wfDrawerToggle",
36751
+ "emptyStateIcon": "jbF1yq_emptyStateIcon",
36752
+ "navTitleBook": "jbF1yq_navTitleBook",
36615
36753
  "assetEditor": "jbF1yq_assetEditor",
36616
- "cardTitle": "jbF1yq_cardTitle",
36617
- "authorNavBtn": "jbF1yq_authorNavBtn",
36618
- "outlineTocL2": "jbF1yq_outlineTocL2",
36619
- "runChipText": "jbF1yq_runChipText",
36754
+ "wfCard2": "jbF1yq_wfCard2",
36620
36755
  "wfFeedBox": "jbF1yq_wfFeedBox",
36621
- "lfPill": "jbF1yq_lfPill",
36622
- "navAboutUpdate": "jbF1yq_navAboutUpdate",
36623
- "navAbout": "jbF1yq_navAbout",
36624
- "adaptRule": "jbF1yq_adaptRule",
36625
- "cmdMask": "jbF1yq_cmdMask",
36626
- "bookCardProgressFill": "jbF1yq_bookCardProgressFill",
36627
- "topNavNi": "jbF1yq_topNavNi",
36628
- "adaptImpact": "jbF1yq_adaptImpact",
36629
- "wsAppliedBanner": "jbF1yq_wsAppliedBanner",
36630
- "assetFilterChip": "jbF1yq_assetFilterChip",
36631
- "wfFdotOk": "jbF1yq_wfFdotOk",
36756
+ "headChip": "jbF1yq_headChip",
36757
+ "planTreeVol": "jbF1yq_planTreeVol",
36758
+ "chWorkTitle": "jbF1yq_chWorkTitle",
36632
36759
  "progressLineLive": "jbF1yq_progressLineLive",
36633
- "wfDone": "jbF1yq_wfDone",
36634
- "wfStRev": "jbF1yq_wfStRev",
36635
- "wfStFail": "jbF1yq_wfStFail",
36636
- "importPreviewList": "jbF1yq_importPreviewList",
36637
- "diffDel": "jbF1yq_diffDel",
36638
- "assetGroups": "jbF1yq_assetGroups",
36639
- "assetFilterChipActive": "jbF1yq_assetFilterChipActive",
36640
- "outlineDocWrap": "jbF1yq_outlineDocWrap",
36641
- "wsEditor": "jbF1yq_wsEditor",
36642
- "navTitleName": "jbF1yq_navTitleName",
36643
- "outlineVol": "jbF1yq_outlineVol",
36644
- "bookCardCoverFallback": "jbF1yq_bookCardCoverFallback",
36645
- "assetKind": "jbF1yq_assetKind",
36646
- "progressLine": "jbF1yq_progressLine",
36647
- "t2Rail": "jbF1yq_t2Rail",
36648
- "subPageBack": "jbF1yq_subPageBack",
36760
+ "genreDesc": "jbF1yq_genreDesc",
36761
+ "meta": "jbF1yq_meta",
36762
+ "importModalHead": "jbF1yq_importModalHead",
36763
+ "eyebrow": "jbF1yq_eyebrow",
36764
+ "wfTop": "jbF1yq_wfTop",
36765
+ "progressTotalBar": "jbF1yq_progressTotalBar",
36766
+ "readerGroupArrow": "jbF1yq_readerGroupArrow",
36767
+ "readerError": "jbF1yq_readerError",
36768
+ "docsSurface": "jbF1yq_docsSurface",
36649
36769
  "planBeatsLabel": "jbF1yq_planBeatsLabel",
36650
- "shelfEmptyTitle": "jbF1yq_shelfEmptyTitle",
36651
- "shelfStatCard": "jbF1yq_shelfStatCard",
36652
- "busyRow": "jbF1yq_busyRow",
36653
- "brandText": "jbF1yq_brandText",
36654
- "adaptTableScroll": "jbF1yq_adaptTableScroll",
36655
- "panelHidden": "jbF1yq_panelHidden",
36656
- "bookCardCover": "jbF1yq_bookCardCover",
36770
+ "emptyState": "jbF1yq_emptyState",
36657
36771
  "view": "jbF1yq_view",
36658
- "panelHead": "jbF1yq_panelHead",
36772
+ "settingsDrawerBody": "jbF1yq_settingsDrawerBody",
36773
+ "wfNow": "jbF1yq_wfNow",
36659
36774
  "chapterFocus": "jbF1yq_chapterFocus",
36660
- "cmdInput": "jbF1yq_cmdInput",
36661
- "pillNavSliding": "jbF1yq_pillNavSliding",
36775
+ "wsColumn": "jbF1yq_wsColumn",
36662
36776
  "shelfEmptyIcon": "jbF1yq_shelfEmptyIcon",
36663
- "wfDot": "jbF1yq_wfDot",
36664
- "meta": "jbF1yq_meta",
36665
- "settingsDrawer": "jbF1yq_settingsDrawer",
36666
- "wfTop": "jbF1yq_wfTop",
36777
+ "progressLineDone": "jbF1yq_progressLineDone",
36778
+ "wfRow": "jbF1yq_wfRow",
36779
+ "buttonPrimary": "jbF1yq_buttonPrimary",
36780
+ "chWorkMain": "jbF1yq_chWorkMain",
36781
+ "wfDrawerList": "jbF1yq_wfDrawerList",
36782
+ "reviewBox": "jbF1yq_reviewBox",
36783
+ "cmdList": "jbF1yq_cmdList",
36784
+ "wfLiveMark": "jbF1yq_wfLiveMark",
36785
+ "cmdIcon": "jbF1yq_cmdIcon",
36786
+ "shelfHero": "jbF1yq_shelfHero",
36787
+ "chapterMain": "jbF1yq_chapterMain",
36788
+ "chWorkToolActions": "jbF1yq_chWorkToolActions",
36789
+ "topNavBadge": "jbF1yq_topNavBadge",
36790
+ "nfViewIn": "jbF1yq_nfViewIn",
36791
+ "wfPoolBar": "jbF1yq_wfPoolBar",
36792
+ "wfDlDi": "jbF1yq_wfDlDi",
36793
+ "outlineDoc": "jbF1yq_outlineDoc",
36794
+ "wfTopRight": "jbF1yq_wfTopRight",
36667
36795
  "pulse": "jbF1yq_pulse",
36668
- "wsColumn": "jbF1yq_wsColumn",
36669
- "nfToastHost": "jbF1yq_nfToastHost",
36670
- "chapterPreview": "jbF1yq_chapterPreview",
36671
- "emptyState": "jbF1yq_emptyState",
36672
- "genreDesc": "jbF1yq_genreDesc",
36673
- "wfConOk": "jbF1yq_wfConOk",
36674
- "panel": "jbF1yq_panel",
36675
- "contentDrawerBody": "jbF1yq_contentDrawerBody",
36676
- "readerChapterTitle": "jbF1yq_readerChapterTitle",
36677
- "nfToastOut": "jbF1yq_nfToastOut",
36796
+ "segTab": "jbF1yq_segTab",
36797
+ "wfPill": "jbF1yq_wfPill",
36798
+ "statCardDValue": "jbF1yq_statCardDValue",
36799
+ "wfOpsBtn": "jbF1yq_wfOpsBtn",
36800
+ "diffOld": "jbF1yq_diffOld",
36801
+ "importPreview": "jbF1yq_importPreview",
36802
+ "wfSigRight": "jbF1yq_wfSigRight",
36803
+ "wsEditor": "jbF1yq_wsEditor",
36804
+ "wfConsole": "jbF1yq_wfConsole",
36805
+ "bookCardActive": "jbF1yq_bookCardActive",
36806
+ "assetKind": "jbF1yq_assetKind",
36807
+ "wfFt": "jbF1yq_wfFt",
36808
+ "t2Content": "jbF1yq_t2Content",
36678
36809
  "chWork": "jbF1yq_chWork",
36679
- "wfQtTone": "jbF1yq_wfQtTone",
36680
- "subPage": "jbF1yq_subPage",
36681
- "bookCard": "jbF1yq_bookCard",
36682
- "brandName": "jbF1yq_brandName",
36810
+ "nfDrawerIn": "jbF1yq_nfDrawerIn",
36683
36811
  "toolStepError": "jbF1yq_toolStepError",
36684
- "importModalTabs": "jbF1yq_importModalTabs",
36685
- "drawerScrim": "jbF1yq_drawerScrim",
36686
- "subPageMeta": "jbF1yq_subPageMeta",
36687
- "wfPoolBar": "jbF1yq_wfPoolBar",
36688
- "wfQt": "jbF1yq_wfQt",
36689
- "readerError": "jbF1yq_readerError",
36690
- "wfCmdReason": "jbF1yq_wfCmdReason",
36691
- "wfMetrics": "jbF1yq_wfMetrics",
36692
- "wfSigRight": "jbF1yq_wfSigRight",
36693
- "wfRsChk": "jbF1yq_wfRsChk",
36694
- "wfPsLab": "jbF1yq_wfPsLab",
36695
- "wfCn": "jbF1yq_wfCn",
36696
- "settingsCard": "jbF1yq_settingsCard",
36697
- "topNavBadge": "jbF1yq_topNavBadge",
36698
- "pillNav": "jbF1yq_pillNav",
36699
- "wfDlDi": "jbF1yq_wfDlDi",
36700
- "readerLoading": "jbF1yq_readerLoading",
36812
+ "brandLogo": "jbF1yq_brandLogo",
36813
+ "wfRsName": "jbF1yq_wfRsName",
36814
+ "wfOpsRow": "jbF1yq_wfOpsRow",
36815
+ "bookCardTitleRow": "jbF1yq_bookCardTitleRow",
36816
+ "lfDot": "jbF1yq_lfDot",
36817
+ "runChipText": "jbF1yq_runChipText",
36818
+ "t2RailSep": "jbF1yq_t2RailSep",
36819
+ "wfRs": "jbF1yq_wfRs",
36820
+ "chWorkGrid": "jbF1yq_chWorkGrid",
36821
+ "tabular": "jbF1yq_tabular",
36822
+ "fieldLabel": "jbF1yq_fieldLabel",
36823
+ "settingsDrawerHead": "jbF1yq_settingsDrawerHead",
36824
+ "panelHidden": "jbF1yq_panelHidden",
36825
+ "planTreeVolMeta": "jbF1yq_planTreeVolMeta",
36826
+ "wfAccentAmber": "jbF1yq_wfAccentAmber",
36701
36827
  "dimCurrent": "jbF1yq_dimCurrent",
36702
- "importModalOverlay": "jbF1yq_importModalOverlay",
36703
- "cmdEmpty": "jbF1yq_cmdEmpty",
36828
+ "wsPage": "jbF1yq_wsPage",
36829
+ "wsSelected": "jbF1yq_wsSelected",
36704
36830
  "toolSteps": "jbF1yq_toolSteps",
36705
- "wfPl": "jbF1yq_wfPl",
36706
- "wfOpsRow": "jbF1yq_wfOpsRow",
36707
- "wfConsole": "jbF1yq_wfConsole",
36708
- "planTreeVolGroup": "jbF1yq_planTreeVolGroup",
36709
- "wfToolAmber": "jbF1yq_wfToolAmber",
36710
- "importPreview": "jbF1yq_importPreview",
36711
- "runLogTime": "jbF1yq_runLogTime",
36712
- "wfDlName": "jbF1yq_wfDlName",
36713
- "toolStepDetail": "jbF1yq_toolStepDetail",
36714
- "chWorkSideBody": "jbF1yq_chWorkSideBody",
36715
- "cmdBox": "jbF1yq_cmdBox",
36716
- "coverPlaceholder": "jbF1yq_coverPlaceholder",
36717
- "readerItem": "jbF1yq_readerItem",
36718
- "assistantFloat": "jbF1yq_assistantFloat",
36719
- "titleBlock": "jbF1yq_titleBlock",
36720
- "wfBook": "jbF1yq_wfBook",
36721
- "wbIndexTitle": "jbF1yq_wbIndexTitle",
36722
- "subPageBody": "jbF1yq_subPageBody",
36723
- "wfBoxH": "jbF1yq_wfBoxH",
36724
- "wsPageHeader": "jbF1yq_wsPageHeader",
36725
- "importModalActions": "jbF1yq_importModalActions",
36726
- "dimEdit": "jbF1yq_dimEdit",
36727
- "bookCardActions": "jbF1yq_bookCardActions",
36728
- "progressLineError": "jbF1yq_progressLineError",
36729
- "topNavActive": "jbF1yq_topNavActive",
36730
- "bookCardBlurb": "jbF1yq_bookCardBlurb",
36731
- "wfRow": "jbF1yq_wfRow",
36732
- "noticeError": "jbF1yq_noticeError",
36733
- "drawerTitle": "jbF1yq_drawerTitle",
36734
- "dimTitle": "jbF1yq_dimTitle",
36735
- "wbIndexRow": "jbF1yq_wbIndexRow",
36736
- "panelBody": "jbF1yq_panelBody",
36737
- "brandEn": "jbF1yq_brandEn",
36738
- "bookCardBody": "jbF1yq_bookCardBody",
36739
- "planDot": "jbF1yq_planDot",
36740
- "wfTopRight": "jbF1yq_wfTopRight",
36741
- "bookCardName": "jbF1yq_bookCardName",
36742
- "bigProgressBar": "jbF1yq_bigProgressBar",
36743
- "drawerHead": "jbF1yq_drawerHead",
36744
- "t2RailMeta": "jbF1yq_t2RailMeta",
36745
- "readerPara": "jbF1yq_readerPara",
36746
- "diffColumn": "jbF1yq_diffColumn",
36747
- "outlineTocL1": "jbF1yq_outlineTocL1",
36748
- "emptyStateHint": "jbF1yq_emptyStateHint",
36831
+ "input": "jbF1yq_input",
36749
36832
  "progressBar": "jbF1yq_progressBar",
36750
- "switch": "jbF1yq_switch",
36751
- "cmdList": "jbF1yq_cmdList",
36752
- "readerGroup": "jbF1yq_readerGroup",
36753
- "coverPreview": "jbF1yq_coverPreview",
36754
- "wfPsN": "jbF1yq_wfPsN",
36755
- "chWorkGrid": "jbF1yq_chWorkGrid",
36756
- "segTab": "jbF1yq_segTab",
36757
- "wfProgBig": "jbF1yq_wfProgBig",
36758
- "diffList": "jbF1yq_diffList",
36759
- "legendNew": "jbF1yq_legendNew",
36760
- "topNavLabel": "jbF1yq_topNavLabel",
36761
- "readerView": "jbF1yq_readerView",
36762
- "readerSegBtn": "jbF1yq_readerSegBtn",
36763
- "wsPage": "jbF1yq_wsPage",
36764
- "wfColB": "jbF1yq_wfColB",
36765
- "readerGroupCount": "jbF1yq_readerGroupCount",
36766
- "brandLogo": "jbF1yq_brandLogo",
36767
- "planDetailEmpty": "jbF1yq_planDetailEmpty",
36768
- "adaptRules": "jbF1yq_adaptRules",
36769
- "cmdHint": "jbF1yq_cmdHint",
36770
- "button": "jbF1yq_button",
36771
- "wfEmpty": "jbF1yq_wfEmpty",
36772
- "shelfToolbarExtra": "jbF1yq_shelfToolbarExtra",
36833
+ "pageHeader": "jbF1yq_pageHeader",
36773
36834
  "badgeDone": "jbF1yq_badgeDone",
36774
- "dimCard": "jbF1yq_dimCard",
36775
- "readerGroupArrow": "jbF1yq_readerGroupArrow",
36835
+ "t2Rail": "jbF1yq_t2Rail",
36776
36836
  "wfConErr": "jbF1yq_wfConErr",
36837
+ "badgeRejected": "jbF1yq_badgeRejected",
36838
+ "t2RailMeta": "jbF1yq_t2RailMeta",
36839
+ "assetGrid": "jbF1yq_assetGrid",
36840
+ "bookCardProgressFill": "jbF1yq_bookCardProgressFill",
36841
+ "buttonSmall": "jbF1yq_buttonSmall",
36842
+ "pageHeaderTitle": "jbF1yq_pageHeaderTitle",
36843
+ "toolStepName": "jbF1yq_toolStepName",
36844
+ "assetEditorRow": "jbF1yq_assetEditorRow",
36845
+ "wfRail": "jbF1yq_wfRail",
36846
+ "badge": "jbF1yq_badge",
36847
+ "readerView": "jbF1yq_readerView",
36848
+ "assetGroup": "jbF1yq_assetGroup",
36849
+ "adaptImpact": "jbF1yq_adaptImpact",
36850
+ "adaptRuleRow": "jbF1yq_adaptRuleRow",
36851
+ "chapterTitle": "jbF1yq_chapterTitle",
36852
+ "bookCardCover": "jbF1yq_bookCardCover",
36853
+ "importModalBody": "jbF1yq_importModalBody",
36854
+ "wfCmdReason": "jbF1yq_wfCmdReason",
36855
+ "navTitleLogo": "jbF1yq_navTitleLogo",
36856
+ "createBookInner": "jbF1yq_createBookInner",
36857
+ "legendOld": "jbF1yq_legendOld",
36858
+ "chWorkPickBtns": "jbF1yq_chWorkPickBtns",
36859
+ "navTitleName": "jbF1yq_navTitleName",
36860
+ "planTreeVolGroup": "jbF1yq_planTreeVolGroup",
36861
+ "drawerScrim": "jbF1yq_drawerScrim",
36862
+ "wfFdot": "jbF1yq_wfFdot",
36863
+ "chWorkAsideTitle": "jbF1yq_chWorkAsideTitle",
36777
36864
  "wfAccentBlue": "jbF1yq_wfAccentBlue",
36778
- "adaptCell": "jbF1yq_adaptCell",
36779
- "dimCardHead": "jbF1yq_dimCardHead",
36780
- "dropzoneIcon": "jbF1yq_dropzoneIcon",
36781
- "active": "jbF1yq_active",
36782
- "planTreeVolMeta": "jbF1yq_planTreeVolMeta",
36783
- "wfPillDot": "jbF1yq_wfPillDot",
36784
- "settingsDrawerOverlay": "jbF1yq_settingsDrawerOverlay",
36865
+ "adaptMatrix": "jbF1yq_adaptMatrix",
36866
+ "wbIndexVol": "jbF1yq_wbIndexVol",
36867
+ "diffColumn": "jbF1yq_diffColumn",
36868
+ "toolStepIcon": "jbF1yq_toolStepIcon",
36869
+ "assetFilterChip": "jbF1yq_assetFilterChip",
36870
+ "outlineH": "jbF1yq_outlineH",
36871
+ "importResult": "jbF1yq_importResult",
36872
+ "wfFi": "jbF1yq_wfFi",
36873
+ "entryIcon": "jbF1yq_entryIcon",
36874
+ "topNavBtn": "jbF1yq_topNavBtn",
36875
+ "navAbout": "jbF1yq_navAbout",
36876
+ "dimCard": "jbF1yq_dimCard",
36877
+ "wfDlDim": "jbF1yq_wfDlDim",
36878
+ "chWorkDraftBar": "jbF1yq_chWorkDraftBar",
36879
+ "chapterList": "jbF1yq_chapterList",
36880
+ "wfHint": "jbF1yq_wfHint",
36881
+ "chWorkAside": "jbF1yq_chWorkAside",
36882
+ "panelNav": "jbF1yq_panelNav",
36883
+ "ideaCard": "jbF1yq_ideaCard",
36884
+ "wfPreviewActs": "jbF1yq_wfPreviewActs",
36785
36885
  "navBadgePulse": "jbF1yq_navBadgePulse",
36786
- "wsSelectedText": "jbF1yq_wsSelectedText",
36787
- "outlineTocItem": "jbF1yq_outlineTocItem",
36788
- "panelContent": "jbF1yq_panelContent",
36789
- "nfSkeletonShimmer": "jbF1yq_nfSkeletonShimmer",
36790
- "topNav": "jbF1yq_topNav",
36791
- "wfPs": "jbF1yq_wfPs",
36792
- "chWorkSideFoot": "jbF1yq_chWorkSideFoot",
36793
- "navTitle": "jbF1yq_navTitle",
36794
- "wfToolBlue": "jbF1yq_wfToolBlue",
36795
- "rowEnd": "jbF1yq_rowEnd",
36796
- "wfTool": "jbF1yq_wfTool",
36886
+ "noticeError": "jbF1yq_noticeError",
36887
+ "pageHeaderEyebrow": "jbF1yq_pageHeaderEyebrow",
36888
+ "wsPreview": "jbF1yq_wsPreview",
36889
+ "wfStage": "jbF1yq_wfStage",
36890
+ "wfDs": "jbF1yq_wfDs",
36891
+ "diffAdd": "jbF1yq_diffAdd",
36892
+ "nfCmdPop": "jbF1yq_nfCmdPop",
36893
+ "shelfStatGrid": "jbF1yq_shelfStatGrid",
36894
+ "chWorkSideBody": "jbF1yq_chWorkSideBody",
36895
+ "assetTextarea": "jbF1yq_assetTextarea",
36896
+ "wfPreviewLine": "jbF1yq_wfPreviewLine",
36897
+ "topNavActive": "jbF1yq_topNavActive",
36898
+ "outlineTocL3": "jbF1yq_outlineTocL3",
36899
+ "bigProgressBarFill": "jbF1yq_bigProgressBarFill",
36900
+ "fadeIn": "jbF1yq_fadeIn",
36797
36901
  "runChipDot": "jbF1yq_runChipDot",
36798
- "assistantFloatBody": "jbF1yq_assistantFloatBody",
36902
+ "adaptTextarea": "jbF1yq_adaptTextarea",
36903
+ "brandSub": "jbF1yq_brandSub",
36904
+ "runLogLine": "jbF1yq_runLogLine",
36905
+ "readerSegBtn": "jbF1yq_readerSegBtn",
36906
+ "wfColA": "jbF1yq_wfColA",
36907
+ "wfSt": "jbF1yq_wfSt",
36908
+ "wfFdotOk": "jbF1yq_wfFdotOk",
36909
+ "cmdBox": "jbF1yq_cmdBox",
36910
+ "pillNav": "jbF1yq_pillNav",
36799
36911
  "wfPoolWrap": "jbF1yq_wfPoolWrap",
36800
- "wfLiveChip": "jbF1yq_wfLiveChip",
36801
- "drawerSpacer": "jbF1yq_drawerSpacer",
36802
- "nfDrawerIn": "jbF1yq_nfDrawerIn",
36803
- "drawerMax": "jbF1yq_drawerMax",
36804
- "bookCardActive": "jbF1yq_bookCardActive",
36805
- "buttonDanger": "jbF1yq_buttonDanger",
36806
- "chapterActions": "jbF1yq_chapterActions",
36807
- "bookCardTitleRow": "jbF1yq_bookCardTitleRow",
36808
- "assistantFloatHeader": "jbF1yq_assistantFloatHeader",
36809
- "pillNavPill": "jbF1yq_pillNavPill",
36810
- "diffChange": "jbF1yq_diffChange",
36811
- "entryLabel": "jbF1yq_entryLabel",
36812
- "readerSeg": "jbF1yq_readerSeg",
36912
+ "wfProgBig": "jbF1yq_wfProgBig",
36913
+ "wfDl": "jbF1yq_wfDl",
36914
+ "wfFdotErr": "jbF1yq_wfFdotErr",
36915
+ "readerArticle": "jbF1yq_readerArticle",
36916
+ "nfSlideInRight": "jbF1yq_nfSlideInRight",
36917
+ "brandEn": "jbF1yq_brandEn",
36918
+ "readerLoading": "jbF1yq_readerLoading",
36919
+ "dropzone": "jbF1yq_dropzone",
36920
+ "wfPsLab": "jbF1yq_wfPsLab",
36921
+ "chWorkToolbar": "jbF1yq_chWorkToolbar",
36922
+ "assetLabel": "jbF1yq_assetLabel",
36923
+ "panelTop": "jbF1yq_panelTop",
36924
+ "dimEdit": "jbF1yq_dimEdit",
36925
+ "readerBody": "jbF1yq_readerBody",
36926
+ "badgeWritten": "jbF1yq_badgeWritten",
36927
+ "panelNavCollapsed": "jbF1yq_panelNavCollapsed",
36928
+ "authorContent": "jbF1yq_authorContent",
36929
+ "readerSegActive": "jbF1yq_readerSegActive",
36930
+ "shelfEmptyTitle": "jbF1yq_shelfEmptyTitle",
36931
+ "wfBoxH": "jbF1yq_wfBoxH",
36932
+ "t2Master": "jbF1yq_t2Master",
36933
+ "chatScroll": "jbF1yq_chatScroll",
36934
+ "diffSameBody": "jbF1yq_diffSameBody",
36813
36935
  "outlineH4": "jbF1yq_outlineH4",
36814
- "settingsDrawerHead": "jbF1yq_settingsDrawerHead",
36815
- "wfNow": "jbF1yq_wfNow",
36816
- "importModal": "jbF1yq_importModal",
36936
+ "shelfStatCard": "jbF1yq_shelfStatCard",
36817
36937
  "numSerif": "jbF1yq_numSerif",
36818
- "importField": "jbF1yq_importField",
36819
- "assetCardFooter": "jbF1yq_assetCardFooter",
36820
- "wfLiveMark": "jbF1yq_wfLiveMark",
36938
+ "adaptResult": "jbF1yq_adaptResult",
36939
+ "pageHeaderMain": "jbF1yq_pageHeaderMain",
36940
+ "wfQtTone": "jbF1yq_wfQtTone",
36941
+ "chWorkSide": "jbF1yq_chWorkSide",
36942
+ "planDetailEmpty": "jbF1yq_planDetailEmpty",
36943
+ "outlineEdit": "jbF1yq_outlineEdit",
36944
+ "active": "jbF1yq_active",
36945
+ "bigProgressBar": "jbF1yq_bigProgressBar",
36946
+ "topNavNi": "jbF1yq_topNavNi",
36947
+ "brand": "jbF1yq_brand",
36948
+ "wfPsN": "jbF1yq_wfPsN",
36949
+ "wfCoordStuck": "jbF1yq_wfCoordStuck",
36950
+ "shelfEmpty": "jbF1yq_shelfEmpty",
36951
+ "wfStFail": "jbF1yq_wfStFail",
36952
+ "outlineH3": "jbF1yq_outlineH3",
36821
36953
  "bookCardProgressBar": "jbF1yq_bookCardProgressBar",
36822
- "drawerHeadBtn": "jbF1yq_drawerHeadBtn",
36823
- "wfPreviewLine": "jbF1yq_wfPreviewLine",
36824
- "assistantStatusBusy": "jbF1yq_assistantStatusBusy",
36954
+ "wsSelectedText": "jbF1yq_wsSelectedText",
36825
36955
  "outlineToc": "jbF1yq_outlineToc",
36826
- "readerBody": "jbF1yq_readerBody",
36827
- "authorPageHeader": "jbF1yq_authorPageHeader",
36828
- "wfCmdBtn": "jbF1yq_wfCmdBtn",
36829
- "wfConBar": "jbF1yq_wfConBar",
36830
- "planMaster": "jbF1yq_planMaster",
36831
- "assetCardName": "jbF1yq_assetCardName",
36956
+ "shelfToolbar": "jbF1yq_shelfToolbar",
36957
+ "adaptInputCard": "jbF1yq_adaptInputCard",
36958
+ "legendNew": "jbF1yq_legendNew",
36959
+ "brandName": "jbF1yq_brandName",
36960
+ "wfSigGood": "jbF1yq_wfSigGood",
36961
+ "wfBody": "jbF1yq_wfBody",
36832
36962
  "readerSidebar": "jbF1yq_readerSidebar",
36833
- "fadeIn": "jbF1yq_fadeIn",
36834
- "wfDrawerBox": "jbF1yq_wfDrawerBox",
36835
- "cardTitleLg": "jbF1yq_cardTitleLg",
36836
- "pageHeader": "jbF1yq_pageHeader",
36837
- "wfDrawerList": "jbF1yq_wfDrawerList",
36838
- "wfConRow": "jbF1yq_wfConRow",
36839
- "adaptTextarea": "jbF1yq_adaptTextarea",
36840
- "pageHeaderSub": "jbF1yq_pageHeaderSub",
36841
- "badgeError": "jbF1yq_badgeError",
36842
- "chatScroll": "jbF1yq_chatScroll",
36843
- "diffLegend": "jbF1yq_diffLegend",
36844
- "cmdIcon": "jbF1yq_cmdIcon",
36845
- "wfSigBad": "jbF1yq_wfSigBad",
36846
- "planDetailTitle": "jbF1yq_planDetailTitle",
36847
- "panelNav": "jbF1yq_panelNav",
36848
- "shelfSearch": "jbF1yq_shelfSearch",
36849
- "readerSegActive": "jbF1yq_readerSegActive",
36850
- "reviewBox": "jbF1yq_reviewBox",
36851
- "readerGroupTitle": "jbF1yq_readerGroupTitle",
36852
- "statCardDDetail": "jbF1yq_statCardDDetail",
36963
+ "cmdInput": "jbF1yq_cmdInput",
36964
+ "drawerHeadBtn": "jbF1yq_drawerHeadBtn",
36965
+ "navAboutUpdate": "jbF1yq_navAboutUpdate",
36966
+ "assetCardFooter": "jbF1yq_assetCardFooter",
36967
+ "wfBlink": "jbF1yq_wfBlink",
36968
+ "chWorkBar": "jbF1yq_chWorkBar",
36969
+ "cmdHint": "jbF1yq_cmdHint",
36970
+ "subPageTitle": "jbF1yq_subPageTitle",
36971
+ "button": "jbF1yq_button",
36972
+ "wfSignal": "jbF1yq_wfSignal",
36853
36973
  "authorNav": "jbF1yq_authorNav",
36854
- "wfEmpty2": "jbF1yq_wfEmpty2",
36855
- "toolStepStatus": "jbF1yq_toolStepStatus",
36856
- "tnum": "jbF1yq_tnum",
36857
- "settingsDrawerBody": "jbF1yq_settingsDrawerBody",
36858
- "navAboutRow": "jbF1yq_navAboutRow",
36859
- "wfFi": "jbF1yq_wfFi",
36860
- "badgeWritten": "jbF1yq_badgeWritten",
36861
- "readerItemActive": "jbF1yq_readerItemActive",
36862
- "diffNew": "jbF1yq_diffNew",
36863
- "subPageActions": "jbF1yq_subPageActions",
36864
- "diffSameBody": "jbF1yq_diffSameBody",
36865
- "subPageHead": "jbF1yq_subPageHead",
36866
- "assetCardTop": "jbF1yq_assetCardTop",
36867
- "t2RailHead": "jbF1yq_t2RailHead",
36868
- "diffText": "jbF1yq_diffText",
36869
- "nfCmdPop": "jbF1yq_nfCmdPop",
36870
- "wfPreview": "jbF1yq_wfPreview",
36871
- "wfCoordStuck": "jbF1yq_wfCoordStuck",
36872
- "ideaCard": "jbF1yq_ideaCard",
36873
- "t2Content": "jbF1yq_t2Content",
36874
- "chWorkMain": "jbF1yq_chWorkMain",
36875
- "pageHeaderMain": "jbF1yq_pageHeaderMain",
36876
- "assetGroup": "jbF1yq_assetGroup",
36877
- "wfProg": "jbF1yq_wfProg",
36878
- "wfRail": "jbF1yq_wfRail",
36879
- "wfBody": "jbF1yq_wfBody",
36880
- "assetCard": "jbF1yq_assetCard",
36881
- "bookCardMetaRow": "jbF1yq_bookCardMetaRow",
36882
- "wfSig": "jbF1yq_wfSig",
36883
- "entryIcon": "jbF1yq_entryIcon",
36884
- "chWorkSide": "jbF1yq_chWorkSide",
36885
- "wfDrawerToggle": "jbF1yq_wfDrawerToggle",
36886
- "navGroupLabel": "jbF1yq_navGroupLabel",
36887
- "createBookInner": "jbF1yq_createBookInner",
36888
- "chWorkToolbar": "jbF1yq_chWorkToolbar",
36974
+ "subPage": "jbF1yq_subPage",
36975
+ "textarea": "jbF1yq_textarea",
36889
36976
  "adaptOutline": "jbF1yq_adaptOutline",
36890
- "planTreeRow": "jbF1yq_planTreeRow",
36891
- "chWorkAsideTitle": "jbF1yq_chWorkAsideTitle",
36892
- "pillNavSlider": "jbF1yq_pillNavSlider",
36893
- "toolLive": "jbF1yq_toolLive",
36977
+ "planTreeRowTitle": "jbF1yq_planTreeRowTitle",
36978
+ "liveText": "jbF1yq_liveText",
36979
+ "subPageBody": "jbF1yq_subPageBody",
36980
+ "adaptRule": "jbF1yq_adaptRule",
36981
+ "wfSigBad": "jbF1yq_wfSigBad",
36982
+ "subPageMeta": "jbF1yq_subPageMeta",
36983
+ "chapterActions": "jbF1yq_chapterActions",
36984
+ "tab": "jbF1yq_tab",
36985
+ "chapterFocusPulse": "jbF1yq_chapterFocusPulse",
36986
+ "settingsCard": "jbF1yq_settingsCard",
36987
+ "wfSw": "jbF1yq_wfSw",
36988
+ "lfPill": "jbF1yq_lfPill",
36989
+ "diffLegend": "jbF1yq_diffLegend",
36990
+ "nfChipPulse": "jbF1yq_nfChipPulse",
36991
+ "bookCardName": "jbF1yq_bookCardName",
36992
+ "cardTitle": "jbF1yq_cardTitle",
36993
+ "chWorkFootRow": "jbF1yq_chWorkFootRow",
36994
+ "wsPageHeader": "jbF1yq_wsPageHeader",
36995
+ "bookCardBlurb": "jbF1yq_bookCardBlurb",
36894
36996
  "shelfGrid": "jbF1yq_shelfGrid",
36895
- "tabular": "jbF1yq_tabular",
36896
- "readerArticle": "jbF1yq_readerArticle",
36897
- "assistantResize": "jbF1yq_assistantResize",
36898
- "wfPsLegend": "jbF1yq_wfPsLegend",
36899
- "chapterMain": "jbF1yq_chapterMain",
36900
- "pageHeaderTitle": "jbF1yq_pageHeaderTitle",
36901
- "iconButton": "jbF1yq_iconButton",
36902
- "readerMain": "jbF1yq_readerMain",
36903
- "statCardDValue": "jbF1yq_statCardDValue",
36904
- "wfHint": "jbF1yq_wfHint",
36905
- "diffAdd": "jbF1yq_diffAdd",
36906
- "wfFt": "jbF1yq_wfFt",
36907
- "chWorkToolActions": "jbF1yq_chWorkToolActions",
36908
- "toolStepIcon": "jbF1yq_toolStepIcon",
36909
- "ideaCandidate": "jbF1yq_ideaCandidate",
36910
- "outlineH3": "jbF1yq_outlineH3",
36911
- "wfAct": "jbF1yq_wfAct",
36912
- "adaptResult": "jbF1yq_adaptResult",
36913
- "t2RailBtn": "jbF1yq_t2RailBtn",
36914
- "chapterList": "jbF1yq_chapterList",
36915
- "chapterTitle": "jbF1yq_chapterTitle",
36916
- "readerFoot": "jbF1yq_readerFoot",
36997
+ "wfToolAmber": "jbF1yq_wfToolAmber",
36998
+ "wfCmdBtn": "jbF1yq_wfCmdBtn",
36999
+ "dimCardHead": "jbF1yq_dimCardHead",
37000
+ "adaptCell": "jbF1yq_adaptCell",
37001
+ "runChip": "jbF1yq_runChip",
37002
+ "emptyStateHint": "jbF1yq_emptyStateHint",
37003
+ "rowEnd": "jbF1yq_rowEnd",
37004
+ "chatBubbleUser": "jbF1yq_chatBubbleUser",
37005
+ "wfRsChk": "jbF1yq_wfRsChk",
37006
+ "authorPageBodyFlow": "jbF1yq_authorPageBodyFlow",
37007
+ "topNavSpacer": "jbF1yq_topNavSpacer",
37008
+ "toolStep": "jbF1yq_toolStep",
37009
+ "wfPreviewName": "jbF1yq_wfPreviewName",
37010
+ "badgeGenerating": "jbF1yq_badgeGenerating",
36917
37011
  "nfToastIn": "jbF1yq_nfToastIn",
36918
- "noticeSuccess": "jbF1yq_noticeSuccess",
36919
- "chWorkTitle": "jbF1yq_chWorkTitle",
36920
- "wfFeedList": "jbF1yq_wfFeedList",
36921
- "chWorkActions": "jbF1yq_chWorkActions",
36922
- "navTab": "jbF1yq_navTab",
36923
- "progressLineDone": "jbF1yq_progressLineDone",
36924
- "assetTextarea": "jbF1yq_assetTextarea",
36925
- "navSpacer": "jbF1yq_navSpacer",
36926
- "importHint": "jbF1yq_importHint",
36927
- "panelTitle": "jbF1yq_panelTitle",
36928
- "shelfHero": "jbF1yq_shelfHero",
36929
- "wfOpsBtnMain": "jbF1yq_wfOpsBtnMain",
36930
- "importModalBody": "jbF1yq_importModalBody",
36931
- "wfBlink": "jbF1yq_wfBlink",
36932
- "toolStepActive": "jbF1yq_toolStepActive",
36933
- "outlineP": "jbF1yq_outlineP",
36934
- "eyebrow": "jbF1yq_eyebrow",
36935
- "liveText": "jbF1yq_liveText",
36936
- "bigProgressBarFill": "jbF1yq_bigProgressBarFill",
36937
- "badgePending": "jbF1yq_badgePending",
36938
- "readerTitle": "jbF1yq_readerTitle",
36939
- "wfSt": "jbF1yq_wfSt",
37012
+ "wfDone": "jbF1yq_wfDone",
37013
+ "wfMetric": "jbF1yq_wfMetric",
37014
+ "wsAppliedBanner": "jbF1yq_wsAppliedBanner",
36940
37015
  "runLog": "jbF1yq_runLog",
36941
- "nfToast": "jbF1yq_nfToast",
36942
- "wfProgRest": "jbF1yq_wfProgRest",
36943
- "importResult": "jbF1yq_importResult",
37016
+ "outlineTocItem": "jbF1yq_outlineTocItem",
37017
+ "planDetailTitle": "jbF1yq_planDetailTitle",
37018
+ "navSpacer": "jbF1yq_navSpacer",
37019
+ "wfCmd": "jbF1yq_wfCmd",
37020
+ "drawerMax": "jbF1yq_drawerMax",
37021
+ "importPreviewList": "jbF1yq_importPreviewList",
37022
+ "authorTopNav": "jbF1yq_authorTopNav",
37023
+ "assetGroups": "jbF1yq_assetGroups",
37024
+ "outlineVol": "jbF1yq_outlineVol",
37025
+ "wfRsNo": "jbF1yq_wfRsNo",
37026
+ "assetFilterChipActive": "jbF1yq_assetFilterChipActive",
37027
+ "readerHeader": "jbF1yq_readerHeader",
37028
+ "wfQt": "jbF1yq_wfQt",
37029
+ "headSpacer": "jbF1yq_headSpacer",
37030
+ "bookshelf": "jbF1yq_bookshelf",
37031
+ "toolStepDetail": "jbF1yq_toolStepDetail",
37032
+ "assistantFloatHeader": "jbF1yq_assistantFloatHeader",
37033
+ "readerItemActive": "jbF1yq_readerItemActive",
37034
+ "cardTitleLg": "jbF1yq_cardTitleLg",
37035
+ "chWorkPickBar": "jbF1yq_chWorkPickBar",
37036
+ "diffSame": "jbF1yq_diffSame",
37037
+ "readerGroup": "jbF1yq_readerGroup",
37038
+ "authorHome": "jbF1yq_authorHome",
37039
+ "navGroup": "jbF1yq_navGroup",
37040
+ "shelfSearch": "jbF1yq_shelfSearch",
37041
+ "pillNavSliding": "jbF1yq_pillNavSliding",
37042
+ "wfPsLegend": "jbF1yq_wfPsLegend",
37043
+ "wfCoord": "jbF1yq_wfCoord",
37044
+ "planDetailBar": "jbF1yq_planDetailBar",
37045
+ "wfSmallFoot": "jbF1yq_wfSmallFoot",
37046
+ "field": "jbF1yq_field",
37047
+ "diffList": "jbF1yq_diffList",
37048
+ "card": "jbF1yq_card",
37049
+ "assetCardTop": "jbF1yq_assetCardTop",
37050
+ "planTreeRow": "jbF1yq_planTreeRow",
37051
+ "wfDlName": "jbF1yq_wfDlName",
37052
+ "wfConRow": "jbF1yq_wfConRow",
37053
+ "chWorkSideFoot": "jbF1yq_chWorkSideFoot",
37054
+ "wfProg": "jbF1yq_wfProg",
37055
+ "wfConBar": "jbF1yq_wfConBar",
37056
+ "readerItem": "jbF1yq_readerItem",
37057
+ "diffDel": "jbF1yq_diffDel",
37058
+ "statCardDDetail": "jbF1yq_statCardDDetail",
37059
+ "planTreeSearch": "jbF1yq_planTreeSearch",
37060
+ "dimTitle": "jbF1yq_dimTitle",
37061
+ "wfPs": "jbF1yq_wfPs",
37062
+ "wfBox": "jbF1yq_wfBox",
36944
37063
  "readerChapterMeta": "jbF1yq_readerChapterMeta",
36945
- "wfSignal": "jbF1yq_wfSignal",
36946
- "subPageTitle": "jbF1yq_subPageTitle",
36947
- "navTitleLogo": "jbF1yq_navTitleLogo",
36948
- "badgeGenerating": "jbF1yq_badgeGenerating",
36949
- "importModalHead": "jbF1yq_importModalHead",
36950
- "pageHeaderEyebrow": "jbF1yq_pageHeaderEyebrow",
36951
- "adaptImpacts": "jbF1yq_adaptImpacts",
36952
- "planTreeRowNo": "jbF1yq_planTreeRowNo",
36953
- "planDetailBody": "jbF1yq_planDetailBody"
37064
+ "contentDrawerBody": "jbF1yq_contentDrawerBody",
37065
+ "entry": "jbF1yq_entry",
37066
+ "readerChapterTitle": "jbF1yq_readerChapterTitle",
37067
+ "adaptOutlinePre": "jbF1yq_adaptOutlinePre",
37068
+ "panel": "jbF1yq_panel",
37069
+ "outlineDocWrap": "jbF1yq_outlineDocWrap",
37070
+ "planDetail": "jbF1yq_planDetail"
36954
37071
  };
36955
37072
  //#endregion
36956
37073
  //#region src/client/panel/ReasoningSection.tsx
@@ -37052,7 +37169,9 @@ window.__ModuleLoader__.load({
37052
37169
  preview: "",
37053
37170
  totalChars: 0,
37054
37171
  startedAt: frame.at,
37055
- updatedAt: frame.at
37172
+ updatedAt: frame.at,
37173
+ hasPrompt: frame.hasPrompt,
37174
+ promptChars: frame.promptChars
37056
37175
  };
37057
37176
  next[id] = session;
37058
37177
  return next;
@@ -37091,7 +37210,10 @@ window.__ModuleLoader__.load({
37091
37210
  phaseMessage: frame.phase === "failed" ? frame.error ?? "调用失败" : "模型结果已准备完成",
37092
37211
  totalChars: frame.totalChars ?? session.totalChars,
37093
37212
  preview: session.preview || (frame.preview ?? ""),
37094
- updatedAt: frame.at
37213
+ updatedAt: frame.at,
37214
+ usage: frame.usage ?? session.usage,
37215
+ elapsedMs: frame.elapsedMs ?? session.elapsedMs,
37216
+ firstTokenMs: frame.firstTokenMs ?? session.firstTokenMs
37095
37217
  };
37096
37218
  next[id] = session;
37097
37219
  }
@@ -37131,9 +37253,33 @@ window.__ModuleLoader__.load({
37131
37253
  * 「实时调用 / LIVE」—— 内嵌到 AI 进度窗的下半区(详情层),不再独立浮窗。
37132
37254
  * 连接 /llm-live/stream,按 LLM 调用分组展示模型实时输出。
37133
37255
  *
37256
+ * 除实时输出外,这里还承担两件与「花了多少」有关的事:
37257
+ * 1) 每次调用显示 token 用量(输入↑ / 输出↓ / 思考)与耗时、首字耗时;
37258
+ * 2) 「查看 Prompt」按 sessionId 拉取这次调用**实际发送**的 system / user。
37259
+ * 顶部另有一条「本次运行」累计条(进程内,重启归零)。
37260
+ *
37134
37261
  * 状态色全部走 panel.module.css 的 .lfPill / .lfDot(主题令牌 + --nf-streaming),
37135
37262
  * 不再内联硬编码色;布局性 inline style 保留(均为令牌引用或动态值)。
37136
37263
  */
37264
+ /** 毫秒 → 紧凑可读(840ms / 1.2s)。 */
37265
+ function fmtMs(ms) {
37266
+ if (ms === void 0 || ms <= 0) return "";
37267
+ return ms < 1e3 ? Math.round(ms) + "ms" : (ms / 1e3).toFixed(1) + "s";
37268
+ }
37269
+ /** token 数 → 紧凑(12.3k)。 */
37270
+ function fmtTokens(n) {
37271
+ if (n === void 0 || n <= 0) return "0";
37272
+ return n < 1e3 ? String(n) : (n / 1e3).toFixed(1) + "k";
37273
+ }
37274
+ /** 一次调用的用量摘要(输入↑ / 输出↓ / 思考 / 缓存)。 */
37275
+ function usageText(s) {
37276
+ const u = s.usage;
37277
+ if (u === void 0) return "用量未上报";
37278
+ const parts = ["↑" + fmtTokens(u.inputTokens), "↓" + fmtTokens(u.outputTokens)];
37279
+ if ((u.reasoningTokens ?? 0) > 0) parts.push("思考 " + fmtTokens(u.reasoningTokens));
37280
+ if ((u.cacheReadTokens ?? 0) > 0) parts.push("缓存 " + fmtTokens(u.cacheReadTokens));
37281
+ return parts.join(" · ");
37282
+ }
37137
37283
  function PhasePill({ s }) {
37138
37284
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37139
37285
  className: panel_module_css_default.lfPill,
@@ -37141,7 +37287,138 @@ window.__ModuleLoader__.load({
37141
37287
  children: s.phase === "requesting" ? "请求中" : s.phase === "streaming" ? "生成中" : s.phase === "failed" ? "失败" : "完成"
37142
37288
  });
37143
37289
  }
37144
- function SessionCard({ s, brief }) {
37290
+ /** 「查看 Prompt」弹层:拉取这次调用实际发送的 system + user。 */
37291
+ function PromptDialog({ sessionId, onClose }) {
37292
+ const [record, setRecord] = (0, react.useState)(null);
37293
+ const [error, setError] = (0, react.useState)("");
37294
+ const [tab, setTab] = (0, react.useState)("user");
37295
+ (0, react.useEffect)(() => {
37296
+ let cancelled = false;
37297
+ (async () => {
37298
+ try {
37299
+ const res = await fetch(NOVEL_API.llmPrompt + "?sessionId=" + encodeURIComponent(sessionId));
37300
+ if (!res.ok) {
37301
+ const body = await res.json().catch(() => ({}));
37302
+ throw new Error(body.error ?? "HTTP " + res.status);
37303
+ }
37304
+ const data = await res.json();
37305
+ if (!cancelled) setRecord(data);
37306
+ } catch (err) {
37307
+ if (!cancelled) setError(err.message);
37308
+ }
37309
+ })();
37310
+ return () => {
37311
+ cancelled = true;
37312
+ };
37313
+ }, [sessionId]);
37314
+ const text = record === null ? "" : tab === "user" ? record.user ?? "" : record.system ?? "";
37315
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37316
+ style: {
37317
+ position: "fixed",
37318
+ inset: 0,
37319
+ background: "rgba(0,0,0,.45)",
37320
+ zIndex: 60,
37321
+ display: "flex",
37322
+ alignItems: "center",
37323
+ justifyContent: "center",
37324
+ padding: 20
37325
+ },
37326
+ onClick: onClose,
37327
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37328
+ onClick: (e) => e.stopPropagation(),
37329
+ style: {
37330
+ background: "var(--nf-bg)",
37331
+ border: "1px solid var(--nf-border)",
37332
+ borderRadius: 10,
37333
+ width: "min(880px, 96vw)",
37334
+ maxHeight: "86vh",
37335
+ display: "flex",
37336
+ flexDirection: "column",
37337
+ padding: 12,
37338
+ gap: 8
37339
+ },
37340
+ children: [
37341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37342
+ style: {
37343
+ display: "flex",
37344
+ alignItems: "center",
37345
+ gap: 8
37346
+ },
37347
+ children: [
37348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", {
37349
+ style: {
37350
+ flex: 1,
37351
+ fontSize: 13
37352
+ },
37353
+ children: ["实际发送的 Prompt", record?.label !== void 0 ? " · " + record.label : ""]
37354
+ }),
37355
+ record !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37356
+ className: panel_module_css_default.meta,
37357
+ children: [
37358
+ "共 ",
37359
+ record.chars.toLocaleString(),
37360
+ " 字符",
37361
+ record.truncated === true ? "(记录已截断)" : ""
37362
+ ]
37363
+ }),
37364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37365
+ type: "button",
37366
+ className: panel_module_css_default.iconButton,
37367
+ title: "关闭",
37368
+ onClick: onClose,
37369
+ children: "×"
37370
+ })
37371
+ ]
37372
+ }),
37373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37374
+ style: {
37375
+ display: "flex",
37376
+ gap: 6
37377
+ },
37378
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37379
+ type: "button",
37380
+ className: panel_module_css_default.button + (tab === "user" ? " " + panel_module_css_default.buttonPrimary : ""),
37381
+ onClick: () => setTab("user"),
37382
+ children: "User / 输入"
37383
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37384
+ type: "button",
37385
+ className: panel_module_css_default.button + (tab === "system" ? " " + panel_module_css_default.buttonPrimary : ""),
37386
+ onClick: () => setTab("system"),
37387
+ children: "System / 系统提示"
37388
+ })]
37389
+ }),
37390
+ error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37391
+ style: {
37392
+ color: "var(--nf-error)",
37393
+ fontSize: 12
37394
+ },
37395
+ children: error
37396
+ }),
37397
+ record === null && error === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37398
+ className: panel_module_css_default.meta,
37399
+ children: "加载中…"
37400
+ }),
37401
+ record !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
37402
+ style: {
37403
+ flex: 1,
37404
+ minHeight: 0,
37405
+ overflow: "auto",
37406
+ whiteSpace: "pre-wrap",
37407
+ wordBreak: "break-word",
37408
+ fontSize: 11,
37409
+ lineHeight: 1.55,
37410
+ background: "var(--nf-bg-inset)",
37411
+ borderRadius: 8,
37412
+ padding: 8,
37413
+ margin: 0
37414
+ },
37415
+ children: text === "" ? "(空)" : text
37416
+ })
37417
+ ]
37418
+ })
37419
+ });
37420
+ }
37421
+ function SessionCard({ s, brief, onShowPrompt }) {
37145
37422
  const dotState = s.phase === "requesting" || s.phase === "streaming" ? "streaming" : s.phase;
37146
37423
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37147
37424
  style: {
@@ -37190,16 +37467,48 @@ window.__ModuleLoader__.load({
37190
37467
  color: "var(--nf-text-2)",
37191
37468
  marginTop: 2,
37192
37469
  display: "flex",
37193
- gap: 8
37470
+ gap: 8,
37471
+ flexWrap: "wrap",
37472
+ alignItems: "center"
37194
37473
  },
37195
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [s.totalChars, " 字符"] }), s.phaseMessage !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37196
- style: {
37197
- overflow: "hidden",
37198
- textOverflow: "ellipsis",
37199
- whiteSpace: "nowrap"
37200
- },
37201
- children: s.phaseMessage
37202
- })]
37474
+ children: [
37475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [s.totalChars, " 字符"] }),
37476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37477
+ title: "输入 / 输出 token",
37478
+ children: usageText(s)
37479
+ }),
37480
+ s.elapsedMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37481
+ title: "总耗时",
37482
+ children: ["用时 ", fmtMs(s.elapsedMs)]
37483
+ }),
37484
+ s.firstTokenMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37485
+ title: "首个字返回耗时",
37486
+ children: ["首字 ", fmtMs(s.firstTokenMs)]
37487
+ }),
37488
+ s.phaseMessage !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37489
+ style: {
37490
+ overflow: "hidden",
37491
+ textOverflow: "ellipsis",
37492
+ whiteSpace: "nowrap"
37493
+ },
37494
+ children: s.phaseMessage
37495
+ }),
37496
+ s.hasPrompt === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37497
+ type: "button",
37498
+ title: "查看这次调用实际发送的 Prompt",
37499
+ onClick: () => onShowPrompt(s.sessionId),
37500
+ style: {
37501
+ border: "1px solid var(--nf-border)",
37502
+ background: "var(--nf-bg)",
37503
+ color: "var(--nf-text)",
37504
+ borderRadius: 6,
37505
+ fontSize: 10,
37506
+ padding: "0 5px",
37507
+ cursor: "pointer"
37508
+ },
37509
+ children: "查看 Prompt"
37510
+ })
37511
+ ]
37203
37512
  }),
37204
37513
  !brief && s.preview !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
37205
37514
  style: {
@@ -37222,9 +37531,38 @@ window.__ModuleLoader__.load({
37222
37531
  function LiveFeedLog() {
37223
37532
  const [brief, setBrief] = (0, react.useState)(true);
37224
37533
  const [cleared, setCleared] = (0, react.useState)(false);
37534
+ const [promptId, setPromptId] = (0, react.useState)(null);
37225
37535
  const { connected, sessions } = useLlmLiveFeed(true);
37226
37536
  const active = sessions.filter((s) => s.phase === "requesting" || s.phase === "streaming").length;
37227
37537
  const shown = (0, react.useMemo)(() => cleared ? sessions.slice(-8) : sessions, [sessions, cleared]);
37538
+ const [usage, setUsage] = (0, react.useState)(null);
37539
+ const loadUsage = (0, react.useCallback)(async () => {
37540
+ try {
37541
+ const res = await fetch(NOVEL_API.status + "?slim=1");
37542
+ if (!res.ok) return;
37543
+ const data = await res.json();
37544
+ setUsage(data.usage ?? null);
37545
+ } catch {}
37546
+ }, []);
37547
+ (0, react.useEffect)(() => {
37548
+ loadUsage();
37549
+ const timer = window.setInterval(() => {
37550
+ loadUsage();
37551
+ }, 15e3);
37552
+ return () => {
37553
+ window.clearInterval(timer);
37554
+ };
37555
+ }, [loadUsage]);
37556
+ const resetUsage = (0, react.useCallback)(async () => {
37557
+ try {
37558
+ await fetch(NOVEL_API.usageReset, {
37559
+ method: "POST",
37560
+ headers: { "content-type": "application/json" },
37561
+ body: "{}"
37562
+ });
37563
+ await loadUsage();
37564
+ } catch {}
37565
+ }, [loadUsage]);
37228
37566
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37229
37567
  style: {
37230
37568
  borderTop: "1px solid var(--nf-border)",
@@ -37235,80 +37573,136 @@ window.__ModuleLoader__.load({
37235
37573
  display: "flex",
37236
37574
  flexDirection: "column"
37237
37575
  },
37238
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37239
- style: {
37240
- display: "flex",
37241
- alignItems: "center",
37242
- gap: 8,
37243
- fontSize: 12,
37244
- padding: "2px 4px"
37245
- },
37246
- children: [
37247
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37248
- className: panel_module_css_default.lfDot,
37249
- "data-size": "lg",
37250
- "data-state": connected ? "ok" : "idle"
37251
- }),
37252
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
37253
- style: { flex: 1 },
37254
- children: "实时调用 / LIVE"
37255
- }),
37256
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37257
- className: panel_module_css_default.lfPill,
37258
- "data-phase": active > 0 ? "streaming" : "requesting",
37259
- children: active > 0 ? `${active} 项进行中` : connected ? "等待生成" : "正在连接"
37260
- }),
37261
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37262
- type: "button",
37263
- title: brief ? "详细" : "简略",
37264
- onClick: () => setBrief((b) => !b),
37265
- style: {
37266
- border: "1px solid var(--nf-border)",
37267
- background: "var(--nf-bg)",
37268
- color: "var(--nf-text)",
37269
- borderRadius: 6,
37270
- fontSize: 10,
37271
- padding: "1px 6px",
37272
- cursor: "pointer"
37273
- },
37274
- children: brief ? "详细" : "简略"
37275
- }),
37276
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37277
- type: "button",
37278
- title: "清空当前窗口",
37279
- onClick: () => setCleared(true),
37280
- style: {
37281
- border: "1px solid var(--nf-border)",
37282
- background: "var(--nf-bg)",
37283
- color: "var(--nf-text)",
37284
- borderRadius: 6,
37285
- fontSize: 10,
37286
- padding: "1px 6px",
37287
- cursor: "pointer"
37288
- },
37289
- children: "清空"
37290
- })
37291
- ]
37292
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37293
- style: {
37294
- flex: 1,
37295
- minHeight: 0,
37296
- overflowY: "auto",
37297
- display: "flex",
37298
- flexDirection: "column"
37299
- },
37300
- children: shown.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37576
+ children: [
37577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37301
37578
  style: {
37302
- padding: 10,
37579
+ display: "flex",
37580
+ alignItems: "center",
37581
+ gap: 8,
37303
37582
  fontSize: 12,
37304
- color: "var(--nf-text-2)"
37583
+ padding: "2px 4px"
37305
37584
  },
37306
- children: connected ? "暂无实时调用。开始一次生成/审稿/计划即可看到。" : "正在连接 AI 实况服务…"
37307
- }) : shown.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionCard, {
37308
- s,
37309
- brief
37310
- }, s.sessionId))
37311
- })]
37585
+ children: [
37586
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37587
+ className: panel_module_css_default.lfDot,
37588
+ "data-size": "lg",
37589
+ "data-state": connected ? "ok" : "idle"
37590
+ }),
37591
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
37592
+ style: { flex: 1 },
37593
+ children: "实时调用 / LIVE"
37594
+ }),
37595
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37596
+ className: panel_module_css_default.lfPill,
37597
+ "data-phase": active > 0 ? "streaming" : "requesting",
37598
+ children: active > 0 ? `${active} 项进行中` : connected ? "等待生成" : "正在连接"
37599
+ }),
37600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37601
+ type: "button",
37602
+ title: brief ? "详细" : "简略",
37603
+ onClick: () => setBrief((b) => !b),
37604
+ style: {
37605
+ border: "1px solid var(--nf-border)",
37606
+ background: "var(--nf-bg)",
37607
+ color: "var(--nf-text)",
37608
+ borderRadius: 6,
37609
+ fontSize: 10,
37610
+ padding: "1px 6px",
37611
+ cursor: "pointer"
37612
+ },
37613
+ children: brief ? "详细" : "简略"
37614
+ }),
37615
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37616
+ type: "button",
37617
+ title: "清空当前窗口",
37618
+ onClick: () => setCleared(true),
37619
+ style: {
37620
+ border: "1px solid var(--nf-border)",
37621
+ background: "var(--nf-bg)",
37622
+ color: "var(--nf-text)",
37623
+ borderRadius: 6,
37624
+ fontSize: 10,
37625
+ padding: "1px 6px",
37626
+ cursor: "pointer"
37627
+ },
37628
+ children: "清空"
37629
+ })
37630
+ ]
37631
+ }),
37632
+ usage !== null && usage.calls > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37633
+ title: "本次运行累计(进程内,重启归零)",
37634
+ style: {
37635
+ display: "flex",
37636
+ alignItems: "center",
37637
+ gap: 10,
37638
+ flexWrap: "wrap",
37639
+ fontSize: 10,
37640
+ color: "var(--nf-text-2)",
37641
+ padding: "3px 4px",
37642
+ borderTop: "1px dashed var(--nf-border)"
37643
+ },
37644
+ children: [
37645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
37646
+ "本次运行:",
37647
+ usage.calls,
37648
+ " 次调用",
37649
+ usage.failed > 0 ? `(失败 ${usage.failed})` : ""
37650
+ ] }),
37651
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["↑", fmtTokens(usage.inputTokens)] }),
37652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["↓", fmtTokens(usage.outputTokens)] }),
37653
+ usage.reasoningTokens > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["思考 ", fmtTokens(usage.reasoningTokens)] }),
37654
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["用时 ", fmtMs(usage.elapsedMs)] }),
37655
+ usage.callsWithoutUsage > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
37656
+ "(",
37657
+ usage.callsWithoutUsage,
37658
+ " 次未上报用量)"
37659
+ ] }),
37660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
37661
+ type: "button",
37662
+ title: "清零本次运行用量",
37663
+ onClick: () => {
37664
+ resetUsage();
37665
+ },
37666
+ style: {
37667
+ marginLeft: "auto",
37668
+ border: "1px solid var(--nf-border)",
37669
+ background: "var(--nf-bg)",
37670
+ color: "var(--nf-text)",
37671
+ borderRadius: 6,
37672
+ fontSize: 10,
37673
+ padding: "1px 6px",
37674
+ cursor: "pointer"
37675
+ },
37676
+ children: "重新计数"
37677
+ })
37678
+ ]
37679
+ }),
37680
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37681
+ style: {
37682
+ flex: 1,
37683
+ minHeight: 0,
37684
+ overflowY: "auto",
37685
+ display: "flex",
37686
+ flexDirection: "column"
37687
+ },
37688
+ children: shown.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37689
+ style: {
37690
+ padding: 10,
37691
+ fontSize: 12,
37692
+ color: "var(--nf-text-2)"
37693
+ },
37694
+ children: connected ? "暂无实时调用。开始一次生成/审稿/计划即可看到。" : "正在连接 AI 实况服务…"
37695
+ }) : shown.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionCard, {
37696
+ s,
37697
+ brief,
37698
+ onShowPrompt: setPromptId
37699
+ }, s.sessionId))
37700
+ }),
37701
+ promptId !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PromptDialog, {
37702
+ sessionId: promptId,
37703
+ onClose: () => setPromptId(null)
37704
+ })
37705
+ ]
37312
37706
  });
37313
37707
  }
37314
37708
  //#endregion
@@ -38017,7 +38411,7 @@ window.__ModuleLoader__.load({
38017
38411
  //#region src/client/panel/AssetsTab.tsx
38018
38412
  /**
38019
38413
  * 写作资产页签:题材基底库 / 推进模式库 / 反 AI 规则 / 写法引擎。
38020
- * 学习自 AI-Novel-Writing-Assistant 的四大资产模块,注入到生成与审稿提示词中。
38414
+ * 四大资产模块(题材 / 推进模式 / 反 AI 规则 / 叙事风格),注入到生成与审稿提示词中。
38021
38415
  */
38022
38416
  /** 渲染题材树(带勾选当前题材)。 */
38023
38417
  function GenreTree({ node, selected, onSelect }) {
@@ -38471,7 +38865,7 @@ window.__ModuleLoader__.load({
38471
38865
  }),
38472
38866
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
38473
38867
  className: panel_module_css_default.meta,
38474
- children: "从内置 8 套叙事风格模板中一键选用(来自 AI-Novel-Writing-Assistant 写法引擎),无需样本文本;绑定后生成与润色都遵循该风格。"
38868
+ children: "从内置 8 套叙事风格模板中一键选用,无需样本文本;绑定后生成与润色都遵循该风格。"
38475
38869
  }),
38476
38870
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
38477
38871
  style: {
@@ -41355,9 +41749,9 @@ window.__ModuleLoader__.load({
41355
41749
  * ① 模型(提供方管理):提供方卡片(编辑/删除)+ 添加提供方 / 添加自定义提供方;
41356
41750
  * ② 当前写作模型:提供商/模型下拉 + 测试连通 + 推理强度。
41357
41751
  */
41358
- /** 目录不可用时回退的内置预设(历史行为)。 */
41752
+ /** 目录不可用时回退的内置预设(对齐当前模型目录)。 */
41359
41753
  const FALLBACK_MODEL_PRESETS = [
41360
- "deepseek-v4-flash",
41754
+ "deepseek-flash",
41361
41755
  "deepseek-v4-pro",
41362
41756
  "deepseek-chat",
41363
41757
  "deepseek-reasoner"
@@ -42125,6 +42519,8 @@ window.__ModuleLoader__.load({
42125
42519
  generateModel: configDraft.generateModel,
42126
42520
  reviewModel: configDraft.reviewModel,
42127
42521
  auditModel: configDraft.auditModel,
42522
+ fallbackModel: configDraft.fallbackModel ?? "",
42523
+ autoTimeline: configDraft.autoTimeline ?? true,
42128
42524
  reasoningEffort: configDraft.reasoningEffort ?? "off",
42129
42525
  chapterChars: configDraft.chapterChars,
42130
42526
  maxTokens: configDraft.maxTokens,
@@ -42249,41 +42645,108 @@ window.__ModuleLoader__.load({
42249
42645
  className: panel_module_css_default.meta,
42250
42646
  children: notice
42251
42647
  }),
42252
- settingsTab === "model" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42253
- className: panel_module_css_default.card + " " + panel_module_css_default.settingsCard,
42254
- style: { gap: "var(--nf-space-24)" },
42255
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
42256
- className: panel_module_css_default.cardTitle,
42257
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(import_lucide_react.Brain, {
42258
- size: 18,
42259
- style: { verticalAlign: -3 }
42260
- }), " 模型与推理"]
42261
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelManager, {
42262
- api,
42263
- provider: configDraft.provider,
42264
- model: configDraft.model,
42265
- savedModels: configDraft.savedModels ?? [],
42266
- onProvider: (v) => setConfigDraft({
42267
- ...configDraft,
42268
- provider: v
42269
- }),
42270
- onModel: (v) => setConfigDraft({
42271
- ...configDraft,
42272
- model: v
42273
- }),
42274
- onSavedModels: (models) => setConfigDraft({
42648
+ settingsTab === "model" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
42649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42650
+ className: panel_module_css_default.card + " " + panel_module_css_default.settingsCard,
42651
+ style: { gap: "var(--nf-space-24)" },
42652
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
42653
+ className: panel_module_css_default.cardTitle,
42654
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(import_lucide_react.Brain, {
42655
+ size: 18,
42656
+ style: { verticalAlign: -3 }
42657
+ }), " 模型与推理"]
42658
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelManager, {
42659
+ api,
42660
+ provider: configDraft.provider,
42661
+ model: configDraft.model,
42662
+ savedModels: configDraft.savedModels ?? [],
42663
+ onProvider: (v) => setConfigDraft({
42664
+ ...configDraft,
42665
+ provider: v
42666
+ }),
42667
+ onModel: (v) => setConfigDraft({
42668
+ ...configDraft,
42669
+ model: v
42670
+ }),
42671
+ onSavedModels: (models) => setConfigDraft({
42672
+ ...configDraft,
42673
+ savedModels: models
42674
+ })
42675
+ })]
42676
+ }),
42677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42678
+ className: panel_module_css_default.card + " " + panel_module_css_default.settingsCard,
42679
+ style: { gap: "var(--nf-space-12)" },
42680
+ children: [
42681
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42682
+ className: panel_module_css_default.cardTitle,
42683
+ children: "备用模型(故障自动切换)"
42684
+ }),
42685
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42686
+ className: panel_module_css_default.field,
42687
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
42688
+ className: panel_module_css_default.fieldLabel,
42689
+ children: "主模型调用失败(限流 / 超时 / 额度 / 服务不可用)时,自动换它重试一次"
42690
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
42691
+ className: panel_module_css_default.input,
42692
+ value: configDraft.fallbackModel ?? "",
42693
+ onChange: (e) => setConfigDraft({
42694
+ ...configDraft,
42695
+ fallbackModel: e.target.value
42696
+ }),
42697
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
42698
+ value: "",
42699
+ children: "不切换(关闭)"
42700
+ }), [.../* @__PURE__ */ new Set([configDraft.model, ...(configDraft.savedModels ?? []).map((m) => m.model)])].filter((m) => m !== "").map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
42701
+ value: m,
42702
+ children: m
42703
+ }, m))]
42704
+ })]
42705
+ }),
42706
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42707
+ className: panel_module_css_default.meta,
42708
+ children: "只对可重试的失败生效:用户取消、参数错误不会切换;正文生成只在「一个字都没产出」时才换模型重试,避免重复生成。"
42709
+ })
42710
+ ]
42711
+ }),
42712
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42713
+ className: panel_module_css_default.card + " " + panel_module_css_default.settingsCard,
42714
+ style: { gap: "var(--nf-space-12)" },
42715
+ children: [
42716
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42717
+ className: panel_module_css_default.cardTitle,
42718
+ children: "故事时间线"
42719
+ }),
42720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
42721
+ className: panel_module_css_default.row,
42722
+ style: {
42723
+ gap: "var(--nf-space-8)",
42724
+ alignItems: "center"
42725
+ },
42726
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
42727
+ type: "checkbox",
42728
+ checked: configDraft.autoTimeline ?? true,
42729
+ onChange: (e) => setConfigDraft({
42730
+ ...configDraft,
42731
+ autoTimeline: e.target.checked
42732
+ })
42733
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "出章后自动抽取时间线事件(时间 / 地点 / 在场角色 / 事件)" })]
42734
+ }),
42735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
42736
+ className: panel_module_css_default.meta,
42737
+ children: "抽取失败不会阻断出章;可在侧柜「故事时间线」里手工抽取、修正,并一键检查时间倒流 / 地点瞬移等矛盾。"
42738
+ })
42739
+ ]
42740
+ }),
42741
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningSection, {
42742
+ reasoningEffort: configDraft.reasoningEffort ?? "off",
42743
+ analysisReasoning: configDraft.analysisReasoning ?? "low",
42744
+ onChange: (patch) => setConfigDraft({
42275
42745
  ...configDraft,
42276
- savedModels: models
42746
+ ...patch
42277
42747
  })
42278
- })]
42279
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningSection, {
42280
- reasoningEffort: configDraft.reasoningEffort ?? "off",
42281
- analysisReasoning: configDraft.analysisReasoning ?? "low",
42282
- onChange: (patch) => setConfigDraft({
42283
- ...configDraft,
42284
- ...patch
42285
42748
  })
42286
- })] }),
42749
+ ] }),
42287
42750
  settingsTab === "files" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
42288
42751
  className: panel_module_css_default.card + " " + panel_module_css_default.settingsCard,
42289
42752
  style: { gap: "var(--nf-space-24)" },
@@ -43846,7 +44309,7 @@ window.__ModuleLoader__.load({
43846
44309
  * 作者级首页容器:书架页左侧导航 + 内容区。
43847
44310
  * 导航复用工作区 .panelNav 玻璃卡片样式(同款),导航项为作者级:书架/改编/资产库/全局资产库/AI 进度/设置。
43848
44311
  */
43849
- const PLUGIN_VERSION = "1.2.1-alpha";
44312
+ const PLUGIN_VERSION = "1.3.0-alpha";
43850
44313
  /** GitHub 仓库地址(关于区块点击跳转)。 */
43851
44314
  const REPO_URL = "https://github.com/watersxya/dsh-novel-forge";
43852
44315
  /** 顶层 tab(横向,墨案:创作 / 灵感策划 / 资产 / 设置)+ 组内二级页。 */
@@ -47230,322 +47693,1279 @@ window.__ModuleLoader__.load({
47230
47693
  })]
47231
47694
  }, i))
47232
47695
  })]
47233
- })]
47234
- }),
47235
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47236
- className: `${panel_module_css_default.card} ${panel_module_css_default.settingsCard}`,
47237
- style: { gap: "var(--nf-space-6)" },
47238
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47239
- style: {
47240
- fontSize: "var(--nf-fs-16)",
47241
- fontWeight: 600
47242
- },
47243
- children: " 编辑待办(已采纳)"
47244
- }), todos.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47245
- className: panel_module_css_default.meta,
47246
- children: "还没有。点上面「加入待办」,把风险/修复记到这里,做完勾掉。这些也会出现在工作流主页的「待办队列」。"
47247
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
47696
+ })]
47697
+ }),
47698
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47699
+ className: `${panel_module_css_default.card} ${panel_module_css_default.settingsCard}`,
47700
+ style: { gap: "var(--nf-space-6)" },
47701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47702
+ style: {
47703
+ fontSize: "var(--nf-fs-16)",
47704
+ fontWeight: 600
47705
+ },
47706
+ children: " 编辑待办(已采纳)"
47707
+ }), todos.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47708
+ className: panel_module_css_default.meta,
47709
+ children: "还没有。点上面「加入待办」,把风险/修复记到这里,做完勾掉。这些也会出现在工作流主页的「待办队列」。"
47710
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
47711
+ style: {
47712
+ margin: 0,
47713
+ paddingLeft: 0,
47714
+ listStyle: "none",
47715
+ display: "grid",
47716
+ gap: "var(--nf-space-6)",
47717
+ fontSize: "var(--nf-fs-14)"
47718
+ },
47719
+ children: todos.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
47720
+ style: {
47721
+ display: "flex",
47722
+ alignItems: "center",
47723
+ gap: 8
47724
+ },
47725
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
47726
+ style: {
47727
+ display: "flex",
47728
+ alignItems: "center",
47729
+ gap: "var(--nf-space-6)",
47730
+ flex: 1,
47731
+ cursor: "pointer"
47732
+ },
47733
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
47734
+ type: "checkbox",
47735
+ checked: t.done,
47736
+ disabled: todosBusy,
47737
+ onChange: () => {
47738
+ toggleTodo(t.id);
47739
+ }
47740
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47741
+ style: {
47742
+ textDecoration: t.done ? "line-through" : "none",
47743
+ color: t.done ? "var(--nf-text-2)" : void 0
47744
+ },
47745
+ children: t.text
47746
+ })]
47747
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47748
+ type: "button",
47749
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
47750
+ title: "删除",
47751
+ onClick: () => {
47752
+ removeTodo(t.id);
47753
+ },
47754
+ children: "×"
47755
+ })]
47756
+ }, t.id))
47757
+ })]
47758
+ })
47759
+ ]
47760
+ })
47761
+ ]
47762
+ });
47763
+ }
47764
+ //#endregion
47765
+ //#region src/client/panel/KnowledgeBaseView.tsx
47766
+ /**
47767
+ * 书内知识库:添加/删除参考资料,生成/规划时自动检索注入。
47768
+ * T2 主从布局:左列文档列表 + 新建入口,右侧文档详情 / 新建表单。
47769
+ */
47770
+ function KnowledgeBaseView({ api }) {
47771
+ const [docs, setDocs] = (0, react.useState)([]);
47772
+ const [title, setTitle] = (0, react.useState)("");
47773
+ const [content, setContent] = (0, react.useState)("");
47774
+ const [busy, setBusy] = (0, react.useState)(false);
47775
+ const [notice, setNotice] = (0, react.useState)("");
47776
+ const [error, setError] = (0, react.useState)("");
47777
+ /** 左列选中的文档 id;null 且 addMode=false 时显示空态。 */
47778
+ const [selectedId, setSelectedId] = (0, react.useState)(null);
47779
+ /** 新建模式:右栏显示添加表单。 */
47780
+ const [addMode, setAddMode] = (0, react.useState)(false);
47781
+ const load = async () => {
47782
+ try {
47783
+ const r = await api.knowledgeList();
47784
+ setDocs(r.docs);
47785
+ } catch (e) {
47786
+ setError(e.message);
47787
+ }
47788
+ };
47789
+ (0, react.useEffect)(() => {
47790
+ load();
47791
+ }, []);
47792
+ const add = async () => {
47793
+ if (title.trim() === "") {
47794
+ setError("标题不能为空");
47795
+ return;
47796
+ }
47797
+ setBusy(true);
47798
+ setError("");
47799
+ setNotice("");
47800
+ try {
47801
+ const r = await api.knowledgeAdd({
47802
+ title: title.trim(),
47803
+ content: content.trim()
47804
+ });
47805
+ setDocs(r.docs);
47806
+ setTitle("");
47807
+ setContent("");
47808
+ setNotice(" 已添加");
47809
+ setAddMode(false);
47810
+ const added = r.docs[r.docs.length - 1];
47811
+ if (added !== void 0) setSelectedId(added.id);
47812
+ } catch (e) {
47813
+ setError(e.message);
47814
+ } finally {
47815
+ setBusy(false);
47816
+ }
47817
+ };
47818
+ const remove = async (id) => {
47819
+ setBusy(true);
47820
+ setError("");
47821
+ try {
47822
+ const r = await api.knowledgeRemove(id);
47823
+ setDocs(r.docs);
47824
+ if (selectedId === id) setSelectedId(null);
47825
+ } catch (e) {
47826
+ setError(e.message);
47827
+ } finally {
47828
+ setBusy(false);
47829
+ }
47830
+ };
47831
+ const selected = docs.find((d) => d.id === selectedId);
47832
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47833
+ className: panel_module_css_default.authorPageBodyFlow,
47834
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47835
+ className: panel_module_css_default.card,
47836
+ style: {
47837
+ padding: 0,
47838
+ overflow: "hidden"
47839
+ },
47840
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47841
+ className: panel_module_css_default.t2Master,
47842
+ style: { minHeight: 420 },
47843
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
47844
+ className: panel_module_css_default.t2Rail,
47845
+ "aria-label": "知识库文档",
47846
+ children: [
47847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47848
+ className: panel_module_css_default.t2RailHead,
47849
+ children: " 书内知识库"
47850
+ }),
47851
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
47852
+ type: "button",
47853
+ className: panel_module_css_default.t2RailBtn,
47854
+ "data-active": addMode ? "" : void 0,
47855
+ onClick: () => {
47856
+ setAddMode(true);
47857
+ setSelectedId(null);
47858
+ },
47859
+ title: "添加一篇参考文档(生成时按章节检索注入)",
47860
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47861
+ "aria-hidden": true,
47862
+ children: "+"
47863
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "新建文档" })]
47864
+ }),
47865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.t2RailSep }),
47866
+ docs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47867
+ className: panel_module_css_default.t2RailMeta,
47868
+ children: "暂无文档"
47869
+ }) : docs.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
47870
+ type: "button",
47871
+ role: "tab",
47872
+ "aria-selected": !addMode && selectedId === d.id,
47873
+ className: panel_module_css_default.t2RailBtn,
47874
+ "data-active": !addMode && selectedId === d.id ? "" : void 0,
47875
+ title: d.content.slice(0, 80),
47876
+ onClick: () => {
47877
+ setAddMode(false);
47878
+ setSelectedId(d.id);
47879
+ },
47880
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": true }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47881
+ style: {
47882
+ flex: 1,
47883
+ minWidth: 0,
47884
+ overflow: "hidden",
47885
+ textOverflow: "ellipsis",
47886
+ whiteSpace: "nowrap"
47887
+ },
47888
+ children: d.title
47889
+ })]
47890
+ }, d.id)),
47891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.t2RailSep }),
47892
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47893
+ className: panel_module_css_default.t2RailMeta,
47894
+ children: [
47895
+ "共 ",
47896
+ docs.length,
47897
+ " 篇 · 正文生成时按当前章节检索注入"
47898
+ ]
47899
+ })
47900
+ ]
47901
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47902
+ className: panel_module_css_default.t2Content,
47903
+ children: [addMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47904
+ className: panel_module_css_default.cardTitleLg,
47905
+ children: "+ 新建文档"
47906
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47907
+ style: {
47908
+ display: "flex",
47909
+ flexDirection: "column",
47910
+ gap: "var(--nf-space-6)",
47911
+ maxWidth: "var(--nf-measure, 720px)"
47912
+ },
47913
+ children: [
47914
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
47915
+ className: panel_module_css_default.input,
47916
+ value: title,
47917
+ onChange: (e) => setTitle(e.target.value),
47918
+ placeholder: "标题(如:天机锁设定 / 止水驿关系表)"
47919
+ }),
47920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
47921
+ className: panel_module_css_default.input,
47922
+ style: {
47923
+ minHeight: 160,
47924
+ resize: "vertical"
47925
+ },
47926
+ value: content,
47927
+ onChange: (e) => setContent(e.target.value),
47928
+ placeholder: "内容(将被检索注入生成提示)"
47929
+ }),
47930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47931
+ style: {
47932
+ display: "flex",
47933
+ alignItems: "center",
47934
+ gap: "var(--nf-space-8)"
47935
+ },
47936
+ children: [
47937
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47938
+ type: "button",
47939
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonPrimary}`,
47940
+ disabled: busy,
47941
+ onClick: () => {
47942
+ add();
47943
+ },
47944
+ children: "添加"
47945
+ }),
47946
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47947
+ type: "button",
47948
+ className: panel_module_css_default.button,
47949
+ onClick: () => {
47950
+ setAddMode(false);
47951
+ },
47952
+ children: "取消"
47953
+ }),
47954
+ notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47955
+ style: {
47956
+ fontSize: "var(--nf-fs-12)",
47957
+ color: "var(--nf-success)"
47958
+ },
47959
+ children: notice
47960
+ })
47961
+ ]
47962
+ })
47963
+ ]
47964
+ })] }) : selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47965
+ className: panel_module_css_default.row,
47966
+ style: {
47967
+ justifyContent: "space-between",
47968
+ flexWrap: "wrap"
47969
+ },
47970
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
47971
+ className: panel_module_css_default.cardTitleLg,
47972
+ children: [" ", selected.title]
47973
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47974
+ type: "button",
47975
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
47976
+ disabled: busy,
47977
+ onClick: () => {
47978
+ remove(selected.id);
47979
+ },
47980
+ children: "删除"
47981
+ })]
47982
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47983
+ className: `${panel_module_css_default.planBeats} ${panel_module_css_default.docsSurface}`,
47984
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47985
+ style: {
47986
+ fontSize: "var(--nf-fs-14)",
47987
+ lineHeight: 1.7,
47988
+ whiteSpace: "pre-wrap"
47989
+ },
47990
+ children: selected.content !== "" ? selected.content : "(空文档)"
47991
+ })
47992
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47993
+ className: panel_module_css_default.planDetailEmpty,
47994
+ style: { minHeight: 300 },
47995
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47996
+ className: panel_module_css_default.meta,
47997
+ children: "← 从左侧选择文档,或「+ 新建文档」添加参考资料。"
47998
+ })
47999
+ }), error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48000
+ style: {
48001
+ color: "var(--nf-error)",
48002
+ fontSize: "var(--nf-fs-14)"
48003
+ },
48004
+ children: [" ", error]
48005
+ })]
48006
+ })]
48007
+ })
48008
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48009
+ className: panel_module_css_default.meta,
48010
+ children: "自由参考文档:零散设定/资料/碎片放这里;结构化设定(世界观/角色/境界/红线)请放「本书设定」。"
48011
+ })]
48012
+ });
48013
+ }
48014
+ //#endregion
48015
+ //#region src/client/panel/TimelineView.tsx
48016
+ /**
48017
+ * 故事时间线:把逐章抽取的「时间点 / 地点 / 在场角色 / 事件」按章节顺序排开,
48018
+ * 带规则初筛 + AI 复核的矛盾清单,并支持就地修正(手改后标记为 manual)。
48019
+ *
48020
+ * 与「编年录」的分工:编年录记事实状态,这里记**顺序**——专门抓时间倒流、
48021
+ * 地点瞬移、跨度不合理这类长篇最容易崩的地方。
48022
+ */
48023
+ const SEVERITY_LABEL = {
48024
+ high: "高",
48025
+ medium: "中",
48026
+ low: "低"
48027
+ };
48028
+ function TimelineView({ api, chapters }) {
48029
+ const [events, setEvents] = (0, react.useState)([]);
48030
+ const [issues, setIssues] = (0, react.useState)([]);
48031
+ const [busy, setBusy] = (0, react.useState)(false);
48032
+ const [error, setError] = (0, react.useState)("");
48033
+ const [notice, setNotice] = (0, react.useState)("");
48034
+ const [extractNo, setExtractNo] = (0, react.useState)(chapters[chapters.length - 1] ?? "");
48035
+ const [editing, setEditing] = (0, react.useState)(null);
48036
+ const refresh = (0, react.useCallback)(async (showError = true) => {
48037
+ try {
48038
+ const r = await api.timeline();
48039
+ setEvents(r.events);
48040
+ setIssues(r.issues ?? []);
48041
+ } catch (err) {
48042
+ if (showError) setError(err.message);
48043
+ }
48044
+ }, [api]);
48045
+ (0, react.useEffect)(() => {
48046
+ refresh(false);
48047
+ }, [refresh]);
48048
+ const grouped = (0, react.useMemo)(() => {
48049
+ const map = /* @__PURE__ */ new Map();
48050
+ for (const e of events) {
48051
+ const list = map.get(e.chapterNo) ?? [];
48052
+ list.push(e);
48053
+ map.set(e.chapterNo, list);
48054
+ }
48055
+ return [...map.entries()].sort((a, b) => a[0] - b[0]);
48056
+ }, [events]);
48057
+ const run = (0, react.useCallback)(async (label, fn) => {
48058
+ setBusy(true);
48059
+ setError("");
48060
+ setNotice("");
48061
+ try {
48062
+ await fn();
48063
+ setNotice(label);
48064
+ } catch (err) {
48065
+ setError(err.message);
48066
+ } finally {
48067
+ setBusy(false);
48068
+ }
48069
+ }, []);
48070
+ const handleExtract = () => {
48071
+ if (extractNo === "") return;
48072
+ run(`已抽取第 ${extractNo} 章时间线`, async () => {
48073
+ await api.timelineExtract(Number(extractNo));
48074
+ await refresh();
48075
+ });
48076
+ };
48077
+ const handleCheck = () => {
48078
+ run("时间线检查完成", async () => {
48079
+ const r = await api.timelineCheck();
48080
+ setIssues(r.issues ?? []);
48081
+ });
48082
+ };
48083
+ const handleSaveEdit = () => {
48084
+ if (editing === null) return;
48085
+ run("已保存修改", async () => {
48086
+ await api.timelineUpdate({
48087
+ ...editing,
48088
+ source: "manual"
48089
+ });
48090
+ setEditing(null);
48091
+ await refresh();
48092
+ });
48093
+ };
48094
+ const handleRemove = (id) => {
48095
+ run("已删除该事件", async () => {
48096
+ await api.timelineRemove(id);
48097
+ await refresh();
48098
+ });
48099
+ };
48100
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48101
+ className: panel_module_css_default.card,
48102
+ style: { gap: "var(--nf-space-12)" },
48103
+ children: [
48104
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48105
+ className: panel_module_css_default.row,
48106
+ style: {
48107
+ justifyContent: "space-between",
48108
+ alignItems: "center",
48109
+ flexWrap: "wrap",
48110
+ gap: "var(--nf-space-8)"
48111
+ },
48112
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48113
+ className: panel_module_css_default.cardTitle,
48114
+ children: "故事时间线"
48115
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48116
+ className: panel_module_css_default.meta,
48117
+ children: [
48118
+ events.length,
48119
+ " 条事件 · ",
48120
+ grouped.length,
48121
+ " 章"
48122
+ ]
48123
+ })]
48124
+ }),
48125
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48126
+ className: panel_module_css_default.row,
48127
+ style: {
48128
+ flexWrap: "wrap",
48129
+ gap: "var(--nf-space-8)",
48130
+ alignItems: "center"
48131
+ },
48132
+ children: [
48133
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
48134
+ className: panel_module_css_default.input,
48135
+ value: extractNo,
48136
+ onChange: (e) => setExtractNo(e.target.value === "" ? "" : Number(e.target.value)),
48137
+ title: "选择要抽取时间线的章节",
48138
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
48139
+ value: "",
48140
+ children: "选择章节…"
48141
+ }), chapters.map((no) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
48142
+ value: no,
48143
+ children: [
48144
+ "第 ",
48145
+ no,
48146
+ " 章"
48147
+ ]
48148
+ }, no))]
48149
+ }),
48150
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48151
+ type: "button",
48152
+ className: panel_module_css_default.button,
48153
+ disabled: busy || extractNo === "",
48154
+ onClick: handleExtract,
48155
+ children: "抽取本章时间线"
48156
+ }),
48157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48158
+ type: "button",
48159
+ className: panel_module_css_default.button,
48160
+ disabled: busy || events.length === 0,
48161
+ onClick: handleCheck,
48162
+ children: "检查时间线矛盾"
48163
+ })
48164
+ ]
48165
+ }),
48166
+ notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48167
+ style: {
48168
+ color: "var(--nf-success)",
48169
+ fontSize: "var(--nf-text-12)"
48170
+ },
48171
+ children: notice
48172
+ }),
48173
+ error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48174
+ style: {
48175
+ color: "var(--nf-error)",
48176
+ fontSize: "var(--nf-text-12)"
48177
+ },
48178
+ children: error
48179
+ }),
48180
+ issues.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48181
+ style: {
48182
+ border: "1px solid var(--nf-border)",
48183
+ borderRadius: 8,
48184
+ padding: 8,
48185
+ background: "var(--nf-bg-inset)"
48186
+ },
48187
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", {
48188
+ style: { fontSize: "var(--nf-fs-12)" },
48189
+ children: [
48190
+ "发现 ",
48191
+ issues.length,
48192
+ " 处时间线问题"
48193
+ ]
48194
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48195
+ style: {
48196
+ display: "flex",
48197
+ flexDirection: "column",
48198
+ gap: 6,
48199
+ marginTop: 6
48200
+ },
48201
+ children: issues.map((issue, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48202
+ style: { fontSize: "var(--nf-fs-12)" },
48203
+ children: [
48204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48205
+ className: panel_module_css_default.lfPill,
48206
+ "data-phase": issue.severity === "high" ? "failed" : "requesting",
48207
+ children: SEVERITY_LABEL[issue.severity]
48208
+ }),
48209
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48210
+ style: { marginLeft: 6 },
48211
+ children: [issue.chapters.length > 0 ? `第 ${issue.chapters.join("、")} 章:` : "", issue.item]
48212
+ }),
48213
+ issue.suggestion !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48214
+ className: panel_module_css_default.meta,
48215
+ style: { marginLeft: 6 },
48216
+ children: ["建议:", issue.suggestion]
48217
+ })
48218
+ ]
48219
+ }, i))
48220
+ })]
48221
+ }),
48222
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48223
+ style: {
48224
+ display: "flex",
48225
+ flexDirection: "column",
48226
+ gap: 10,
48227
+ overflowY: "auto",
48228
+ maxHeight: "52vh"
48229
+ },
48230
+ children: [events.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48231
+ className: panel_module_css_default.meta,
48232
+ children: "还没有时间线事件。生成章节后会自动抽取,也可以在上面选一章手工抽取。"
48233
+ }), grouped.map(([no, list]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48234
+ className: panel_module_css_default.meta,
48235
+ style: { marginBottom: 4 },
48236
+ children: [
48237
+ "第 ",
48238
+ no,
48239
+ " 章 · ",
48240
+ list.length,
48241
+ " 条"
48242
+ ]
48243
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48244
+ style: {
48245
+ display: "flex",
48246
+ flexDirection: "column",
48247
+ gap: 4
48248
+ },
48249
+ children: list.map((e) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48250
+ style: {
48251
+ border: "1px solid var(--nf-border)",
48252
+ borderRadius: 8,
48253
+ padding: "6px 8px",
48254
+ fontSize: "var(--nf-fs-12)"
48255
+ },
48256
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48257
+ style: {
48258
+ display: "flex",
48259
+ gap: 6,
48260
+ alignItems: "baseline",
48261
+ flexWrap: "wrap"
48262
+ },
48263
+ children: [
48264
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: e.time !== "" ? e.time : "(未标注时间)" }),
48265
+ e.place !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48266
+ className: panel_module_css_default.meta,
48267
+ children: ["@", e.place]
48268
+ }),
48269
+ e.characters.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48270
+ className: panel_module_css_default.meta,
48271
+ children: e.characters.join("、")
48272
+ }),
48273
+ e.source === "manual" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48274
+ className: panel_module_css_default.meta,
48275
+ children: "(手改)"
48276
+ }),
48277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48278
+ style: {
48279
+ marginLeft: "auto",
48280
+ display: "flex",
48281
+ gap: 4
48282
+ },
48283
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48284
+ type: "button",
48285
+ className: panel_module_css_default.iconButton,
48286
+ title: "修正这条事件",
48287
+ onClick: () => setEditing({ ...e }),
48288
+ children: "✎"
48289
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48290
+ type: "button",
48291
+ className: panel_module_css_default.iconButton,
48292
+ title: "删除这条事件",
48293
+ onClick: () => handleRemove(e.id),
48294
+ children: "×"
48295
+ })]
48296
+ })
48297
+ ]
48298
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48299
+ style: { marginTop: 2 },
48300
+ children: e.event
48301
+ })]
48302
+ }, e.id))
48303
+ })] }, no))]
48304
+ }),
48305
+ editing !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48306
+ style: {
48307
+ position: "fixed",
48308
+ inset: 0,
48309
+ background: "rgba(0,0,0,.45)",
48310
+ zIndex: 60,
48311
+ display: "flex",
48312
+ alignItems: "center",
48313
+ justifyContent: "center",
48314
+ padding: 20
48315
+ },
48316
+ onClick: () => setEditing(null),
48317
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48318
+ onClick: (ev) => ev.stopPropagation(),
48319
+ style: {
48320
+ background: "var(--nf-bg)",
48321
+ border: "1px solid var(--nf-border)",
48322
+ borderRadius: 10,
48323
+ width: "min(560px, 94vw)",
48324
+ padding: 12,
48325
+ display: "flex",
48326
+ flexDirection: "column",
48327
+ gap: 8
48328
+ },
48329
+ children: [
48330
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", {
48331
+ style: { fontSize: "var(--nf-fs-13)" },
48332
+ children: [
48333
+ "修正时间线事件 · 第 ",
48334
+ editing.chapterNo,
48335
+ " 章"
48336
+ ]
48337
+ }),
48338
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
48339
+ className: panel_module_css_default.field,
48340
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48341
+ className: panel_module_css_default.fieldLabel,
48342
+ children: "故事内时间"
48343
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
48344
+ className: panel_module_css_default.input,
48345
+ value: editing.time,
48346
+ onChange: (e) => setEditing({
48347
+ ...editing,
48348
+ time: e.target.value
48349
+ }),
48350
+ placeholder: "如:第三日黄昏 / 入宗三个月后"
48351
+ })]
48352
+ }),
48353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
48354
+ className: panel_module_css_default.field,
48355
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48356
+ className: panel_module_css_default.fieldLabel,
48357
+ children: "地点"
48358
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
48359
+ className: panel_module_css_default.input,
48360
+ value: editing.place,
48361
+ onChange: (e) => setEditing({
48362
+ ...editing,
48363
+ place: e.target.value
48364
+ })
48365
+ })]
48366
+ }),
48367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
48368
+ className: panel_module_css_default.field,
48369
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48370
+ className: panel_module_css_default.fieldLabel,
48371
+ children: "在场角色(顿号或逗号分隔)"
48372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
48373
+ className: panel_module_css_default.input,
48374
+ value: editing.characters.join("、"),
48375
+ onChange: (e) => setEditing({
48376
+ ...editing,
48377
+ characters: e.target.value.split(/[、,,\s]+/).filter(Boolean)
48378
+ })
48379
+ })]
48380
+ }),
48381
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
48382
+ className: panel_module_css_default.field,
48383
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48384
+ className: panel_module_css_default.fieldLabel,
48385
+ children: "事件"
48386
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
48387
+ className: panel_module_css_default.textarea,
48388
+ rows: 3,
48389
+ value: editing.event,
48390
+ onChange: (e) => setEditing({
48391
+ ...editing,
48392
+ event: e.target.value
48393
+ })
48394
+ })]
48395
+ }),
48396
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48397
+ className: panel_module_css_default.row,
48398
+ style: {
48399
+ justifyContent: "flex-end",
48400
+ gap: 6
48401
+ },
48402
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48403
+ type: "button",
48404
+ className: panel_module_css_default.button,
48405
+ onClick: () => setEditing(null),
48406
+ children: "取消"
48407
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48408
+ type: "button",
48409
+ className: panel_module_css_default.button + " " + panel_module_css_default.buttonPrimary,
48410
+ disabled: busy,
48411
+ onClick: handleSaveEdit,
48412
+ children: "保存"
48413
+ })]
48414
+ })
48415
+ ]
48416
+ })
48417
+ })
48418
+ ]
48419
+ });
48420
+ }
48421
+ //#endregion
48422
+ //#region src/tension.ts
48423
+ /** 参考曲线的中文标签(面板展示)。 */
48424
+ const TENSION_PRESET_LABELS = {
48425
+ escalation: "递进爬升",
48426
+ suspense: "悬疑加压",
48427
+ wave: "波浪呼吸",
48428
+ frontLoad: "前紧后稳",
48429
+ flat: "平稳推进",
48430
+ custom: "自定义"
48431
+ };
48432
+ //#endregion
48433
+ //#region src/client/panel/TensionView.tsx
48434
+ /**
48435
+ * 张力曲线:把「每章该有多紧」画出来并可逐章调整。
48436
+ *
48437
+ * 图上是三条线:
48438
+ * 参考(虚线,形状预设) / 目标(实线,作者或规划设定) / 实际(散点,审稿打分)。
48439
+ * 下方列出规则核对发现的曲线级问题(连续同值、高位不回落、长期低位、实际偏离目标)。
48440
+ *
48441
+ * 张力只影响写作提示词里的"松紧"指引,不改变任何校验规则。
48442
+ */
48443
+ const PRESETS = [
48444
+ "escalation",
48445
+ "suspense",
48446
+ "wave",
48447
+ "frontLoad",
48448
+ "flat",
48449
+ "custom"
48450
+ ];
48451
+ /** 简易折线图(SVG,无依赖):宽高固定,按章节数均分横轴。 */
48452
+ function CurveChart({ points, onPick }) {
48453
+ const W = 720;
48454
+ const H = 200;
48455
+ const padX = 28;
48456
+ const padY = 16;
48457
+ const n = points.length;
48458
+ const x = (i) => n <= 1 ? W / 2 : padX + i / (n - 1) * (W - padX * 2);
48459
+ const y = (v) => padY + (1 - v / 100) * (H - padY * 2);
48460
+ const line = (pick) => points.map((p, i) => {
48461
+ const v = pick(p);
48462
+ return v === void 0 ? null : `${x(i).toFixed(1)},${y(v).toFixed(1)}`;
48463
+ }).filter((s) => s !== null).join(" ");
48464
+ const refLine = line((p) => p.reference);
48465
+ const targetLine = line((p) => p.target);
48466
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
48467
+ viewBox: `0 0 ${W} ${H}`,
48468
+ width: "100%",
48469
+ height: H,
48470
+ role: "img",
48471
+ "aria-label": "张力曲线",
48472
+ children: [
48473
+ [
48474
+ 0,
48475
+ 40,
48476
+ 80,
48477
+ 100
48478
+ ].map((v) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
48479
+ x1: padX,
48480
+ x2: W - padX,
48481
+ y1: y(v),
48482
+ y2: y(v),
48483
+ stroke: "var(--nf-border)",
48484
+ strokeWidth: v === 0 || v === 100 ? 1 : .5,
48485
+ strokeDasharray: v === 0 || v === 100 ? "" : "3 4"
48486
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
48487
+ x: 4,
48488
+ y: y(v) + 3,
48489
+ fontSize: 9,
48490
+ fill: "var(--nf-text-2)",
48491
+ children: v
48492
+ })] }, v)),
48493
+ refLine !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", {
48494
+ points: refLine,
48495
+ fill: "none",
48496
+ stroke: "var(--nf-text-2)",
48497
+ strokeWidth: 1,
48498
+ strokeDasharray: "4 4",
48499
+ opacity: .7
48500
+ }),
48501
+ targetLine !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", {
48502
+ points: targetLine,
48503
+ fill: "none",
48504
+ stroke: "var(--nf-accent)",
48505
+ strokeWidth: 2
48506
+ }),
48507
+ points.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { children: [
48508
+ p.actual !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
48509
+ cx: x(i),
48510
+ cy: y(p.actual),
48511
+ r: 3,
48512
+ fill: "var(--nf-success)"
48513
+ }),
48514
+ p.target !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
48515
+ cx: x(i),
48516
+ cy: y(p.target),
48517
+ r: 2.5,
48518
+ fill: "var(--nf-accent)"
48519
+ }),
48520
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
48521
+ x: x(i) - 6,
48522
+ y: padY,
48523
+ width: 12,
48524
+ height: H - padY * 2,
48525
+ fill: "transparent",
48526
+ style: { cursor: "pointer" },
48527
+ onClick: () => onPick(p.no),
48528
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", { children: `第${p.no}章 ${p.title}|目标 ${p.target ?? "—"}|实际 ${p.actual ?? "—"}|参考 ${p.reference ?? "—"}` })
48529
+ })
48530
+ ] }, p.no))
48531
+ ]
48532
+ });
48533
+ }
48534
+ function TensionView({ api }) {
48535
+ const [data, setData] = (0, react.useState)(null);
48536
+ const [busy, setBusy] = (0, react.useState)(false);
48537
+ const [error, setError] = (0, react.useState)("");
48538
+ const [notice, setNotice] = (0, react.useState)("");
48539
+ const [editing, setEditing] = (0, react.useState)(null);
48540
+ const [draft, setDraft] = (0, react.useState)(50);
48541
+ const refresh = (0, react.useCallback)(async (showError = true) => {
48542
+ try {
48543
+ setData(await api.tension());
48544
+ } catch (err) {
48545
+ if (showError) setError(err.message);
48546
+ }
48547
+ }, [api]);
48548
+ (0, react.useEffect)(() => {
48549
+ refresh(false);
48550
+ }, [refresh]);
48551
+ const run = (0, react.useCallback)(async (label, fn) => {
48552
+ setBusy(true);
48553
+ setError("");
48554
+ setNotice("");
48555
+ try {
48556
+ await fn();
48557
+ setNotice(label);
48558
+ } catch (err) {
48559
+ setError(err.message);
48560
+ } finally {
48561
+ setBusy(false);
48562
+ }
48563
+ }, []);
48564
+ const changePreset = (preset) => {
48565
+ run(`已切换参考曲线:${TENSION_PRESET_LABELS[preset]}`, async () => {
48566
+ await api.tensionPreset(preset);
48567
+ await refresh();
48568
+ });
48569
+ };
48570
+ const saveEdit = () => {
48571
+ if (editing === null) return;
48572
+ run(`第 ${editing} 章目标张力已设为 ${draft}`, async () => {
48573
+ await api.tensionSet(editing, draft);
48574
+ setEditing(null);
48575
+ await refresh();
48576
+ });
48577
+ };
48578
+ const clearEdit = () => {
48579
+ if (editing === null) return;
48580
+ run(`已清除第 ${editing} 章的目标张力`, async () => {
48581
+ await api.tensionSet(editing);
48582
+ setEditing(null);
48583
+ await refresh();
48584
+ });
48585
+ };
48586
+ const issues = data?.issues ?? [];
48587
+ const points = data?.points ?? [];
48588
+ const stat = (0, react.useMemo)(() => {
48589
+ const targets = points.map((p) => p.target).filter((v) => v !== void 0);
48590
+ const actuals = points.map((p) => p.actual).filter((v) => v !== void 0);
48591
+ const avg = (xs) => xs.length === 0 ? "—" : String(Math.round(xs.reduce((a, b) => a + b, 0) / xs.length));
48592
+ return {
48593
+ targetAvg: avg(targets),
48594
+ actualAvg: avg(actuals),
48595
+ rated: actuals.length,
48596
+ total: points.length
48597
+ };
48598
+ }, [points]);
48599
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48600
+ className: panel_module_css_default.card,
48601
+ style: { gap: "var(--nf-space-12)" },
48602
+ children: [
48603
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48604
+ className: panel_module_css_default.row,
48605
+ style: {
48606
+ justifyContent: "space-between",
48607
+ alignItems: "center",
48608
+ flexWrap: "wrap",
48609
+ gap: "var(--nf-space-8)"
48610
+ },
48611
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48612
+ className: panel_module_css_default.cardTitle,
48613
+ children: "张力曲线"
48614
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48615
+ className: panel_module_css_default.meta,
48616
+ children: [
48617
+ points.length,
48618
+ " 章 · 已评 ",
48619
+ stat.rated,
48620
+ " 章 · 目标均 ",
48621
+ stat.targetAvg,
48622
+ " / 实际均 ",
48623
+ stat.actualAvg
48624
+ ]
48625
+ })]
48626
+ }),
48627
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48628
+ className: panel_module_css_default.row,
48629
+ style: {
48630
+ gap: "var(--nf-space-8)",
48631
+ alignItems: "center",
48632
+ flexWrap: "wrap"
48633
+ },
48634
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
48635
+ className: panel_module_css_default.input,
48636
+ value: data?.preset ?? "escalation",
48637
+ disabled: busy,
48638
+ onChange: (e) => changePreset(e.target.value),
48639
+ children: PRESETS.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
48640
+ value: p,
48641
+ children: TENSION_PRESET_LABELS[p]
48642
+ }, p))
48643
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48644
+ className: panel_module_css_default.meta,
48645
+ children: "虚线=参考曲线形状;实线=目标;绿点=审稿实际。点图中任意一章可改目标张力。"
48646
+ })]
48647
+ }),
48648
+ notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48649
+ style: {
48650
+ color: "var(--nf-success)",
48651
+ fontSize: "var(--nf-text-12)"
48652
+ },
48653
+ children: notice
48654
+ }),
48655
+ error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48656
+ style: {
48657
+ color: "var(--nf-error)",
48658
+ fontSize: "var(--nf-text-12)"
48659
+ },
48660
+ children: error
48661
+ }),
48662
+ points.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48663
+ className: panel_module_css_default.meta,
48664
+ children: "还没有章节计划。先在「总编台 → 卷/章节计划」排出章节,这里才有曲线可看。"
48665
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurveChart, {
48666
+ points,
48667
+ onPick: (no) => {
48668
+ const p = points.find((x) => x.no === no);
48669
+ setEditing(no);
48670
+ setDraft(p?.target ?? p?.actual ?? 50);
48671
+ }
48672
+ }),
48673
+ issues.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48674
+ style: {
48675
+ border: "1px solid var(--nf-border)",
48676
+ borderRadius: 8,
48677
+ padding: 8,
48678
+ background: "var(--nf-bg-inset)"
48679
+ },
48680
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", {
48681
+ style: { fontSize: "var(--nf-fs-12)" },
48682
+ children: [
48683
+ "曲线级问题 ",
48684
+ issues.length,
48685
+ " 条"
48686
+ ]
48687
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48688
+ style: {
48689
+ display: "flex",
48690
+ flexDirection: "column",
48691
+ gap: 6,
48692
+ marginTop: 6
48693
+ },
48694
+ children: issues.map((issue, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48695
+ style: { fontSize: "var(--nf-fs-12)" },
48696
+ children: [
48697
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48698
+ className: panel_module_css_default.lfPill,
48699
+ "data-phase": issue.severity === "high" ? "failed" : "requesting",
48700
+ children: issue.severity === "high" ? "高" : issue.severity === "medium" ? "中" : "低"
48701
+ }),
48702
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48703
+ style: { marginLeft: 6 },
48704
+ children: [issue.chapters.length > 0 ? `第 ${issue.chapters.join("、")} 章:` : "", issue.item]
48705
+ }),
48706
+ issue.suggestion !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48707
+ className: panel_module_css_default.meta,
48708
+ style: { marginLeft: 6 },
48709
+ children: ["建议:", issue.suggestion]
48710
+ })
48711
+ ]
48712
+ }, i))
48713
+ })]
48714
+ }),
48715
+ editing !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
48716
+ style: {
48717
+ position: "fixed",
48718
+ inset: 0,
48719
+ background: "rgba(0,0,0,.45)",
48720
+ zIndex: 60,
48721
+ display: "flex",
48722
+ alignItems: "center",
48723
+ justifyContent: "center",
48724
+ padding: 20
48725
+ },
48726
+ onClick: () => setEditing(null),
48727
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48728
+ onClick: (e) => e.stopPropagation(),
48729
+ style: {
48730
+ background: "var(--nf-bg)",
48731
+ border: "1px solid var(--nf-border)",
48732
+ borderRadius: 10,
48733
+ width: "min(420px, 92vw)",
48734
+ padding: 12,
48735
+ display: "flex",
48736
+ flexDirection: "column",
48737
+ gap: 8
48738
+ },
48739
+ children: [
48740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", {
48741
+ style: { fontSize: "var(--nf-fs-13)" },
48742
+ children: [
48743
+ "第 ",
48744
+ editing,
48745
+ " 章 · 目标张力"
48746
+ ]
48747
+ }),
48748
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
48749
+ type: "range",
48750
+ min: 0,
48751
+ max: 100,
48752
+ value: draft,
48753
+ onChange: (e) => setDraft(Number(e.target.value))
48754
+ }),
48755
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48756
+ className: panel_module_css_default.row,
48757
+ style: { justifyContent: "space-between" },
48758
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48759
+ className: panel_module_css_default.meta,
48760
+ children: [
48761
+ draft,
48762
+ " / 100(",
48763
+ draft < 40 ? "偏松:铺垫、日常、消化信息" : draft > 80 ? "偏紧:对抗、揭示、抉择、代价" : "中等:推进为主",
48764
+ ")"
48765
+ ]
48766
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
48767
+ className: panel_module_css_default.input,
48768
+ style: { width: 72 },
48769
+ type: "number",
48770
+ min: 0,
48771
+ max: 100,
48772
+ value: draft,
48773
+ onChange: (e) => setDraft(Math.max(0, Math.min(100, Number(e.target.value))))
48774
+ })]
48775
+ }),
48776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48777
+ className: panel_module_css_default.row,
47248
48778
  style: {
47249
- margin: 0,
47250
- paddingLeft: 0,
47251
- listStyle: "none",
47252
- display: "grid",
47253
- gap: "var(--nf-space-6)",
47254
- fontSize: "var(--nf-fs-14)"
48779
+ justifyContent: "flex-end",
48780
+ gap: 6
47255
48781
  },
47256
- children: todos.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
47257
- style: {
47258
- display: "flex",
47259
- alignItems: "center",
47260
- gap: 8
47261
- },
47262
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
47263
- style: {
47264
- display: "flex",
47265
- alignItems: "center",
47266
- gap: "var(--nf-space-6)",
47267
- flex: 1,
47268
- cursor: "pointer"
47269
- },
47270
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
47271
- type: "checkbox",
47272
- checked: t.done,
47273
- disabled: todosBusy,
47274
- onChange: () => {
47275
- toggleTodo(t.id);
47276
- }
47277
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47278
- style: {
47279
- textDecoration: t.done ? "line-through" : "none",
47280
- color: t.done ? "var(--nf-text-2)" : void 0
47281
- },
47282
- children: t.text
47283
- })]
47284
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48782
+ children: [
48783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47285
48784
  type: "button",
47286
- className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
47287
- title: "删除",
47288
- onClick: () => {
47289
- removeTodo(t.id);
47290
- },
47291
- children: "×"
47292
- })]
47293
- }, t.id))
47294
- })]
47295
- })
47296
- ]
48785
+ className: panel_module_css_default.button,
48786
+ onClick: clearEdit,
48787
+ disabled: busy,
48788
+ children: "清除"
48789
+ }),
48790
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48791
+ type: "button",
48792
+ className: panel_module_css_default.button,
48793
+ onClick: () => setEditing(null),
48794
+ children: "取消"
48795
+ }),
48796
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48797
+ type: "button",
48798
+ className: panel_module_css_default.button + " " + panel_module_css_default.buttonPrimary,
48799
+ onClick: saveEdit,
48800
+ disabled: busy,
48801
+ children: "保存"
48802
+ })
48803
+ ]
48804
+ })
48805
+ ]
48806
+ })
47297
48807
  })
47298
48808
  ]
47299
48809
  });
47300
48810
  }
47301
48811
  //#endregion
47302
- //#region src/client/panel/KnowledgeBaseView.tsx
48812
+ //#region src/client/panel/PromptSlotsView.tsx
47303
48813
  /**
47304
- * 书内知识库:添加/删除参考资料,生成/规划时自动检索注入。
47305
- * T2 主从布局:左列文档列表 + 新建入口,右侧文档详情 / 新建表单。
48814
+ * 提示词槽位:给作者留几个**安全可改**的位置来微调本书表达偏好。
48815
+ *
48816
+ * 边界写在界面上:槽位只**追加偏好**,不能覆盖道藏、写作红线、九条内容合规红线、
48817
+ * 反 AI 规则与阶段契约 —— 这些由宿主锁定。清空即恢复默认。
47306
48818
  */
47307
- function KnowledgeBaseView({ api }) {
47308
- const [docs, setDocs] = (0, react.useState)([]);
47309
- const [title, setTitle] = (0, react.useState)("");
47310
- const [content, setContent] = (0, react.useState)("");
48819
+ function PromptSlotsView({ api }) {
48820
+ const [slots, setSlots] = (0, react.useState)([]);
48821
+ const [drafts, setDrafts] = (0, react.useState)({});
47311
48822
  const [busy, setBusy] = (0, react.useState)(false);
47312
- const [notice, setNotice] = (0, react.useState)("");
47313
48823
  const [error, setError] = (0, react.useState)("");
47314
- /** 左列选中的文档 id;null 且 addMode=false 时显示空态。 */
47315
- const [selectedId, setSelectedId] = (0, react.useState)(null);
47316
- /** 新建模式:右栏显示添加表单。 */
47317
- const [addMode, setAddMode] = (0, react.useState)(false);
47318
- const load = async () => {
48824
+ const [notice, setNotice] = (0, react.useState)("");
48825
+ const refresh = (0, react.useCallback)(async (showError = true) => {
47319
48826
  try {
47320
- const r = await api.knowledgeList();
47321
- setDocs(r.docs);
47322
- } catch (e) {
47323
- setError(e.message);
48827
+ const r = await api.promptSlots();
48828
+ setSlots(r.slots);
48829
+ setDrafts(Object.fromEntries(r.slots.map((s) => [s.id, s.value])));
48830
+ } catch (err) {
48831
+ if (showError) setError(err.message);
47324
48832
  }
47325
- };
48833
+ }, [api]);
47326
48834
  (0, react.useEffect)(() => {
47327
- load();
47328
- }, []);
47329
- const add = async () => {
47330
- if (title.trim() === "") {
47331
- setError("标题不能为空");
47332
- return;
47333
- }
48835
+ refresh(false);
48836
+ }, [refresh]);
48837
+ const save = (0, react.useCallback)(async (id) => {
47334
48838
  setBusy(true);
47335
48839
  setError("");
47336
48840
  setNotice("");
47337
48841
  try {
47338
- const r = await api.knowledgeAdd({
47339
- title: title.trim(),
47340
- content: content.trim()
47341
- });
47342
- setDocs(r.docs);
47343
- setTitle("");
47344
- setContent("");
47345
- setNotice(" 已添加");
47346
- setAddMode(false);
47347
- const added = r.docs[r.docs.length - 1];
47348
- if (added !== void 0) setSelectedId(added.id);
47349
- } catch (e) {
47350
- setError(e.message);
47351
- } finally {
47352
- setBusy(false);
47353
- }
47354
- };
47355
- const remove = async (id) => {
47356
- setBusy(true);
47357
- setError("");
47358
- try {
47359
- const r = await api.knowledgeRemove(id);
47360
- setDocs(r.docs);
47361
- if (selectedId === id) setSelectedId(null);
47362
- } catch (e) {
47363
- setError(e.message);
48842
+ await api.promptSlotSet(id, drafts[id] ?? "");
48843
+ await refresh();
48844
+ setNotice((drafts[id] ?? "").trim() === "" ? "已清空该槽位(恢复默认)" : "已保存,下一次生成/审稿生效");
48845
+ } catch (err) {
48846
+ setError(err.message);
47364
48847
  } finally {
47365
48848
  setBusy(false);
47366
48849
  }
47367
- };
47368
- const selected = docs.find((d) => d.id === selectedId);
48850
+ }, [
48851
+ api,
48852
+ drafts,
48853
+ refresh
48854
+ ]);
48855
+ const dirty = (slot) => (drafts[slot.id] ?? "") !== slot.value;
47369
48856
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47370
- className: panel_module_css_default.authorPageBodyFlow,
47371
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47372
- className: panel_module_css_default.card,
47373
- style: {
47374
- padding: 0,
47375
- overflow: "hidden"
47376
- },
47377
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47378
- className: panel_module_css_default.t2Master,
47379
- style: { minHeight: 420 },
47380
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
47381
- className: panel_module_css_default.t2Rail,
47382
- "aria-label": "知识库文档",
48857
+ className: panel_module_css_default.card,
48858
+ style: { gap: "var(--nf-space-12)" },
48859
+ children: [
48860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48861
+ className: panel_module_css_default.row,
48862
+ style: {
48863
+ justifyContent: "space-between",
48864
+ alignItems: "center",
48865
+ flexWrap: "wrap",
48866
+ gap: "var(--nf-space-8)"
48867
+ },
48868
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48869
+ className: panel_module_css_default.cardTitle,
48870
+ children: "提示词槽位(本书)"
48871
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48872
+ className: panel_module_css_default.meta,
48873
+ children: "只追加偏好,不覆盖道藏 / 红线 / 合规"
48874
+ })]
48875
+ }),
48876
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48877
+ className: panel_module_css_default.meta,
48878
+ children: "这里的文字会追加到写作提示词末尾,用来微调表达方式。**规则类内容不可改**: 道藏、写作红线、内容合规红线、反 AI 规则、阶段契约由宿主锁定,槽位碰不到。"
48879
+ }),
48880
+ notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48881
+ style: {
48882
+ color: "var(--nf-success)",
48883
+ fontSize: "var(--nf-text-12)"
48884
+ },
48885
+ children: notice
48886
+ }),
48887
+ error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48888
+ style: {
48889
+ color: "var(--nf-error)",
48890
+ fontSize: "var(--nf-text-12)"
48891
+ },
48892
+ children: error
48893
+ }),
48894
+ slots.map((slot) => {
48895
+ const value = drafts[slot.id] ?? "";
48896
+ const over = value.length > slot.maxChars;
48897
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48898
+ style: {
48899
+ border: "1px solid var(--nf-border)",
48900
+ borderRadius: 8,
48901
+ padding: 8,
48902
+ display: "flex",
48903
+ flexDirection: "column",
48904
+ gap: 6
48905
+ },
47383
48906
  children: [
47384
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47385
- className: panel_module_css_default.t2RailHead,
47386
- children: " 书内知识库"
47387
- }),
47388
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
47389
- type: "button",
47390
- className: panel_module_css_default.t2RailBtn,
47391
- "data-active": addMode ? "" : void 0,
47392
- onClick: () => {
47393
- setAddMode(true);
47394
- setSelectedId(null);
48907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48908
+ className: panel_module_css_default.row,
48909
+ style: {
48910
+ justifyContent: "space-between",
48911
+ alignItems: "baseline",
48912
+ gap: "var(--nf-space-8)"
47395
48913
  },
47396
- title: "添加一篇参考文档(生成时按章节检索注入)",
47397
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47398
- "aria-hidden": true,
47399
- children: "+"
47400
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "新建文档" })]
48914
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
48915
+ style: { fontSize: "var(--nf-fs-12)" },
48916
+ children: slot.label
48917
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
48918
+ className: panel_module_css_default.meta,
48919
+ style: { color: over ? "var(--nf-error)" : void 0 },
48920
+ children: [
48921
+ value.length,
48922
+ " / ",
48923
+ slot.maxChars
48924
+ ]
48925
+ })]
47401
48926
  }),
47402
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.t2RailSep }),
47403
- docs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47404
- className: panel_module_css_default.t2RailMeta,
47405
- children: "暂无文档"
47406
- }) : docs.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
47407
- type: "button",
47408
- role: "tab",
47409
- "aria-selected": !addMode && selectedId === d.id,
47410
- className: panel_module_css_default.t2RailBtn,
47411
- "data-active": !addMode && selectedId === d.id ? "" : void 0,
47412
- title: d.content.slice(0, 80),
47413
- onClick: () => {
47414
- setAddMode(false);
47415
- setSelectedId(d.id);
48927
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
48928
+ className: panel_module_css_default.meta,
48929
+ children: slot.hint
48930
+ }),
48931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
48932
+ className: panel_module_css_default.textarea,
48933
+ rows: 3,
48934
+ value,
48935
+ placeholder: slot.placeholder,
48936
+ onChange: (e) => setDrafts((prev) => ({
48937
+ ...prev,
48938
+ [slot.id]: e.target.value
48939
+ }))
48940
+ }),
48941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48942
+ className: panel_module_css_default.row,
48943
+ style: {
48944
+ justifyContent: "flex-end",
48945
+ gap: 6
47416
48946
  },
47417
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": true }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47418
- style: {
47419
- flex: 1,
47420
- minWidth: 0,
47421
- overflow: "hidden",
47422
- textOverflow: "ellipsis",
47423
- whiteSpace: "nowrap"
48947
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48948
+ type: "button",
48949
+ className: panel_module_css_default.button,
48950
+ disabled: busy || value === "",
48951
+ onClick: () => {
48952
+ save(slot.id);
47424
48953
  },
47425
- children: d.title
48954
+ children: "清空"
48955
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
48956
+ type: "button",
48957
+ className: panel_module_css_default.button + " " + panel_module_css_default.buttonPrimary,
48958
+ disabled: busy || !dirty(slot),
48959
+ onClick: () => {
48960
+ save(slot.id);
48961
+ },
48962
+ children: "保存"
47426
48963
  })]
47427
- }, d.id)),
47428
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.t2RailSep }),
47429
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47430
- className: panel_module_css_default.t2RailMeta,
47431
- children: [
47432
- "共 ",
47433
- docs.length,
47434
- " 篇 · 正文生成时按当前章节检索注入"
47435
- ]
47436
48964
  })
47437
48965
  ]
47438
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47439
- className: panel_module_css_default.t2Content,
47440
- children: [addMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47441
- className: panel_module_css_default.cardTitleLg,
47442
- children: "+ 新建文档"
47443
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47444
- style: {
47445
- display: "flex",
47446
- flexDirection: "column",
47447
- gap: "var(--nf-space-6)",
47448
- maxWidth: "var(--nf-measure, 720px)"
47449
- },
47450
- children: [
47451
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
47452
- className: panel_module_css_default.input,
47453
- value: title,
47454
- onChange: (e) => setTitle(e.target.value),
47455
- placeholder: "标题(如:天机锁设定 / 止水驿关系表)"
47456
- }),
47457
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
47458
- className: panel_module_css_default.input,
47459
- style: {
47460
- minHeight: 160,
47461
- resize: "vertical"
47462
- },
47463
- value: content,
47464
- onChange: (e) => setContent(e.target.value),
47465
- placeholder: "内容(将被检索注入生成提示)"
47466
- }),
47467
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47468
- style: {
47469
- display: "flex",
47470
- alignItems: "center",
47471
- gap: "var(--nf-space-8)"
47472
- },
47473
- children: [
47474
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47475
- type: "button",
47476
- className: `${panel_module_css_default.button} ${panel_module_css_default.buttonPrimary}`,
47477
- disabled: busy,
47478
- onClick: () => {
47479
- add();
47480
- },
47481
- children: "添加"
47482
- }),
47483
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47484
- type: "button",
47485
- className: panel_module_css_default.button,
47486
- onClick: () => {
47487
- setAddMode(false);
47488
- },
47489
- children: "取消"
47490
- }),
47491
- notice !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47492
- style: {
47493
- fontSize: "var(--nf-fs-12)",
47494
- color: "var(--nf-success)"
47495
- },
47496
- children: notice
47497
- })
47498
- ]
47499
- })
47500
- ]
47501
- })] }) : selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47502
- className: panel_module_css_default.row,
47503
- style: {
47504
- justifyContent: "space-between",
47505
- flexWrap: "wrap"
47506
- },
47507
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
47508
- className: panel_module_css_default.cardTitleLg,
47509
- children: [" ", selected.title]
47510
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
47511
- type: "button",
47512
- className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
47513
- disabled: busy,
47514
- onClick: () => {
47515
- remove(selected.id);
47516
- },
47517
- children: "删除"
47518
- })]
47519
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47520
- className: `${panel_module_css_default.planBeats} ${panel_module_css_default.docsSurface}`,
47521
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47522
- style: {
47523
- fontSize: "var(--nf-fs-14)",
47524
- lineHeight: 1.7,
47525
- whiteSpace: "pre-wrap"
47526
- },
47527
- children: selected.content !== "" ? selected.content : "(空文档)"
47528
- })
47529
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
47530
- className: panel_module_css_default.planDetailEmpty,
47531
- style: { minHeight: 300 },
47532
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47533
- className: panel_module_css_default.meta,
47534
- children: "← 从左侧选择文档,或「+ 新建文档」添加参考资料。"
47535
- })
47536
- }), error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
47537
- style: {
47538
- color: "var(--nf-error)",
47539
- fontSize: "var(--nf-fs-14)"
47540
- },
47541
- children: [" ", error]
47542
- })]
47543
- })]
48966
+ }, slot.id);
47544
48967
  })
47545
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
47546
- className: panel_module_css_default.meta,
47547
- children: "自由参考文档:零散设定/资料/碎片放这里;结构化设定(世界观/角色/境界/红线)请放「本书设定」。"
47548
- })]
48968
+ ]
47549
48969
  });
47550
48970
  }
47551
48971
  //#endregion
@@ -48187,7 +49607,7 @@ window.__ModuleLoader__.load({
48187
49607
  run: "run",
48188
49608
  breakdown: "breakdown"
48189
49609
  };
48190
- /** The navigation groups (AI-Novel-Writing-Assistant style grouping). */
49610
+ /** 侧栏导航分组(按创作阶段归类)。 */
48191
49611
  const NAV_GROUPS = [
48192
49612
  {
48193
49613
  id: "dashboard",
@@ -48548,7 +49968,7 @@ window.__ModuleLoader__.load({
48548
49968
  * 章节工作台:**独立全页**(不塞进抽屉或主从布局,内容太重)。
48549
49969
  * 三栏并发 —— 左:本章节拍;中:正文;右:对比 / 审稿 / 复盘。
48550
49970
  */
48551
- function ChapterWorkbench({ chapter, text, draft, fontSize, busy, busyAny, onBack, onWrite, onReview, onApprove, onEdit, onRevisePicked, onReset, onRegenerate, onBackfillAuthor, tab, onTabChange, draftReport, onApplyDraft, onDiscardDraft, onCheckDraft }) {
49971
+ function ChapterWorkbench({ chapter, text, draft, fontSize, busy, busyAny, onBack, onWrite, onReview, onApprove, onEdit, onRevisePicked, onReset, onRegenerate, onBackfillAuthor, tab, onTabChange, draftReport, onApplyDraft, onDiscardDraft, onCheckDraft, snapshots, onRestoreSnapshot, onCreateSnapshot }) {
48552
49972
  const badge = statusBadge(chapter);
48553
49973
  const review = chapter.review;
48554
49974
  const author = chapter.authorReview;
@@ -48800,6 +50220,10 @@ window.__ModuleLoader__.load({
48800
50220
  {
48801
50221
  id: "author",
48802
50222
  label: "复盘"
50223
+ },
50224
+ {
50225
+ id: "versions",
50226
+ label: snapshots.length > 0 ? `历史版本 · ${snapshots.length}` : "历史版本"
48803
50227
  }
48804
50228
  ],
48805
50229
  active: tab,
@@ -48809,6 +50233,71 @@ window.__ModuleLoader__.load({
48809
50233
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48810
50234
  className: panel_module_css_default.chWorkSideBody,
48811
50235
  children: [
50236
+ tab === "versions" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
50237
+ style: {
50238
+ display: "flex",
50239
+ flexDirection: "column",
50240
+ gap: "var(--nf-space-8)"
50241
+ },
50242
+ children: [
50243
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
50244
+ className: panel_module_css_default.row,
50245
+ style: {
50246
+ justifyContent: "space-between",
50247
+ alignItems: "center"
50248
+ },
50249
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
50250
+ className: panel_module_css_default.meta,
50251
+ children: "覆盖写入前会自动留存;回滚也可再撤销。"
50252
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50253
+ type: "button",
50254
+ className: panel_module_css_default.button,
50255
+ onClick: onCreateSnapshot,
50256
+ children: "存档当前正文"
50257
+ })]
50258
+ }),
50259
+ snapshots.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
50260
+ className: panel_module_css_default.meta,
50261
+ children: "这一章还没有历史版本。"
50262
+ }),
50263
+ snapshots.slice().sort((a, b) => b.at.localeCompare(a.at)).map((snapshot) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
50264
+ style: {
50265
+ border: "1px solid var(--nf-border)",
50266
+ borderRadius: 8,
50267
+ padding: "6px 8px",
50268
+ fontSize: "var(--nf-fs-12)"
50269
+ },
50270
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
50271
+ style: {
50272
+ display: "flex",
50273
+ gap: 6,
50274
+ alignItems: "baseline",
50275
+ flexWrap: "wrap"
50276
+ },
50277
+ children: [
50278
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: new Date(snapshot.at).toLocaleString() }),
50279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
50280
+ className: panel_module_css_default.meta,
50281
+ children: snapshot.reason
50282
+ }),
50283
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
50284
+ className: panel_module_css_default.meta,
50285
+ children: [snapshot.chars, " 字"]
50286
+ }),
50287
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50288
+ type: "button",
50289
+ className: panel_module_css_default.button,
50290
+ style: { marginLeft: "auto" },
50291
+ disabled: busyAny,
50292
+ onClick: () => onRestoreSnapshot(snapshot.id),
50293
+ title: "用这一版覆盖当前正文(回滚前会先留存当前正文)",
50294
+ children: "回滚到这一版"
50295
+ })
50296
+ ]
50297
+ })
50298
+ }, snapshot.id))
50299
+ ]
50300
+ }),
48812
50301
  tab === "diff" && (hasDraft ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
48813
50302
  className: panel_module_css_default.chWorkDraftBar,
48814
50303
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -49062,6 +50551,10 @@ window.__ModuleLoader__.load({
49062
50551
  * 的聊天记录,而新消息已经发到另一本书里。
49063
50552
  */
49064
50553
  const [bookKey, setBookKey] = (0, react.useState)("");
50554
+ /** 导出范围(设置页导出卡片):整本正文 / 设定 / 规划 / 角色 / 质检记录 / 项目备份。 */
50555
+ const [exportScope, setExportScope] = (0, react.useState)("book");
50556
+ /** 章节历史版本(快照):打开工作台时拉取,回滚/存档后刷新。 */
50557
+ const [snapshots, setSnapshots] = (0, react.useState)([]);
49065
50558
  const [dragActive, setDragActive] = (0, react.useState)(false);
49066
50559
  const fileInputRef = (0, react.useRef)(null);
49067
50560
  const [planCount, setPlanCount] = (0, react.useState)(30);
@@ -49711,6 +51204,9 @@ window.__ModuleLoader__.load({
49711
51204
  if (status.project?.roleStatus !== void 0) setCharCards(status.project.roleStatus);
49712
51205
  else setCharCards(null);
49713
51206
  setGeneratedFiles(status.generatedFiles);
51207
+ try {
51208
+ setSnapshots((await api.snapshots()).snapshots);
51209
+ } catch {}
49714
51210
  const withDraft = status.project?.chapters.find((c) => c.pendingDraft !== void 0 && c.pendingDraft !== "");
49715
51211
  setDraftNo(withDraft?.no ?? null);
49716
51212
  const nextOutline = status.project?.outline;
@@ -51088,11 +52584,47 @@ window.__ModuleLoader__.load({
51088
52584
  }
51089
52585
  };
51090
52586
  /** Export the book. */
51091
- const handleExport = async (format) => {
52587
+ /** 刷新章节历史版本列表。 */
52588
+ const refreshSnapshots = async () => {
52589
+ try {
52590
+ setSnapshots((await api.snapshots()).snapshots);
52591
+ } catch {}
52592
+ };
52593
+ /** 回滚到某个历史版本(回滚前宿主会先留存当前正文,所以可撤销)。 */
52594
+ const handleRestoreSnapshot = async (id) => {
52595
+ if (!window.confirm("用这一版覆盖当前正文?回滚前会自动留存当前正文,可以再换回来。")) return;
52596
+ setBusy(true);
52597
+ setError("");
52598
+ try {
52599
+ await api.snapshotRestore(id);
52600
+ await refresh(false, true);
52601
+ await refreshSnapshots();
52602
+ setNotice("已回滚到所选版本");
52603
+ } catch (err) {
52604
+ setError(err.message);
52605
+ } finally {
52606
+ setBusy(false);
52607
+ }
52608
+ };
52609
+ /** 手工给当前章节存档一个历史版本。 */
52610
+ const handleCreateSnapshot = async (no) => {
52611
+ setBusy(true);
52612
+ setError("");
52613
+ try {
52614
+ await api.snapshotCreate(no);
52615
+ await refreshSnapshots();
52616
+ setNotice("已存档当前正文");
52617
+ } catch (err) {
52618
+ setError(err.message);
52619
+ } finally {
52620
+ setBusy(false);
52621
+ }
52622
+ };
52623
+ const handleExport = async (format, scope = "book") => {
51092
52624
  setBusy(true);
51093
52625
  setError("");
51094
52626
  try {
51095
- const result = await api.exportBook(format);
52627
+ const result = await api.exportBook(format, scope);
51096
52628
  setNotice(tt("settings.exported", {
51097
52629
  file: result.file,
51098
52630
  chars: result.chars,
@@ -51343,7 +52875,7 @@ window.__ModuleLoader__.load({
51343
52875
  };
51344
52876
  /** 侧栏当前书卡(demo 风格):书架激活书 → 封面首字 / 书名 / 进度。 */
51345
52877
  const activeBook = shelf?.books.find((b) => b.id === shelf?.activeBookId) ?? null;
51346
- /** 主行动卡片:推荐下一步(AI-Novel-Writing-Assistant 首页主卡模式)。 */
52878
+ /** 主行动卡片:同一刻只给作者一个「推荐下一步」。 */
51347
52879
  const nextAction = (0, react.useMemo)(() => {
51348
52880
  if (project === null) return {
51349
52881
  eyebrow: "开始你的第一本书",
@@ -51526,6 +53058,36 @@ window.__ModuleLoader__.load({
51526
53058
  openDrawer("run");
51527
53059
  }
51528
53060
  },
53061
+ {
53062
+ key: "tension",
53063
+ group: "tools",
53064
+ name: "张力曲线",
53065
+ di: chapters.length > 0 ? `● ${chapters.length} 章曲线` : "○ 需先排章节",
53066
+ dim: project === null,
53067
+ go: () => {
53068
+ openDrawer("tension");
53069
+ }
53070
+ },
53071
+ {
53072
+ key: "promptSlots",
53073
+ group: "tools",
53074
+ name: "提示词槽位",
53075
+ di: Object.keys(project?.promptSlots ?? {}).length > 0 ? `● 已设 ${Object.keys(project?.promptSlots ?? {}).length} 项` : "○ 未设置",
53076
+ dim: project === null,
53077
+ go: () => {
53078
+ openDrawer("promptSlots");
53079
+ }
53080
+ },
53081
+ {
53082
+ key: "timeline",
53083
+ group: "tools",
53084
+ name: "故事时间线",
53085
+ di: (project?.timeline ?? []).length > 0 ? `● ${(project?.timeline ?? []).length} 条事件` : "○ 未抽取",
53086
+ dim: project === null,
53087
+ go: () => {
53088
+ openDrawer("timeline");
53089
+ }
53090
+ },
51529
53091
  {
51530
53092
  key: "knowledge",
51531
53093
  group: "tools",
@@ -51843,6 +53405,13 @@ window.__ModuleLoader__.load({
51843
53405
  },
51844
53406
  onCheckDraft: () => {
51845
53407
  handleWbCheckDraft(workbench.no);
53408
+ },
53409
+ snapshots: snapshots.filter((s) => s.chapterNo === workbench.no),
53410
+ onRestoreSnapshot: (id) => {
53411
+ handleRestoreSnapshot(id);
53412
+ },
53413
+ onCreateSnapshot: () => {
53414
+ handleCreateSnapshot(workbench.no);
51846
53415
  }
51847
53416
  }),
51848
53417
  rightDrawer !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -53147,12 +54716,33 @@ window.__ModuleLoader__.load({
53147
54716
  }),
53148
54717
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53149
54718
  className: panel_module_css_default.row,
53150
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54719
+ style: {
54720
+ flexWrap: "wrap",
54721
+ gap: "var(--nf-space-8)"
54722
+ },
54723
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
54724
+ className: panel_module_css_default.input,
54725
+ value: exportScope,
54726
+ onChange: (e) => setExportScope(e.target.value),
54727
+ title: "选择导出范围",
54728
+ children: EXPORT_SCOPE_VALUES.map((scope) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
54729
+ value: scope,
54730
+ children: EXPORT_SCOPE_LABELS[scope]
54731
+ }, scope))
54732
+ }), exportScope === "project" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54733
+ type: "button",
54734
+ className: panel_module_css_default.button,
54735
+ disabled: busy,
54736
+ onClick: () => {
54737
+ handleExport("json", "project");
54738
+ },
54739
+ children: "导出项目备份 JSON"
54740
+ }) : exportScope === "book" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
53151
54741
  type: "button",
53152
54742
  className: panel_module_css_default.button,
53153
54743
  disabled: busy || chapters.length === 0,
53154
54744
  onClick: () => {
53155
- handleExport("txt");
54745
+ handleExport("txt", "book");
53156
54746
  },
53157
54747
  children: tt("settings.exportTxt")
53158
54748
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -53160,14 +54750,30 @@ window.__ModuleLoader__.load({
53160
54750
  className: panel_module_css_default.button,
53161
54751
  disabled: busy || chapters.length === 0,
53162
54752
  onClick: () => {
53163
- handleExport("md");
54753
+ handleExport("md", "book");
53164
54754
  },
53165
54755
  children: tt("settings.exportMd")
53166
- })]
54756
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54757
+ type: "button",
54758
+ className: panel_module_css_default.button,
54759
+ disabled: busy,
54760
+ onClick: () => {
54761
+ handleExport("txt", exportScope);
54762
+ },
54763
+ children: "导出 TXT"
54764
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54765
+ type: "button",
54766
+ className: panel_module_css_default.button,
54767
+ disabled: busy,
54768
+ onClick: () => {
54769
+ handleExport("md", exportScope);
54770
+ },
54771
+ children: "导出 Markdown"
54772
+ })] })]
53167
54773
  }),
53168
54774
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53169
54775
  className: panel_module_css_default.meta,
53170
- children: "导出当前书的全部已写章节。"
54776
+ children: exportScope === "project" ? "导出完整项目 JSON(含设定/规划/角色/事实库),用于备份或迁移到其它机器。" : exportScope === "book" ? "导出当前书的全部已写章节。" : `导出「${EXPORT_SCOPE_LABELS[exportScope]}」,不含正文,便于单独存档或交给他人。`
53171
54777
  })
53172
54778
  ]
53173
54779
  })
@@ -53953,6 +55559,12 @@ window.__ModuleLoader__.load({
53953
55559
  })
53954
55560
  }),
53955
55561
  rightDrawer === "knowledge" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KnowledgeBaseView, { api }),
55562
+ rightDrawer === "timeline" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TimelineView, {
55563
+ api,
55564
+ chapters: chapters.map((c) => c.no)
55565
+ }),
55566
+ rightDrawer === "tension" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TensionView, { api }),
55567
+ rightDrawer === "promptSlots" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PromptSlotsView, { api }),
53956
55568
  rightDrawer === "run" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RunPanel, {
53957
55569
  api,
53958
55570
  totalChapters: chapters.length