@lemoncat7/dsh-partner 2.5.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (653) hide show
  1. package/README.md +4 -0
  2. package/docs/THIRD_PARTY_LICENSES.md +13 -0
  3. package/docs/attachment-storage.md +18 -0
  4. package/docs/director-skills.md +24 -0
  5. package/docs/long-task-continuation.md +1 -1
  6. package/docs/releases/2.6.0.md +11 -0
  7. package/docs/releases/2.7.1.md +10 -0
  8. package/lib/agent-runtime.d.ts +3 -1
  9. package/lib/agent-runtime.d.ts.map +1 -1
  10. package/lib/agent-runtime.js +35 -11
  11. package/lib/agent-runtime.js.map +1 -1
  12. package/lib/api/features/attachments-api.d.ts.map +1 -1
  13. package/lib/api/features/attachments-api.js +31 -1
  14. package/lib/api/features/attachments-api.js.map +1 -1
  15. package/lib/api.d.ts +1 -1
  16. package/lib/api.d.ts.map +1 -1
  17. package/lib/api.js +1 -1
  18. package/lib/api.js.map +1 -1
  19. package/lib/attachments/grouped-storage.d.ts +5 -0
  20. package/lib/attachments/grouped-storage.d.ts.map +1 -0
  21. package/lib/attachments/grouped-storage.js +47 -0
  22. package/lib/attachments/grouped-storage.js.map +1 -0
  23. package/lib/attachments/service.d.ts +17 -0
  24. package/lib/attachments/service.d.ts.map +1 -1
  25. package/lib/attachments/service.js +100 -8
  26. package/lib/attachments/service.js.map +1 -1
  27. package/lib/attachments/shared-storage.d.ts +7 -0
  28. package/lib/attachments/shared-storage.d.ts.map +1 -0
  29. package/lib/attachments/shared-storage.js +44 -0
  30. package/lib/attachments/shared-storage.js.map +1 -0
  31. package/lib/attachments/storage-admin.d.ts +6 -0
  32. package/lib/attachments/storage-admin.d.ts.map +1 -0
  33. package/lib/attachments/storage-admin.js +43 -0
  34. package/lib/attachments/storage-admin.js.map +1 -0
  35. package/lib/attachments/storage-types.d.ts +33 -0
  36. package/lib/attachments/storage-types.d.ts.map +1 -0
  37. package/lib/attachments/storage-types.js +2 -0
  38. package/lib/attachments/storage-types.js.map +1 -0
  39. package/lib/attachments/tool.d.ts.map +1 -1
  40. package/lib/attachments/tool.js +16 -5
  41. package/lib/attachments/tool.js.map +1 -1
  42. package/lib/channels/delivery-policy.d.ts.map +1 -1
  43. package/lib/channels/delivery-policy.js +6 -4
  44. package/lib/channels/delivery-policy.js.map +1 -1
  45. package/lib/channels/direct/message-format.js +294 -157
  46. package/lib/channels/direct/message-format.js.map +4 -4
  47. package/lib/channels/manager.d.ts +6 -3
  48. package/lib/channels/manager.d.ts.map +1 -1
  49. package/lib/channels/manager.js +64 -44
  50. package/lib/channels/manager.js.map +1 -1
  51. package/lib/channels/weixin/api.d.ts.map +1 -1
  52. package/lib/channels/weixin/api.js +7 -2
  53. package/lib/channels/weixin/api.js.map +1 -1
  54. package/lib/channels/weixin/errors.d.ts +14 -0
  55. package/lib/channels/weixin/errors.d.ts.map +1 -0
  56. package/lib/channels/weixin/errors.js +29 -0
  57. package/lib/channels/weixin/errors.js.map +1 -0
  58. package/lib/channels/weixin/poll.d.ts +14 -0
  59. package/lib/channels/weixin/poll.d.ts.map +1 -0
  60. package/lib/channels/weixin/poll.js +50 -0
  61. package/lib/channels/weixin/poll.js.map +1 -0
  62. package/lib/client-controller.d.ts.map +1 -1
  63. package/lib/client-controller.js +3 -3
  64. package/lib/client-controller.js.map +1 -1
  65. package/lib/client.d.ts.map +1 -1
  66. package/lib/client.js +983 -575
  67. package/lib/client.js.map +4 -4
  68. package/lib/collaboration/composition.d.ts.map +1 -1
  69. package/lib/collaboration/composition.js +16 -6
  70. package/lib/collaboration/composition.js.map +1 -1
  71. package/lib/collaboration/domain.d.ts +6 -0
  72. package/lib/collaboration/domain.d.ts.map +1 -1
  73. package/lib/collaboration/domain.js.map +1 -1
  74. package/lib/collaboration/service.d.ts.map +1 -1
  75. package/lib/collaboration/service.js +33 -3
  76. package/lib/collaboration/service.js.map +1 -1
  77. package/lib/concern-notification.d.ts.map +1 -1
  78. package/lib/concern-notification.js +2 -1
  79. package/lib/concern-notification.js.map +1 -1
  80. package/lib/concern-tool.js +1 -1
  81. package/lib/concern-tool.js.map +1 -1
  82. package/lib/execution/reply-progress.d.ts.map +1 -1
  83. package/lib/execution/reply-progress.js +4 -2
  84. package/lib/execution/reply-progress.js.map +1 -1
  85. package/lib/execution/service.d.ts +1 -1
  86. package/lib/execution/service.d.ts.map +1 -1
  87. package/lib/execution/service.js +4 -7
  88. package/lib/execution/service.js.map +1 -1
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +3 -1
  91. package/lib/index.js.map +1 -1
  92. package/lib/memory-reflection.js +2 -2
  93. package/lib/memory-reflection.js.map +1 -1
  94. package/lib/message-source.d.ts +25 -0
  95. package/lib/message-source.d.ts.map +1 -0
  96. package/lib/message-source.js +8 -0
  97. package/lib/message-source.js.map +1 -0
  98. package/lib/observation-loop.js +4 -4
  99. package/lib/observation-loop.js.map +1 -1
  100. package/lib/pendant/reader.js +1 -1
  101. package/lib/pendant/reader.js.map +1 -1
  102. package/lib/pendant/renderer.d.ts.map +1 -1
  103. package/lib/pendant/renderer.js +18 -2
  104. package/lib/pendant/renderer.js.map +1 -1
  105. package/lib/pendant/settings-panel.js +1 -1
  106. package/lib/pendant/settings-panel.js.map +1 -1
  107. package/lib/pendant/startup.d.ts +4 -0
  108. package/lib/pendant/startup.d.ts.map +1 -0
  109. package/lib/pendant/startup.js +64 -0
  110. package/lib/pendant/startup.js.map +1 -0
  111. package/lib/pendant/widget.d.ts.map +1 -1
  112. package/lib/pendant/widget.js +2 -1
  113. package/lib/pendant/widget.js.map +1 -1
  114. package/lib/pendant-renderer.js +169 -169
  115. package/lib/persona/service.js +1 -1
  116. package/lib/persona/service.js.map +1 -1
  117. package/lib/requirements/attachments.d.ts +7 -0
  118. package/lib/requirements/attachments.d.ts.map +1 -0
  119. package/lib/requirements/attachments.js +26 -0
  120. package/lib/requirements/attachments.js.map +1 -0
  121. package/lib/requirements/domain.d.ts +1 -1
  122. package/lib/requirements/domain.d.ts.map +1 -1
  123. package/lib/requirements/service.d.ts.map +1 -1
  124. package/lib/requirements/service.js +4 -1
  125. package/lib/requirements/service.js.map +1 -1
  126. package/lib/scheduler/channel-origin.d.ts +7 -0
  127. package/lib/scheduler/channel-origin.d.ts.map +1 -0
  128. package/lib/scheduler/channel-origin.js +27 -0
  129. package/lib/scheduler/channel-origin.js.map +1 -0
  130. package/lib/scheduler/continuations.d.ts +2 -2
  131. package/lib/scheduler/continuations.d.ts.map +1 -1
  132. package/lib/scheduler/continuations.js +28 -8
  133. package/lib/scheduler/continuations.js.map +1 -1
  134. package/lib/scheduler/domain.d.ts +13 -0
  135. package/lib/scheduler/domain.d.ts.map +1 -1
  136. package/lib/scheduler/final-reply.d.ts +13 -0
  137. package/lib/scheduler/final-reply.d.ts.map +1 -0
  138. package/lib/scheduler/final-reply.js +21 -0
  139. package/lib/scheduler/final-reply.js.map +1 -0
  140. package/lib/skills/builtin-resources.d.ts +6 -0
  141. package/lib/skills/builtin-resources.d.ts.map +1 -0
  142. package/lib/skills/builtin-resources.js +47 -0
  143. package/lib/skills/builtin-resources.js.map +1 -0
  144. package/lib/skills/builtin.d.ts +2 -5
  145. package/lib/skills/builtin.d.ts.map +1 -1
  146. package/lib/skills/builtin.js +6 -0
  147. package/lib/skills/builtin.js.map +1 -1
  148. package/lib/skills/director-builtins.d.ts +8 -0
  149. package/lib/skills/director-builtins.d.ts.map +1 -0
  150. package/lib/skills/director-builtins.js +65 -0
  151. package/lib/skills/director-builtins.js.map +1 -0
  152. package/lib/skills/drama-builtin.d.ts +3 -0
  153. package/lib/skills/drama-builtin.d.ts.map +1 -0
  154. package/lib/skills/drama-builtin.js +49 -0
  155. package/lib/skills/drama-builtin.js.map +1 -0
  156. package/lib/skills/long-task-continuation.d.ts +2 -2
  157. package/lib/skills/long-task-continuation.d.ts.map +1 -1
  158. package/lib/skills/long-task-continuation.js +2 -2
  159. package/lib/skills/service.d.ts.map +1 -1
  160. package/lib/skills/service.js +8 -3
  161. package/lib/skills/service.js.map +1 -1
  162. package/lib/skills/viora-canvas-builtin.d.ts +3 -0
  163. package/lib/skills/viora-canvas-builtin.d.ts.map +1 -0
  164. package/lib/skills/viora-canvas-builtin.js +19 -0
  165. package/lib/skills/viora-canvas-builtin.js.map +1 -0
  166. package/lib/storage/migrate-layout.d.ts.map +1 -1
  167. package/lib/storage/migrate-layout.js +13 -4
  168. package/lib/storage/migrate-layout.js.map +1 -1
  169. package/lib/tasks/context.js +1 -0
  170. package/lib/tasks/context.js.map +1 -1
  171. package/lib/tasks/continuation.d.ts.map +1 -1
  172. package/lib/tasks/continuation.js +3 -0
  173. package/lib/tasks/continuation.js.map +1 -1
  174. package/lib/tasks/domain.d.ts +2 -0
  175. package/lib/tasks/domain.d.ts.map +1 -1
  176. package/lib/tasks/service.d.ts +2 -1
  177. package/lib/tasks/service.d.ts.map +1 -1
  178. package/lib/tasks/service.js +15 -1
  179. package/lib/tasks/service.js.map +1 -1
  180. package/lib/ui/attachment-storage.d.ts +2 -0
  181. package/lib/ui/attachment-storage.d.ts.map +1 -0
  182. package/lib/ui/attachment-storage.js +59 -0
  183. package/lib/ui/attachment-storage.js.map +1 -0
  184. package/lib/ui/capability-editor.js +1 -1
  185. package/lib/ui/capability-editor.js.map +1 -1
  186. package/lib/ui/companion-access-panel.js +1 -1
  187. package/lib/ui/companion-access-panel.js.map +1 -1
  188. package/lib/ui/companion-create.js +1 -1
  189. package/lib/ui/companion-create.js.map +1 -1
  190. package/lib/ui/companion-picker.js +1 -1
  191. package/lib/ui/companion-picker.js.map +1 -1
  192. package/lib/ui/identity-editor.js +1 -1
  193. package/lib/ui/identity-editor.js.map +1 -1
  194. package/lib/ui/mcp-capabilities.js +1 -1
  195. package/lib/ui/mcp-capabilities.js.map +1 -1
  196. package/lib/ui/mcp-panel.js +1 -1
  197. package/lib/ui/mcp-panel.js.map +1 -1
  198. package/lib/ui/memory/concern-board.js +2 -2
  199. package/lib/ui/memory/concern-board.js.map +1 -1
  200. package/lib/ui/memory/concern-deletion.d.ts +1 -1
  201. package/lib/ui/memory/concern-deletion.d.ts.map +1 -1
  202. package/lib/ui/memory/concern-deletion.js +9 -4
  203. package/lib/ui/memory/concern-deletion.js.map +1 -1
  204. package/lib/ui/memory/concern-panel.js +1 -1
  205. package/lib/ui/memory/concern-panel.js.map +1 -1
  206. package/lib/ui/memory/memory-library.js +1 -1
  207. package/lib/ui/memory/memory-library.js.map +1 -1
  208. package/lib/ui/memory/memory-panel.js +1 -1
  209. package/lib/ui/memory/memory-panel.js.map +1 -1
  210. package/lib/ui/partner-components.js +1 -1
  211. package/lib/ui/partner-components.js.map +1 -1
  212. package/lib/ui/requirement-board.js +1 -1
  213. package/lib/ui/requirement-board.js.map +1 -1
  214. package/lib/ui/requirement-dialog.js +1 -1
  215. package/lib/ui/requirement-dialog.js.map +1 -1
  216. package/lib/ui/requirement-tasks-panel.js +1 -1
  217. package/lib/ui/requirement-tasks-panel.js.map +1 -1
  218. package/lib/ui/schedule-panel.js +1 -1
  219. package/lib/ui/schedule-panel.js.map +1 -1
  220. package/lib/ui/skills-panel.js +1 -1
  221. package/lib/ui/skills-panel.js.map +1 -1
  222. package/lib/ui/storage-settings.d.ts.map +1 -1
  223. package/lib/ui/storage-settings.js +2 -1
  224. package/lib/ui/storage-settings.js.map +1 -1
  225. package/lib/ui/task-board-stage.js +1 -1
  226. package/lib/ui/task-board-stage.js.map +1 -1
  227. package/lib/ui/workspace-components.js +1 -1
  228. package/lib/ui/workspace-components.js.map +1 -1
  229. package/lib/ui/workspace-navigation.d.ts +16 -16
  230. package/lib/ui/workspace-navigation.js +1 -1
  231. package/lib/ui/workspace-navigation.js.map +1 -1
  232. package/package.json +53 -41
  233. package/resources/skills/cinematic-director/manifest.json +205 -0
  234. package/resources/skills/cinematic-director/upstream/CHANGELOG.md +213 -0
  235. package/resources/skills/cinematic-director/upstream/CONTRIBUTING.md +316 -0
  236. package/resources/skills/cinematic-director/upstream/LICENSE +21 -0
  237. package/resources/skills/cinematic-director/upstream/README.md +507 -0
  238. package/resources/skills/cinematic-director/upstream/SKILL.md +420 -0
  239. package/resources/skills/cinematic-director/upstream/assets/beat-sheet-template.md +97 -0
  240. package/resources/skills/cinematic-director/upstream/assets/director-book-template.md +254 -0
  241. package/resources/skills/cinematic-director/upstream/assets/edit-timeline-template.md +147 -0
  242. package/resources/skills/cinematic-director/upstream/assets/keyframe-prompt-template.md +334 -0
  243. package/resources/skills/cinematic-director/upstream/assets/qc-checklist.md +153 -0
  244. package/resources/skills/cinematic-director/upstream/assets/shot-plan-template.md +95 -0
  245. package/resources/skills/cinematic-director/upstream/assets/sound-plan-template.md +181 -0
  246. package/resources/skills/cinematic-director/upstream/assets/video-prompt-template.md +305 -0
  247. package/resources/skills/cinematic-director/upstream/evals/run.md +69 -0
  248. package/resources/skills/cinematic-director/upstream/references/ai-video-tool-adapters.md +527 -0
  249. package/resources/skills/cinematic-director/upstream/references/blocking-and-staging.md +424 -0
  250. package/resources/skills/cinematic-director/upstream/references/cinematic-language.md +595 -0
  251. package/resources/skills/cinematic-director/upstream/references/continuity-bible.md +410 -0
  252. package/resources/skills/cinematic-director/upstream/references/director_styles/01_spielberg.md +192 -0
  253. package/resources/skills/cinematic-director/upstream/references/director_styles/02_hitchcock.md +193 -0
  254. package/resources/skills/cinematic-director/upstream/references/director_styles/03_kubrick.md +196 -0
  255. package/resources/skills/cinematic-director/upstream/references/director_styles/04_kurosawa.md +196 -0
  256. package/resources/skills/cinematic-director/upstream/references/director_styles/05_scorsese.md +195 -0
  257. package/resources/skills/cinematic-director/upstream/references/director_styles/06_fellini.md +197 -0
  258. package/resources/skills/cinematic-director/upstream/references/director_styles/07_bergman.md +197 -0
  259. package/resources/skills/cinematic-director/upstream/references/director_styles/08_tarkovsky.md +184 -0
  260. package/resources/skills/cinematic-director/upstream/references/director_styles/09_wong_kar_wai.md +189 -0
  261. package/resources/skills/cinematic-director/upstream/references/director_styles/10_nolan.md +185 -0
  262. package/resources/skills/cinematic-director/upstream/references/director_styles/11_villeneuve.md +188 -0
  263. package/resources/skills/cinematic-director/upstream/references/director_styles/12_fincher.md +185 -0
  264. package/resources/skills/cinematic-director/upstream/references/director_styles/13_refn.md +186 -0
  265. package/resources/skills/cinematic-director/upstream/references/director_styles/14_bi_gan.md +188 -0
  266. package/resources/skills/cinematic-director/upstream/references/director_styles/15_zhang_yimou.md +192 -0
  267. package/resources/skills/cinematic-director/upstream/references/director_styles/16_hou_hsiao_hsien.md +192 -0
  268. package/resources/skills/cinematic-director/upstream/references/director_styles/17_park_chan_wook.md +192 -0
  269. package/resources/skills/cinematic-director/upstream/references/director_styles/18_malick.md +191 -0
  270. package/resources/skills/cinematic-director/upstream/references/director_styles/19_michael_mann.md +192 -0
  271. package/resources/skills/cinematic-director/upstream/references/director_styles/20_coen_brothers.md +194 -0
  272. package/resources/skills/cinematic-director/upstream/references/director_styles/README.md +89 -0
  273. package/resources/skills/cinematic-director/upstream/references/director_styles/example_comparisons.md +140 -0
  274. package/resources/skills/cinematic-director/upstream/references/editing-and-assembly.md +263 -0
  275. package/resources/skills/cinematic-director/upstream/references/failure-modes.md +594 -0
  276. package/resources/skills/cinematic-director/upstream/references/genre-playbooks.md +407 -0
  277. package/resources/skills/cinematic-director/upstream/references/image-model-adapters.md +290 -0
  278. package/resources/skills/cinematic-director/upstream/references/lighting-and-color.md +349 -0
  279. package/resources/skills/cinematic-director/upstream/references/product-and-macro.md +207 -0
  280. package/resources/skills/cinematic-director/upstream/references/production-workflow.md +238 -0
  281. package/resources/skills/cinematic-director/upstream/references/prompt-lexicon.md +602 -0
  282. package/resources/skills/cinematic-director/upstream/references/sound-and-dialogue.md +364 -0
  283. package/resources/skills/director-skills/manifest.json +541 -0
  284. package/resources/skills/director-skills/upstream/CHANGELOG.md +32 -0
  285. package/resources/skills/director-skills/upstream/LICENSE +21 -0
  286. package/resources/skills/director-skills/upstream/MANIFEST.md +366 -0
  287. package/resources/skills/director-skills/upstream/README.md +251 -0
  288. package/resources/skills/director-skills/upstream/RESEARCH_PROVENANCE.md +37 -0
  289. package/resources/skills/director-skills/upstream/SKILL.md +114 -0
  290. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/SKILL.md +81 -0
  291. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/checklists/continuity_checklist.md +8 -0
  292. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/character_continuity_update.md +9 -0
  293. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/examples/location_continuity_update.md +9 -0
  294. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/continuity_system.md +21 -0
  295. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/golden_image_workflow.md +29 -0
  296. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/identity_anchors.md +14 -0
  297. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/references/visual_bible_system.md +15 -0
  298. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/character_profile.md +15 -0
  299. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/continuity_checklist.md +11 -0
  300. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/costume_profile.md +12 -0
  301. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/location_profile.md +14 -0
  302. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/prop_profile.md +13 -0
  303. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/templates/visual_style_bible.md +15 -0
  304. package/resources/skills/director-skills/upstream/skills/character-location-prop-bible/tests/continuity_tests.md +6 -0
  305. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/SKILL.md +80 -0
  306. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/checklists/image_prompt_quality_checklist.md +10 -0
  307. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/examples/shot_list_to_image_prompts.md +4 -0
  308. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/image_prompt_components.md +22 -0
  309. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/lighting_and_photorealism.md +19 -0
  310. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/negative_prompt_patterns.md +18 -0
  311. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/references/prompt_anatomy.md +18 -0
  312. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/character_reference_sheet_prompt.md +5 -0
  313. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/cinematic_still_prompt.md +3 -0
  314. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/keyframe_prompt.md +6 -0
  315. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/location_reference_prompt.md +3 -0
  316. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/templates/prop_prompt.md +3 -0
  317. package/resources/skills/director-skills/upstream/skills/cinematic-image-prompting/tests/image_prompt_completeness_tests.md +5 -0
  318. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/SKILL.md +84 -0
  319. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/checklists/video_prompt_quality_checklist.md +9 -0
  320. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/examples/image_prompt_to_video_prompt.md +3 -0
  321. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/ai_film_production_pipeline.md +21 -0
  322. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/audio_motion_timing.md +16 -0
  323. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/camera_lens_movement.md +26 -0
  324. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/negative_prompt_patterns.md +18 -0
  325. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/prompt_anatomy.md +18 -0
  326. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/references/video_prompt_components.md +25 -0
  327. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/image_to_video_prompt.md +5 -0
  328. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/text_to_video_prompt.md +5 -0
  329. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/templates/video_beat_prompt.md +9 -0
  330. package/resources/skills/director-skills/upstream/skills/cinematic-video-prompting/tests/video_prompt_completeness_tests.md +5 -0
  331. package/resources/skills/director-skills/upstream/skills/model-adaptation/SKILL.md +82 -0
  332. package/resources/skills/director-skills/upstream/skills/model-adaptation/checklists/model_export_checklist.md +9 -0
  333. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/failed_model_transfer_diagnosis.md +5 -0
  334. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/image_prompt_translation.md +10 -0
  335. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/same_scene_across_models.md +9 -0
  336. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/universal_prompt_to_model_specific_exports.md +14 -0
  337. package/resources/skills/director-skills/upstream/skills/model-adaptation/examples/video_prompt_translation.md +9 -0
  338. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/failure_modes_by_model.md +13 -0
  339. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/image_model_profiles.md +226 -0
  340. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_capability_matrix.md +24 -0
  341. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/model_update_protocol.md +10 -0
  342. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/parameter_compatibility.md +20 -0
  343. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/prompt_translation_rules.md +18 -0
  344. package/resources/skills/director-skills/upstream/skills/model-adaptation/references/video_model_profiles.md +251 -0
  345. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_comparison_table.md +4 -0
  346. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/model_specific_prompt_export.md +21 -0
  347. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/multi_model_export_package.md +18 -0
  348. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/prompt_translation_report.md +13 -0
  349. package/resources/skills/director-skills/upstream/skills/model-adaptation/templates/universal_prompt_brief.md +25 -0
  350. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/model_routing_tests.md +8 -0
  351. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/prompt_translation_tests.md +7 -0
  352. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/research_provenance_tests.md +6 -0
  353. package/resources/skills/director-skills/upstream/skills/model-adaptation/tests/unknown_capability_tests.md +7 -0
  354. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/SKILL.md +82 -0
  355. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/diagnostic_checklist.md +8 -0
  356. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/checklists/release_quality_checklist.md +9 -0
  357. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/failed_output_to_revised_prompt.md +10 -0
  358. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/examples/final_prompt_package_export.md +10 -0
  359. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/failure_modes.md +13 -0
  360. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/iteration_diagnostics.md +17 -0
  361. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/revision_loops.md +17 -0
  362. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/references/story_voice_release_qc.md +30 -0
  363. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/failed_output_intake.md +13 -0
  364. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/final_production_package.md +11 -0
  365. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/templates/prompt_revision_report.md +17 -0
  366. package/resources/skills/director-skills/upstream/skills/prompt-iteration-and-diagnostics/tests/prompt_quality_checklist_tests.md +6 -0
  367. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/SKILL.md +81 -0
  368. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/checklists/scene_quality_checklist.md +8 -0
  369. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/examples/scene_to_screenplay_excerpt.md +18 -0
  370. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/dialogue_and_subtext.md +15 -0
  371. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/fountain_workflow.md +29 -0
  372. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/scene_construction.md +20 -0
  373. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/references/screenplay_format.md +18 -0
  374. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/beat_sheet.md +6 -0
  375. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/dialogue_pass.md +9 -0
  376. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/fountain_scene.md +25 -0
  377. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/templates/screenplay_scene.md +19 -0
  378. package/resources/skills/director-skills/upstream/skills/screenplay-and-scene-writing/tests/screenplay_scene_tests.md +6 -0
  379. package/resources/skills/director-skills/upstream/skills/short-film-development/SKILL.md +83 -0
  380. package/resources/skills/director-skills/upstream/skills/short-film-development/checklists/concept_quality_checklist.md +8 -0
  381. package/resources/skills/director-skills/upstream/skills/short-film-development/examples/idea_to_logline.md +13 -0
  382. package/resources/skills/director-skills/upstream/skills/short-film-development/examples/logline_to_outline.md +10 -0
  383. package/resources/skills/director-skills/upstream/skills/short-film-development/references/ai_storytelling_workflow.md +46 -0
  384. package/resources/skills/director-skills/upstream/skills/short-film-development/references/continuity_handoff.md +8 -0
  385. package/resources/skills/director-skills/upstream/skills/short-film-development/references/screenplay_structure.md +19 -0
  386. package/resources/skills/director-skills/upstream/skills/short-film-development/references/story_development_workflow.md +23 -0
  387. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/beat_sheet.md +7 -0
  388. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/idea_intake.md +16 -0
  389. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/logline.md +10 -0
  390. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_film_treatment.md +23 -0
  391. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/short_story_plan.md +22 -0
  392. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_premise.md +19 -0
  393. package/resources/skills/director-skills/upstream/skills/short-film-development/templates/story_revision_audit.md +22 -0
  394. package/resources/skills/director-skills/upstream/skills/short-film-development/tests/output_format_tests.md +3 -0
  395. package/resources/skills/director-skills/upstream/skills/short-film-development/tests/routing_tests.md +7 -0
  396. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/SKILL.md +77 -0
  397. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/checklists/shotlist_quality_checklist.md +8 -0
  398. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/examples/screenplay_scene_to_shot_list.md +8 -0
  399. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/camera_plan.md +11 -0
  400. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/production_breakdown.md +11 -0
  401. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/references/shotlist_schema.md +21 -0
  402. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/asset_list.md +7 -0
  403. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/camera_plan.md +10 -0
  404. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/scene_breakdown.md +16 -0
  405. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/templates/shot_list.md +5 -0
  406. package/resources/skills/director-skills/upstream/skills/shotlist-and-visual-breakdown/tests/screenplay_to_shotlist_tests.md +5 -0
  407. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/SKILL.md +77 -0
  408. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/checklists/style_quality_checklist.md +8 -0
  409. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/examples/genre_to_visual_direction.md +15 -0
  410. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/camera_lens_movement.md +26 -0
  411. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/cinematic_language.md +32 -0
  412. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/genre_style_tone.md +17 -0
  413. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/references/lighting_and_photorealism.md +19 -0
  414. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/templates/visual_style_bible.md +33 -0
  415. package/resources/skills/director-skills/upstream/skills/style-cinematography-director/tests/style_translation_tests.md +5 -0
  416. package/resources/skills/director-skills/upstream/tests/master_router_tests.md +80 -0
  417. package/resources/skills/drama-production/manifest.json +933 -0
  418. package/resources/skills/drama-production/upstream/LICENSE +21 -0
  419. package/resources/skills/drama-production/upstream/skills/short-drama/SKILL.md +153 -0
  420. package/resources/skills/drama-production/upstream/skills/short-drama/agents/openai.yaml +4 -0
  421. package/resources/skills/drama-production/upstream/skills/short-drama/assets/creator-decision.example.jsonl +4 -0
  422. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/app.js +1263 -0
  423. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/index.html +114 -0
  424. package/resources/skills/drama-production/upstream/skills/short-drama/assets/dashboard/styles.css +297 -0
  425. package/resources/skills/drama-production/upstream/skills/short-drama/assets/production-observation.example.jsonl +2 -0
  426. package/resources/skills/drama-production/upstream/skills/short-drama/assets/project-template/short-drama.json +36 -0
  427. package/resources/skills/drama-production/upstream/skills/short-drama/assets/reference-observation.example.jsonl +2 -0
  428. package/resources/skills/drama-production/upstream/skills/short-drama/references/audience-reveal.md +21 -0
  429. package/resources/skills/drama-production/upstream/skills/short-drama/references/contract-and-ownership.md +45 -0
  430. package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-documents.md +230 -0
  431. package/resources/skills/drama-production/upstream/skills/short-drama/references/creator-workflow.md +98 -0
  432. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards/Q/347/211/210/350/241/250/350/276/276.md +73 -0
  433. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//344/272/214/347/273/264/345/212/250/346/200/201/346/274/253.md +74 -0
  434. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//345/233/275/346/274/253/344/272/214/346/254/241/345/205/203.md +79 -0
  435. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//345/256/236/346/213/215.md +75 -0
  436. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//346/260/264/345/242/250/347/254/224/350/247/246.md +85 -0
  437. package/resources/skills/drama-production/upstream/skills/short-drama/references/form-cards//351/243/216/346/240/274/345/214/226/344/270/211/347/273/264.md +72 -0
  438. package/resources/skills/drama-production/upstream/skills/short-drama/references/knowhow-index.md +85 -0
  439. package/resources/skills/drama-production/upstream/skills/short-drama/references/look-development.md +51 -0
  440. package/resources/skills/drama-production/upstream/skills/short-drama/references/pickup-and-alternate.md +24 -0
  441. package/resources/skills/drama-production/upstream/skills/short-drama/references/production-form-profiles.md +126 -0
  442. package/resources/skills/drama-production/upstream/skills/short-drama/references/reference-roles.md +53 -0
  443. package/resources/skills/drama-production/upstream/skills/short-drama/references/runtime-preflight.md +24 -0
  444. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/creator_markdown_check.py +1413 -0
  445. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/dashboard_server.py +1798 -0
  446. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/project_tool.py +2454 -0
  447. package/resources/skills/drama-production/upstream/skills/short-drama/scripts/selftest.py +287 -0
  448. package/resources/skills/drama-production/upstream/skills/short-drama-assets/SKILL.md +76 -0
  449. package/resources/skills/drama-production/upstream/skills/short-drama-assets/agents/openai.yaml +4 -0
  450. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/character-look.example.jsonl +3 -0
  451. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/continuity.example.jsonl +4 -0
  452. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/decisions.example.jsonl +5 -0
  453. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/location-view.example.jsonl +3 -0
  454. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/occurrences.example.jsonl +5 -0
  455. package/resources/skills/drama-production/upstream/skills/short-drama-assets/assets/prop-state.example.jsonl +3 -0
  456. package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/characters.jsonl +2 -0
  457. package/resources/skills/drama-production/upstream/skills/short-drama-assets/examples/minimal/looks.jsonl +2 -0
  458. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/asset-review-checklist.md +82 -0
  459. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/character-and-look.md +190 -0
  460. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-delta.md +120 -0
  461. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/continuity-lock.md +122 -0
  462. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/identity-vs-variant.md +140 -0
  463. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/location-and-view.md +84 -0
  464. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/occurrence-extraction.md +108 -0
  465. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/prop-and-state.md +126 -0
  466. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/stage-contract.md +43 -0
  467. package/resources/skills/drama-production/upstream/skills/short-drama-assets/references/voice-direction.md +179 -0
  468. package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/asset_check.py +267 -0
  469. package/resources/skills/drama-production/upstream/skills/short-drama-assets/scripts/selftest.py +89 -0
  470. package/resources/skills/drama-production/upstream/skills/short-drama-develop/SKILL.md +157 -0
  471. package/resources/skills/drama-production/upstream/skills/short-drama-develop/agents/openai.yaml +4 -0
  472. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -0
  473. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/creative-brief.md +120 -0
  474. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/episode-map.jsonl +1 -0
  475. package/resources/skills/drama-production/upstream/skills/short-drama-develop/assets/story-engine.md +181 -0
  476. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/adaptation-craft.md +219 -0
  477. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/creative-reference-intake.md +57 -0
  478. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/director-brief-craft.md +113 -0
  479. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/episode-design.md +509 -0
  480. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-and-hook-playbook.md +240 -0
  481. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//344/272/262/345/255/220/351/232/220/347/247/230.md +112 -0
  482. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//344/273/231/344/276/240/344/277/256/347/234/237.md +51 -0
  483. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/212/250/344/275/234/344/273/273/345/212/241.md +51 -0
  484. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/217/244/350/243/205/346/235/203/350/260/213.md +51 -0
  485. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +51 -0
  486. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//345/256/266/345/272/255/345/205/263/347/263/273.md +51 -0
  487. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//346/202/254/347/226/221/350/247/204/345/210/231.md +51 -0
  488. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//347/224/237/346/264/273/346/265/201.md +51 -0
  489. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//347/240/264/351/225/234/351/207/215/345/234/206.md +109 -0
  490. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/201/214/345/234/272/345/226/234/345/211/247.md +51 -0
  491. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/261/252/351/227/250/345/251/232/346/201/213.md +51 -0
  492. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards//350/272/253/344/273/275/351/224/231/344/275/215.md +51 -0
  493. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/genre-cards.md +92 -0
  494. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/mechanism-loop.md +282 -0
  495. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/multi-episode-intake.md +128 -0
  496. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/premise-devices.md +211 -0
  497. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/reveal-reversal-payoff.md +99 -0
  498. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/serial-character-and-memory.md +293 -0
  499. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/stage-contract.md +88 -0
  500. package/resources/skills/drama-production/upstream/skills/short-drama-develop/references/story-craft.md +359 -0
  501. package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/episode_intake.py +699 -0
  502. package/resources/skills/drama-production/upstream/skills/short-drama-develop/scripts/selftest.py +47 -0
  503. package/resources/skills/drama-production/upstream/skills/short-drama-edit/SKILL.md +146 -0
  504. package/resources/skills/drama-production/upstream/skills/short-drama-edit/agents/openai.yaml +4 -0
  505. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/README.md +60 -0
  506. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/package.json +15 -0
  507. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  508. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  509. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  510. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  511. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  512. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  513. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  514. package/resources/skills/drama-production/upstream/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  515. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/cut-craft.md +38 -0
  516. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/delivery-verify.md +29 -0
  517. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/generated-footage.md +54 -0
  518. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
  519. package/resources/skills/drama-production/upstream/skills/short-drama-edit/references/stage-contract.md +51 -0
  520. package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
  521. package/resources/skills/drama-production/upstream/skills/short-drama-edit/scripts/selftest.py +520 -0
  522. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/SKILL.md +73 -0
  523. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/agents/openai.yaml +4 -0
  524. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompt-spec.jsonl.md +187 -0
  525. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/image-prompts.md +42 -0
  526. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-frame-spec.jsonl.md +128 -0
  527. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/assets/lookdev-prompts.md +34 -0
  528. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/examples/minimal-image-prompt-specs.jsonl +2 -0
  529. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/character-and-look.md +153 -0
  530. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/common-recipe.md +241 -0
  531. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/edit-and-revision.md +110 -0
  532. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/location-plate.md +151 -0
  533. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/look-and-state-variant.md +97 -0
  534. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/lookdev-frame.md +34 -0
  535. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/production-sheet-recipes.md +177 -0
  536. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/prop-plate.md +137 -0
  537. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/review-and-fixtures.md +92 -0
  538. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/references/stage-contract.md +32 -0
  539. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/image_prompt_check.py +443 -0
  540. package/resources/skills/drama-production/upstream/skills/short-drama-image-prompts/scripts/selftest.py +95 -0
  541. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/SKILL.md +245 -0
  542. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/agents/openai.yaml +4 -0
  543. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +3 -0
  544. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-triage.md +116 -0
  545. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/adaptation-value.md +124 -0
  546. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +155 -0
  547. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/chapter-extraction.md +144 -0
  548. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/references/stage-contract.md +67 -0
  549. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/novel_index.py +651 -0
  550. package/resources/skills/drama-production/upstream/skills/short-drama-novel-analyze/scripts/selftest.py +51 -0
  551. package/resources/skills/drama-production/upstream/skills/short-drama-produce/SKILL.md +183 -0
  552. package/resources/skills/drama-production/upstream/skills/short-drama-produce/agents/openai.yaml +4 -0
  553. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/adapter-contract.md +219 -0
  554. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/gpt-image-2.md +24 -0
  555. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-h3-video.md +86 -0
  556. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-music.md +25 -0
  557. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  558. package/resources/skills/drama-production/upstream/skills/short-drama-produce/references/providers/seedance.md +64 -0
  559. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/fixture_adapter.py +77 -0
  560. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/production_tool.py +2205 -0
  561. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/provider_adapters.py +1747 -0
  562. package/resources/skills/drama-production/upstream/skills/short-drama-produce/scripts/selftest.py +135 -0
  563. package/resources/skills/drama-production/upstream/skills/short-drama-review/SKILL.md +118 -0
  564. package/resources/skills/drama-production/upstream/skills/short-drama-review/agents/openai.yaml +4 -0
  565. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/finding-template.jsonl +2 -0
  566. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/supersession-decision.example.json +42 -0
  567. package/resources/skills/drama-production/upstream/skills/short-drama-review/assets/verdict-template.json +32 -0
  568. package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-findings.jsonl +2 -0
  569. package/resources/skills/drama-production/upstream/skills/short-drama-review/examples/minimal-verdict.json +34 -0
  570. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/anti-template-repair.md +109 -0
  571. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/production-quality-gates.md +276 -0
  572. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/project-calibration.md +83 -0
  573. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/review-method.md +81 -0
  574. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-assets-prompts.md +111 -0
  575. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-source-analysis.md +108 -0
  576. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-story-script.md +264 -0
  577. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/rubric-visual-motion.md +184 -0
  578. package/resources/skills/drama-production/upstream/skills/short-drama-review/references/stage-contract.md +28 -0
  579. package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/review_check.py +325 -0
  580. package/resources/skills/drama-production/upstream/skills/short-drama-review/scripts/selftest.py +87 -0
  581. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/SKILL.md +175 -0
  582. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/agents/openai.yaml +4 -0
  583. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-audition.example.jsonl +2 -0
  584. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/coverage-template.json +49 -0
  585. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-prompts.md +36 -0
  586. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/keyframe-template.jsonl +2 -0
  587. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/revision-lineage.fragment.json +12 -0
  588. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/scene-visual-plan.example.jsonl +2 -0
  589. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/assets/shot-template.jsonl +2 -0
  590. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/blocking-playbooks.md +216 -0
  591. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/comic-keyframe-lexicon.md +156 -0
  592. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/coverage-audition.md +34 -0
  593. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/keyframe-craft.md +174 -0
  594. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  595. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/production-shot-grammar.md +279 -0
  596. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/review-and-fixtures.md +36 -0
  597. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/scene-visual-plan.md +30 -0
  598. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +155 -0
  599. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-craft.md +266 -0
  600. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/shot-revision-identity.md +25 -0
  601. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/references/stage-contract.md +57 -0
  602. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/selftest.py +191 -0
  603. package/resources/skills/drama-production/upstream/skills/short-drama-storyboard/scripts/storyboard_check.py +824 -0
  604. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/SKILL.md +154 -0
  605. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/agents/openai.yaml +4 -0
  606. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/coverage-scope.fragment.json +21 -0
  607. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/delivery-container.jsonl.md +101 -0
  608. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +217 -0
  609. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/motion-terminal.example.jsonl +2 -0
  610. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/music-spec.jsonl.md +31 -0
  611. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/performance.fragment.json +38 -0
  612. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/assets/video-prompts.md +67 -0
  613. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/examples/minimal-music-specs.jsonl +2 -0
  614. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/camera-audio-continuity.md +259 -0
  615. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/delivery-profile.md +273 -0
  616. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/generability.md +93 -0
  617. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/minimax-h3.md +253 -0
  618. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/motion-recipe.md +315 -0
  619. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/performance-action-timing.md +209 -0
  620. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/production-prompt-grammar.md +304 -0
  621. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/review-and-fixtures.md +139 -0
  622. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.0.md +50 -0
  623. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/seedance-2.5.md +122 -0
  624. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/stage-contract.md +60 -0
  625. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/references/target-model-profile.md +116 -0
  626. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/container_check.py +329 -0
  627. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/motion_timing_check.py +450 -0
  628. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/music_spec_check.py +327 -0
  629. package/resources/skills/drama-production/upstream/skills/short-drama-video-prompts/scripts/selftest.py +82 -0
  630. package/resources/skills/drama-production/upstream/skills/short-drama-write/SKILL.md +99 -0
  631. package/resources/skills/drama-production/upstream/skills/short-drama-write/agents/openai.yaml +4 -0
  632. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/beats.jsonl +2 -0
  633. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card-standalone.json +120 -0
  634. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/episode-card.json +55 -0
  635. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/screenplay.md +36 -0
  636. package/resources/skills/drama-production/upstream/skills/short-drama-write/assets/voice-record-sheet.jsonl.md +89 -0
  637. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/dialogue-craft.md +526 -0
  638. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/production-format-dialect.md +273 -0
  639. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-handoff-capsule.md +25 -0
  640. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/scene-sound-dramaturgy.md +43 -0
  641. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/screenplay-format.md +266 -0
  642. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/script-craft.md +745 -0
  643. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/stage-contract.md +36 -0
  644. package/resources/skills/drama-production/upstream/skills/short-drama-write/references/substitutable-realization.md +107 -0
  645. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/duration_estimate.py +309 -0
  646. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/screenplay_index.py +1091 -0
  647. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/selftest.py +229 -0
  648. package/resources/skills/drama-production/upstream/skills/short-drama-write/scripts/voice_sheet_check.py +416 -0
  649. package/resources/skills/viora-canvas-production/manifest.json +23 -0
  650. package/resources/skills/viora-canvas-production/references/canvas-contract.md +50 -0
  651. package/resources/skills/viora-canvas-production/references/creative-production.md +50 -0
  652. package/resources/skills/viora-canvas-production/references/generation-delivery.md +36 -0
  653. package/resources/skills/viora-canvas-production/references/production-ledger.md +25 -0
@@ -0,0 +1,1263 @@
1
+ "use strict";
2
+
3
+ const $ = (id) => (typeof document === "undefined" ? null : document.getElementById(id));
4
+ const state = {
5
+ project: null,
6
+ files: [],
7
+ visibleFiles: [],
8
+ status: null,
9
+ selected: null,
10
+ version: null,
11
+ dirty: false,
12
+ saving: false,
13
+ saveSequence: 0,
14
+ loadSequence: 0,
15
+ projectLoadSequence: 0,
16
+ projectSwitching: false,
17
+ apiBase: "",
18
+ view: "preview",
19
+ expandedGroups: new Set(),
20
+ };
21
+
22
+ const ROOT_ROLES = {
23
+ inputs: "sources", "输入": "sources",
24
+ development: "project", "项目开发": "project",
25
+ bible: "bible", "设定集": "bible",
26
+ episodes: "episodes", "剧集": "episodes",
27
+ };
28
+
29
+ const HIDDEN_ROOTS = new Set([
30
+ ".short-drama", "creator-decisions", "创作者决策", "reviews", "审查",
31
+ "delivery", "交付", "transactions", "事务",
32
+ ]);
33
+
34
+ const HIDDEN_FILES = new Set([
35
+ "short-drama.json", "manifest.json", "coverage.json", "delivery-containers.jsonl",
36
+ "screenplay-index.jsonl",
37
+ ]);
38
+
39
+ // Reading order inside one group: what the creator wrote, then what was built
40
+ // from it. Raw directory order otherwise buries the screenplay below the prompts.
41
+ const SECTION_ORDER = ["story", "project", "sources", "analysis", "cast", "visual", "storyboard", "prompts", "production", "review", "other"];
42
+
43
+ const CONTENT_META = {
44
+ sources: { label: "原始资料", description: "故事原稿与参考内容" },
45
+ analysis: { label: "原著分析", description: "拆原著留下的索引、逐章提取与分集候选" },
46
+ project: { label: "项目设定", description: "故事方向与导演表达" },
47
+ cast: { label: "人物场景", description: "角色、造型、场景与道具" },
48
+ story: { label: "故事与剧本", description: "分集构思、节拍与台词" },
49
+ prompts: { label: "生成文案", description: "用于生成图片、关键帧与视频的文案" },
50
+ visual: { label: "画面设计", description: "图片方案与视觉参考" },
51
+ storyboard: { label: "分镜画面", description: "镜头、关键帧与运动" },
52
+ production: { label: "制作成果", description: "项目内已有的图片、视频与声音" },
53
+ review: { label: "审查意见", description: "当前版本的问题、证据与修订要求" },
54
+ other: { label: "其他内容", description: "放在标准目录之外的创作文件" },
55
+ };
56
+
57
+ const IMAGE_SUFFIXES = new Set(["png", "jpg", "jpeg", "webp", "gif"]);
58
+ const VIDEO_SUFFIXES = new Set(["mp4", "webm", "mov"]);
59
+ const AUDIO_SUFFIXES = new Set(["wav", "mp3", "m4a", "aac", "flac", "opus"]);
60
+ const GALLERY_IMAGE_LIMIT = 4 * 1024 * 1024;
61
+
62
+ // Icons are drawn once in index.html's <template> and cloned here. Letting the
63
+ // HTML parser own them keeps the SVG namespace out of this file, so the shipped
64
+ // tree stays free of anything shaped like a URL.
65
+ function iconElement(name, className = "icon") {
66
+ const drawings = [...($("iconTemplates")?.content.children || [])];
67
+ const source = drawings.find((node) => node.dataset.icon === name) ||
68
+ drawings.find((node) => node.dataset.icon === "other");
69
+ if (!source) return element("span", className);
70
+ const icon = source.cloneNode(true);
71
+ icon.setAttribute("class", className);
72
+ return icon;
73
+ }
74
+
75
+ const INTERNAL_KEY_PARTS = [
76
+ "hash", "sha", "path", "ref", "src", "sources", "owner", "schema", "artifact",
77
+ "authority", "lifecycle", "manifest", "checksum", "evidence", "transaction",
78
+ "snapshot", "candidate", "reviewer", "verdict",
79
+ ];
80
+
81
+ const INTERNAL_INLINE_VALUE_PATTERNS = [
82
+ /\b[a-f0-9]{20,128}\b/gi,
83
+ /\bshort-drama-[a-z0-9-]+\b/gi,
84
+ /\b(?:[a-z][a-z0-9+.-]*:\/\/|file:)[^\s,。;、)》\]]*/gi,
85
+ /(?:\.short-drama|剧集|设定集|项目开发|输入|创作者决策|审查|交付|事务|episodes|bible|development|inputs|creator-decisions|reviews|delivery|transactions)[\\/][^\s,。;、)》\]]*/gi,
86
+ /(?:^|[\s((])(?:[/\\~]|\.\.?[/\\]|[a-z]:[/\\])[^\s,。;、)》\]]*/gi,
87
+ ];
88
+
89
+ const INTERNAL_WHOLE_VALUE_PATTERNS = [
90
+ /^(?:[^\\/]+[\\/])+(?:[^\\/]+\.(?:md|txt|jsonl?|ya?ml|mp4|mov|webm|png|jpe?g|webp))$/i,
91
+ /^(?:[^\s\\/]+\\){2,}[^\s\\/]+$/,
92
+ ];
93
+
94
+ const INTERNAL_VALUE_TOKENS = new Set([
95
+ "absent", "in_progress", "materialized", "not_run", "pass_with_warnings",
96
+ "not_requested", "provisional", "approve_with_notes", "not_evaluated",
97
+ "delivered", "blocked", "candidate", "artifact", "snapshot", "transaction",
98
+ "accepted", "rejected", "approve", "ready", "pending", "revise", "stale",
99
+ "failed", "fail", "pass",
100
+ ]);
101
+
102
+ const INTERNAL_EXACT_KEYS = new Set([
103
+ "build_state", "validation_state", "creator_acceptance", "independent_review",
104
+ "delivery_gate", "active_transaction", "last_action", "project_root", "project_id",
105
+ ]);
106
+
107
+ const FILE_LABELS = {
108
+ "readme.md": "项目说明",
109
+ "creative-brief.md": "创作简报",
110
+ "story-engine.md": "故事引擎",
111
+ "director-brief.md": "导演阐述",
112
+ "adaptation-map.jsonl": "改编要点",
113
+ "series-arc.json": "全剧走向",
114
+ "episode-map.jsonl": "分集安排",
115
+ "characters.jsonl": "人物设定",
116
+ "looks.jsonl": "造型设定",
117
+ "locations.jsonl": "场景设定",
118
+ "location-views.jsonl": "场景视角",
119
+ "props.jsonl": "关键道具",
120
+ "prop-states.jsonl": "道具变化",
121
+ "episode-card.json": "本集提要",
122
+ "beats.jsonl": "剧情节拍",
123
+ "screenplay.md": "剧本",
124
+ "剧本.md": "剧本",
125
+ "视觉设定.md": "视觉设定",
126
+ "分镜.md": "分镜",
127
+ "图片提示词.md": "图片提示词",
128
+ "视频提示词.md": "视频提示词",
129
+ "screenplay-index.jsonl": "场次索引",
130
+ "voice-record-sheet.jsonl": "配音稿",
131
+ "occurrences.jsonl": "出场安排",
132
+ "decisions.jsonl": "画面选择",
133
+ "continuity.jsonl": "连续性",
134
+ "image-prompt-specs.jsonl": "图片生成方案",
135
+ "image-prompts.md": "图片生成文案",
136
+ "shots.jsonl": "镜头表",
137
+ "keyframes.jsonl": "关键帧",
138
+ "keyframe-prompts.md": "关键帧生成文案",
139
+ "motion-specs.jsonl": "镜头运动",
140
+ "video-prompts.md": "视频生成文案",
141
+ };
142
+
143
+ // The server speaks a fixed English protocol vocabulary. The workspace is
144
+ // creator-facing, so each known message gets a Chinese sentence that also says
145
+ // what to do next; anything unmapped passes through rather than being hidden.
146
+ const FAILURE_COPY = {
147
+ "file changed since it was opened": "这份内容在别处已经更新,请重新打开后再修改。",
148
+ "text file cannot be opened safely": "这份内容暂时无法打开,请刷新后重试。",
149
+ "text file cannot be replaced safely": "这份内容暂时无法保存,请刷新后重试。",
150
+ "file is locked or not writable": "这份文件被其他程序占用或不可写;关掉正在用它的程序,或检查文件权限。",
151
+ "media file cannot be opened safely": "这段画面暂时无法打开,请刷新后重试。",
152
+ "file type is not editable text": "这种内容不能在工作台里直接修改。",
153
+ "content exceeds file limit": "内容太长,无法保存。",
154
+ "file exceeds preview limit": "内容太长,无法在这里展示。",
155
+ "media exceeds preview limit": "这段画面太大,无法在这里预览。",
156
+ "path is not a file": "找不到这份内容,可能已被移动。",
157
+ "media path is not a file": "找不到这段画面,可能已被移动。",
158
+ "project not found": "找不到这个项目。",
159
+ "project path changed during the save": "项目位置在保存过程中发生变化,请重新打开。",
160
+ "unsupported preview media": "这种画面格式无法在这里预览。",
161
+ "request body is too large": "内容太长,无法提交。",
162
+ "internal dashboard error": "工作台遇到问题,请刷新后重试。",
163
+ "invalid dashboard response": "工作台收到无效数据,请刷新后重试。",
164
+ };
165
+
166
+ function friendlyFailure(message) {
167
+ return FAILURE_COPY[String(message || "").trim()] || String(message || "");
168
+ }
169
+
170
+ // CSS.escape is unavailable in older WebKit; the group keys are our own
171
+ // ("project", "episode:EP001") so a conservative escape is enough.
172
+ function cssEscape(value) {
173
+ return String(value).replace(/["\\]/g, "\\$&");
174
+ }
175
+
176
+ function creatorTitle(title) {
177
+ return typeof title === "string" && title.trim() ? title.trim() : "未命名短剧";
178
+ }
179
+
180
+ function pathSegments(path) {
181
+ return String(path || "").split("/").filter(Boolean);
182
+ }
183
+
184
+ // Which stage owns a file is recorded in the project state and travels with
185
+ // `/api/status`. Path rules stay in charge -- they encode real structure, like
186
+ // `assets` under an episode being visual design rather than the asset bible --
187
+ // and this map only catches what those rules do not recognise.
188
+ const OWNER_SECTIONS = {
189
+ "short-drama": "project",
190
+ "short-drama-novel-analyze": "analysis",
191
+ "short-drama-develop": "project",
192
+ "short-drama-write": "story",
193
+ "short-drama-assets": "cast",
194
+ "short-drama-image-prompts": "prompts",
195
+ "short-drama-storyboard": "storyboard",
196
+ "short-drama-video-prompts": "prompts",
197
+ "short-drama-produce": "production",
198
+ "short-drama-edit": "production",
199
+ };
200
+
201
+ function ownerSection(path) {
202
+ const owner = state.status?.ownership?.[path];
203
+ return owner ? OWNER_SECTIONS[owner] || null : null;
204
+ }
205
+
206
+ function creatorSection(path) {
207
+ const parts = pathSegments(path);
208
+ const first = parts[0] || "";
209
+ const lowerFirst = first.toLowerCase();
210
+ const filename = (parts.at(-1) || "").toLowerCase();
211
+ const creatorReview = ["reviews", "审查"].includes(lowerFirst) && filename.endsWith("-审查.md");
212
+ if (creatorReview) return "review";
213
+ if (!parts.length || HIDDEN_ROOTS.has(first) || HIDDEN_ROOTS.has(lowerFirst)) return null;
214
+ if (HIDDEN_FILES.has(filename)) return null;
215
+ if (parts.length === 1) {
216
+ if (filename === "readme.md") return "project";
217
+ return ownerSection(path) || "other";
218
+ }
219
+ const root = ROOT_ROLES[first] || ROOT_ROLES[lowerFirst];
220
+ // Pulling a novel apart leaves twenty-odd chapter extracts plus the index and
221
+ // the aggregates. They are working material, not something read one file at a
222
+ // time, so they get their own group instead of burying the brief and the map.
223
+ if (parts[1] === "source-analysis") return "analysis";
224
+ if (root === "sources" || root === "project") return root;
225
+ if (root === "bible") return "cast";
226
+ if (root !== "episodes") return ownerSection(path) || "other";
227
+ const area = (parts[2] || "").toLowerCase();
228
+ if (["production", "制作成果"].includes(area)) return "production";
229
+ if (filename === "视觉设定.md") return "cast";
230
+ if (filename === "分镜.md") return "storyboard";
231
+ if (["图片提示词.md", "视频提示词.md"].includes(filename)) return "prompts";
232
+ if (filename === "剧本.md") return "story";
233
+ if (/prompts?\.(?:md|jsonl?)$/i.test(filename) || filename.includes("prompt")) return "prompts";
234
+ if (["assets", "资产"].includes(area)) return "visual";
235
+ if (["storyboard", "分镜"].includes(area)) return "storyboard";
236
+ return "story";
237
+ }
238
+
239
+ function creatorProjection(value) {
240
+ if (typeof value === "string") {
241
+ const normalized = value.trim().toLowerCase();
242
+ if (INTERNAL_VALUE_TOKENS.has(normalized) || INTERNAL_WHOLE_VALUE_PATTERNS.some((pattern) => pattern.test(value.trim()))) return undefined;
243
+ let cleaned = value;
244
+ for (const pattern of INTERNAL_INLINE_VALUE_PATTERNS) cleaned = cleaned.replace(pattern, "");
245
+ cleaned = cleaned.replace(/\s{2,}/g, " ").replace(/\s+([,。;、])/g, "$1").trim();
246
+ return cleaned || undefined;
247
+ }
248
+ if (value === null || typeof value !== "object") return value;
249
+ if (Array.isArray(value)) {
250
+ return value.map(creatorProjection).filter((item) => item !== undefined);
251
+ }
252
+ const projected = {};
253
+ for (const [key, raw] of Object.entries(value)) {
254
+ const normalized = String(key)
255
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
256
+ .toLowerCase()
257
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/g, "_")
258
+ .replace(/^_+|_+$/g, "");
259
+ const keyParts = normalized.split("_").filter(Boolean);
260
+ if (INTERNAL_EXACT_KEYS.has(normalized) || INTERNAL_KEY_PARTS.some((part) => keyParts.includes(part))) continue;
261
+ const child = creatorProjection(raw);
262
+ if (child !== undefined) projected[key] = child;
263
+ }
264
+ return projected;
265
+ }
266
+
267
+ function valueIs(axis, wanted) {
268
+ if (typeof axis === "string") return axis === wanted;
269
+ return Boolean(axis && typeof axis === "object" && Number(axis[wanted]) > 0);
270
+ }
271
+
272
+ function axisOnly(axis, allowed) {
273
+ if (typeof axis === "string") return allowed.includes(axis);
274
+ if (!axis || typeof axis !== "object") return false;
275
+ const active = Object.entries(axis).filter(([, count]) => Number(count) > 0).map(([value]) => value);
276
+ return active.length > 0 && active.every((value) => allowed.includes(value));
277
+ }
278
+
279
+ function creatorStatus(lifecycle, recovery = null) {
280
+ if (!lifecycle || typeof lifecycle !== "object") return ["创作中", "neutral"];
281
+ const simple = lifecycle.artifact_state;
282
+ if (typeof simple === "string" || (simple && typeof simple === "object")) {
283
+ if (valueIs(simple, "revise")) return ["需要修改", "danger"];
284
+ if (valueIs(simple, "update_needed")) return ["需要更新", "warning"];
285
+ if (valueIs(simple, "needs_confirmation")) return ["待你确认", "warning"];
286
+ if (axisOnly(simple, ["approved"])) return ["可以导出", "success"];
287
+ if (valueIs(simple, "accepted") || valueIs(simple, "approved")) return ["已采用", "success"];
288
+ return ["创作中", "neutral"];
289
+ }
290
+ if (
291
+ valueIs(lifecycle.build_state, "failed") ||
292
+ valueIs(lifecycle.build_state, "fail") ||
293
+ valueIs(lifecycle.validation_state, "failed") ||
294
+ valueIs(lifecycle.validation_state, "fail") ||
295
+ valueIs(lifecycle.creator_acceptance, "rejected") ||
296
+ valueIs(lifecycle.independent_review, "rejected") ||
297
+ valueIs(lifecycle.independent_review, "revise")
298
+ ) return ["需要修改", "danger"];
299
+ if (recovery?.needed) return ["需要更新", "warning"];
300
+ if (valueIs(lifecycle.build_state, "stale")) return ["需要更新", "warning"];
301
+ if (valueIs(lifecycle.creator_acceptance, "pending")) return ["待你确认", "warning"];
302
+ const accepted = axisOnly(lifecycle.creator_acceptance, ["accepted"]);
303
+ const reviewed = axisOnly(lifecycle.independent_review, ["approve", "approve_with_notes"]);
304
+ const ready = axisOnly(lifecycle.delivery_gate, ["ready", "delivered"]);
305
+ const built = lifecycle.build_state === undefined || axisOnly(lifecycle.build_state, ["materialized"]);
306
+ const valid = lifecycle.validation_state === undefined || axisOnly(lifecycle.validation_state, ["pass", "pass_with_warnings"]);
307
+ if (accepted && reviewed && ready && built && valid) return ["可以导出", "success"];
308
+ const unfinished =
309
+ valueIs(lifecycle.build_state, "absent") ||
310
+ valueIs(lifecycle.build_state, "in_progress") ||
311
+ valueIs(lifecycle.validation_state, "not_run") ||
312
+ valueIs(lifecycle.independent_review, "not_requested") ||
313
+ valueIs(lifecycle.independent_review, "provisional") ||
314
+ valueIs(lifecycle.delivery_gate, "not_evaluated") ||
315
+ valueIs(lifecycle.delivery_gate, "blocked");
316
+ if (accepted && unfinished) return ["已采用", "neutral"];
317
+ if (accepted) return ["已采用", "success"];
318
+ return ["创作中", "neutral"];
319
+ }
320
+
321
+ function projectRecovery(status) {
322
+ return { needed: Boolean(status?.layout?.mode === "mixed") };
323
+ }
324
+
325
+ function collectEpisodes(files) {
326
+ const episodes = new Map();
327
+ for (const file of files || []) {
328
+ const parts = pathSegments(file.path);
329
+ const root = ROOT_ROLES[parts[0]] || ROOT_ROLES[(parts[0] || "").toLowerCase()];
330
+ if (root !== "episodes" || !parts[1] || !creatorSection(file.path)) continue;
331
+ if (!episodes.has(parts[1])) episodes.set(parts[1], []);
332
+ episodes.get(parts[1]).push(file);
333
+ }
334
+ return [...episodes.entries()]
335
+ .map(([id, episodeFiles]) => ({ id, files: episodeFiles }))
336
+ .sort((left, right) => left.id.localeCompare(right.id, "zh-CN", { numeric: true }));
337
+ }
338
+
339
+ function mediaKind(fileOrPath) {
340
+ const path = typeof fileOrPath === "string" ? fileOrPath : fileOrPath?.path;
341
+ const suffix = String(path || "").split(".").at(-1).toLowerCase();
342
+ if (IMAGE_SUFFIXES.has(suffix)) return "image";
343
+ if (VIDEO_SUFFIXES.has(suffix)) return "video";
344
+ if (AUDIO_SUFFIXES.has(suffix)) return "audio";
345
+ return "media";
346
+ }
347
+
348
+ function formatBytes(value) {
349
+ const bytes = Number(value) || 0;
350
+ if (bytes < 1024) return `${bytes} B`;
351
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)} KB`;
352
+ return `${(bytes / (1024 * 1024)).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`;
353
+ }
354
+
355
+ function episodePresentation(files) {
356
+ const names = files.map((file) => String(file.path || "").toLowerCase());
357
+ const media = files.filter((file) => file.type === "media").length;
358
+ if (media) return { label: "已有媒体", detail: `${media} 项成果` };
359
+ if (names.some((path) => /(?:video-prompts|视频提示词|图片提示词)\.md$/.test(path))) return { label: "提示词就绪", detail: "可在 skill 中确认投产" };
360
+ if (names.some((path) => /\/(?:shots|keyframes)\.jsonl$/.test(path))) return { label: "分镜中", detail: "镜头资料已建立" };
361
+ if (names.some((path) => /(?:screenplay|剧本)\.md$/.test(path))) return { label: "剧本就绪", detail: "可继续做资产与分镜" };
362
+ return { label: "筹备中", detail: `${files.length} 项内容` };
363
+ }
364
+
365
+ function projectOverviewModel(files, status) {
366
+ const visible = (files || []).filter((file) => creatorSection(file.path));
367
+ const episodes = collectEpisodes(visible);
368
+ const media = visible.filter((file) => file.type === "media");
369
+ return {
370
+ title: creatorTitle(status?.title),
371
+ episodes,
372
+ media,
373
+ documents: visible.filter((file) => file.type !== "media").length,
374
+ };
375
+ }
376
+
377
+ function savedContentIsCurrent(submitted, current) {
378
+ return submitted === current;
379
+ }
380
+
381
+ function statusRefreshFailureMessage() {
382
+ return "内容已保存,但状态刷新失败,请稍后重试";
383
+ }
384
+
385
+ function fileLabel(path) {
386
+ const name = (pathSegments(path).at(-1) || "内容").toLowerCase();
387
+ return FILE_LABELS[name] || name.replace(/\.(md|jsonl?|txt)$/i, "").replace(/[-_]/g, " ");
388
+ }
389
+
390
+ function episodeName(path) {
391
+ const parts = pathSegments(path);
392
+ const root = ROOT_ROLES[parts[0]] || ROOT_ROLES[(parts[0] || "").toLowerCase()];
393
+ return root === "episodes" ? parts[1] : "";
394
+ }
395
+
396
+ function contentGroupKey(file) {
397
+ const episode = episodeName(file?.path);
398
+ return episode ? `episode:${episode}` : "project";
399
+ }
400
+
401
+ function creatorEditable(file) {
402
+ // Mirrors the server's TEXT_EXTENSIONS. Structured files stay editable because
403
+ // the server rejects invalid JSON on save, and subtitles are a shipped feature.
404
+ return Boolean(file?.writable && /\.(md|txt|srt|ass|json|jsonl)$/i.test(file.path));
405
+ }
406
+
407
+ function element(tag, className, text) {
408
+ const node = document.createElement(tag);
409
+ if (className) node.className = className;
410
+ if (text !== undefined) node.textContent = text;
411
+ return node;
412
+ }
413
+
414
+ function button(text, className, action) {
415
+ const node = element("button", className, text);
416
+ node.onclick = action;
417
+ return node;
418
+ }
419
+
420
+ function statusPill(lifecycle, recovery = null) {
421
+ const [label, tone] = creatorStatus(lifecycle, recovery);
422
+ const pill = element("span", "status-pill", label);
423
+ pill.dataset.tone = tone;
424
+ return pill;
425
+ }
426
+
427
+ async function api(path, options) {
428
+ const requestPath = state.apiBase && path.startsWith("/api/") ? `${state.apiBase}${path}` : path;
429
+ const response = await fetch(requestPath, options);
430
+ const data = await response.json();
431
+ if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
432
+ return data;
433
+ }
434
+
435
+ async function establishSession() {
436
+ const hashValue = location.hash.startsWith("#") ? location.hash.slice(1) : "";
437
+ const token = ["home", "creation", "tasks", "export"].includes(hashValue) ? "" : hashValue;
438
+ const storageKey = "shortDramaApiBase";
439
+ if (!token) {
440
+ state.apiBase = sessionStorage.getItem(storageKey) || "";
441
+ return;
442
+ }
443
+ const response = await fetch("/api/session", { method: "POST", headers: { "X-Short-Drama-Token": token } });
444
+ const data = await response.json();
445
+ if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
446
+ if (typeof data.apiBase !== "string" || !data.apiBase.startsWith("/_short_drama/")) throw new Error("本机会话响应无效");
447
+ state.apiBase = data.apiBase;
448
+ sessionStorage.setItem(storageKey, state.apiBase);
449
+ history.replaceState(null, "", `${location.pathname}${location.search}`);
450
+ }
451
+
452
+ function flatten(nodes, out = []) {
453
+ for (const node of nodes || []) {
454
+ if (node.type === "directory") flatten(node.children, out);
455
+ else out.push(node);
456
+ }
457
+ return out;
458
+ }
459
+
460
+ function setMessage(text, tone = "neutral") {
461
+ $("message").textContent = text;
462
+ $("message").dataset.tone = tone;
463
+ }
464
+
465
+ function showNotice(text, tone = "warning") {
466
+ const notice = element("div", "notice", text);
467
+ notice.dataset.tone = tone;
468
+ $("notices").replaceChildren(notice);
469
+ }
470
+
471
+ function clearNotice() {
472
+ $("notices").replaceChildren();
473
+ }
474
+
475
+ function renderProjectSummary() {
476
+ $("workspaceStatus").replaceChildren(statusPill(state.status?.lifecycle, projectRecovery(state.status)));
477
+ }
478
+
479
+ function overviewStat(value, label) {
480
+ const card = element("div", "overview-stat");
481
+ card.append(element("strong", "", String(value)), element("span", "", label));
482
+ return card;
483
+ }
484
+
485
+ function mediaContentUrl(file) {
486
+ const path = `/api/media/content?project=${encodeURIComponent(state.project)}&path=${encodeURIComponent(file.path)}`;
487
+ return state.apiBase ? `${state.apiBase}${path}` : path;
488
+ }
489
+
490
+ function mediaCard(file) {
491
+ const kind = mediaKind(file);
492
+ const labels = { image: "图片", video: "视频", audio: "声音", media: "媒体" };
493
+ const card = button("", "media-card", () => openFile(file, true));
494
+ const visual = element("span", "media-card-visual");
495
+ if (kind === "image" && !file.oversize && Number(file.size) <= GALLERY_IMAGE_LIMIT) {
496
+ const image = document.createElement("img");
497
+ image.src = mediaContentUrl(file);
498
+ image.alt = "";
499
+ image.loading = "lazy";
500
+ visual.append(image);
501
+ } else {
502
+ visual.append(iconElement(kind, "icon"));
503
+ }
504
+ visual.append(element("span", "media-card-type", labels[kind]));
505
+ const copy = element("span", "media-card-copy");
506
+ copy.append(
507
+ element("strong", "", fileLabel(file.path)),
508
+ element("small", "", [episodeName(file.path) || "全剧", formatBytes(file.size)].join(" · ")),
509
+ );
510
+ card.append(visual, copy);
511
+ return card;
512
+ }
513
+
514
+ function episodeCard(episode, index) {
515
+ const presentation = episodePresentation(episode.files);
516
+ const card = button("", "episode-card", async () => {
517
+ state.expandedGroups.add(`episode:${episode.id}`);
518
+ const first = orderedForReading(episode.files)[0];
519
+ if (first) await openFile(first, true);
520
+ });
521
+ card.append(
522
+ element("span", "episode-number", String(index + 1).padStart(2, "0")),
523
+ (() => {
524
+ const copy = element("span", "episode-copy");
525
+ copy.append(element("strong", "", episode.id), element("small", "", presentation.detail));
526
+ return copy;
527
+ })(),
528
+ element("span", "episode-state", presentation.label),
529
+ );
530
+ return card;
531
+ }
532
+
533
+ function renderProjectOverview() {
534
+ const model = projectOverviewModel(state.visibleFiles, state.status);
535
+ const [statusLabel] = creatorStatus(state.status?.lifecycle, projectRecovery(state.status));
536
+ $("projectTitle").textContent = model.title;
537
+ $("overviewStats").replaceChildren(
538
+ overviewStat(model.episodes.length, "分集"),
539
+ overviewStat(model.documents, "创作文件"),
540
+ overviewStat(model.media.length, "已有媒体"),
541
+ overviewStat(statusLabel, "当前状态"),
542
+ );
543
+ $("episodeHint").textContent = model.episodes.length ? `${model.episodes.length} 集可浏览` : "尚未建立分集";
544
+ $("episodeStrip").replaceChildren(
545
+ ...(model.episodes.length
546
+ ? model.episodes.slice(0, 6).map(episodeCard)
547
+ : [element("p", "empty-copy", "项目级内容已就绪,分集建立后会显示在这里。")]),
548
+ );
549
+ const media = model.media.slice(0, 6);
550
+ $("mediaShowcase").hidden = media.length === 0;
551
+ $("mediaGallery").replaceChildren(...media.map(mediaCard));
552
+ const screenplay = state.visibleFiles.find((file) => /(?:^|\/)(?:screenplay|剧本)\.md$/i.test(file.path));
553
+ $("openScreenplay").disabled = !screenplay;
554
+ $("openScreenplay").onclick = screenplay ? () => openFile(screenplay, true) : null;
555
+ }
556
+
557
+ function navigationItem(file) {
558
+ const section = creatorSection(file.path);
559
+ const meta = CONTENT_META[section] || CONTENT_META.project;
560
+ const item = button("", "content-link", () => openFile(file, true));
561
+ item.classList.toggle("active", state.selected?.path === file.path);
562
+ if (state.selected?.path === file.path) item.setAttribute("aria-current", "true");
563
+ const icon = iconElement(file.type === "media" ? "media" : section, "icon content-link-icon");
564
+ const copy = element("span", "content-link-copy");
565
+ copy.append(element("strong", "", fileLabel(file.path)), element("small", "", file.type === "media" ? "画面预览" : meta.label));
566
+ item.append(icon, copy);
567
+ return item;
568
+ }
569
+
570
+ function orderedForReading(files) {
571
+ return [...files].sort((left, right) => {
572
+ const rank = SECTION_ORDER.indexOf(creatorSection(left.path)) - SECTION_ORDER.indexOf(creatorSection(right.path));
573
+ if (rank !== 0) return rank;
574
+ return fileLabel(left.path).localeCompare(fileLabel(right.path), "zh-Hans-CN");
575
+ });
576
+ }
577
+
578
+ function navigationGroup(title, files, groupKey, forceExpanded = false) {
579
+ const group = element("section", "content-nav-group");
580
+ const expanded = forceExpanded || state.expandedGroups.has(groupKey);
581
+ const heading = button("", "content-nav-group-toggle", () => {
582
+ // While searching, every group is force-expanded. Toggle the stored state to
583
+ // match what the creator sees, so clearing the search cannot collapse the
584
+ // group they just opened — or the one holding the open document.
585
+ if (state.expandedGroups.has(groupKey)) state.expandedGroups.delete(groupKey);
586
+ else state.expandedGroups.add(groupKey);
587
+ renderContentList();
588
+ // renderContentList replaces this button, so focus would fall to <body>.
589
+ const restored = $("contentList")?.querySelector(`[data-group-key="${cssEscape(groupKey)}"]`);
590
+ if (restored) restored.focus();
591
+ });
592
+ heading.dataset.groupKey = groupKey;
593
+ heading.setAttribute("aria-expanded", String(expanded));
594
+ heading.append(
595
+ iconElement("chevron", "icon content-nav-group-chevron"),
596
+ element("strong", "", title),
597
+ element("span", "content-nav-group-count", `${files.length} 项`),
598
+ );
599
+ const list = element("div", "content-link-list");
600
+ list.hidden = !expanded;
601
+ for (const file of orderedForReading(files)) list.append(navigationItem(file));
602
+ group.append(heading, list);
603
+ return group;
604
+ }
605
+
606
+ function renderContentList() {
607
+ const host = $("contentList");
608
+ const term = $("search").value.trim().toLowerCase();
609
+ const files = state.visibleFiles.filter((file) => `${fileLabel(file.path)} ${episodeName(file.path)}`.toLowerCase().includes(term));
610
+ const groups = [];
611
+ const projectFiles = files.filter((file) => !episodeName(file.path) && creatorSection(file.path) !== "analysis");
612
+ const analysisFiles = files.filter((file) => creatorSection(file.path) === "analysis");
613
+ if (projectFiles.length) groups.push(navigationGroup("全剧", projectFiles, "project", Boolean(term)));
614
+ // Collapsed unless the creator opens it or is searching: on a full-length novel
615
+ // this is twenty-odd files, and none of them is what someone came here to read.
616
+ if (analysisFiles.length) {
617
+ groups.push(navigationGroup(`原著分析(${analysisFiles.length})`, analysisFiles, "analysis", Boolean(term)));
618
+ }
619
+ for (const episode of collectEpisodes(files)) {
620
+ groups.push(navigationGroup(episode.id, episode.files, `episode:${episode.id}`, Boolean(term)));
621
+ }
622
+ if (!groups.length && term) groups.push(element("p", "empty-copy", "没有找到相关内容。"));
623
+ host.replaceChildren(...groups);
624
+ }
625
+
626
+ function scrollBehavior() {
627
+ return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth";
628
+ }
629
+
630
+ function focusDocument() {
631
+ $("documentPane").scrollIntoView({ behavior: scrollBehavior(), block: "start" });
632
+ }
633
+
634
+ function renderTaskSummary() {
635
+ const host = $("taskSummary");
636
+ const [label] = creatorStatus(state.status?.lifecycle, projectRecovery(state.status));
637
+ host.replaceChildren();
638
+ host.hidden = !["需要修改", "需要更新", "待你确认"].includes(label);
639
+ if (host.hidden) return;
640
+ const title = label === "待你确认" ? "有新版内容待确认" : label === "需要更新" ? "内容需要更新" : "当前内容需要修改";
641
+ const description = label === "待你确认" ? "请确认是否采用当前版本。" : label === "需要更新" ? "请重新整理受影响的内容。" : "请处理当前版本中的问题。";
642
+ host.append(element("h2", "", title), element("p", "", description));
643
+ }
644
+
645
+ function renderExportSummary() {
646
+ const host = $("exportSummary");
647
+ const [label] = creatorStatus(state.status?.lifecycle, projectRecovery(state.status));
648
+ const scope = episodeName(state.selected?.path) || collectEpisodes(state.visibleFiles)[0]?.id || "当前短剧";
649
+ host.replaceChildren();
650
+ host.hidden = label !== "可以导出";
651
+ if (host.hidden) return;
652
+ host.append(
653
+ element("h2", "", `${scope} 可以导出`),
654
+ button("复制导出指令", "primary", () => copyExportRequest("完整制作资料", scope)),
655
+ );
656
+ }
657
+
658
+ function updateAssistRow() {
659
+ const visible = [$("taskSummary"), $("exportSummary")].filter((node) => !node.hidden).length;
660
+ $("assistRow").hidden = visible === 0;
661
+ $("assistRow").classList.toggle("single", visible === 1);
662
+ }
663
+
664
+ async function copyExportRequest(requestName, scope) {
665
+ const request = `请导出 ${scope} 的${requestName}。`;
666
+ try {
667
+ await navigator.clipboard.writeText(request);
668
+ showNotice(`已复制:${request}`, "success");
669
+ } catch (_error) {
670
+ showNotice(`请复制这句话发送给创作助手:${request}`, "warning");
671
+ }
672
+ }
673
+
674
+ function renderWorkspace() {
675
+ renderProjectSummary();
676
+ renderProjectOverview();
677
+ renderContentList();
678
+ renderTaskSummary();
679
+ renderExportSummary();
680
+ updateAssistRow();
681
+ }
682
+
683
+ function appendInlineText(node, text) {
684
+ const tokens = text.split(/(`[^`]+`|\*\*[^*]+\*\*)/g).filter(Boolean);
685
+ for (const token of tokens) {
686
+ if (token.startsWith("`") && token.endsWith("`")) node.append(element("code", "", token.slice(1, -1)));
687
+ else if (token.startsWith("**") && token.endsWith("**")) node.append(element("strong", "", token.slice(2, -2)));
688
+ else node.append(document.createTextNode(token));
689
+ }
690
+ }
691
+
692
+ function renderMarkdown(content) {
693
+ const fragment = document.createDocumentFragment();
694
+ // `list` holds the open <ul>/<ol>; `listKind` tracks which, so a bullet block
695
+ // followed by a numbered block does not get merged into one list.
696
+ let list = null;
697
+ let listKind = null;
698
+ // A generated prompt is meant to be selected and copied as one block, so a
699
+ // fenced run is captured verbatim instead of being re-parsed as Markdown.
700
+ let fence = null;
701
+ // Consecutive `>` lines are one blockquote in Markdown, and a copyable prompt
702
+ // is written that way on purpose: a MiniMax H3 reference body is six lines
703
+ // that go into one request. Rendering each line as its own bordered box made
704
+ // one prompt look like six separate ones, and creators asked which to copy.
705
+ let quoteNode = null;
706
+ // 剧本 format sanctions Markdown comments for creator notes, so a document
707
+ // legitimately opens with several lines of them. Rendering those as body text
708
+ // put the author's private notes at the top of the one pane where they read
709
+ // the screenplay. `comment` holds the lines of an open block: closed, they are
710
+ // dropped; unterminated, they are rendered verbatim, because the same rule
711
+ // says unrecognised Markdown is preserved rather than quietly "fixed".
712
+ let comment = null;
713
+ const closeList = () => { list = null; listKind = null; };
714
+ const closeQuote = () => { quoteNode = null; };
715
+ const paragraph = (text) => {
716
+ const node = element("p");
717
+ appendInlineText(node, text);
718
+ fragment.append(node);
719
+ };
720
+ for (let line of content.split("\n")) {
721
+ if (fence !== null) {
722
+ if (/^\s*```/.test(line)) {
723
+ const pre = element("pre", "code-block");
724
+ pre.append(element("code", "", fence.join("\n")));
725
+ fragment.append(pre);
726
+ fence = null;
727
+ } else {
728
+ fence.push(line);
729
+ }
730
+ continue;
731
+ }
732
+ // Before the fence test: a ``` line inside a comment is commented out, and
733
+ // letting it open a fence would render the hidden sample as a code block.
734
+ if (comment !== null) {
735
+ const closeAt = line.indexOf("-->");
736
+ if (closeAt === -1) { comment.push(line); continue; }
737
+ comment = null;
738
+ // Text after the terminator is body text in both Markdown and HTML.
739
+ line = line.slice(closeAt + 3);
740
+ if (!line.trim()) { closeQuote(); continue; }
741
+ }
742
+ if (/^\s*```/.test(line)) { closeList(); closeQuote(); fence = []; continue; }
743
+ // Strip closed comments where they sit, so `正文 <!-- 注 -->` keeps its text.
744
+ // One pass is not enough: removing the inner comment of `<!<!-- x -->-- y -->`
745
+ // joins its neighbours back into a new `<!--`, so repeat until stable.
746
+ let stripped = line;
747
+ for (let previous = null; previous !== stripped; ) {
748
+ previous = stripped;
749
+ stripped = stripped.replace(/<!--[\s\S]*?-->/g, "");
750
+ }
751
+ const openAt = stripped.indexOf("<!--");
752
+ if (openAt !== -1) {
753
+ closeList();
754
+ closeQuote();
755
+ const before = stripped.slice(0, openAt);
756
+ if (before.trim()) paragraph(before);
757
+ // Only the part that is actually still open — keeping the whole original
758
+ // line would re-emit the prefix already rendered above if the block never
759
+ // closes.
760
+ comment = [stripped.slice(openAt)];
761
+ continue;
762
+ }
763
+ if (stripped !== line) {
764
+ if (!stripped.trim()) { closeQuote(); continue; }
765
+ line = stripped;
766
+ }
767
+ const heading = /^(#{1,4})\s+(.+)$/.exec(line);
768
+ if (heading) {
769
+ closeList();
770
+ closeQuote();
771
+ const node = element(`h${heading[1].length}`);
772
+ appendInlineText(node, heading[2]);
773
+ fragment.append(node);
774
+ continue;
775
+ }
776
+ const bullet = /^[-*]\s+(.+)$/.exec(line);
777
+ const ordered = /^\d+[.)]\s+(.+)$/.exec(line);
778
+ if (bullet || ordered) {
779
+ closeQuote();
780
+ const kind = bullet ? "ul" : "ol";
781
+ if (!list || listKind !== kind) { list = element(kind); listKind = kind; fragment.append(list); }
782
+ const node = element("li");
783
+ appendInlineText(node, (bullet || ordered)[1]);
784
+ list.append(node);
785
+ continue;
786
+ }
787
+ closeList();
788
+ if (!line.trim()) { closeQuote(); continue; }
789
+ const quote = /^>\s?(.*)$/.exec(line);
790
+ if (quote) {
791
+ if (quoteNode) quoteNode.append(element("br"));
792
+ else { quoteNode = element("blockquote"); fragment.append(quoteNode); }
793
+ appendInlineText(quoteNode, quote[1]);
794
+ continue;
795
+ }
796
+ closeQuote();
797
+ const node = element("p");
798
+ appendInlineText(node, line);
799
+ fragment.append(node);
800
+ }
801
+ // An unterminated comment renders verbatim rather than eating the document.
802
+ if (comment !== null) for (const line of comment) paragraph(line);
803
+ // An unterminated fence still renders as a block rather than vanishing.
804
+ if (fence !== null && fence.length) {
805
+ const pre = element("pre", "code-block");
806
+ pre.append(element("code", "", fence.join("\n")));
807
+ fragment.append(pre);
808
+ }
809
+ return fragment;
810
+ }
811
+
812
+ function parseJsonLines(content) {
813
+ return content.split("\n").map((line, index) => ({ line: index + 1, text: line.trim() })).filter((row) => row.text).map((row) => {
814
+ try { return JSON.parse(row.text); }
815
+ catch (error) { throw new Error(`第 ${row.line} 项内容无法读取:${error.message}`); }
816
+ });
817
+ }
818
+
819
+ // Preview must survive a half-written file: one truncated record from an
820
+ // interrupted agent run should not hide every valid record around it, because
821
+ // the creator has no other way to see the file.
822
+ function readJsonLines(content) {
823
+ return content.split("\n").map((line, index) => ({ line: index + 1, text: line.trim() })).filter((row) => row.text).map((row) => {
824
+ try { return { line: row.line, record: JSON.parse(row.text) }; }
825
+ catch (error) { return { line: row.line, error: error.message, text: row.text }; }
826
+ });
827
+ }
828
+
829
+ // A .jsonl file declares the upstream snapshots it references on a header
830
+ // record. That record is bookkeeping rather than one of the creator's items, so
831
+ // it stays out of the preview and out of the item numbering: "第 1 项" must be
832
+ // the first thing the creator actually wrote.
833
+ function isSourcesHeader(record) {
834
+ return Boolean(record) && typeof record === "object" && !Array.isArray(record) &&
835
+ record.record_type === "sources";
836
+ }
837
+
838
+ function previewRecords(content) {
839
+ return readJsonLines(content).filter((row) => !isSourcesHeader(row.record));
840
+ }
841
+
842
+ function validateStructuredText(path, content) {
843
+ if (/\.json$/i.test(path)) JSON.parse(content);
844
+ else if (/\.jsonl$/i.test(path)) parseJsonLines(content);
845
+ }
846
+
847
+ function friendlyKey(key) {
848
+ const labels = {
849
+ id: "编号", name: "名称", title: "标题", description: "说明", summary: "概要",
850
+ character: "人物", character_id: "人物编号", location: "场景", location_id: "场景编号",
851
+ dialogue: "台词", action: "动作", prompt: "生成文案", role: "作用", type: "类型",
852
+ episode_id: "剧集", scene_id: "场次", shot_id: "镜头", beat_id: "剧情节拍",
853
+ objective: "目标", conflict: "冲突", turn: "转折", emotion: "情绪", relationship: "关系",
854
+ costume: "服装", prop: "道具", props: "道具", lighting: "光线", camera: "摄影",
855
+ composition: "构图", duration: "时长", start_boundary: "开始画面", end_boundary: "结束画面",
856
+ boundary_role: "画面位置", continuity_state: "连续性", notes: "备注", value: "内容",
857
+ };
858
+ const normalized = String(key).toLowerCase().replace(/-/g, "_");
859
+ if (labels[normalized]) return labels[normalized];
860
+ if (/[\u4e00-\u9fff]/.test(String(key))) return String(key).replace(/[_-]/g, " ");
861
+ // An unknown key keeps its own name. Collapsing every unrecognized field to
862
+ // one generic label makes all rows on a record read identically, so the
863
+ // creator can no longer tell which value is the id and which is the source.
864
+ return String(key).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim() || "补充信息";
865
+ }
866
+
867
+ function appendStructuredValue(node, value) {
868
+ if (Array.isArray(value)) {
869
+ if (!value.length) { node.textContent = "—"; return; }
870
+ const list = element("ul", "structured-list");
871
+ for (const item of value) {
872
+ const row = element("li");
873
+ appendStructuredValue(row, item);
874
+ list.append(row);
875
+ }
876
+ node.append(list);
877
+ return;
878
+ }
879
+ if (value && typeof value === "object") {
880
+ const entries = Object.entries(value);
881
+ if (!entries.length) { node.textContent = "—"; return; }
882
+ const list = element("dl", "structured-nested");
883
+ for (const [key, child] of entries) {
884
+ const term = element("dt", "", friendlyKey(key));
885
+ const detail = element("dd");
886
+ appendStructuredValue(detail, child);
887
+ list.append(term, detail);
888
+ }
889
+ node.append(list);
890
+ return;
891
+ }
892
+ node.textContent = value === null || value === "" ? "—" : String(value);
893
+ }
894
+
895
+ function structuredCard(value, index) {
896
+ const card = element("article", "structured-card");
897
+ const projected = creatorProjection(value);
898
+ if (projected === undefined) {
899
+ card.append(element("p", "", "暂无可展示内容"));
900
+ return card;
901
+ }
902
+ if (projected === null || typeof projected !== "object") {
903
+ card.append(element("p", "", String(projected)));
904
+ return card;
905
+ }
906
+ if (index !== null) card.append(element("span", "record-number", `第 ${index + 1} 项`));
907
+ const list = element("dl", "structured-fields");
908
+ for (const [key, raw] of Object.entries(projected)) {
909
+ const term = element("dt", "", friendlyKey(key));
910
+ const detail = element("dd");
911
+ appendStructuredValue(detail, raw);
912
+ list.append(term, detail);
913
+ }
914
+ if (!list.childNodes.length) list.append(element("dt", "", "内容"), element("dd", "", "暂无可展示内容"));
915
+ card.append(list);
916
+ return card;
917
+ }
918
+
919
+ function renderPreview() {
920
+ const preview = $("preview");
921
+ const content = $("editor").value;
922
+ const path = state.selected?.path || "";
923
+ preview.replaceChildren();
924
+ try {
925
+ if (/\.md$/i.test(path)) preview.append(renderMarkdown(content));
926
+ else if (/\.json$/i.test(path)) preview.append(structuredCard(JSON.parse(content), null));
927
+ else if (/\.jsonl$/i.test(path)) {
928
+ preview.append(...previewRecords(content).map((row, index) => (
929
+ row.error
930
+ ? element("div", "preview-warning", `第 ${row.line} 项内容还不完整,暂时无法展示。`)
931
+ : structuredCard(row.record, index)
932
+ )));
933
+ }
934
+ else preview.append(element("div", "plain-copy", content));
935
+ } catch (error) {
936
+ preview.append(element("div", "preview-warning", "这份内容还不完整,暂时无法展示。"));
937
+ }
938
+ }
939
+
940
+ // The document pane is the whole screen. Holding its shape while the file
941
+ // loads keeps the page from collapsing to an empty column and back.
942
+ function showLoadingSkeleton() {
943
+ const preview = $("preview");
944
+ preview.classList.remove("empty-document");
945
+ preview.classList.add("document-loading");
946
+ const shell = element("div", "skeleton");
947
+ shell.append(element("div", "skeleton-line skeleton-heading"));
948
+ for (const width of ["92%", "78%", "88%", "64%", "84%", "72%"]) {
949
+ const line = element("div", "skeleton-line");
950
+ line.style.width = width;
951
+ shell.append(line);
952
+ }
953
+ preview.replaceChildren(shell);
954
+ }
955
+
956
+ function clearLoadingSkeleton() {
957
+ $("preview").classList.remove("document-loading");
958
+ }
959
+
960
+ function cleanupMedia() {
961
+ const video = $("media").querySelector("video");
962
+ if (video) { video.pause(); video.removeAttribute("src"); video.load(); }
963
+ const audio = $("media").querySelector("audio");
964
+ if (audio) { audio.pause(); audio.removeAttribute("src"); audio.load(); }
965
+ const image = $("media").querySelector("img");
966
+ if (image) image.removeAttribute("src");
967
+ $("media").replaceChildren();
968
+ }
969
+
970
+ function scrollProgress(node) {
971
+ const available = Math.max(0, node.scrollHeight - node.clientHeight);
972
+ return available ? node.scrollTop / available : 0;
973
+ }
974
+
975
+ function restoreScrollProgress(node, progress) {
976
+ const available = Math.max(0, node.scrollHeight - node.clientHeight);
977
+ node.scrollTop = available * Math.max(0, Math.min(1, progress));
978
+ }
979
+
980
+ function setView(view) {
981
+ const media = state.selected?.type === "media";
982
+ const contentStage = document.querySelector(".content-stage");
983
+ const previous = state.view === "edit" ? $("editor") : contentStage;
984
+ const progress = media ? 0 : scrollProgress(previous);
985
+ state.view = view;
986
+ $("editor").hidden = media || view !== "edit";
987
+ $("preview").hidden = media || view !== "preview";
988
+ $("media").hidden = !media;
989
+ $("editMode").setAttribute("aria-pressed", String(view === "edit"));
990
+ $("editMode").textContent = view === "edit" ? "返回阅读" : "修改正文";
991
+ if (!media && view === "preview") renderPreview();
992
+ if (!media) restoreScrollProgress(view === "edit" ? $("editor") : contentStage, progress);
993
+ }
994
+
995
+ function renderMedia(info) {
996
+ cleanupMedia();
997
+ const shell = element("div", "media-shell");
998
+ const stage = element("div", "media-stage");
999
+ const mediaNode = document.createElement(info.kind === "video" ? "video" : info.kind === "audio" ? "audio" : "img");
1000
+ mediaNode.src = info.contentUrl;
1001
+ mediaNode.setAttribute("aria-label", fileLabel(state.selected.path));
1002
+ if (info.kind === "video" || info.kind === "audio") {
1003
+ mediaNode.controls = true;
1004
+ mediaNode.preload = "metadata";
1005
+ if (info.kind === "video") mediaNode.playsInline = true;
1006
+ } else {
1007
+ mediaNode.alt = fileLabel(state.selected.path);
1008
+ }
1009
+ mediaNode.onerror = () => setMessage("媒体加载失败或文件过大", "danger");
1010
+ stage.append(mediaNode);
1011
+ const facts = element("div", "media-facts");
1012
+ const labels = { image: "图片预览", video: "视频预览", audio: "声音预览" };
1013
+ facts.append(statusPill(info.lifecycle), element("span", "", labels[info.kind] || "媒体预览"));
1014
+ shell.append(stage, facts);
1015
+ $("media").replaceChildren(shell);
1016
+ setMessage("媒体预览已载入。", "success");
1017
+ }
1018
+
1019
+ function warnLeave() {
1020
+ return !state.dirty || confirm("当前修改还没有保存,确认放弃吗?");
1021
+ }
1022
+
1023
+ function setDirty(value) {
1024
+ state.dirty = value;
1025
+ $("save").disabled = state.projectSwitching || state.saving || !value || !creatorEditable(state.selected);
1026
+ $("save").textContent = state.saving ? "保存中…" : value ? "保存修改" : "已保存";
1027
+ document.title = `${value ? "● " : ""}短剧创作台`;
1028
+ $("fileMeta").textContent = state.dirty ? "有未保存修改" : "";
1029
+ }
1030
+
1031
+ async function openFile(file, scrollToContent = false) {
1032
+ if (!warnLeave()) return;
1033
+ const sequence = ++state.loadSequence;
1034
+ cleanupMedia();
1035
+ state.selected = file;
1036
+ state.expandedGroups.add(contentGroupKey(file));
1037
+ state.version = null;
1038
+ $("editor").value = "";
1039
+ $("editor").scrollTop = 0;
1040
+ document.querySelector(".content-stage").scrollTop = 0;
1041
+ $("editor").disabled = true;
1042
+ $("preview").classList.add("empty-document");
1043
+ $("preview").replaceChildren();
1044
+ $("filename").textContent = fileLabel(file.path);
1045
+ $("fileKind").textContent = [episodeName(file.path), CONTENT_META[creatorSection(file.path)]?.label].filter(Boolean).join(" · ") || "创作内容";
1046
+ $("editMode").disabled = true;
1047
+ setDirty(false);
1048
+ setMessage("正在载入…");
1049
+ setView("preview");
1050
+ showLoadingSkeleton();
1051
+ renderContentList();
1052
+ renderExportSummary();
1053
+ updateAssistRow();
1054
+ if (scrollToContent && matchMedia("(max-width: 860px)").matches) focusDocument();
1055
+ try {
1056
+ if (file.type === "media") {
1057
+ setView("preview");
1058
+ const info = await api(`/api/media?project=${encodeURIComponent(state.project)}&path=${encodeURIComponent(file.path)}`);
1059
+ if (sequence === state.loadSequence && state.selected?.path === file.path) {
1060
+ clearLoadingSkeleton();
1061
+ renderMedia(info);
1062
+ }
1063
+ return;
1064
+ }
1065
+ const data = await api(`/api/file?project=${encodeURIComponent(state.project)}&path=${encodeURIComponent(file.path)}`);
1066
+ if (sequence !== state.loadSequence || state.selected?.path !== file.path) return;
1067
+ state.version = data.version;
1068
+ $("editor").value = data.content;
1069
+ const editable = Boolean(data.writable && creatorEditable(file));
1070
+ $("editor").disabled = !editable;
1071
+ $("editMode").disabled = !editable;
1072
+ clearLoadingSkeleton();
1073
+ $("preview").classList.remove("empty-document");
1074
+ setView("preview");
1075
+ setMessage("内容已载入");
1076
+ } catch (error) {
1077
+ if (sequence !== state.loadSequence) return;
1078
+ state.view = "preview";
1079
+ $("editor").hidden = true;
1080
+ $("preview").hidden = false;
1081
+ $("media").hidden = true;
1082
+ $("editMode").disabled = true;
1083
+ $("editMode").setAttribute("aria-pressed", "false");
1084
+ $("editMode").textContent = "修改正文";
1085
+ clearLoadingSkeleton();
1086
+ $("preview").classList.add("empty-document");
1087
+ $("preview").replaceChildren(element("p", "preview-warning", "内容无法打开"));
1088
+ setMessage(friendlyFailure(error.message), "danger");
1089
+ }
1090
+ }
1091
+
1092
+ async function selectProject(id, preferredPath = "") {
1093
+ if (!warnLeave()) { $("projects").value = state.project; return; }
1094
+ const previousProject = state.project;
1095
+ const sequence = ++state.projectLoadSequence;
1096
+ const controls = {
1097
+ contentListInert: $("contentList").inert,
1098
+ editorDisabled: $("editor").disabled,
1099
+ editModeDisabled: $("editMode").disabled,
1100
+ };
1101
+ let committed = false;
1102
+ state.projectSwitching = true;
1103
+ $("projects").disabled = true;
1104
+ $("contentList").inert = true;
1105
+ $("editor").disabled = true;
1106
+ $("editMode").disabled = true;
1107
+ $("documentPane").setAttribute("aria-busy", "true");
1108
+ setDirty(state.dirty);
1109
+ setMessage("正在切换项目…");
1110
+ clearNotice();
1111
+ try {
1112
+ const [tree, projectStatus] = await Promise.all([
1113
+ api(`/api/tree?project=${encodeURIComponent(id)}`),
1114
+ api(`/api/status?project=${encodeURIComponent(id)}`),
1115
+ ]);
1116
+ if (sequence !== state.projectLoadSequence) return;
1117
+ const files = flatten(tree.tree);
1118
+ cleanupMedia();
1119
+ ++state.loadSequence;
1120
+ committed = true;
1121
+ state.project = id;
1122
+ state.selected = null;
1123
+ state.expandedGroups.clear();
1124
+ state.version = null;
1125
+ state.dirty = false;
1126
+ state.files = files;
1127
+ state.visibleFiles = state.files.filter((file) => creatorSection(file.path));
1128
+ state.status = projectStatus;
1129
+ $("search").value = "";
1130
+ $("filename").textContent = "正在打开创作内容…";
1131
+ $("fileKind").textContent = "创作正文";
1132
+ $("preview").classList.add("empty-document");
1133
+ $("preview").replaceChildren(document.createTextNode("正在载入第一份创作内容…"));
1134
+ const selectedOption = $("projects").selectedOptions[0];
1135
+ if (selectedOption) selectedOption.textContent = creatorTitle(projectStatus.title);
1136
+ if (tree.warnings?.length) showNotice("部分内容暂时无法读取,已展示其余创作资料。", "warning");
1137
+ renderWorkspace();
1138
+ const initial = state.visibleFiles.find((file) => file.path === preferredPath) ||
1139
+ state.visibleFiles.find((file) => /(?:^|\/)(?:screenplay|剧本)\.md$/i.test(file.path)) ||
1140
+ state.visibleFiles.find((file) => file.path.toLowerCase() === "readme.md") ||
1141
+ state.visibleFiles[0];
1142
+ if (initial) {
1143
+ $("preview").classList.remove("empty-document");
1144
+ await openFile(initial);
1145
+ } else {
1146
+ $("filename").textContent = "暂无创作内容";
1147
+ $("editor").disabled = true;
1148
+ $("editMode").disabled = true;
1149
+ clearLoadingSkeleton();
1150
+ $("preview").replaceChildren();
1151
+ setMessage("项目中还没有创作内容");
1152
+ }
1153
+ } catch (error) {
1154
+ if (sequence !== state.projectLoadSequence) return;
1155
+ if (!committed) {
1156
+ $("projects").value = previousProject || "";
1157
+ setMessage(previousProject ? "项目未切换" : "项目无法打开", "danger");
1158
+ }
1159
+ showNotice(friendlyFailure(error.message), "danger");
1160
+ } finally {
1161
+ if (sequence === state.projectLoadSequence) {
1162
+ state.projectSwitching = false;
1163
+ $("projects").disabled = false;
1164
+ $("contentList").inert = controls.contentListInert;
1165
+ $("documentPane").removeAttribute("aria-busy");
1166
+ if (!committed) {
1167
+ $("editor").disabled = controls.editorDisabled;
1168
+ $("editMode").disabled = controls.editModeDisabled;
1169
+ }
1170
+ setDirty(state.dirty);
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ async function save() {
1176
+ if (state.projectSwitching || !state.dirty || !state.selected || state.saving) return;
1177
+ const snapshot = { sequence: ++state.saveSequence, project: state.project, path: state.selected.path, version: state.version, content: $("editor").value };
1178
+ state.saving = true;
1179
+ setDirty(true);
1180
+ try {
1181
+ validateStructuredText(snapshot.path, snapshot.content);
1182
+ const result = await api(`/api/file?project=${encodeURIComponent(snapshot.project)}&path=${encodeURIComponent(snapshot.path)}`, {
1183
+ method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content: snapshot.content, expectedVersion: snapshot.version }),
1184
+ });
1185
+ if (snapshot.sequence !== state.saveSequence || state.project !== snapshot.project || state.selected?.path !== snapshot.path) return;
1186
+ state.version = result.version;
1187
+ state.selected.size = new TextEncoder().encode(snapshot.content).length;
1188
+ setDirty(!savedContentIsCurrent(snapshot.content, $("editor").value));
1189
+ setMessage(`已保存 · ${new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`, "success");
1190
+ try {
1191
+ const refreshed = await api(`/api/status?project=${encodeURIComponent(snapshot.project)}`);
1192
+ if (state.project !== snapshot.project) return;
1193
+ state.status = refreshed;
1194
+ renderWorkspace();
1195
+ } catch (_error) { setMessage(statusRefreshFailureMessage(), "warning"); }
1196
+ } catch (error) {
1197
+ if (
1198
+ snapshot.sequence === state.saveSequence &&
1199
+ state.project === snapshot.project &&
1200
+ state.selected?.path === snapshot.path
1201
+ ) setMessage(friendlyFailure(error.message), "danger");
1202
+ }
1203
+ finally { if (snapshot.sequence === state.saveSequence) { state.saving = false; setDirty(state.dirty); } }
1204
+ }
1205
+
1206
+ async function boot() {
1207
+ try {
1208
+ await establishSession();
1209
+ const data = await api("/api/projects");
1210
+ if (!data || !Array.isArray(data.projects)) throw new Error("invalid dashboard response");
1211
+ const options = data.projects.map((project) => {
1212
+ const option = element("option", "", project.title || "未命名短剧");
1213
+ option.value = project.id;
1214
+ return option;
1215
+ });
1216
+ $("projects").replaceChildren(...options);
1217
+ if (data.projects.length) await selectProject(data.projects[0].id);
1218
+ else showNotice("还没有发现可打开的短剧项目。", "warning");
1219
+ } catch (error) { showNotice(friendlyFailure(error.message), "danger"); }
1220
+ }
1221
+
1222
+ function start() {
1223
+ $("projects").onchange = (event) => selectProject(event.target.value);
1224
+ $("search").oninput = renderContentList;
1225
+ $("editor").oninput = () => setDirty(true);
1226
+ $("save").onclick = save;
1227
+ $("editMode").onclick = () => setView(state.view === "edit" ? "preview" : "edit");
1228
+ document.querySelector(".brand").onclick = (event) => {
1229
+ event.preventDefault();
1230
+ scrollTo({ top: 0, behavior: scrollBehavior() });
1231
+ $("mainContent").focus({ preventScroll: true });
1232
+ };
1233
+ addEventListener("beforeunload", (event) => { if (state.dirty) { event.preventDefault(); event.returnValue = ""; } });
1234
+ addEventListener("pagehide", cleanupMedia);
1235
+ addEventListener("keydown", (event) => {
1236
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "s") { event.preventDefault(); save(); }
1237
+ });
1238
+ boot();
1239
+ }
1240
+
1241
+ if (typeof document !== "undefined") start();
1242
+ if (typeof module !== "undefined" && module.exports) {
1243
+ module.exports = {
1244
+ collectEpisodes,
1245
+ creatorProjection,
1246
+ creatorSection,
1247
+ creatorStatus,
1248
+ creatorEditable,
1249
+ creatorTitle,
1250
+ episodePresentation,
1251
+ formatBytes,
1252
+ friendlyFailure,
1253
+ friendlyKey,
1254
+ mediaKind,
1255
+ orderedForReading,
1256
+ previewRecords,
1257
+ projectOverviewModel,
1258
+ readJsonLines,
1259
+ renderMarkdown,
1260
+ savedContentIsCurrent,
1261
+ statusRefreshFailureMessage,
1262
+ };
1263
+ }