@waterwx/dsh-novel-forge 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -67,6 +67,8 @@ window.__ModuleLoader__.load({
67
67
  plotlines: "/api/dsh-novel-forge/plotlines",
68
68
  /** 角色库:AI 提炼 / 采纳 / 更新 / 删除。 */
69
69
  roles: "/api/dsh-novel-forge/roles",
70
+ scenes: "/api/dsh-novel-forge/scenes",
71
+ visualRules: "/api/dsh-novel-forge/visual-rules",
70
72
  /** 作者复盘补跑:对已写章节补齐 authorReview(全书流式 / 单章 JSON)。 */
71
73
  reviewBackfill: "/api/dsh-novel-forge/review/backfill",
72
74
  /** 章节复位:generating 卡死 → pending(可重新生成)。 */
@@ -80,9 +82,8 @@ window.__ModuleLoader__.load({
80
82
  /** 拆书分析:对已写章节做结构/人物/文风/卖点四维体检(两阶段:源笔记→分节分析)。 */
81
83
  breakdown: "/api/dsh-novel-forge/breakdown",
82
84
  /** 漫剧分镜生成:章节 → 角色锚点 + 分镜表(可适配豆包/Seedance/SD)。 */
83
- storyboard: "/api/dsh-novel-forge/storyboard",
84
85
  /** 漫剧分集计划:读一卷 → 按故事弧线分集(高潮拆集/过渡并章)。 */
85
- storyboardPlan: "/api/dsh-novel-forge/storyboard/plan",
86
+ /** 漫画脚本:章节 → 分页分格漫画脚本(含角色视觉锚点)。 */
86
87
  /** 生产单:启动批量生产(计划补足 + 逐章生成 + 被拒分级处理)。 */
87
88
  runStart: "/api/dsh-novel-forge/run/start",
88
89
  /** 生产单控制:pause / resume / stop。 */
@@ -155,23 +156,6 @@ window.__ModuleLoader__.load({
155
156
  budgetTokens
156
157
  });
157
158
  }
158
- /** 漫剧分镜生成:章节 → 角色锚点 + 分镜表。 */
159
- async storyboard(chapterNo, genre, platform, tool) {
160
- return postJson(NOVEL_API.storyboard, {
161
- chapterNo,
162
- genre,
163
- platform,
164
- tool
165
- });
166
- }
167
- /** 漫剧分集计划:读一卷 → 按故事弧线分集。 */
168
- async storyboardPlan(volumeNo, platform, maxEpisodes) {
169
- return postJson(NOVEL_API.storyboardPlan, {
170
- volumeNo,
171
- platform,
172
- maxEpisodes
173
- });
174
- }
175
159
  async plan(outline, chapterCount, volume) {
176
160
  return postJson(NOVEL_API.plan, {
177
161
  outline,
@@ -286,6 +270,14 @@ window.__ModuleLoader__.load({
286
270
  async roles(req) {
287
271
  return postJson(NOVEL_API.roles, req);
288
272
  }
273
+ /** 场景库:AI 提炼 / 采纳 / 更新 / 删除 / 图集。 */
274
+ async scenes(req) {
275
+ return postJson(NOVEL_API.scenes, req);
276
+ }
277
+ /** 视觉世界观规则:提炼 / 保存。 */
278
+ async visualRules(req) {
279
+ return postJson(NOVEL_API.visualRules, req);
280
+ }
289
281
  /** 小说简介:AI 生成/补全(partial 留空 = 全量),或手动保存。 */
290
282
  async blurb(action, text, partial) {
291
283
  return postJson(NOVEL_API.blurb, {
@@ -918,272 +910,272 @@ window.__ModuleLoader__.load({
918
910
  document.head.appendChild(tag);
919
911
  }
920
912
  var panel_module_css_default = {
921
- "toolSteps": "zE6uZW_toolSteps",
922
- "readerItemActive": "zE6uZW_readerItemActive",
923
- "bookCardCoverTitle": "zE6uZW_bookCardCoverTitle",
924
- "readerPara": "zE6uZW_readerPara",
925
- "navTitleName": "zE6uZW_navTitleName",
926
- "entryLabel": "zE6uZW_entryLabel",
927
- "progress": "zE6uZW_progress",
928
- "navGroup": "zE6uZW_navGroup",
929
- "outlineReadonly": "zE6uZW_outlineReadonly",
930
- "coverPreview": "zE6uZW_coverPreview",
931
- "createBookTop": "zE6uZW_createBookTop",
932
- "createBookCard": "zE6uZW_createBookCard",
933
- "dropzoneActive": "zE6uZW_dropzoneActive",
934
- "navTabBadgeLive": "zE6uZW_navTabBadgeLive",
935
- "navTabIcon": "zE6uZW_navTabIcon",
936
- "statRowD": "zE6uZW_statRowD",
937
- "bookCardProgressBar": "zE6uZW_bookCardProgressBar",
938
- "readerGroupArrow": "zE6uZW_readerGroupArrow",
939
- "progressBar": "zE6uZW_progressBar",
940
- "bookCreateForm": "zE6uZW_bookCreateForm",
941
- "panelContent": "zE6uZW_panelContent",
942
- "bookshelfLabel": "zE6uZW_bookshelfLabel",
943
- "toolStepDetail": "zE6uZW_toolStepDetail",
913
+ "workflowRow": "zE6uZW_workflowRow",
914
+ "genreDesc": "zE6uZW_genreDesc",
915
+ "readerTitle": "zE6uZW_readerTitle",
916
+ "diffTagOld": "zE6uZW_diffTagOld",
917
+ "dashFacts": "zE6uZW_dashFacts",
918
+ "toolStepStatus": "zE6uZW_toolStepStatus",
919
+ "readerLoading": "zE6uZW_readerLoading",
920
+ "volumeGroup": "zE6uZW_volumeGroup",
921
+ "fileList": "zE6uZW_fileList",
922
+ "chatScroll": "zE6uZW_chatScroll",
944
923
  "progressLineLive": "zE6uZW_progressLineLive",
945
- "bookCreateOutline": "zE6uZW_bookCreateOutline",
946
- "bookCardCoverFallback": "zE6uZW_bookCardCoverFallback",
924
+ "workflowDotDone": "zE6uZW_workflowDotDone",
925
+ "dashHeroActionTitle": "zE6uZW_dashHeroActionTitle",
926
+ "bookCardMetaRow": "zE6uZW_bookCardMetaRow",
927
+ "navGroupLabel": "zE6uZW_navGroupLabel",
928
+ "navTabBadgeDone": "zE6uZW_navTabBadgeDone",
929
+ "toolSteps": "zE6uZW_toolSteps",
930
+ "shelfTitleRow": "zE6uZW_shelfTitleRow",
947
931
  "buttonSmall": "zE6uZW_buttonSmall",
948
- "tab": "zE6uZW_tab",
949
- "toolStepError": "zE6uZW_toolStepError",
932
+ "bookChipMeta": "zE6uZW_bookChipMeta",
933
+ "row": "zE6uZW_row",
934
+ "readerItemBadge": "zE6uZW_readerItemBadge",
950
935
  "shelfEmpty": "zE6uZW_shelfEmpty",
951
- "toolStep": "zE6uZW_toolStep",
952
- "fileList": "zE6uZW_fileList",
953
- "wsSelected": "zE6uZW_wsSelected",
954
- "dashFact": "zE6uZW_dashFact",
955
- "chapterBeats": "zE6uZW_chapterBeats",
936
+ "readerSeg": "zE6uZW_readerSeg",
937
+ "fieldLabel": "zE6uZW_fieldLabel",
938
+ "tlSegTrack": "zE6uZW_tlSegTrack",
939
+ "spaceBetween": "zE6uZW_spaceBetween",
940
+ "chapterTitle": "zE6uZW_chapterTitle",
941
+ "bookCardBlurb": "zE6uZW_bookCardBlurb",
942
+ "ideaCard": "zE6uZW_ideaCard",
943
+ "bookAdd": "zE6uZW_bookAdd",
956
944
  "dropzoneIcon": "zE6uZW_dropzoneIcon",
945
+ "bookCardActions": "zE6uZW_bookCardActions",
946
+ "bookCardCoverTitle": "zE6uZW_bookCardCoverTitle",
947
+ "createBookTitle": "zE6uZW_createBookTitle",
948
+ "bookCardActive": "zE6uZW_bookCardActive",
949
+ "bookshelfList": "zE6uZW_bookshelfList",
950
+ "tlSegLabel": "zE6uZW_tlSegLabel",
951
+ "bookSwitch": "zE6uZW_bookSwitch",
952
+ "chapterMain": "zE6uZW_chapterMain",
953
+ "assetStatDetail": "zE6uZW_assetStatDetail",
954
+ "progressLineError": "zE6uZW_progressLineError",
957
955
  "iconButton": "zE6uZW_iconButton",
958
- "readerTitle": "zE6uZW_readerTitle",
959
- "view": "zE6uZW_view",
960
- "volumeGroupToggle": "zE6uZW_volumeGroupToggle",
961
- "bookCardProgressFill": "zE6uZW_bookCardProgressFill",
962
- "volumeGroupHeader": "zE6uZW_volumeGroupHeader",
963
- "badgeWritten": "zE6uZW_badgeWritten",
964
- "chatRole": "zE6uZW_chatRole",
965
- "bigProgressBarFill": "zE6uZW_bigProgressBarFill",
966
- "toolLive": "zE6uZW_toolLive",
967
- "statCardDLabel": "zE6uZW_statCardDLabel",
968
- "readerGroupTitle": "zE6uZW_readerGroupTitle",
969
- "readerSegBtn": "zE6uZW_readerSegBtn",
970
- "assetStatLabel": "zE6uZW_assetStatLabel",
971
- "toolStepIcon": "zE6uZW_toolStepIcon",
972
- "dropzone": "zE6uZW_dropzone",
973
- "navSpacer": "zE6uZW_navSpacer",
974
- "dashHero": "zE6uZW_dashHero",
975
- "bookChipMeta": "zE6uZW_bookChipMeta",
976
- "bookshelf": "zE6uZW_bookshelf",
977
- "chapterPreview": "zE6uZW_chapterPreview",
978
- "chapterActions": "zE6uZW_chapterActions",
979
- "readerError": "zE6uZW_readerError",
980
- "diffTagOld": "zE6uZW_diffTagOld",
981
- "statCardDDown": "zE6uZW_statCardDDown",
982
- "workflowDot": "zE6uZW_workflowDot",
956
+ "dashJourneyStages": "zE6uZW_dashJourneyStages",
983
957
  "readerItem": "zE6uZW_readerItem",
984
- "navGroupSep": "zE6uZW_navGroupSep",
985
- "buttonDanger": "zE6uZW_buttonDanger",
986
- "dashJourney": "zE6uZW_dashJourney",
987
- "createBookView": "zE6uZW_createBookView",
988
- "navActionBtn": "zE6uZW_navActionBtn",
989
- "dashHeroActionBody": "zE6uZW_dashHeroActionBody",
990
- "workflowDotActive": "zE6uZW_workflowDotActive",
991
- "progressLineError": "zE6uZW_progressLineError",
992
- "card": "zE6uZW_card",
993
- "todoDanger": "zE6uZW_todoDanger",
994
- "dashHeroAction": "zE6uZW_dashHeroAction",
995
- "cardTitle": "zE6uZW_cardTitle",
996
- "wsEditor": "zE6uZW_wsEditor",
997
- "ideaCard": "zE6uZW_ideaCard",
998
- "wsPreviewText": "zE6uZW_wsPreviewText",
999
- "bookAddIcon": "zE6uZW_bookAddIcon",
1000
- "shelfGrid": "zE6uZW_shelfGrid",
1001
- "row": "zE6uZW_row",
1002
- "busyRow": "zE6uZW_busyRow",
1003
- "assistantStatus": "zE6uZW_assistantStatus",
1004
- "statCardD": "zE6uZW_statCardD",
1005
- "bookChipName": "zE6uZW_bookChipName",
1006
- "fadeIn": "zE6uZW_fadeIn",
1007
- "chapter": "zE6uZW_chapter",
1008
- "readerChapterTitle": "zE6uZW_readerChapterTitle",
1009
- "readerGroup": "zE6uZW_readerGroup",
1010
- "readerItemNo": "zE6uZW_readerItemNo",
958
+ "buttonPrimary": "zE6uZW_buttonPrimary",
959
+ "panelTitle": "zE6uZW_panelTitle",
960
+ "todoInfo": "zE6uZW_todoInfo",
961
+ "toolStepActive": "zE6uZW_toolStepActive",
962
+ "wsPreview": "zE6uZW_wsPreview",
963
+ "dashStageDone": "zE6uZW_dashStageDone",
964
+ "progress": "zE6uZW_progress",
965
+ "workflowLabel": "zE6uZW_workflowLabel",
966
+ "panel": "zE6uZW_panel",
967
+ "bookCard": "zE6uZW_bookCard",
968
+ "readerSegActive": "zE6uZW_readerSegActive",
1011
969
  "toolStepName": "zE6uZW_toolStepName",
1012
- "bookChipRemove": "zE6uZW_bookChipRemove",
1013
- "badgeRejected": "zE6uZW_badgeRejected",
970
+ "panelNav": "zE6uZW_panelNav",
971
+ "bookCardProgressBar": "zE6uZW_bookCardProgressBar",
972
+ "navAboutUpdate": "zE6uZW_navAboutUpdate",
973
+ "field": "zE6uZW_field",
974
+ "readerGroupArrow": "zE6uZW_readerGroupArrow",
975
+ "coverPlaceholder": "zE6uZW_coverPlaceholder",
976
+ "meta": "zE6uZW_meta",
977
+ "bookshelfLabel": "zE6uZW_bookshelfLabel",
1014
978
  "button": "zE6uZW_button",
1015
- "diffLegend": "zE6uZW_diffLegend",
1016
- "navBadgePulse": "zE6uZW_navBadgePulse",
1017
- "assetStatValue": "zE6uZW_assetStatValue",
1018
- "shelfTitleRow": "zE6uZW_shelfTitleRow",
1019
- "tlSegLabel": "zE6uZW_tlSegLabel",
1020
- "dashJourneyStages": "zE6uZW_dashJourneyStages",
1021
- "ideaCandidate": "zE6uZW_ideaCandidate",
1022
- "workflowList": "zE6uZW_workflowList",
1023
- "readerBody": "zE6uZW_readerBody",
1024
- "workflowBody": "zE6uZW_workflowBody",
1025
- "ideaToggle": "zE6uZW_ideaToggle",
1026
- "wsPage": "zE6uZW_wsPage",
1027
- "todoText": "zE6uZW_todoText",
1028
- "bookAdd": "zE6uZW_bookAdd",
1029
- "chapterList": "zE6uZW_chapterList",
1030
- "bookSwitchMeta": "zE6uZW_bookSwitchMeta",
1031
- "reviewBox": "zE6uZW_reviewBox",
1032
- "assetGrid": "zE6uZW_assetGrid",
1033
- "entryIcon": "zE6uZW_entryIcon",
1034
- "readerItemTitle": "zE6uZW_readerItemTitle",
1035
- "panelNavCollapsed": "zE6uZW_panelNavCollapsed",
1036
- "navGroupLabel": "zE6uZW_navGroupLabel",
979
+ "badgeError": "zE6uZW_badgeError",
980
+ "assistantFloatHeader": "zE6uZW_assistantFloatHeader",
1037
981
  "wsSelectedText": "zE6uZW_wsSelectedText",
982
+ "bookCardTitleRow": "zE6uZW_bookCardTitleRow",
983
+ "createBookCard": "zE6uZW_createBookCard",
984
+ "onlyChanges": "zE6uZW_onlyChanges",
985
+ "panelBody": "zE6uZW_panelBody",
1038
986
  "navTabBadgeWarn": "zE6uZW_navTabBadgeWarn",
1039
- "chapterMain": "zE6uZW_chapterMain",
1040
- "assetStatDetail": "zE6uZW_assetStatDetail",
1041
- "workflowLabel": "zE6uZW_workflowLabel",
1042
- "bookCard": "zE6uZW_bookCard",
1043
- "diffText": "zE6uZW_diffText",
1044
- "workflowHint": "zE6uZW_workflowHint",
1045
- "tlSeg": "zE6uZW_tlSeg",
1046
- "assistantResize": "zE6uZW_assistantResize",
1047
- "readerHeader": "zE6uZW_readerHeader",
1048
- "diffTagNew": "zE6uZW_diffTagNew",
1049
- "pulse": "zE6uZW_pulse",
1050
- "assistantFloat": "zE6uZW_assistantFloat",
987
+ "tlSegFill": "zE6uZW_tlSegFill",
988
+ "workflowDot": "zE6uZW_workflowDot",
989
+ "progressBarFill": "zE6uZW_progressBarFill",
990
+ "readerGroup": "zE6uZW_readerGroup",
991
+ "statCardDValue": "zE6uZW_statCardDValue",
992
+ "diffOld": "zE6uZW_diffOld",
993
+ "bookChipRemove": "zE6uZW_bookChipRemove",
994
+ "navTabBadgeLive": "zE6uZW_navTabBadgeLive",
995
+ "bookSwitchArrow": "zE6uZW_bookSwitchArrow",
1051
996
  "tlSegCurrent": "zE6uZW_tlSegCurrent",
1052
- "volumeGroup": "zE6uZW_volumeGroup",
1053
- "assistantFloatBody": "zE6uZW_assistantFloatBody",
1054
- "bookAddCard": "zE6uZW_bookAddCard",
997
+ "dropzoneActive": "zE6uZW_dropzoneActive",
998
+ "chapterFocus": "zE6uZW_chapterFocus",
999
+ "chatRole": "zE6uZW_chatRole",
1000
+ "toolStepIcon": "zE6uZW_toolStepIcon",
1001
+ "readerItemTitle": "zE6uZW_readerItemTitle",
1002
+ "chapterNum": "zE6uZW_chapterNum",
1003
+ "statCardDIcon": "zE6uZW_statCardDIcon",
1004
+ "assistantResize": "zE6uZW_assistantResize",
1005
+ "assistantStatusBusy": "zE6uZW_assistantStatusBusy",
1055
1006
  "shelfView": "zE6uZW_shelfView",
1056
- "assetStat": "zE6uZW_assetStat",
1057
- "dashHeroSparkle": "zE6uZW_dashHeroSparkle",
1007
+ "progressBar": "zE6uZW_progressBar",
1008
+ "assistantFloat": "zE6uZW_assistantFloat",
1058
1009
  "dashGrid": "zE6uZW_dashGrid",
1059
- "bookCardCover": "zE6uZW_bookCardCover",
1060
- "navTabBadgeDone": "zE6uZW_navTabBadgeDone",
1061
- "navAbout": "zE6uZW_navAbout",
1062
- "shelfToolbar": "zE6uZW_shelfToolbar",
1010
+ "navTitleName": "zE6uZW_navTitleName",
1011
+ "assistantStatus": "zE6uZW_assistantStatus",
1012
+ "navTitle": "zE6uZW_navTitle",
1013
+ "assetStat": "zE6uZW_assetStat",
1014
+ "assistantFloatBody": "zE6uZW_assistantFloatBody",
1015
+ "busyRow": "zE6uZW_busyRow",
1016
+ "badgeDone": "zE6uZW_badgeDone",
1017
+ "legendOld": "zE6uZW_legendOld",
1018
+ "tab": "zE6uZW_tab",
1019
+ "reviewBox": "zE6uZW_reviewBox",
1020
+ "bookAddIcon": "zE6uZW_bookAddIcon",
1021
+ "dashFact": "zE6uZW_dashFact",
1022
+ "badgeRejected": "zE6uZW_badgeRejected",
1023
+ "chatBubbleAssistant": "zE6uZW_chatBubbleAssistant",
1024
+ "navGroup": "zE6uZW_navGroup",
1063
1025
  "navTitleBook": "zE6uZW_navTitleBook",
1064
- "navTabBadge": "zE6uZW_navTabBadge",
1065
- "dashStageCurrent": "zE6uZW_dashStageCurrent",
1066
- "readerSidebar": "zE6uZW_readerSidebar",
1067
- "readerItemBadge": "zE6uZW_readerItemBadge",
1068
- "panelBody": "zE6uZW_panelBody",
1069
- "navAboutUpdate": "zE6uZW_navAboutUpdate",
1070
- "shelfEmptyIcon": "zE6uZW_shelfEmptyIcon",
1071
- "bigProgressBar": "zE6uZW_bigProgressBar",
1072
- "dashJourneyBar": "zE6uZW_dashJourneyBar",
1073
- "diffColumn": "zE6uZW_diffColumn",
1074
- "diffAdd": "zE6uZW_diffAdd",
1075
- "tlSegTrack": "zE6uZW_tlSegTrack",
1076
- "tlSegTodo": "zE6uZW_tlSegTodo",
1077
- "readerFoot": "zE6uZW_readerFoot",
1078
- "coverPlaceholder": "zE6uZW_coverPlaceholder",
1079
- "textarea": "zE6uZW_textarea",
1080
- "dashStageDone": "zE6uZW_dashStageDone",
1081
- "liveText": "zE6uZW_liveText",
1026
+ "diffDel": "zE6uZW_diffDel",
1027
+ "bookCardCover": "zE6uZW_bookCardCover",
1028
+ "todoText": "zE6uZW_todoText",
1082
1029
  "bookSwitchName": "zE6uZW_bookSwitchName",
1083
- "shelfFilters": "zE6uZW_shelfFilters",
1084
- "bookCardBlurb": "zE6uZW_bookCardBlurb",
1085
- "toolStepStatus": "zE6uZW_toolStepStatus",
1086
- "wsColumn": "zE6uZW_wsColumn",
1087
- "todoInfo": "zE6uZW_todoInfo",
1088
- "bookCardMetaRow": "zE6uZW_bookCardMetaRow",
1089
- "workflowDotDone": "zE6uZW_workflowDotDone",
1030
+ "bookCardName": "zE6uZW_bookCardName",
1031
+ "assetStatLabel": "zE6uZW_assetStatLabel",
1032
+ "navTabBadgeDanger": "zE6uZW_navTabBadgeDanger",
1033
+ "tlBar": "zE6uZW_tlBar",
1034
+ "workflowHint": "zE6uZW_workflowHint",
1035
+ "bookCreateForm": "zE6uZW_bookCreateForm",
1036
+ "readerItemActive": "zE6uZW_readerItemActive",
1037
+ "fadeIn": "zE6uZW_fadeIn",
1038
+ "bookshelf": "zE6uZW_bookshelf",
1039
+ "pulse": "zE6uZW_pulse",
1040
+ "assetGrid": "zE6uZW_assetGrid",
1041
+ "statCardDDown": "zE6uZW_statCardDDown",
1090
1042
  "input": "zE6uZW_input",
1091
- "legendOld": "zE6uZW_legendOld",
1092
- "dashHeroArrow": "zE6uZW_dashHeroArrow",
1093
- "bookCardTitleRow": "zE6uZW_bookCardTitleRow",
1043
+ "dashJourneyFill": "zE6uZW_dashJourneyFill",
1044
+ "readerPara": "zE6uZW_readerPara",
1045
+ "badgeWritten": "zE6uZW_badgeWritten",
1046
+ "badgeGenerating": "zE6uZW_badgeGenerating",
1047
+ "volumeGroupHeader": "zE6uZW_volumeGroupHeader",
1048
+ "view": "zE6uZW_view",
1049
+ "diffText": "zE6uZW_diffText",
1050
+ "navBadgePulse": "zE6uZW_navBadgePulse",
1051
+ "cardTitle": "zE6uZW_cardTitle",
1052
+ "dashHeroAction": "zE6uZW_dashHeroAction",
1053
+ "toolStepError": "zE6uZW_toolStepError",
1054
+ "badge": "zE6uZW_badge",
1055
+ "diffSameBody": "zE6uZW_diffSameBody",
1056
+ "readerError": "zE6uZW_readerError",
1057
+ "ideaToggle": "zE6uZW_ideaToggle",
1058
+ "entryIcon": "zE6uZW_entryIcon",
1059
+ "workflowList": "zE6uZW_workflowList",
1060
+ "panelContent": "zE6uZW_panelContent",
1094
1061
  "shelfSearch": "zE6uZW_shelfSearch",
1062
+ "shelfEmptyIcon": "zE6uZW_shelfEmptyIcon",
1063
+ "chapterPreview": "zE6uZW_chapterPreview",
1095
1064
  "wsColumns": "zE6uZW_wsColumns",
1096
- "readerArticle": "zE6uZW_readerArticle",
1097
- "badgeGenerating": "zE6uZW_badgeGenerating",
1098
- "panelTitle": "zE6uZW_panelTitle",
1099
- "fieldLabel": "zE6uZW_fieldLabel",
1100
- "readerSegActive": "zE6uZW_readerSegActive",
1101
- "diffDel": "zE6uZW_diffDel",
1102
- "bookCreateCard": "zE6uZW_bookCreateCard",
1103
- "readerMain": "zE6uZW_readerMain",
1104
- "dashFacts": "zE6uZW_dashFacts",
1105
- "todoWarning": "zE6uZW_todoWarning",
1106
- "shelfHeader": "zE6uZW_shelfHeader",
1107
- "tlSegFill": "zE6uZW_tlSegFill",
1108
- "createBookTitle": "zE6uZW_createBookTitle",
1109
- "bookSwitchArrow": "zE6uZW_bookSwitchArrow",
1110
- "bookSwitchCover": "zE6uZW_bookSwitchCover",
1111
- "dashHeroBook": "zE6uZW_dashHeroBook",
1112
- "progressLine": "zE6uZW_progressLine",
1113
- "tlBar": "zE6uZW_tlBar",
1065
+ "tlSegDone": "zE6uZW_tlSegDone",
1066
+ "dashHeroArrow": "zE6uZW_dashHeroArrow",
1067
+ "ideaCandidate": "zE6uZW_ideaCandidate",
1068
+ "readerChapterTitle": "zE6uZW_readerChapterTitle",
1069
+ "progressLineDone": "zE6uZW_progressLineDone",
1070
+ "wsPage": "zE6uZW_wsPage",
1071
+ "badgePending": "zE6uZW_badgePending",
1072
+ "dashHeroEyebrow": "zE6uZW_dashHeroEyebrow",
1073
+ "readerSegBtn": "zE6uZW_readerSegBtn",
1074
+ "toolStepDetail": "zE6uZW_toolStepDetail",
1114
1075
  "dashStageDot": "zE6uZW_dashStageDot",
1115
- "toolStepActive": "zE6uZW_toolStepActive",
1116
- "statCardDIcon": "zE6uZW_statCardDIcon",
1076
+ "toolStep": "zE6uZW_toolStep",
1077
+ "progressLine": "zE6uZW_progressLine",
1117
1078
  "diffList": "zE6uZW_diffList",
1118
- "dashHeroActionTitle": "zE6uZW_dashHeroActionTitle",
1119
- "navActions": "zE6uZW_navActions",
1120
- "assistantFloatHeader": "zE6uZW_assistantFloatHeader",
1121
- "workflowRow": "zE6uZW_workflowRow",
1122
- "navTab": "zE6uZW_navTab",
1123
- "todoItem": "zE6uZW_todoItem",
1124
- "statCardDUnit": "zE6uZW_statCardDUnit",
1125
- "tabBar": "zE6uZW_tabBar",
1126
- "badgePending": "zE6uZW_badgePending",
1127
- "badgeDone": "zE6uZW_badgeDone",
1128
- "diffSameBody": "zE6uZW_diffSameBody",
1129
- "meta": "zE6uZW_meta",
1130
- "navTabLabel": "zE6uZW_navTabLabel",
1131
- "bookCardName": "zE6uZW_bookCardName",
1079
+ "buttonDanger": "zE6uZW_buttonDanger",
1080
+ "diffColumn": "zE6uZW_diffColumn",
1081
+ "dashJourney": "zE6uZW_dashJourney",
1082
+ "bookCreateOutline": "zE6uZW_bookCreateOutline",
1083
+ "readerSidebar": "zE6uZW_readerSidebar",
1084
+ "readerItemNo": "zE6uZW_readerItemNo",
1085
+ "createBookTop": "zE6uZW_createBookTop",
1086
+ "bookCardCoverFallback": "zE6uZW_bookCardCoverFallback",
1087
+ "createBookIcon": "zE6uZW_createBookIcon",
1132
1088
  "diffChange": "zE6uZW_diffChange",
1133
- "wsPreview": "zE6uZW_wsPreview",
1134
- "navTitle": "zE6uZW_navTitle",
1135
- "statCardDValue": "zE6uZW_statCardDValue",
1136
- "chapterNum": "zE6uZW_chapterNum",
1137
- "bookshelfList": "zE6uZW_bookshelfList",
1138
- "readerChapterMeta": "zE6uZW_readerChapterMeta",
1139
- "dashStage": "zE6uZW_dashStage",
1140
- "bookChipActive": "zE6uZW_bookChipActive",
1141
- "buttonPrimary": "zE6uZW_buttonPrimary",
1142
- "chapterTitle": "zE6uZW_chapterTitle",
1143
- "navAboutRow": "zE6uZW_navAboutRow",
1144
- "wsAppliedBanner": "zE6uZW_wsAppliedBanner",
1145
- "bookCardActive": "zE6uZW_bookCardActive",
1146
1089
  "statCardDDetail": "zE6uZW_statCardDDetail",
1147
- "badge": "zE6uZW_badge",
1148
- "genreDesc": "zE6uZW_genreDesc",
1149
- "statCardDIconRed": "zE6uZW_statCardDIconRed",
1150
- "progressLineDone": "zE6uZW_progressLineDone",
1151
- "readerLoading": "zE6uZW_readerLoading",
1152
- "assistantStatusBusy": "zE6uZW_assistantStatusBusy",
1090
+ "tlSeg": "zE6uZW_tlSeg",
1091
+ "navActions": "zE6uZW_navActions",
1092
+ "shelfToolbar": "zE6uZW_shelfToolbar",
1093
+ "readerGroupCount": "zE6uZW_readerGroupCount",
1094
+ "shelfFilters": "zE6uZW_shelfFilters",
1095
+ "statRowD": "zE6uZW_statRowD",
1096
+ "tlSegTodo": "zE6uZW_tlSegTodo",
1097
+ "bigProgressBarFill": "zE6uZW_bigProgressBarFill",
1153
1098
  "legendNew": "zE6uZW_legendNew",
1154
- "bookCardBody": "zE6uZW_bookCardBody",
1155
- "chatBubbleAssistant": "zE6uZW_chatBubbleAssistant",
1156
- "navTabBadgeDanger": "zE6uZW_navTabBadgeDanger",
1099
+ "todoDanger": "zE6uZW_todoDanger",
1100
+ "panelNavCollapsed": "zE6uZW_panelNavCollapsed",
1101
+ "navSpacer": "zE6uZW_navSpacer",
1102
+ "bigProgressBar": "zE6uZW_bigProgressBar",
1103
+ "shelfHeader": "zE6uZW_shelfHeader",
1157
1104
  "wsPageHeader": "zE6uZW_wsPageHeader",
1158
- "bookChip": "zE6uZW_bookChip",
1159
- "bookCardActions": "zE6uZW_bookCardActions",
1160
- "readerGroupCount": "zE6uZW_readerGroupCount",
1161
- "diffNew": "zE6uZW_diffNew",
1162
- "bookSwitch": "zE6uZW_bookSwitch",
1163
- "chatBubbleUser": "zE6uZW_chatBubbleUser",
1164
- "tlSegDone": "zE6uZW_tlSegDone",
1165
- "chapterFocusPulse": "zE6uZW_chapterFocusPulse",
1166
- "dashHeroEyebrow": "zE6uZW_dashHeroEyebrow",
1167
- "chapterFocus": "zE6uZW_chapterFocus",
1168
- "chatScroll": "zE6uZW_chatScroll",
1169
- "badgeError": "zE6uZW_badgeError",
1170
- "shelfEmptyTitle": "zE6uZW_shelfEmptyTitle",
1105
+ "dashJourneyBar": "zE6uZW_dashJourneyBar",
1106
+ "liveText": "zE6uZW_liveText",
1171
1107
  "readerView": "zE6uZW_readerView",
1172
- "navTitleLogo": "zE6uZW_navTitleLogo",
1108
+ "navAbout": "zE6uZW_navAbout",
1109
+ "chapterFocusPulse": "zE6uZW_chapterFocusPulse",
1110
+ "bookSwitchCover": "zE6uZW_bookSwitchCover",
1111
+ "readerBody": "zE6uZW_readerBody",
1112
+ "coverPreview": "zE6uZW_coverPreview",
1113
+ "readerMain": "zE6uZW_readerMain",
1114
+ "wsEditor": "zE6uZW_wsEditor",
1115
+ "bookCardBody": "zE6uZW_bookCardBody",
1116
+ "bookCreateCard": "zE6uZW_bookCreateCard",
1117
+ "dashHeroActionBody": "zE6uZW_dashHeroActionBody",
1118
+ "readerArticle": "zE6uZW_readerArticle",
1119
+ "dashStage": "zE6uZW_dashStage",
1120
+ "wsSelected": "zE6uZW_wsSelected",
1121
+ "todoItem": "zE6uZW_todoItem",
1122
+ "navTabIcon": "zE6uZW_navTabIcon",
1123
+ "textarea": "zE6uZW_textarea",
1124
+ "bookSwitchMeta": "zE6uZW_bookSwitchMeta",
1125
+ "tabBar": "zE6uZW_tabBar",
1126
+ "statCardDIconRed": "zE6uZW_statCardDIconRed",
1127
+ "chatBubbleUser": "zE6uZW_chatBubbleUser",
1128
+ "dashHeroSparkle": "zE6uZW_dashHeroSparkle",
1129
+ "toolLive": "zE6uZW_toolLive",
1130
+ "dashHeroBook": "zE6uZW_dashHeroBook",
1131
+ "diffTagNew": "zE6uZW_diffTagNew",
1132
+ "wsColumn": "zE6uZW_wsColumn",
1173
1133
  "dashHeroTitle": "zE6uZW_dashHeroTitle",
1174
- "onlyChanges": "zE6uZW_onlyChanges",
1175
- "panelNav": "zE6uZW_panelNav",
1176
- "field": "zE6uZW_field",
1134
+ "createBookView": "zE6uZW_createBookView",
1135
+ "bookAddCard": "zE6uZW_bookAddCard",
1136
+ "diffLegend": "zE6uZW_diffLegend",
1137
+ "wsPreviewText": "zE6uZW_wsPreviewText",
1138
+ "navAboutRow": "zE6uZW_navAboutRow",
1139
+ "chapterBeats": "zE6uZW_chapterBeats",
1140
+ "bookChip": "zE6uZW_bookChip",
1141
+ "diffAdd": "zE6uZW_diffAdd",
1142
+ "card": "zE6uZW_card",
1143
+ "readerText": "zE6uZW_readerText",
1144
+ "readerFoot": "zE6uZW_readerFoot",
1177
1145
  "entry": "zE6uZW_entry",
1146
+ "navTab": "zE6uZW_navTab",
1147
+ "chapterList": "zE6uZW_chapterList",
1148
+ "navGroupSep": "zE6uZW_navGroupSep",
1149
+ "readerChapterMeta": "zE6uZW_readerChapterMeta",
1150
+ "readerHeader": "zE6uZW_readerHeader",
1151
+ "outlineReadonly": "zE6uZW_outlineReadonly",
1152
+ "navTitleLogo": "zE6uZW_navTitleLogo",
1153
+ "diffNew": "zE6uZW_diffNew",
1154
+ "shelfGrid": "zE6uZW_shelfGrid",
1155
+ "workflowDotActive": "zE6uZW_workflowDotActive",
1156
+ "dashStageCurrent": "zE6uZW_dashStageCurrent",
1157
+ "statCardDUnit": "zE6uZW_statCardDUnit",
1158
+ "workflowBody": "zE6uZW_workflowBody",
1159
+ "navActionBtn": "zE6uZW_navActionBtn",
1160
+ "chapterActions": "zE6uZW_chapterActions",
1161
+ "dashHero": "zE6uZW_dashHero",
1162
+ "navTabLabel": "zE6uZW_navTabLabel",
1163
+ "bookChipName": "zE6uZW_bookChipName",
1164
+ "entryLabel": "zE6uZW_entryLabel",
1165
+ "wsAppliedBanner": "zE6uZW_wsAppliedBanner",
1166
+ "todoWarning": "zE6uZW_todoWarning",
1167
+ "assetStatValue": "zE6uZW_assetStatValue",
1168
+ "readerGroupTitle": "zE6uZW_readerGroupTitle",
1169
+ "statCardDLabel": "zE6uZW_statCardDLabel",
1170
+ "bookCardProgressFill": "zE6uZW_bookCardProgressFill",
1171
+ "navTabBadge": "zE6uZW_navTabBadge",
1172
+ "statCardD": "zE6uZW_statCardD",
1178
1173
  "diffSame": "zE6uZW_diffSame",
1179
- "diffOld": "zE6uZW_diffOld",
1180
- "dashJourneyFill": "zE6uZW_dashJourneyFill",
1181
- "spaceBetween": "zE6uZW_spaceBetween",
1182
- "readerText": "zE6uZW_readerText",
1183
- "readerSeg": "zE6uZW_readerSeg",
1184
- "panel": "zE6uZW_panel",
1185
- "progressBarFill": "zE6uZW_progressBarFill",
1186
- "createBookIcon": "zE6uZW_createBookIcon"
1174
+ "chapter": "zE6uZW_chapter",
1175
+ "dropzone": "zE6uZW_dropzone",
1176
+ "bookChipActive": "zE6uZW_bookChipActive",
1177
+ "shelfEmptyTitle": "zE6uZW_shelfEmptyTitle",
1178
+ "volumeGroupToggle": "zE6uZW_volumeGroupToggle"
1187
1179
  };
1188
1180
  //#endregion
1189
1181
  //#region src/client/panel/AssistantTab.tsx
@@ -5567,9 +5559,14 @@ window.__ModuleLoader__.load({
5567
5559
  icon: "🔍"
5568
5560
  },
5569
5561
  {
5570
- id: "storyboard",
5571
- label: "漫剧工坊",
5572
- icon: "🎬"
5562
+ id: "roleImage",
5563
+ label: "角色形象",
5564
+ icon: "🖼️"
5565
+ },
5566
+ {
5567
+ id: "scenes",
5568
+ label: "场景库",
5569
+ icon: "🏞️"
5573
5570
  },
5574
5571
  {
5575
5572
  id: "run",
@@ -5607,7 +5604,7 @@ window.__ModuleLoader__.load({
5607
5604
  "high",
5608
5605
  "max"
5609
5606
  ];
5610
- const PLUGIN_VERSION = "1.2.0";
5607
+ const PLUGIN_VERSION = "1.3.0";
5611
5608
  /** GitHub 仓库地址(关于区块点击跳转)。 */
5612
5609
  const REPO_URL = "https://github.com/watersxya/dsh-novel-forge";
5613
5610
  /** Whether any chapter is being generated right now. */
@@ -6036,16 +6033,23 @@ window.__ModuleLoader__.load({
6036
6033
  const [breakdownResult, setBreakdownResult] = (0, react.useState)(null);
6037
6034
  const [breakdownScope, setBreakdownScope] = (0, react.useState)("recent");
6038
6035
  const [breakdownPreset, setBreakdownPreset] = (0, react.useState)("quick");
6039
- /** 漫剧分镜结果(null = 未生成)。 */
6040
- const [storyboardResult, setStoryboardResult] = (0, react.useState)(null);
6041
- const [storyboardTool, setStoryboardTool] = (0, react.useState)("doubao");
6042
- /** 漫剧工坊当前选中的章节号。 */
6043
- const [storyboardChapterNo, setStoryboardChapterNo] = (0, react.useState)(0);
6044
- /** 漫剧分集计划结果(null = 未生成)。 */
6045
- const [storyboardPlanResult, setStoryboardPlanResult] = (0, react.useState)(null);
6046
- const [storyboardPlanVolume, setStoryboardPlanVolume] = (0, react.useState)(1);
6047
- /** 漫剧工坊子视图:single=单集分镜 / plan=按卷规划。 */
6048
- const [storyboardView, setStoryboardView] = (0, react.useState)("single");
6036
+ /** 角色图集上传:当前等待上传的角色名。 */
6037
+ const [roleImageTarget, setRoleImageTarget] = (0, react.useState)(null);
6038
+ /** 角色图集上传:当前用途标签(立绘/四视图/表情-x/场景/细节)。 */
6039
+ const [roleImageLabel, setRoleImageLabel] = (0, react.useState)("立绘");
6040
+ /** 角色形象详情:当前打开的角色名(null = 关闭)。 */
6041
+ const [detailRoleName, setDetailRoleName] = (0, react.useState)(null);
6042
+ /** 详情面板:上传标签输入。 */
6043
+ const [detailUploadLabel, setDetailUploadLabel] = (0, react.useState)("立绘");
6044
+ /** 场景库:AI 提炼候选(null = 未运行)。 */
6045
+ const [sceneCandidates, setSceneCandidates] = (0, react.useState)(null);
6046
+ /** 场景库:当前打开的场景名。 */
6047
+ const [sceneDetailName, setSceneDetailName] = (0, react.useState)(null);
6048
+ /** 场景库:上传标签输入。 */
6049
+ const [sceneUploadLabel, setSceneUploadLabel] = (0, react.useState)("全景");
6050
+ /** 场景库:编辑草稿(null = 不在编辑)。 */
6051
+ const [sceneDraft, setSceneDraft] = (0, react.useState)(null);
6052
+ const roleImageInputRef = (0, react.useRef)(null);
6049
6053
  /** AI 建议的剧情线候选(null = 未运行)。 */
6050
6054
  const [plotlineSuggestions, setPlotlineSuggestions] = (0, react.useState)(null);
6051
6055
  /** 剧情健康检查报告(null = 未运行)。 */
@@ -6712,77 +6716,136 @@ window.__ModuleLoader__.load({
6712
6716
  setBusyLabel("");
6713
6717
  }
6714
6718
  };
6715
- /** 漫剧分镜:对某章生成角色锚点 + 分镜表(约 1-2 分钟)。 */
6716
- const handleStoryboard = async (no) => {
6719
+ /** 生成单个角色的形象锚点(复用角色库 visual 能力)。 */
6720
+ const handleRoleVisual = async (name) => {
6717
6721
  setBusy(true);
6718
- setBusyLabel(`生成第${no}章漫剧分镜…`);
6722
+ setBusyLabel(`生成「${name}」形象锚点…`);
6719
6723
  setError("");
6720
6724
  try {
6721
- const result = await api.storyboard(no, void 0, "抖音", storyboardTool);
6722
- setStoryboardResult(result);
6723
- pushProgress(`第${no}章分镜完成:${result.panels.length} 格 · 角色卡 ${result.characters.length} 张`, "done");
6725
+ await api.roles({
6726
+ op: "visual",
6727
+ name
6728
+ });
6729
+ await refresh(false);
6730
+ pushProgress(`已生成「${name}」形象锚点`, "done");
6724
6731
  } catch (err) {
6725
6732
  setError(err.message);
6726
- pushProgress(`分镜生成失败:${err.message}`, "error");
6733
+ pushProgress(`生成「${name}」形象锚点失败:${err.message}`, "error");
6727
6734
  } finally {
6728
6735
  setBusy(false);
6729
6736
  setBusyLabel("");
6730
6737
  }
6731
6738
  };
6732
- /** 漫剧分集计划:AI 读一卷按故事弧线分集。 */
6733
- const handleStoryboardPlan = async () => {
6739
+ /** 上传角色图:读取本地图片为 dataURL 并写入角色卡(带用途标签 图集)。 */
6740
+ const handleRoleImageUpload = async (name, file, label = "立绘") => {
6741
+ const dataUrl = await new Promise((resolve, reject) => {
6742
+ const reader = new FileReader();
6743
+ reader.onload = () => resolve(String(reader.result));
6744
+ reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("读取图片失败"));
6745
+ reader.readAsDataURL(file);
6746
+ });
6734
6747
  setBusy(true);
6735
- setBusyLabel(`规划第${storyboardPlanVolume}卷漫剧分集…`);
6748
+ setBusyLabel(`上传「${name}」${label}…`);
6736
6749
  setError("");
6737
6750
  try {
6738
- const result = await api.storyboardPlan(storyboardPlanVolume, "抖音", 25);
6739
- setStoryboardPlanResult(result);
6740
- pushProgress(`第${storyboardPlanVolume}卷分集计划完成:${result.episodes.length} 集(覆盖 ${result.chaptersScanned} 章)`, "done");
6751
+ await api.roles({
6752
+ op: "image",
6753
+ name,
6754
+ dataUrl,
6755
+ label
6756
+ });
6757
+ await refresh(false);
6758
+ pushProgress(`已上传「${name}」${label}`, "done");
6741
6759
  } catch (err) {
6742
6760
  setError(err.message);
6743
- pushProgress(`分集计划失败:${err.message}`, "error");
6761
+ pushProgress(`上传「${name}」${label}失败:${err.message}`, "error");
6744
6762
  } finally {
6745
6763
  setBusy(false);
6746
6764
  setBusyLabel("");
6765
+ setRoleImageTarget(null);
6766
+ if (roleImageInputRef.current !== null) roleImageInputRef.current.value = "";
6747
6767
  }
6748
6768
  };
6749
- /** 作者复盘补跑:全书缺失章节(流式)。 */
6750
- const handleAuthorBackfillAll = async () => {
6751
- const missing = chapters.filter((c) => c.status !== "pending" && c.status !== "generating" && c.status !== "error" && c.authorReview === void 0).length;
6769
+ /** 用豆包/Seedream 为角色生成参考图。 */
6770
+ const handleRoleImageGenerate = async (name) => {
6752
6771
  setBusy(true);
6753
- setBusyLabel(`补齐历史章节作者复盘(${missing} 章)…`);
6772
+ setBusyLabel(`用豆包生成「${name}」参考图…`);
6754
6773
  setError("");
6755
6774
  try {
6756
- await api.reviewBackfillAll((frame) => {
6757
- applyJobFrame(frame, () => "");
6775
+ await api.roles({
6776
+ op: "imageGenerate",
6777
+ name
6758
6778
  });
6779
+ await refresh(false);
6780
+ pushProgress(`已生成「${name}」参考图`, "done");
6759
6781
  } catch (err) {
6760
6782
  setError(err.message);
6761
- pushProgress(`补齐作者复盘失败:${err.message}`, "error");
6783
+ pushProgress(`生成「${name}」参考图失败:${err.message}`, "error");
6762
6784
  } finally {
6763
6785
  setBusy(false);
6764
6786
  setBusyLabel("");
6765
6787
  }
6766
6788
  };
6767
- /** 作者复盘补跑:单章。 */
6768
- const handleAuthorBackfillChapter = async (no) => {
6789
+ /** 更新角色漫画重要性(main/support/extra)。 */
6790
+ const handleRoleImportance = async (role, importance) => {
6769
6791
  setBusy(true);
6770
- setBusyLabel(`生成第 ${no} 章作者复盘…`);
6792
+ setBusyLabel(`更新「${role.name}」重要性…`);
6771
6793
  setError("");
6772
6794
  try {
6773
- const result = await api.reviewBackfillChapter(no);
6774
- setProject((prev) => prev === null ? prev : {
6775
- ...prev,
6776
- chapters: prev.chapters.map((c) => c.no === result.no ? {
6777
- ...c,
6778
- authorReview: result.review
6779
- } : c)
6795
+ await api.roles({
6796
+ op: "update",
6797
+ role: {
6798
+ ...role,
6799
+ importance
6800
+ }
6780
6801
  });
6781
- const r = result.review;
6782
- pushProgress(`📋 第${no}章作者复盘:钩子${r.hookHonored ? "已兑现 ✓" : "未兑现 ✗"} · 结尾钩子 ${r.endingHook}/10`, r.hookHonored && r.endingHook >= 6 ? "done" : "error");
6802
+ await refresh(false);
6803
+ pushProgress(`已更新「${role.name}」重要性`, "done");
6783
6804
  } catch (err) {
6784
6805
  setError(err.message);
6785
- pushProgress(`生成第 ${no} 章作者复盘失败:${err.message}`, "error");
6806
+ pushProgress(`更新「${role.name}」重要性失败:${err.message}`, "error");
6807
+ } finally {
6808
+ setBusy(false);
6809
+ setBusyLabel("");
6810
+ }
6811
+ };
6812
+ /** 作者复盘补跑:全书缺失章节(流式)。 */
6813
+ const handleAuthorBackfillAll = async () => {
6814
+ const missing = chapters.filter((c) => c.status !== "pending" && c.status !== "generating" && c.status !== "error" && c.authorReview === void 0).length;
6815
+ setBusy(true);
6816
+ setBusyLabel(`补齐历史章节作者复盘(${missing} 章)…`);
6817
+ setError("");
6818
+ try {
6819
+ await api.reviewBackfillAll((frame) => {
6820
+ applyJobFrame(frame, () => "");
6821
+ });
6822
+ } catch (err) {
6823
+ setError(err.message);
6824
+ pushProgress(`补齐作者复盘失败:${err.message}`, "error");
6825
+ } finally {
6826
+ setBusy(false);
6827
+ setBusyLabel("");
6828
+ }
6829
+ };
6830
+ /** 作者复盘补跑:单章。 */
6831
+ const handleAuthorBackfillChapter = async (no) => {
6832
+ setBusy(true);
6833
+ setBusyLabel(`生成第 ${no} 章作者复盘…`);
6834
+ setError("");
6835
+ try {
6836
+ const result = await api.reviewBackfillChapter(no);
6837
+ setProject((prev) => prev === null ? prev : {
6838
+ ...prev,
6839
+ chapters: prev.chapters.map((c) => c.no === result.no ? {
6840
+ ...c,
6841
+ authorReview: result.review
6842
+ } : c)
6843
+ });
6844
+ const r = result.review;
6845
+ pushProgress(`📋 第${no}章作者复盘:钩子${r.hookHonored ? "已兑现 ✓" : "未兑现 ✗"} · 结尾钩子 ${r.endingHook}/10`, r.hookHonored && r.endingHook >= 6 ? "done" : "error");
6846
+ } catch (err) {
6847
+ setError(err.message);
6848
+ pushProgress(`生成第 ${no} 章作者复盘失败:${err.message}`, "error");
6786
6849
  } finally {
6787
6850
  setBusy(false);
6788
6851
  setBusyLabel("");
@@ -7631,7 +7694,10 @@ window.__ModuleLoader__.load({
7631
7694
  reviewPassScore: configDraft.reviewPassScore,
7632
7695
  autoReview: configDraft.autoReview,
7633
7696
  autoAuthorReview: configDraft.autoAuthorReview,
7634
- autoReviewAfterRevise: configDraft.autoReviewAfterRevise
7697
+ autoReviewAfterRevise: configDraft.autoReviewAfterRevise,
7698
+ imageApiKey: configDraft.imageApiKey,
7699
+ imageApiModel: configDraft.imageApiModel,
7700
+ imageApiEnabled: configDraft.imageApiEnabled ?? false
7635
7701
  });
7636
7702
  setConfig(result.config);
7637
7703
  setConfigDraft(result.config);
@@ -10697,6 +10763,79 @@ window.__ModuleLoader__.load({
10697
10763
  })
10698
10764
  ]
10699
10765
  }),
10766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10767
+ className: panel_module_css_default.row,
10768
+ children: [
10769
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10770
+ className: panel_module_css_default.field,
10771
+ style: { flex: 1 },
10772
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
10773
+ className: panel_module_css_default.fieldLabel,
10774
+ children: "豆包 API Key(生图)"
10775
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
10776
+ className: panel_module_css_default.input,
10777
+ type: "password",
10778
+ placeholder: "ark-...",
10779
+ value: configDraft.imageApiKey ?? "",
10780
+ onChange: (e) => {
10781
+ setConfigDraft({
10782
+ ...configDraft,
10783
+ imageApiKey: e.target.value
10784
+ });
10785
+ }
10786
+ })]
10787
+ }),
10788
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10789
+ className: panel_module_css_default.field,
10790
+ style: { flex: 1 },
10791
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
10792
+ className: panel_module_css_default.fieldLabel,
10793
+ children: "豆包生图模型"
10794
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
10795
+ className: panel_module_css_default.input,
10796
+ placeholder: "doubao-seedream-5-0-pro-260628",
10797
+ value: configDraft.imageApiModel ?? "",
10798
+ onChange: (e) => {
10799
+ setConfigDraft({
10800
+ ...configDraft,
10801
+ imageApiModel: e.target.value
10802
+ });
10803
+ }
10804
+ })]
10805
+ }),
10806
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10807
+ className: panel_module_css_default.field,
10808
+ style: { flex: 1 },
10809
+ children: [
10810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
10811
+ className: panel_module_css_default.fieldLabel,
10812
+ children: "启用豆包生图"
10813
+ }),
10814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
10815
+ className: panel_module_css_default.input,
10816
+ value: configDraft.imageApiEnabled ? "1" : "0",
10817
+ onChange: (e) => {
10818
+ setConfigDraft({
10819
+ ...configDraft,
10820
+ imageApiEnabled: e.target.value === "1"
10821
+ });
10822
+ },
10823
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
10824
+ value: "0",
10825
+ children: "关(默认)"
10826
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
10827
+ value: "1",
10828
+ children: "开"
10829
+ })]
10830
+ }),
10831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10832
+ className: panel_module_css_default.meta,
10833
+ children: "开启后漫画工坊才显示「豆包生成」按钮"
10834
+ })
10835
+ ]
10836
+ })
10837
+ ]
10838
+ }),
10700
10839
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10701
10840
  className: panel_module_css_default.row,
10702
10841
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -11976,432 +12115,1742 @@ window.__ModuleLoader__.load({
11976
12115
  })
11977
12116
  ]
11978
12117
  }),
11979
- activeTab === "storyboard" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12118
+ activeTab === "roleImage" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11980
12119
  className: panel_module_css_default.card,
12120
+ style: {
12121
+ flex: 1,
12122
+ minHeight: 0
12123
+ },
11981
12124
  children: [
11982
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12125
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11983
12126
  className: panel_module_css_default.row,
11984
12127
  style: {
11985
12128
  justifyContent: "space-between",
11986
12129
  flexWrap: "wrap"
11987
12130
  },
11988
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12131
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11989
12132
  className: panel_module_css_default.cardTitle,
11990
12133
  style: {
11991
12134
  fontSize: 17,
11992
12135
  fontWeight: 700
11993
12136
  },
11994
- children: "🎬 漫剧工坊"
11995
- })
11996
- }),
11997
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11998
- className: panel_module_css_default.row,
11999
- style: {
12000
- flexWrap: "wrap",
12001
- gap: 6
12002
- },
12003
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12004
- type: "button",
12005
- className: `${panel_module_css_default.button} ${storyboardView === "single" ? panel_module_css_default.buttonPrimary : ""}`,
12006
- style: {
12007
- fontSize: 14,
12008
- flex: 1
12009
- },
12010
- onClick: () => {
12011
- setStoryboardView("single");
12012
- },
12013
- children: "🎞️ 单集分镜"
12014
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12015
- type: "button",
12016
- className: `${panel_module_css_default.button} ${storyboardView === "plan" ? panel_module_css_default.buttonPrimary : ""}`,
12017
- style: {
12018
- fontSize: 14,
12019
- flex: 1
12020
- },
12021
- onClick: () => {
12022
- setStoryboardView("plan");
12023
- },
12024
- children: "📺 按卷规划"
12137
+ children: "🖼️ 角色形象"
12138
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12139
+ className: panel_module_css_default.meta,
12140
+ children: "角色形象锚点 / 参考图(漫画功能已移除,形象保留用于立绘与一致性)"
12025
12141
  })]
12026
12142
  }),
12027
- storyboardView === "plan" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
12144
+ ref: roleImageInputRef,
12145
+ type: "file",
12146
+ accept: "image/*",
12147
+ style: { display: "none" },
12148
+ onChange: (e) => {
12149
+ const file = e.target.files?.[0];
12150
+ const target = roleImageTarget;
12151
+ if (file === void 0 || target === null) return;
12152
+ const readDataUrl = () => new Promise((resolve, reject) => {
12153
+ const reader = new FileReader();
12154
+ reader.onload = () => resolve(String(reader.result));
12155
+ reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("读取图片失败"));
12156
+ reader.readAsDataURL(file);
12157
+ });
12158
+ (async () => {
12159
+ const dataUrl = await readDataUrl();
12160
+ if (target.endsWith("||scene")) {
12161
+ const sceneName = target.slice(0, -7);
12162
+ setBusy(true);
12163
+ setBusyLabel(`上传场景图「${sceneName}」${roleImageLabel}…`);
12164
+ setError("");
12165
+ try {
12166
+ await api.scenes({
12167
+ op: "image",
12168
+ name: sceneName,
12169
+ dataUrl,
12170
+ label: roleImageLabel
12171
+ });
12172
+ await refresh(false);
12173
+ pushProgress(`已上传场景「${sceneName}」${roleImageLabel}`, "done");
12174
+ } catch (err) {
12175
+ setError(err.message);
12176
+ pushProgress(`场景图上传失败:${err.message}`, "error");
12177
+ } finally {
12178
+ setBusy(false);
12179
+ setBusyLabel("");
12180
+ setRoleImageTarget(null);
12181
+ if (roleImageInputRef.current !== null) roleImageInputRef.current.value = "";
12182
+ }
12183
+ } else await handleRoleImageUpload(target, file, roleImageLabel);
12184
+ })();
12185
+ }
12186
+ }),
12187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12028
12188
  style: {
12029
12189
  display: "flex",
12030
12190
  flexDirection: "column",
12031
- gap: 10
12191
+ gap: 10,
12192
+ flex: 1,
12193
+ minHeight: 0,
12194
+ overflowY: "auto"
12032
12195
  },
12033
12196
  children: [
12034
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12197
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12035
12198
  className: panel_module_css_default.row,
12036
12199
  style: {
12037
12200
  flexWrap: "wrap",
12038
- gap: 6
12201
+ gap: 6,
12202
+ justifyContent: "space-between"
12039
12203
  },
12040
- children: [
12041
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
12042
- className: panel_module_css_default.input,
12043
- style: { width: 170 },
12044
- value: storyboardPlanVolume,
12045
- onChange: (e) => {
12046
- setStoryboardPlanVolume(Number(e.target.value));
12047
- },
12048
- title: "选择要规划的卷",
12049
- children: volumes !== void 0 && volumes.map((v) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
12050
- value: v.no,
12051
- children: [
12052
- "第",
12053
- v.no,
12054
- "卷《",
12055
- v.title,
12056
- "》"
12057
- ]
12058
- }, v.no))
12059
- }),
12060
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12204
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12205
+ className: panel_module_css_default.row,
12206
+ style: { gap: 6 },
12207
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12061
12208
  type: "button",
12062
- className: `${panel_module_css_default.button} ${panel_module_css_default.buttonPrimary}`,
12209
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12063
12210
  disabled: busy,
12064
12211
  onClick: () => {
12065
- handleStoryboardPlan();
12212
+ (async () => {
12213
+ setBusy(true);
12214
+ setBusyLabel("⚠️ 提炼本书视觉规则…");
12215
+ setError("");
12216
+ try {
12217
+ const result = await api.visualRules({ op: "extract" });
12218
+ await refresh(false);
12219
+ pushProgress(`已提炼 ${result.rules.length} 条视觉规则(已注入所有提示词)`, "done");
12220
+ } catch (err) {
12221
+ setError(err.message);
12222
+ pushProgress(`视觉规则提炼失败:${err.message}`, "error");
12223
+ } finally {
12224
+ setBusy(false);
12225
+ setBusyLabel("");
12226
+ }
12227
+ })();
12066
12228
  },
12067
- title: "AI 通读整卷章节摘要,按故事弧线把章节分组成漫剧集(高潮章单独成集、过渡章合并),约 1-3 分钟",
12068
- children: busy ? "⏳ 规划中…" : " 生成分集计划"
12069
- }),
12070
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12071
- className: panel_module_css_default.meta,
12072
- children: "AI 读卷 → 按弧线分集(不再是固定一章一集)"
12073
- })
12074
- ]
12075
- }),
12076
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12077
- className: panel_module_css_default.meta,
12078
- children: "分集原则:高潮章单独成集或两章一集;过渡章 2-3 章合并;断点选在钩子最强处;每集 60-120 秒。生成后逐集确认,满意再用「单集分镜」为某集生成分镜。"
12229
+ children: "⚠️ 提炼视觉规则"
12230
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12231
+ type: "button",
12232
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12233
+ onClick: () => {
12234
+ refresh(false);
12235
+ pushProgress("角色库已刷新", "done");
12236
+ },
12237
+ children: "🔄 刷新角色库"
12238
+ })]
12239
+ })
12079
12240
  }),
12080
- storyboardPlanResult === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12081
- className: panel_module_css_default.shelfEmpty,
12082
- style: { minHeight: 140 },
12083
- children: [
12084
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12085
- className: panel_module_css_default.shelfEmptyIcon,
12086
- children: "📺"
12087
- }),
12088
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12089
- className: panel_module_css_default.shelfEmptyTitle,
12090
- children: "尚未生成分集计划"
12091
- }),
12092
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12093
- className: panel_module_css_default.meta,
12094
- children: "选卷后点「生成分集计划」,AI 把整卷按故事弧线分成漫剧集"
12095
- })
12096
- ]
12097
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12241
+ (project?.visualRules ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12098
12242
  style: {
12099
12243
  display: "flex",
12100
- flexDirection: "column",
12101
- gap: 6
12244
+ flexWrap: "wrap",
12245
+ gap: 4,
12246
+ alignItems: "center",
12247
+ marginTop: 6
12102
12248
  },
12103
- children: [
12104
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12105
- className: panel_module_css_default.meta,
12106
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "策略:" }), storyboardPlanResult.strategy]
12107
- }),
12108
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12109
- className: panel_module_css_default.meta,
12110
- children: [
12111
- " ",
12112
- storyboardPlanResult.episodes.length,
12113
- " 集 · 覆盖 ",
12114
- storyboardPlanResult.chaptersScanned,
12115
- " 章"
12116
- ]
12117
- }),
12118
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12249
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12250
+ className: panel_module_css_default.meta,
12251
+ style: { fontSize: 11 },
12252
+ children: "⚠️ 本书视觉规则:"
12253
+ }), (project?.visualRules ?? []).map((r, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12254
+ style: {
12255
+ border: "1px solid var(--nf-warn, #b8860b)",
12256
+ borderRadius: 999,
12257
+ padding: "1px 8px",
12258
+ fontSize: 10,
12259
+ color: "var(--nf-warn, #b8860b)"
12260
+ },
12261
+ children: r
12262
+ }, i))]
12263
+ }),
12264
+ (() => {
12265
+ const roles = project?.roles ?? [];
12266
+ if (roles.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12267
+ className: panel_module_css_default.shelfEmpty,
12268
+ style: {
12269
+ minHeight: 140,
12270
+ flex: 1
12271
+ },
12272
+ children: [
12273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12274
+ className: panel_module_css_default.shelfEmptyIcon,
12275
+ children: "🎭"
12276
+ }),
12277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12278
+ className: panel_module_css_default.shelfEmptyTitle,
12279
+ children: "角色库为空"
12280
+ }),
12281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12282
+ className: panel_module_css_default.meta,
12283
+ children: "请先到「角色库」提炼角色并生成形象锚点"
12284
+ }),
12285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12286
+ type: "button",
12287
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12288
+ onClick: () => {
12289
+ setActiveTab("roles");
12290
+ },
12291
+ children: "去角色库"
12292
+ })
12293
+ ]
12294
+ });
12295
+ const detailRole = detailRoleName !== null ? roles.find((r) => r.name === detailRoleName) : void 0;
12296
+ const group = (role) => {
12297
+ const g = role.gallery ?? [];
12298
+ const byLabel = (key) => g.filter((x) => x.label === key);
12299
+ const byPrefix = (pre) => g.filter((x) => x.label.startsWith(pre));
12300
+ return {
12301
+ portrait: byLabel("立绘")[0],
12302
+ sheet: byLabel("四视图")[0],
12303
+ expressions: byPrefix("表情-"),
12304
+ others: g.filter((x) => x.label !== "立绘" && x.label !== "四视图" && !x.label.startsWith("表情-"))
12305
+ };
12306
+ };
12307
+ const uploadInDetail = (role) => {
12308
+ setRoleImageLabel(detailUploadLabel.trim() !== "" ? detailUploadLabel.trim() : "立绘");
12309
+ setRoleImageTarget(role.name);
12310
+ roleImageInputRef.current?.click();
12311
+ };
12312
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12313
+ style: {
12314
+ display: "grid",
12315
+ gridTemplateColumns: "repeat(auto-fill, minmax(130px, 1fr))",
12316
+ gap: 10
12317
+ },
12318
+ children: roles.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12319
+ onClick: () => {
12320
+ setDetailRoleName(r.name);
12321
+ setDetailUploadLabel("立绘");
12322
+ },
12119
12323
  style: {
12324
+ cursor: "pointer",
12325
+ border: "1px solid var(--nf-border)",
12326
+ borderRadius: 12,
12327
+ overflow: "hidden",
12328
+ background: "var(--nf-bg-inset)",
12120
12329
  display: "flex",
12121
- flexDirection: "column",
12122
- gap: 4,
12123
- maxHeight: 380,
12124
- overflowY: "auto"
12330
+ flexDirection: "column"
12125
12331
  },
12126
- children: storyboardPlanResult.episodes.map((ep) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12332
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12127
12333
  style: {
12128
- border: "1px solid var(--nf-border)",
12129
- borderRadius: 8,
12130
- padding: "6px 10px",
12131
- fontSize: 12
12334
+ aspectRatio: "3/4",
12335
+ display: "flex",
12336
+ alignItems: "center",
12337
+ justifyContent: "center",
12338
+ background: "var(--nf-bg-inset)",
12339
+ borderBottom: "1px solid var(--nf-border)",
12340
+ position: "relative",
12341
+ overflow: "hidden"
12132
12342
  },
12133
- children: [
12134
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12135
- className: panel_module_css_default.row,
12136
- style: {
12137
- flexWrap: "wrap",
12138
- gap: 6
12139
- },
12140
- children: [
12141
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12142
- className: panel_module_css_default.badge,
12143
- style: {
12144
- borderColor: "var(--nf-accent)",
12145
- color: "var(--nf-accent)",
12146
- flex: "none"
12147
- },
12148
- children: [
12149
- "第",
12150
- ep.index,
12151
- ""
12152
- ]
12153
- }),
12154
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: ep.title }),
12155
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12156
- className: panel_module_css_default.meta,
12157
- children: [
12158
- "涵盖:第",
12159
- ep.chapters[0],
12160
- "-",
12161
- ep.chapters[ep.chapters.length - 1],
12162
- "章(",
12163
- ep.chapters.length,
12164
- "章)"
12165
- ]
12166
- })
12167
- ]
12168
- }),
12169
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12170
- className: panel_module_css_default.meta,
12171
- children: ep.narrativeJob
12172
- }),
12173
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12174
- className: panel_module_css_default.meta,
12175
- style: { color: "var(--nf-info)" },
12176
- children: ["开头钩子:", ep.openingHook]
12177
- }),
12178
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12179
- className: panel_module_css_default.meta,
12180
- style: { color: "var(--nf-warn)" },
12181
- children: ["结尾钩子:", ep.endingHook]
12182
- })
12183
- ]
12184
- }, ep.index))
12185
- })
12186
- ]
12187
- })
12343
+ children: [r.imageUrl !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
12344
+ src: r.imageUrl,
12345
+ alt: r.name,
12346
+ style: {
12347
+ width: "100%",
12348
+ height: "100%",
12349
+ objectFit: "cover",
12350
+ objectPosition: "center 20%"
12351
+ }
12352
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12353
+ className: panel_module_css_default.meta,
12354
+ children: "暂无形象"
12355
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12356
+ style: {
12357
+ position: "absolute",
12358
+ bottom: 4,
12359
+ right: 4,
12360
+ background: "rgba(0,0,0,0.6)",
12361
+ color: "#fff",
12362
+ fontSize: 10,
12363
+ borderRadius: 6,
12364
+ padding: "1px 6px"
12365
+ },
12366
+ children: ["🖼 ", (r.gallery ?? []).length]
12367
+ })]
12368
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12369
+ style: {
12370
+ padding: 6,
12371
+ display: "flex",
12372
+ flexDirection: "column",
12373
+ gap: 2
12374
+ },
12375
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12376
+ style: {
12377
+ display: "flex",
12378
+ justifyContent: "space-between",
12379
+ alignItems: "center"
12380
+ },
12381
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12382
+ style: { fontSize: 13 },
12383
+ children: r.name
12384
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12385
+ className: panel_module_css_default.meta,
12386
+ style: { fontSize: 10 },
12387
+ children: r.roleLabel
12388
+ })]
12389
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12390
+ className: panel_module_css_default.meta,
12391
+ style: {
12392
+ fontSize: 10,
12393
+ whiteSpace: "nowrap",
12394
+ overflow: "hidden",
12395
+ textOverflow: "ellipsis"
12396
+ },
12397
+ children: [
12398
+ r.imagePrompt !== void 0 ? "✓ 锚点" : "⚠ 无锚点",
12399
+ " · ",
12400
+ (r.identity ?? "").slice(0, 16)
12401
+ ]
12402
+ })]
12403
+ })]
12404
+ }, r.name))
12405
+ }), detailRole !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12406
+ style: {
12407
+ position: "fixed",
12408
+ inset: 0,
12409
+ background: "rgba(0,0,0,0.55)",
12410
+ zIndex: 1e3,
12411
+ display: "flex",
12412
+ alignItems: "center",
12413
+ justifyContent: "center",
12414
+ padding: 24
12415
+ },
12416
+ onClick: () => {
12417
+ setDetailRoleName(null);
12418
+ },
12419
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12420
+ style: {
12421
+ background: "var(--nf-bg)",
12422
+ border: "1px solid var(--nf-border)",
12423
+ borderRadius: 16,
12424
+ width: "min(960px, 100%)",
12425
+ maxHeight: "92vh",
12426
+ overflowY: "auto",
12427
+ padding: 20,
12428
+ position: "relative"
12429
+ },
12430
+ onClick: (e) => e.stopPropagation(),
12431
+ children: [
12432
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12433
+ type: "button",
12434
+ className: panel_module_css_default.iconButton,
12435
+ style: {
12436
+ position: "absolute",
12437
+ top: 12,
12438
+ right: 12,
12439
+ fontSize: 18
12440
+ },
12441
+ title: "关闭",
12442
+ "aria-label": "关闭",
12443
+ onClick: () => {
12444
+ setDetailRoleName(null);
12445
+ },
12446
+ children: "✕"
12447
+ }),
12448
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12449
+ style: { marginBottom: 12 },
12450
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12451
+ style: {
12452
+ display: "flex",
12453
+ alignItems: "baseline",
12454
+ gap: 10,
12455
+ flexWrap: "wrap"
12456
+ },
12457
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12458
+ style: { fontSize: 22 },
12459
+ children: detailRole.name
12460
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12461
+ className: panel_module_css_default.badge,
12462
+ style: {
12463
+ borderColor: "var(--nf-accent)",
12464
+ color: "var(--nf-accent)"
12465
+ },
12466
+ children: detailRole.roleLabel
12467
+ })]
12468
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12469
+ className: panel_module_css_default.meta,
12470
+ style: { marginTop: 4 },
12471
+ children: detailRole.identity
12472
+ })]
12473
+ }),
12474
+ (project?.visualRules ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12475
+ style: {
12476
+ border: "1px solid var(--nf-warn, #b8860b)",
12477
+ borderRadius: 10,
12478
+ padding: "6px 10px",
12479
+ marginBottom: 10,
12480
+ fontSize: 11
12481
+ },
12482
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "⚠️ 本书视觉规则(已内嵌到下方所有提示词)" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12483
+ style: {
12484
+ display: "flex",
12485
+ flexWrap: "wrap",
12486
+ gap: 4,
12487
+ marginTop: 4
12488
+ },
12489
+ children: (project?.visualRules ?? []).map((r, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12490
+ style: {
12491
+ border: "1px solid var(--nf-border)",
12492
+ borderRadius: 999,
12493
+ padding: "1px 8px",
12494
+ fontSize: 10
12495
+ },
12496
+ children: r
12497
+ }, i))
12498
+ })]
12499
+ }),
12500
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
12501
+ style: {
12502
+ marginBottom: 10,
12503
+ fontSize: 12
12504
+ },
12505
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
12506
+ style: {
12507
+ cursor: "pointer",
12508
+ color: "var(--nf-text-2)"
12509
+ },
12510
+ children: [
12511
+ "🖼 图集(",
12512
+ (detailRole.gallery ?? []).length,
12513
+ " 张 · 点击展开/收起)"
12514
+ ]
12515
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12516
+ style: { marginTop: 8 },
12517
+ children: (() => {
12518
+ const g = group(detailRole);
12519
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
12520
+ (g.portrait !== void 0 || g.sheet !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12521
+ style: {
12522
+ display: "grid",
12523
+ gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
12524
+ gap: 10,
12525
+ marginBottom: 12
12526
+ },
12527
+ children: [g.portrait !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12528
+ style: {
12529
+ border: "1px solid var(--nf-border)",
12530
+ borderRadius: 12,
12531
+ overflow: "hidden"
12532
+ },
12533
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
12534
+ src: g.portrait.dataUrl,
12535
+ alt: "立绘",
12536
+ style: {
12537
+ width: "100%",
12538
+ maxHeight: 380,
12539
+ objectFit: "contain",
12540
+ display: "block",
12541
+ background: "#111"
12542
+ }
12543
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12544
+ className: panel_module_css_default.meta,
12545
+ style: {
12546
+ padding: "2px 8px",
12547
+ fontSize: 11
12548
+ },
12549
+ children: "立绘"
12550
+ })]
12551
+ }), g.sheet !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12552
+ style: {
12553
+ border: "1px solid var(--nf-border)",
12554
+ borderRadius: 12,
12555
+ overflow: "hidden"
12556
+ },
12557
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
12558
+ src: g.sheet.dataUrl,
12559
+ alt: "四视图",
12560
+ style: {
12561
+ width: "100%",
12562
+ maxHeight: 380,
12563
+ objectFit: "contain",
12564
+ display: "block",
12565
+ background: "#111"
12566
+ }
12567
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12568
+ className: panel_module_css_default.meta,
12569
+ style: {
12570
+ padding: "2px 8px",
12571
+ fontSize: 11
12572
+ },
12573
+ children: "四视图"
12574
+ })]
12575
+ })]
12576
+ }),
12577
+ g.expressions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12578
+ style: { marginBottom: 12 },
12579
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12580
+ style: { fontSize: 13 },
12581
+ children: "表情设定"
12582
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12583
+ style: {
12584
+ display: "grid",
12585
+ gridTemplateColumns: "repeat(auto-fill, minmax(110px, 1fr))",
12586
+ gap: 8,
12587
+ marginTop: 6
12588
+ },
12589
+ children: g.expressions.map((img) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12590
+ style: {
12591
+ border: "1px solid var(--nf-border)",
12592
+ borderRadius: 10,
12593
+ overflow: "hidden",
12594
+ position: "relative"
12595
+ },
12596
+ children: [
12597
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
12598
+ src: img.dataUrl,
12599
+ alt: img.label,
12600
+ style: {
12601
+ width: "100%",
12602
+ height: 110,
12603
+ objectFit: "cover",
12604
+ display: "block"
12605
+ }
12606
+ }),
12607
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12608
+ style: {
12609
+ background: "rgba(0,0,0,0.55)",
12610
+ color: "#fff",
12611
+ fontSize: 10,
12612
+ padding: "1px 6px",
12613
+ textAlign: "center"
12614
+ },
12615
+ children: img.label.replace("表情-", "")
12616
+ }),
12617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12618
+ type: "button",
12619
+ className: panel_module_css_default.iconButton,
12620
+ style: {
12621
+ position: "absolute",
12622
+ top: 2,
12623
+ right: 2,
12624
+ fontSize: 11
12625
+ },
12626
+ title: "删除",
12627
+ "aria-label": "删除",
12628
+ onClick: () => {
12629
+ (async () => {
12630
+ try {
12631
+ await api.roles({
12632
+ op: "removeImage",
12633
+ name: detailRole.name,
12634
+ label: img.label
12635
+ });
12636
+ await refresh(false);
12637
+ pushProgress(`已删除「${detailRole.name}」${img.label}`, "done");
12638
+ } catch (err) {
12639
+ setError(err.message);
12640
+ }
12641
+ })();
12642
+ },
12643
+ children: "×"
12644
+ })
12645
+ ]
12646
+ }, img.label))
12647
+ })]
12648
+ }),
12649
+ g.others.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12650
+ style: { marginBottom: 12 },
12651
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12652
+ style: { fontSize: 13 },
12653
+ children: "场景 / 细节"
12654
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12655
+ style: {
12656
+ display: "grid",
12657
+ gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))",
12658
+ gap: 8,
12659
+ marginTop: 6
12660
+ },
12661
+ children: g.others.map((img) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12662
+ style: {
12663
+ border: "1px solid var(--nf-border)",
12664
+ borderRadius: 10,
12665
+ overflow: "hidden",
12666
+ position: "relative"
12667
+ },
12668
+ children: [
12669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
12670
+ src: img.dataUrl,
12671
+ alt: img.label,
12672
+ style: {
12673
+ width: "100%",
12674
+ height: 90,
12675
+ objectFit: "cover",
12676
+ display: "block"
12677
+ }
12678
+ }),
12679
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12680
+ style: {
12681
+ background: "rgba(0,0,0,0.55)",
12682
+ color: "#fff",
12683
+ fontSize: 10,
12684
+ padding: "1px 6px",
12685
+ textAlign: "center"
12686
+ },
12687
+ children: img.label
12688
+ }),
12689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12690
+ type: "button",
12691
+ className: panel_module_css_default.iconButton,
12692
+ style: {
12693
+ position: "absolute",
12694
+ top: 2,
12695
+ right: 2,
12696
+ fontSize: 11
12697
+ },
12698
+ title: "删除",
12699
+ "aria-label": "删除",
12700
+ onClick: () => {
12701
+ (async () => {
12702
+ try {
12703
+ await api.roles({
12704
+ op: "removeImage",
12705
+ name: detailRole.name,
12706
+ label: img.label
12707
+ });
12708
+ await refresh(false);
12709
+ pushProgress(`已删除「${detailRole.name}」${img.label}`, "done");
12710
+ } catch (err) {
12711
+ setError(err.message);
12712
+ }
12713
+ })();
12714
+ },
12715
+ children: "×"
12716
+ })
12717
+ ]
12718
+ }, img.label))
12719
+ })]
12720
+ })
12721
+ ] });
12722
+ })()
12723
+ })]
12724
+ }),
12725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12726
+ style: {
12727
+ display: "grid",
12728
+ gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))",
12729
+ gap: 14,
12730
+ marginBottom: 12
12731
+ },
12732
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12733
+ style: { fontSize: 13 },
12734
+ children: "基本信息"
12735
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12736
+ style: {
12737
+ fontSize: 12,
12738
+ lineHeight: 1.8,
12739
+ marginTop: 4
12740
+ },
12741
+ children: [
12742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12743
+ className: panel_module_css_default.meta,
12744
+ children: "性格:"
12745
+ }), (detailRole.traits ?? []).join("、") || "—"] }),
12746
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12747
+ className: panel_module_css_default.meta,
12748
+ children: "目标:"
12749
+ }), detailRole.goals || "—"] }),
12750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12751
+ className: panel_module_css_default.meta,
12752
+ children: "关系:"
12753
+ }), (detailRole.relations ?? []).join(";") || "—"] }),
12754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12755
+ className: panel_module_css_default.meta,
12756
+ children: "成长线:"
12757
+ }), (detailRole.arc ?? []).join(";") || "—"] })
12758
+ ]
12759
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
12760
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12761
+ className: panel_module_css_default.row,
12762
+ style: {
12763
+ justifyContent: "space-between",
12764
+ flexWrap: "wrap"
12765
+ },
12766
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12767
+ style: { fontSize: 13 },
12768
+ children: "📄 提示词包"
12769
+ }), detailRole.imagePrompt !== void 0 && detailRole.promptKit === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12770
+ type: "button",
12771
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12772
+ disabled: busy,
12773
+ onClick: () => {
12774
+ (async () => {
12775
+ setBusy(true);
12776
+ setBusyLabel(`精修「${detailRole.name}」提示词…`);
12777
+ setError("");
12778
+ try {
12779
+ await api.roles({
12780
+ op: "promptKit",
12781
+ name: detailRole.name
12782
+ });
12783
+ await refresh(false);
12784
+ pushProgress(`已生成「${detailRole.name}」四类精修提示词`, "done");
12785
+ } catch (err) {
12786
+ setError(err.message);
12787
+ pushProgress(`提示词精修失败:${err.message}`, "error");
12788
+ } finally {
12789
+ setBusy(false);
12790
+ setBusyLabel("");
12791
+ }
12792
+ })();
12793
+ },
12794
+ children: "✨ 生成精修版"
12795
+ })]
12796
+ }),
12797
+ detailRole.imagePrompt === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12798
+ className: panel_module_css_default.meta,
12799
+ children: "未生成锚点,先点「✨ 生成锚点」"
12800
+ }),
12801
+ (() => {
12802
+ const anchor = detailRole.imagePrompt;
12803
+ if (anchor === void 0) return null;
12804
+ const rules = project?.visualRules ?? [];
12805
+ const rulesZh = rules.length > 0 ? "\n【本书视觉规则】" + rules.join(";") : "";
12806
+ const expressions = detailRole.expressions ?? ["平静"];
12807
+ const expName = (n) => n.replace(/^表情-/, "");
12808
+ const kit = detailRole.promptKit;
12809
+ const blocks = [];
12810
+ if (kit !== void 0) {
12811
+ blocks.push({
12812
+ key: "portrait",
12813
+ title: "立绘",
12814
+ zh: kit.portrait.zh,
12815
+ en: kit.portrait.en
12816
+ });
12817
+ blocks.push({
12818
+ key: "sheet",
12819
+ title: "四视图",
12820
+ zh: kit.sheet.zh,
12821
+ en: kit.sheet.en
12822
+ });
12823
+ for (const e of kit.expressions) blocks.push({
12824
+ key: "exp-" + e.name,
12825
+ title: `表情·${e.name}`,
12826
+ zh: e.zh,
12827
+ en: e.en
12828
+ });
12829
+ blocks.push({
12830
+ key: "details",
12831
+ title: "细节",
12832
+ zh: kit.details.zh,
12833
+ en: kit.details.en
12834
+ });
12835
+ } else {
12836
+ blocks.push({
12837
+ key: "portrait",
12838
+ title: "立绘",
12839
+ zh: anchor.zh + "。全身/半身正视图,写实电影感。" + rulesZh,
12840
+ en: anchor.en + ", full body, front view, plain background"
12841
+ });
12842
+ blocks.push({
12843
+ key: "sheet",
12844
+ title: "四视图",
12845
+ zh: anchor.zh + "。同一角色的正面/左侧面/右侧面/背面四视图设定表,纯白背景,四个视角分别描述。" + rulesZh,
12846
+ en: anchor.en + ", character sheet, front view, left side view, right side view, back view, full body, plain white background"
12847
+ });
12848
+ for (const n of expressions) blocks.push({
12849
+ key: "exp-" + n,
12850
+ title: `表情·${expName(n)}`,
12851
+ zh: anchor.zh + `。表情:${expName(n)},脸部特写(头部到锁骨),纯白背景,五官与角色定稿完全一致,皮肤纹理细节完整,无多余杂物。` + rulesZh,
12852
+ en: anchor.en + `, facial close-up, head to collarbone, expression: ${expName(n)}, plain white background, consistent with character design, detailed skin texture, no extra objects`
12853
+ });
12854
+ blocks.push({
12855
+ key: "details",
12856
+ title: "细节",
12857
+ zh: `多组局部细节集合参考图,纯白背景:${anchor.tags.map((t) => t + "特写").join(";")}。细节清晰锐利,角色细节参考稿,无多余杂物。` + rulesZh,
12858
+ en: anchor.tags.join(", ") + ", multi-panel detail reference sheet, plain white background, macro close-up, sharp details, character detail sheet, no extra objects"
12859
+ });
12860
+ }
12861
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12862
+ style: {
12863
+ display: "flex",
12864
+ flexDirection: "column",
12865
+ gap: 8,
12866
+ marginTop: 8
12867
+ },
12868
+ children: [blocks.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12869
+ style: {
12870
+ border: "1px solid var(--nf-border)",
12871
+ borderRadius: 8,
12872
+ padding: "6px 8px"
12873
+ },
12874
+ children: [
12875
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12876
+ className: panel_module_css_default.row,
12877
+ style: { justifyContent: "space-between" },
12878
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
12879
+ style: { fontSize: 11 },
12880
+ children: b.title
12881
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12882
+ className: panel_module_css_default.row,
12883
+ style: { gap: 4 },
12884
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12885
+ type: "button",
12886
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12887
+ style: { fontSize: 10 },
12888
+ onClick: () => {
12889
+ navigator.clipboard?.writeText(b.zh).then(() => {
12890
+ pushProgress(`已复制「${detailRole.name}」${b.title}中文提示词`, "done");
12891
+ }).catch(() => {});
12892
+ },
12893
+ children: "复制"
12894
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12895
+ type: "button",
12896
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12897
+ style: { fontSize: 10 },
12898
+ onClick: () => {
12899
+ navigator.clipboard?.writeText(b.en).then(() => {
12900
+ pushProgress(`已复制「${detailRole.name}」${b.title}英文标签`, "done");
12901
+ }).catch(() => {});
12902
+ },
12903
+ children: "EN"
12904
+ })]
12905
+ })]
12906
+ }),
12907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12908
+ className: panel_module_css_default.meta,
12909
+ style: {
12910
+ fontSize: 11,
12911
+ lineHeight: 1.5,
12912
+ marginTop: 3
12913
+ },
12914
+ children: b.zh
12915
+ }),
12916
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
12917
+ style: { marginTop: 3 },
12918
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
12919
+ style: {
12920
+ cursor: "pointer",
12921
+ fontSize: 10,
12922
+ color: "var(--nf-text-2)"
12923
+ },
12924
+ children: "🌐 英文标签(点击展开)"
12925
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12926
+ className: panel_module_css_default.meta,
12927
+ style: {
12928
+ fontSize: 9,
12929
+ fontFamily: "monospace",
12930
+ marginTop: 2,
12931
+ wordBreak: "break-all",
12932
+ color: "var(--nf-text-2)"
12933
+ },
12934
+ children: b.en
12935
+ })]
12936
+ })
12937
+ ]
12938
+ }, b.key)), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12939
+ className: panel_module_css_default.meta,
12940
+ style: { fontSize: 10 },
12941
+ children: ["关键词:", (anchor.tags ?? []).join("、")]
12942
+ })]
12943
+ });
12944
+ })()
12945
+ ] })]
12946
+ }),
12947
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12948
+ style: {
12949
+ borderTop: "1px solid var(--nf-border)",
12950
+ paddingTop: 10,
12951
+ display: "flex",
12952
+ flexWrap: "wrap",
12953
+ gap: 6,
12954
+ alignItems: "center"
12955
+ },
12956
+ children: [
12957
+ detailRole.imagePrompt === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12958
+ type: "button",
12959
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12960
+ disabled: busy,
12961
+ onClick: () => {
12962
+ handleRoleVisual(detailRole.name);
12963
+ },
12964
+ children: "✨ 生成锚点"
12965
+ }),
12966
+ detailRole.imagePrompt !== void 0 && config?.imageApiEnabled === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12967
+ type: "button",
12968
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12969
+ disabled: busy,
12970
+ onClick: () => {
12971
+ handleRoleImageGenerate(detailRole.name);
12972
+ },
12973
+ children: "豆包生成"
12974
+ }),
12975
+ detailRole.imagePrompt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12976
+ type: "button",
12977
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12978
+ onClick: () => {
12979
+ const text = [detailRole.imagePrompt.en, detailRole.imagePrompt.tags.join(", ")].filter(Boolean).join("\n");
12980
+ navigator.clipboard?.writeText(text).then(() => {
12981
+ pushProgress(`已复制「${detailRole.name}」英文生图提示词`, "done");
12982
+ }).catch(() => {});
12983
+ },
12984
+ children: "复制提示词"
12985
+ }),
12986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
12987
+ className: panel_module_css_default.input,
12988
+ style: {
12989
+ width: 140,
12990
+ fontSize: 12,
12991
+ padding: "3px 6px"
12992
+ },
12993
+ placeholder: "图集标签",
12994
+ value: detailUploadLabel,
12995
+ onChange: (e) => {
12996
+ setDetailUploadLabel(e.target.value);
12997
+ }
12998
+ }),
12999
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13000
+ type: "button",
13001
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13002
+ disabled: busy,
13003
+ onClick: () => {
13004
+ uploadInDetail(detailRole);
13005
+ },
13006
+ children: "📤 上传图"
13007
+ }),
13008
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
13009
+ className: panel_module_css_default.input,
13010
+ style: {
13011
+ fontSize: 12,
13012
+ padding: "3px 4px",
13013
+ width: 90
13014
+ },
13015
+ value: detailRole.importance ?? "support",
13016
+ onChange: (e) => {
13017
+ handleRoleImportance(detailRole, e.target.value);
13018
+ },
13019
+ title: "漫剧重要性",
13020
+ children: [
13021
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
13022
+ value: "main",
13023
+ children: "重要"
13024
+ }),
13025
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
13026
+ value: "support",
13027
+ children: "配角"
13028
+ }),
13029
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
13030
+ value: "extra",
13031
+ children: "路人"
13032
+ })
13033
+ ]
13034
+ })
13035
+ ]
13036
+ })
13037
+ ]
13038
+ })
13039
+ })] });
13040
+ })()
12188
13041
  ]
12189
- }),
12190
- storyboardView === "single" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13042
+ })
13043
+ ]
13044
+ }),
13045
+ activeTab === "scenes" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13046
+ className: panel_module_css_default.card,
13047
+ style: {
13048
+ flex: 1,
13049
+ minHeight: 0
13050
+ },
13051
+ children: [
13052
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12191
13053
  className: panel_module_css_default.row,
12192
13054
  style: {
12193
13055
  justifyContent: "space-between",
12194
13056
  flexWrap: "wrap"
12195
13057
  },
12196
13058
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13059
+ className: panel_module_css_default.cardTitle,
13060
+ style: {
13061
+ fontSize: 17,
13062
+ fontWeight: 700
13063
+ },
13064
+ children: "🏞️ 场景库"
13065
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12197
13066
  className: panel_module_css_default.meta,
12198
- children: "把一章网文改编为高完播率的漫剧分镜:角色视觉锚点(复用角色库形象词)+ 8-12 格分镜表(画面/台词/转场/AI 提示词),可直接用于豆包/即梦/SD 出图或视频。黄金开局 3 秒 + 每 10 秒小高潮 + 结尾钩子。"
12199
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12200
- className: panel_module_css_default.row,
12201
- style: { flexWrap: "wrap" },
12202
- children: [
12203
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
13067
+ children: "场景视觉锚点:提炼自正文,供漫剧分镜/生图锁定\"在哪、什么氛围\""
13068
+ })]
13069
+ }),
13070
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13071
+ className: panel_module_css_default.row,
13072
+ style: {
13073
+ flexWrap: "wrap",
13074
+ gap: 6,
13075
+ margin: "8px 0"
13076
+ },
13077
+ children: [
13078
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13079
+ type: "button",
13080
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonPrimary}`,
13081
+ disabled: busy,
13082
+ onClick: () => {
13083
+ (async () => {
13084
+ setBusy(true);
13085
+ setBusyLabel("✨ 从全书提炼场景…");
13086
+ setError("");
13087
+ try {
13088
+ const result = await api.scenes({ op: "extract" });
13089
+ setSceneCandidates(result.candidates ?? []);
13090
+ pushProgress(`提炼出 ${(result.candidates ?? []).length} 个候选场景,可采纳或修改后采纳`, "done");
13091
+ } catch (err) {
13092
+ setError(err.message);
13093
+ pushProgress(`场景提炼失败:${err.message}`, "error");
13094
+ } finally {
13095
+ setBusy(false);
13096
+ setBusyLabel("");
13097
+ }
13098
+ })();
13099
+ },
13100
+ children: busy ? "⏳ 提炼中…" : "✨ 从全书提炼场景"
13101
+ }),
13102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13103
+ type: "button",
13104
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13105
+ onClick: () => {
13106
+ refresh(false);
13107
+ pushProgress("场景库已刷新", "done");
13108
+ },
13109
+ children: "🔄 刷新"
13110
+ }),
13111
+ sceneCandidates !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13112
+ type: "button",
13113
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13114
+ onClick: () => {
13115
+ setSceneCandidates(null);
13116
+ },
13117
+ children: "收起候选"
13118
+ })
13119
+ ]
13120
+ }),
13121
+ sceneDraft !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13122
+ style: {
13123
+ border: "1px solid var(--nf-accent)",
13124
+ borderRadius: 10,
13125
+ padding: 10,
13126
+ marginBottom: 10,
13127
+ display: "flex",
13128
+ flexDirection: "column",
13129
+ gap: 6
13130
+ },
13131
+ children: [
13132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13133
+ style: { fontSize: 13 },
13134
+ children: "编辑场景"
13135
+ }),
13136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
13137
+ className: panel_module_css_default.input,
13138
+ style: {
13139
+ fontSize: 12,
13140
+ padding: "4px 8px"
13141
+ },
13142
+ placeholder: "场景名",
13143
+ value: sceneDraft.name,
13144
+ onChange: (e) => {
13145
+ setSceneDraft({
13146
+ ...sceneDraft,
13147
+ name: e.target.value
13148
+ });
13149
+ }
13150
+ }),
13151
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
13152
+ className: panel_module_css_default.input,
13153
+ style: {
13154
+ fontSize: 12,
13155
+ padding: "4px 8px"
13156
+ },
13157
+ placeholder: "一句话定位",
13158
+ value: sceneDraft.summary,
13159
+ onChange: (e) => {
13160
+ setSceneDraft({
13161
+ ...sceneDraft,
13162
+ summary: e.target.value
13163
+ });
13164
+ }
13165
+ }),
13166
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13167
+ className: panel_module_css_default.row,
13168
+ style: { gap: 6 },
13169
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
12204
13170
  className: panel_module_css_default.input,
12205
- style: { width: 150 },
12206
- value: storyboardChapterNo,
12207
- onChange: (e) => {
12208
- setStoryboardChapterNo(Number(e.target.value));
13171
+ style: {
13172
+ fontSize: 12,
13173
+ padding: "4px 8px",
13174
+ flex: 1
12209
13175
  },
12210
- title: "选择要改编的章节",
12211
- children: chapters.filter((c) => c.status !== "pending" && c.status !== "generating").map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
12212
- value: c.no,
12213
- children: [
12214
- "第",
12215
- c.no,
12216
- "章 ",
12217
- c.title
12218
- ]
12219
- }, c.no))
12220
- }),
12221
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
12222
- className: panel_module_css_default.input,
12223
- style: { width: 100 },
12224
- value: storyboardTool,
13176
+ placeholder: "幕归属(第一幕后场…)",
13177
+ value: sceneDraft.act ?? "",
12225
13178
  onChange: (e) => {
12226
- setStoryboardTool(e.target.value);
13179
+ setSceneDraft({
13180
+ ...sceneDraft,
13181
+ act: e.target.value
13182
+ });
13183
+ }
13184
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
13185
+ className: panel_module_css_default.input,
13186
+ style: {
13187
+ fontSize: 12,
13188
+ padding: "4px 8px",
13189
+ flex: 1
12227
13190
  },
12228
- title: "AI 工具(影响提示词格式)",
12229
- children: [
12230
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
12231
- value: "doubao",
12232
- children: "豆包"
12233
- }),
12234
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
12235
- value: "seedance",
12236
- children: "Seedance"
12237
- }),
12238
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
12239
- value: "sd",
12240
- children: "SD/ComfyUI"
12241
- }),
12242
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
12243
- value: "mj",
12244
- children: "Midjourney"
12245
- })
12246
- ]
12247
- }),
12248
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13191
+ placeholder: "时间光态(雨夜/闭店后…)",
13192
+ value: sceneDraft.moment ?? "",
13193
+ onChange: (e) => {
13194
+ setSceneDraft({
13195
+ ...sceneDraft,
13196
+ moment: e.target.value
13197
+ });
13198
+ }
13199
+ })]
13200
+ }),
13201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
13202
+ className: panel_module_css_default.input,
13203
+ style: {
13204
+ fontSize: 12,
13205
+ padding: "4px 8px",
13206
+ minHeight: 48
13207
+ },
13208
+ placeholder: "关键镜头(每行一条:人物动作+情绪+镜头)",
13209
+ value: (sceneDraft.beats ?? []).join("\n"),
13210
+ onChange: (e) => {
13211
+ setSceneDraft({
13212
+ ...sceneDraft,
13213
+ beats: e.target.value.split(/\n+/).map((x) => x.trim()).filter(Boolean)
13214
+ });
13215
+ }
13216
+ }),
13217
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
13218
+ className: panel_module_css_default.input,
13219
+ style: {
13220
+ fontSize: 12,
13221
+ padding: "4px 8px"
13222
+ },
13223
+ placeholder: "人物在场状态(含标志物细节)",
13224
+ value: sceneDraft.characterState ?? "",
13225
+ onChange: (e) => {
13226
+ setSceneDraft({
13227
+ ...sceneDraft,
13228
+ characterState: e.target.value
13229
+ });
13230
+ }
13231
+ }),
13232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
13233
+ className: panel_module_css_default.input,
13234
+ style: {
13235
+ fontSize: 12,
13236
+ padding: "4px 8px",
13237
+ minHeight: 60
13238
+ },
13239
+ placeholder: "环境构成(每行一项)",
13240
+ value: (sceneDraft.elements ?? []).join("\n"),
13241
+ onChange: (e) => {
13242
+ setSceneDraft({
13243
+ ...sceneDraft,
13244
+ elements: e.target.value.split(/\n+/).map((x) => x.trim()).filter(Boolean)
13245
+ });
13246
+ }
13247
+ }),
13248
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
13249
+ className: panel_module_css_default.input,
13250
+ style: {
13251
+ fontSize: 12,
13252
+ padding: "4px 8px",
13253
+ minHeight: 60
13254
+ },
13255
+ placeholder: "中文生图提示词",
13256
+ value: sceneDraft.zh,
13257
+ onChange: (e) => {
13258
+ setSceneDraft({
13259
+ ...sceneDraft,
13260
+ zh: e.target.value
13261
+ });
13262
+ }
13263
+ }),
13264
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
13265
+ className: panel_module_css_default.input,
13266
+ style: {
13267
+ fontSize: 12,
13268
+ padding: "4px 8px",
13269
+ minHeight: 60
13270
+ },
13271
+ placeholder: "英文生图提示词",
13272
+ value: sceneDraft.en,
13273
+ onChange: (e) => {
13274
+ setSceneDraft({
13275
+ ...sceneDraft,
13276
+ en: e.target.value
13277
+ });
13278
+ }
13279
+ }),
13280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13281
+ className: panel_module_css_default.row,
13282
+ style: { gap: 6 },
13283
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12249
13284
  type: "button",
12250
13285
  className: `${panel_module_css_default.button} ${panel_module_css_default.buttonPrimary}`,
12251
- disabled: busy,
13286
+ disabled: busy || sceneDraft.name.trim() === "",
13287
+ onClick: () => {
13288
+ (async () => {
13289
+ setBusy(true);
13290
+ try {
13291
+ await api.scenes({
13292
+ op: "update",
13293
+ scene: {
13294
+ ...sceneDraft,
13295
+ name: sceneDraft.name.trim()
13296
+ }
13297
+ });
13298
+ await refresh(false);
13299
+ setSceneDraft(null);
13300
+ pushProgress(`已保存场景「${sceneDraft.name.trim()}」`, "done");
13301
+ } catch (err) {
13302
+ setError(err.message);
13303
+ } finally {
13304
+ setBusy(false);
13305
+ }
13306
+ })();
13307
+ },
13308
+ children: "保存"
13309
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13310
+ type: "button",
13311
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
12252
13312
  onClick: () => {
12253
- handleStoryboard(storyboardChapterNo);
13313
+ setSceneDraft(null);
12254
13314
  },
12255
- title: "把所选章节改编为抖音漫剧分镜(约 1-2 分钟,消耗 LLM 额度)",
12256
- children: busy ? "⏳ 生成中…" : "✨ 生成分镜"
12257
- })
12258
- ]
12259
- })]
12260
- }), storyboardResult === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12261
- className: panel_module_css_default.shelfEmpty,
12262
- style: { minHeight: 140 },
12263
- children: [
12264
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12265
- className: panel_module_css_default.shelfEmptyIcon,
12266
- children: "🎬"
12267
- }),
12268
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12269
- className: panel_module_css_default.shelfEmptyTitle,
12270
- children: "尚未生成分镜"
12271
- }),
12272
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12273
- className: panel_module_css_default.meta,
12274
- children: "选择章节与 AI 工具,点「生成分镜」开始"
13315
+ children: "取消"
13316
+ })]
12275
13317
  })
12276
13318
  ]
12277
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13319
+ }),
13320
+ sceneCandidates !== null && sceneCandidates.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12278
13321
  style: {
12279
13322
  display: "flex",
12280
13323
  flexDirection: "column",
12281
- gap: 10
13324
+ gap: 6,
13325
+ marginBottom: 10
12282
13326
  },
12283
- children: [
12284
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12285
- className: panel_module_css_default.meta,
12286
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "本集:" }), storyboardResult.title]
12287
- }),
12288
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12289
- className: panel_module_css_default.meta,
12290
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "赛道节奏:" }), storyboardResult.pacingNote]
12291
- }),
12292
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12293
- className: panel_module_css_default.meta,
12294
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "本集钩子:" }), storyboardResult.hook]
12295
- }),
12296
- storyboardResult.characters.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12297
- style: {
12298
- display: "flex",
12299
- flexDirection: "column",
12300
- gap: 6
12301
- },
12302
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: "角色视觉锚点(全分镜强制复用)" }), storyboardResult.characters.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13327
+ children: sceneCandidates.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13328
+ style: {
13329
+ border: "1px solid var(--nf-accent)",
13330
+ borderRadius: 10,
13331
+ padding: "8px 10px",
13332
+ fontSize: 12
13333
+ },
13334
+ children: [
13335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13336
+ className: panel_module_css_default.row,
12303
13337
  style: {
12304
- border: "1px solid var(--nf-border)",
12305
- borderRadius: 8,
12306
- padding: "6px 10px",
12307
- fontSize: 12
13338
+ flexWrap: "wrap",
13339
+ gap: 6,
13340
+ justifyContent: "space-between"
12308
13341
  },
12309
- children: [
12310
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: c.name }) }),
12311
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13342
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
13343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: s.name }),
13344
+ " ",
13345
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12312
13346
  className: panel_module_css_default.meta,
12313
- children: c.visualAnchor
12314
- }),
12315
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13347
+ children: ["· ", (s.moods ?? []).join("、")]
13348
+ })
13349
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
13350
+ className: panel_module_css_default.row,
13351
+ style: { gap: 4 },
13352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13353
+ type: "button",
13354
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13355
+ onClick: () => {
13356
+ (async () => {
13357
+ try {
13358
+ await api.scenes({
13359
+ op: "adopt",
13360
+ scene: s
13361
+ });
13362
+ setSceneCandidates((sceneCandidates ?? []).filter((x) => x.name !== s.name));
13363
+ await refresh(false);
13364
+ pushProgress(`已采纳场景「${s.name}」`, "done");
13365
+ } catch (err) {
13366
+ setError(err.message);
13367
+ }
13368
+ })();
13369
+ },
13370
+ children: "采纳"
13371
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13372
+ type: "button",
13373
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13374
+ onClick: () => {
13375
+ setSceneDraft(s);
13376
+ setSceneCandidates(null);
13377
+ },
13378
+ children: "修改后采纳"
13379
+ })]
13380
+ })]
13381
+ }),
13382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13383
+ className: panel_module_css_default.meta,
13384
+ style: { marginTop: 2 },
13385
+ children: s.summary
13386
+ }),
13387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13388
+ className: panel_module_css_default.meta,
13389
+ style: {
13390
+ marginTop: 2,
13391
+ fontSize: 11
13392
+ },
13393
+ children: ["要素:", (s.elements ?? []).join(";")]
13394
+ })
13395
+ ]
13396
+ }, s.name))
13397
+ }),
13398
+ (() => {
13399
+ const scenes = project?.scenes ?? [];
13400
+ const detail = sceneDetailName !== null ? scenes.find((s) => s.name === sceneDetailName) : void 0;
13401
+ if (scenes.length === 0 && sceneCandidates === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13402
+ className: panel_module_css_default.shelfEmpty,
13403
+ style: {
13404
+ minHeight: 140,
13405
+ flex: 1
13406
+ },
13407
+ children: [
13408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13409
+ className: panel_module_css_default.shelfEmptyIcon,
13410
+ children: "🏞️"
13411
+ }),
13412
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13413
+ className: panel_module_css_default.shelfEmptyTitle,
13414
+ children: "场景库为空"
13415
+ }),
13416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13417
+ className: panel_module_css_default.meta,
13418
+ children: "点「✨ 从全书提炼场景」自动建立,或手动新增"
13419
+ })
13420
+ ]
13421
+ });
13422
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13423
+ style: {
13424
+ display: "grid",
13425
+ gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
13426
+ gap: 10
13427
+ },
13428
+ children: scenes.map((s) => {
13429
+ const cover = (s.gallery ?? [])[0];
13430
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13431
+ onClick: () => {
13432
+ setSceneDetailName(s.name);
13433
+ setSceneUploadLabel("全景");
13434
+ },
13435
+ style: {
13436
+ cursor: "pointer",
13437
+ border: "1px solid var(--nf-border)",
13438
+ borderRadius: 12,
13439
+ overflow: "hidden",
13440
+ background: "var(--nf-bg-inset)",
13441
+ display: "flex",
13442
+ flexDirection: "column"
13443
+ },
13444
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13445
+ style: {
13446
+ height: 100,
13447
+ display: "flex",
13448
+ alignItems: "center",
13449
+ justifyContent: "center",
13450
+ background: "var(--nf-bg-inset)",
13451
+ borderBottom: "1px solid var(--nf-border)",
13452
+ position: "relative"
13453
+ },
13454
+ children: [cover !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
13455
+ src: cover.dataUrl,
13456
+ alt: s.name,
13457
+ style: {
13458
+ width: "100%",
13459
+ height: "100%",
13460
+ objectFit: "cover"
13461
+ }
13462
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12316
13463
  className: panel_module_css_default.meta,
13464
+ children: "暂无场景图"
13465
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12317
13466
  style: {
12318
- fontFamily: "monospace",
12319
- fontSize: 11
13467
+ position: "absolute",
13468
+ bottom: 4,
13469
+ right: 4,
13470
+ background: "rgba(0,0,0,0.6)",
13471
+ color: "#fff",
13472
+ fontSize: 10,
13473
+ borderRadius: 6,
13474
+ padding: "1px 6px"
12320
13475
  },
12321
- children: c.tags
12322
- })
12323
- ]
12324
- }, c.name))]
12325
- }),
12326
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13476
+ children: ["🖼 ", (s.gallery ?? []).length]
13477
+ })]
13478
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13479
+ style: {
13480
+ padding: 6,
13481
+ display: "flex",
13482
+ flexDirection: "column",
13483
+ gap: 2
13484
+ },
13485
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13486
+ style: { fontSize: 13 },
13487
+ children: s.name
13488
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13489
+ className: panel_module_css_default.meta,
13490
+ style: { fontSize: 10 },
13491
+ children: (s.moods ?? []).join("、") || s.summary.slice(0, 16)
13492
+ })]
13493
+ })]
13494
+ }, s.name);
13495
+ })
13496
+ }), detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13497
+ style: {
13498
+ position: "fixed",
13499
+ inset: 0,
13500
+ background: "rgba(0,0,0,0.55)",
13501
+ zIndex: 1e3,
13502
+ display: "flex",
13503
+ alignItems: "center",
13504
+ justifyContent: "center",
13505
+ padding: 24
13506
+ },
13507
+ onClick: () => {
13508
+ setSceneDetailName(null);
13509
+ },
13510
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12327
13511
  style: {
12328
- display: "flex",
12329
- flexDirection: "column",
12330
- gap: 6
13512
+ background: "var(--nf-bg)",
13513
+ border: "1px solid var(--nf-border)",
13514
+ borderRadius: 16,
13515
+ width: "min(860px, 100%)",
13516
+ maxHeight: "92vh",
13517
+ overflowY: "auto",
13518
+ padding: 20,
13519
+ position: "relative"
12331
13520
  },
12332
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12333
- className: panel_module_css_default.row,
12334
- style: { justifyContent: "space-between" },
12335
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [
12336
- "分镜表(",
12337
- storyboardResult.panels.length,
12338
- " 格)"
12339
- ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13521
+ onClick: (e) => e.stopPropagation(),
13522
+ children: [
13523
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12340
13524
  type: "button",
12341
- className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13525
+ className: panel_module_css_default.iconButton,
13526
+ style: {
13527
+ position: "absolute",
13528
+ top: 12,
13529
+ right: 12,
13530
+ fontSize: 18
13531
+ },
13532
+ title: "关闭",
13533
+ "aria-label": "关闭",
12342
13534
  onClick: () => {
12343
- const text = storyboardResult.panels.map((p) => `[${p.timecode}] ${p.shot}\n画面:${p.visual}\n台词:${p.dialogue}\n转场:${p.transition}\n提示词:${p.prompt}`).join("\n\n");
12344
- navigator.clipboard?.writeText(text).then(() => {
12345
- pushProgress("分镜表已复制到剪贴板", "done");
12346
- }).catch(() => {});
13535
+ setSceneDetailName(null);
12347
13536
  },
12348
- title: "复制全部分镜提示词",
12349
- children: "📋 复制全部"
12350
- })]
12351
- }), storyboardResult.panels.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12352
- style: {
12353
- border: "1px solid var(--nf-border)",
12354
- borderRadius: 8,
12355
- padding: "6px 10px",
12356
- fontSize: 12
12357
- },
12358
- children: [
12359
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12360
- className: panel_module_css_default.row,
13537
+ children: ""
13538
+ }),
13539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13540
+ style: { marginBottom: 12 },
13541
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13542
+ style: { fontSize: 20 },
13543
+ children: detail.name
13544
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13545
+ className: panel_module_css_default.meta,
13546
+ style: { marginTop: 4 },
13547
+ children: detail.summary
13548
+ })]
13549
+ }),
13550
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13551
+ style: {
13552
+ display: "grid",
13553
+ gridTemplateColumns: "repeat(auto-fill, minmax(160px, 1fr))",
13554
+ gap: 8,
13555
+ marginBottom: 12
13556
+ },
13557
+ children: [(detail.gallery ?? []).map((img) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12361
13558
  style: {
12362
- flexWrap: "wrap",
12363
- gap: 6
13559
+ border: "1px solid var(--nf-border)",
13560
+ borderRadius: 10,
13561
+ overflow: "hidden",
13562
+ position: "relative"
12364
13563
  },
12365
13564
  children: [
12366
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12367
- className: panel_module_css_default.badge,
13565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
13566
+ src: img.dataUrl,
13567
+ alt: img.label,
13568
+ style: {
13569
+ width: "100%",
13570
+ height: 100,
13571
+ objectFit: "cover",
13572
+ display: "block"
13573
+ }
13574
+ }),
13575
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12368
13576
  style: {
12369
- borderColor: "var(--nf-accent)",
12370
- color: "var(--nf-accent)",
12371
- flex: "none"
13577
+ background: "rgba(0,0,0,0.55)",
13578
+ color: "#fff",
13579
+ fontSize: 10,
13580
+ padding: "1px 6px",
13581
+ textAlign: "center"
12372
13582
  },
12373
- children: p.timecode
13583
+ children: img.label
12374
13584
  }),
12375
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: p.shot }),
12376
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12377
- className: panel_module_css_default.meta,
12378
- children: ["转场:", p.transition]
13585
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13586
+ type: "button",
13587
+ className: panel_module_css_default.iconButton,
13588
+ style: {
13589
+ position: "absolute",
13590
+ top: 2,
13591
+ right: 2,
13592
+ fontSize: 11
13593
+ },
13594
+ title: "删除",
13595
+ "aria-label": "删除",
13596
+ onClick: () => {
13597
+ (async () => {
13598
+ try {
13599
+ await api.scenes({
13600
+ op: "removeImage",
13601
+ name: detail.name,
13602
+ label: img.label
13603
+ });
13604
+ await refresh(false);
13605
+ pushProgress(`已删除「${detail.name}」${img.label}`, "done");
13606
+ } catch (err) {
13607
+ setError(err.message);
13608
+ }
13609
+ })();
13610
+ },
13611
+ children: "×"
12379
13612
  })
12380
13613
  ]
12381
- }),
12382
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12383
- className: panel_module_css_default.meta,
12384
- children: p.visual
12385
- }),
12386
- p.dialogue !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12387
- className: panel_module_css_default.meta,
12388
- style: { color: "var(--nf-info)" },
12389
- children: ["💬 ", p.dialogue]
12390
- }),
12391
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13614
+ }, img.label)), (detail.gallery ?? []).length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12392
13615
  className: panel_module_css_default.meta,
13616
+ children: "暂无场景图"
13617
+ })]
13618
+ }),
13619
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13620
+ style: {
13621
+ display: "grid",
13622
+ gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))",
13623
+ gap: 14,
13624
+ marginBottom: 12
13625
+ },
13626
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12393
13627
  style: {
12394
- fontFamily: "monospace",
12395
- fontSize: 11,
12396
- color: "var(--nf-text-2)"
13628
+ fontSize: 12,
13629
+ lineHeight: 1.8
12397
13630
  },
12398
- children: p.prompt
12399
- })
12400
- ]
12401
- }, p.index))]
13631
+ children: [
13632
+ (detail.act !== void 0 || detail.moment !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13633
+ style: { marginBottom: 6 },
13634
+ children: [detail.act !== void 0 && detail.act !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13635
+ className: panel_module_css_default.badge,
13636
+ style: {
13637
+ borderColor: "var(--nf-accent)",
13638
+ color: "var(--nf-accent)",
13639
+ marginRight: 6
13640
+ },
13641
+ children: detail.act
13642
+ }), detail.moment !== void 0 && detail.moment !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13643
+ className: panel_module_css_default.meta,
13644
+ children: detail.moment
13645
+ })]
13646
+ }),
13647
+ (detail.beats ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13648
+ style: { marginBottom: 8 },
13649
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13650
+ style: { fontSize: 13 },
13651
+ children: "关键镜头"
13652
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13653
+ style: { marginTop: 4 },
13654
+ children: (detail.beats ?? []).map((b, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13655
+ style: {
13656
+ fontSize: 11,
13657
+ color: "var(--nf-text-2)",
13658
+ marginTop: 2
13659
+ },
13660
+ children: ["🎬 ", b]
13661
+ }, i))
13662
+ })]
13663
+ }),
13664
+ detail.characterState !== void 0 && detail.characterState !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13665
+ style: { marginBottom: 8 },
13666
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13667
+ style: { fontSize: 13 },
13668
+ children: "人物状态"
13669
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13670
+ className: panel_module_css_default.meta,
13671
+ style: { marginTop: 2 },
13672
+ children: detail.characterState
13673
+ })]
13674
+ }),
13675
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13676
+ style: { fontSize: 13 },
13677
+ children: "环境构成"
13678
+ }),
13679
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13680
+ style: { marginTop: 4 },
13681
+ children: (detail.elements ?? []).map((e) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13682
+ style: {
13683
+ border: "1px solid var(--nf-border)",
13684
+ borderRadius: 999,
13685
+ padding: "1px 8px",
13686
+ fontSize: 11,
13687
+ margin: "0 4px 4px 0",
13688
+ display: "inline-block"
13689
+ },
13690
+ children: e
13691
+ }, e))
13692
+ }),
13693
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13694
+ style: { marginTop: 8 },
13695
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13696
+ style: { fontSize: 13 },
13697
+ children: "色调光影"
13698
+ })
13699
+ }),
13700
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13701
+ style: { marginTop: 4 },
13702
+ children: (detail.palette ?? []).join(";") || "—"
13703
+ }),
13704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13705
+ style: { marginTop: 8 },
13706
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13707
+ style: { fontSize: 13 },
13708
+ children: "氛围"
13709
+ })
13710
+ }),
13711
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13712
+ style: { marginTop: 4 },
13713
+ children: (detail.moods ?? []).join("、") || "—"
13714
+ }),
13715
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13716
+ style: { marginTop: 8 },
13717
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13718
+ style: { fontSize: 13 },
13719
+ children: "依据"
13720
+ })
13721
+ }),
13722
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13723
+ className: panel_module_css_default.meta,
13724
+ style: { marginTop: 4 },
13725
+ children: detail.source || "—"
13726
+ })
13727
+ ]
13728
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
13729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
13730
+ style: { fontSize: 13 },
13731
+ children: "生图提示词"
13732
+ }),
13733
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13734
+ className: panel_module_css_default.meta,
13735
+ style: {
13736
+ fontSize: 11,
13737
+ lineHeight: 1.6,
13738
+ marginTop: 4
13739
+ },
13740
+ children: detail.zh
13741
+ }),
13742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13743
+ className: panel_module_css_default.meta,
13744
+ style: {
13745
+ fontSize: 10,
13746
+ fontFamily: "monospace",
13747
+ marginTop: 4,
13748
+ wordBreak: "break-all"
13749
+ },
13750
+ children: detail.en
13751
+ }),
13752
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13753
+ style: {
13754
+ display: "flex",
13755
+ flexWrap: "wrap",
13756
+ gap: 4,
13757
+ marginTop: 8
13758
+ },
13759
+ children: (detail.tags ?? []).map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13760
+ style: {
13761
+ border: "1px solid var(--nf-border)",
13762
+ borderRadius: 999,
13763
+ padding: "2px 8px",
13764
+ fontSize: 11
13765
+ },
13766
+ children: t
13767
+ }, t))
13768
+ })
13769
+ ] })]
13770
+ }),
13771
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13772
+ style: {
13773
+ borderTop: "1px solid var(--nf-border)",
13774
+ paddingTop: 10,
13775
+ display: "flex",
13776
+ flexWrap: "wrap",
13777
+ gap: 6,
13778
+ alignItems: "center"
13779
+ },
13780
+ children: [
13781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13782
+ type: "button",
13783
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13784
+ onClick: () => {
13785
+ const text = [detail.en, detail.tags.join(", ")].filter(Boolean).join("\n");
13786
+ navigator.clipboard?.writeText(text).then(() => {
13787
+ pushProgress(`已复制「${detail.name}」英文生图提示词`, "done");
13788
+ }).catch(() => {});
13789
+ },
13790
+ children: "复制提示词"
13791
+ }),
13792
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
13793
+ className: panel_module_css_default.input,
13794
+ style: {
13795
+ width: 130,
13796
+ fontSize: 12,
13797
+ padding: "3px 6px"
13798
+ },
13799
+ placeholder: "图集标签",
13800
+ value: sceneUploadLabel,
13801
+ onChange: (e) => {
13802
+ setSceneUploadLabel(e.target.value);
13803
+ }
13804
+ }),
13805
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13806
+ type: "button",
13807
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13808
+ disabled: busy,
13809
+ onClick: () => {
13810
+ setRoleImageLabel(sceneUploadLabel.trim() !== "" ? sceneUploadLabel.trim() : "全景");
13811
+ setSceneUploadLabel(sceneUploadLabel.trim() !== "" ? sceneUploadLabel.trim() : "全景");
13812
+ setRoleImageTarget(detail.name + "||scene");
13813
+ roleImageInputRef.current?.click();
13814
+ },
13815
+ children: "📤 上传场景图"
13816
+ }),
13817
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13818
+ type: "button",
13819
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13820
+ onClick: () => {
13821
+ setSceneDraft(detail);
13822
+ setSceneDetailName(null);
13823
+ },
13824
+ children: "✏️ 编辑"
13825
+ }),
13826
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13827
+ type: "button",
13828
+ className: `${panel_module_css_default.button} ${panel_module_css_default.buttonSmall}`,
13829
+ style: { color: "var(--nf-danger, #e05)" },
13830
+ onClick: () => {
13831
+ (async () => {
13832
+ if (!window.confirm(`确定删除场景「${detail.name}」?`)) return;
13833
+ try {
13834
+ await api.scenes({
13835
+ op: "remove",
13836
+ name: detail.name
13837
+ });
13838
+ await refresh(false);
13839
+ setSceneDetailName(null);
13840
+ pushProgress(`已删除场景「${detail.name}」`, "done");
13841
+ } catch (err) {
13842
+ setError(err.message);
13843
+ }
13844
+ })();
13845
+ },
13846
+ children: "🗑 删除"
13847
+ })
13848
+ ]
13849
+ })
13850
+ ]
12402
13851
  })
12403
- ]
12404
- })] })
13852
+ })] });
13853
+ })()
12405
13854
  ]
12406
13855
  })
12407
13856
  ] })