@oh-story/dsh 0.1.10 → 0.1.12

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 (444) hide show
  1. package/README.md +11 -11
  2. package/lib/client.js +7 -3
  3. package/lib/client.js.map +2 -2
  4. package/lib/drama/manifest.json +212 -142
  5. package/lib/drama/skills/short-drama/SKILL.md +14 -2
  6. package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
  8. package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
  9. package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
  10. package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
  11. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
  12. package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
  13. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  14. package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
  15. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
  16. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
  17. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
  18. package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
  19. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
  20. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
  21. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
  22. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
  23. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
  24. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
  25. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
  26. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
  27. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
  28. package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
  29. package/lib/drama/skills/short-drama-edit/SKILL.md +43 -20
  30. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
  31. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
  32. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
  33. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
  34. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
  35. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
  36. package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
  37. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
  38. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
  39. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
  40. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
  41. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
  42. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
  43. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
  44. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
  45. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
  46. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +4 -2
  47. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +3 -1
  48. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +6 -2
  49. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +12 -6
  50. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +13 -8
  51. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +2 -0
  52. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1153 -121
  53. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +49 -9
  54. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
  55. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
  56. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
  57. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
  58. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
  59. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
  60. package/lib/drama/skills/short-drama-review/SKILL.md +2 -2
  61. package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
  62. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
  63. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
  64. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +15 -0
  65. package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
  66. package/lib/drama/skills/short-drama-storyboard/SKILL.md +13 -5
  67. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
  68. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
  69. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +82 -14
  70. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +4 -2
  71. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +5 -3
  72. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +12 -0
  73. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
  74. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
  75. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +40 -1
  76. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +20 -14
  77. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +1 -1
  78. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +8 -7
  79. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
  80. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
  81. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
  82. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
  83. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
  84. package/lib/drama/skills/short-drama-write/SKILL.md +8 -3
  85. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
  86. package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
  87. package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
  88. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
  89. package/lib/index.js +71 -21
  90. package/lib/index.js.map +3 -3
  91. package/lib/oh-story/manifest.json +545 -310
  92. package/lib/oh-story/roles/chapter-extractor.md +11 -11
  93. package/lib/oh-story/roles/character-designer.md +1 -2
  94. package/lib/oh-story/roles/consistency-checker.md +11 -9
  95. package/lib/oh-story/roles/narrative-writer.md +24 -25
  96. package/lib/oh-story/roles/story-architect.md +9 -61
  97. package/lib/oh-story/roles/story-explorer.md +20 -125
  98. package/lib/oh-story/roles/story-researcher.md +0 -1
  99. package/lib/oh-story/skills/story/SKILL.md +6 -6
  100. package/lib/oh-story/skills/story/VERSION +1 -1
  101. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
  102. package/lib/oh-story/skills/story/references/author-memory.md +40 -118
  103. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +3 -3
  104. package/lib/oh-story/skills/story-deslop/SKILL.md +24 -25
  105. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
  106. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +1 -1
  107. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  108. package/lib/oh-story/skills/story-deslop/references/author-memory.md +40 -118
  109. package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
  110. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +21 -4
  111. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
  112. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +3 -3
  113. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
  114. package/lib/oh-story/skills/story-import/SKILL.md +63 -579
  115. package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
  116. package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
  117. package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
  118. package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
  119. package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
  120. package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
  121. package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
  122. package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
  123. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +95 -91
  124. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
  125. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
  126. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +11 -9
  127. package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
  128. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +101 -14
  129. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +278 -23
  130. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +179 -23
  131. package/lib/oh-story/skills/story-long-analyze/SKILL.md +28 -50
  132. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +12 -5
  133. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +1 -1
  134. package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
  135. package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
  136. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +21 -55
  137. package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  138. package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  139. package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  140. package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  141. package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
  142. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +17 -0
  143. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +1 -1
  144. package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  145. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +47 -8
  146. package/lib/oh-story/skills/story-long-scan/SKILL.md +41 -193
  147. package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  148. package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
  149. package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  150. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
  151. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
  152. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
  153. package/lib/oh-story/skills/story-long-write/SKILL.md +32 -73
  154. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
  155. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +1 -1
  156. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +18 -201
  157. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  158. package/lib/oh-story/skills/story-long-write/references/author-memory.md +40 -118
  159. package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
  160. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +6 -10
  161. package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
  162. package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
  163. package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
  164. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
  165. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  166. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +3 -2
  167. package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
  168. package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
  169. package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
  170. package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
  171. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
  172. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
  173. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
  174. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
  175. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
  176. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
  177. package/lib/oh-story/skills/story-long-write/references/project-files.md +10 -6
  178. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +1 -1
  179. package/lib/oh-story/skills/story-long-write/references/reference-index.md +4 -4
  180. package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
  181. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
  182. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  183. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
  184. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
  185. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +11 -9
  186. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +33 -44
  187. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +18 -51
  188. package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
  189. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +53 -67
  190. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -251
  191. package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
  192. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +5 -3
  193. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +18 -56
  194. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +3 -3
  195. package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  196. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +96 -16
  197. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
  198. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +101 -14
  199. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
  200. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +2 -1
  201. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +250 -50
  202. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +278 -23
  203. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +179 -23
  204. package/lib/oh-story/skills/story-review/SKILL.md +19 -273
  205. package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
  206. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +1 -1
  207. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
  208. package/lib/oh-story/skills/story-review/references/author-memory.md +40 -118
  209. package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
  210. package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
  211. package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
  212. package/lib/oh-story/skills/story-review/references/review-quality.md +2 -1
  213. package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
  214. package/lib/oh-story/skills/story-review/references/solo.md +52 -0
  215. package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
  216. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
  217. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +11 -9
  218. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +3 -3
  219. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
  220. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +278 -23
  221. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +179 -23
  222. package/lib/oh-story/skills/story-setup/SKILL.md +69 -458
  223. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
  224. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +1 -1
  225. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
  226. package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  227. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
  228. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +21 -4
  229. package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  230. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
  231. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
  232. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
  234. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
  235. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
  236. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
  237. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
  238. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
  239. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
  240. package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
  241. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
  242. package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
  243. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
  244. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  245. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
  246. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +18 -56
  247. package/lib/oh-story/skills/story-setup/references/diagnostics.md +3 -3
  248. package/lib/oh-story/skills/story-short-analyze/SKILL.md +37 -110
  249. package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
  250. package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
  251. package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
  252. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
  253. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
  254. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
  255. package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  256. package/lib/oh-story/skills/story-short-analyze/references/{material-decomposition.md → stage4-6-reversal-summary.md} +303 -374
  257. package/lib/oh-story/skills/story-short-scan/SKILL.md +30 -64
  258. package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
  259. package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  260. package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
  261. package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
  262. package/lib/oh-story/skills/story-short-write/SKILL.md +23 -34
  263. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
  264. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  265. package/lib/oh-story/skills/story-short-write/references/author-memory.md +40 -118
  266. package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
  267. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
  268. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
  269. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
  270. package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
  271. package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -49
  272. package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
  273. package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
  274. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
  275. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
  276. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
  277. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
  278. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
  279. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
  280. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +19 -12
  281. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +22 -32
  282. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +84 -17
  283. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
  284. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +3 -3
  285. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
  286. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
  287. package/lib/types/client/video-studio.d.ts.map +1 -1
  288. package/lib/types/native-hooks.d.ts +2 -2
  289. package/lib/types/native-hooks.d.ts.map +1 -1
  290. package/lib/types/role-tool.d.ts.map +1 -1
  291. package/lib/types/skill-provider.d.ts.map +1 -1
  292. package/lib/types/video-project.d.ts.map +1 -1
  293. package/lib/types/workspace-route.d.ts.map +1 -1
  294. package/lib/video-recap/CHANGELOG.md +60 -1
  295. package/lib/video-recap/README.en.md +348 -137
  296. package/lib/video-recap/README.md +340 -136
  297. package/lib/video-recap/manifest.json +406 -251
  298. package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
  299. package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
  300. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  301. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
  302. package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
  303. package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
  304. package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
  305. package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
  306. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
  307. package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  308. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  309. package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  310. package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  311. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  312. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  313. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
  314. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
  315. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
  316. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
  317. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
  318. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
  319. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
  320. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
  321. package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  322. package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
  323. package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
  324. package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
  325. package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
  326. package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
  327. package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
  328. package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
  329. package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
  330. package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
  331. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
  332. package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
  333. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
  334. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
  335. package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
  336. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
  337. package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  338. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
  339. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
  340. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
  341. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  342. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
  343. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
  344. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
  345. package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
  346. package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
  347. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
  348. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
  349. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
  350. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
  351. package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
  352. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
  353. package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
  354. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
  355. package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
  356. package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
  357. package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
  358. package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
  359. package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
  360. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
  361. package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
  362. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
  363. package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
  364. package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
  365. package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
  366. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
  367. package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
  368. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
  369. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
  370. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
  371. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
  372. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
  373. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
  374. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
  375. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
  376. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
  377. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
  378. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
  379. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
  380. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
  381. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
  382. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
  383. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
  384. package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
  385. package/lib/video-recap/skills/video-script/SKILL.md +16 -13
  386. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
  387. package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
  388. package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
  389. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
  390. package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
  391. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
  392. package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
  393. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
  394. package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
  395. package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
  396. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
  397. package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
  398. package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
  399. package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
  400. package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
  401. package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
  402. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  403. package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
  404. package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  405. package/lib/video-recap/skills/video-understanding/scripts/{agent_brief.py → briefing/builder.py} +38 -35
  406. package/lib/video-recap/skills/{video-script/scripts/brief_context.py → video-understanding/scripts/briefing/context.py} +4 -35
  407. package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
  408. package/lib/video-recap/skills/video-understanding/scripts/{brief_timeline.py → briefing/timeline.py} +5 -6
  409. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
  410. package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
  411. package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
  412. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
  413. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
  414. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
  415. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
  416. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
  417. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
  418. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
  419. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
  420. package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
  421. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
  422. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  423. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
  424. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
  425. package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  426. package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  427. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
  428. package/package.json +41 -41
  429. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -538
  430. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -603
  431. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
  432. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
  433. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
  434. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
  435. package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
  436. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
  437. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +0 -399
  438. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +0 -327
  439. package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
  440. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +0 -348
  441. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
  442. /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
  443. /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
  444. /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
@@ -0,0 +1,188 @@
1
+ // Decisions the overlay makes that do not need a browser. Plain JavaScript so
2
+ // the suite's tests can import this file with Node and no build step.
3
+
4
+ // ---------------------------------------------------------------- fonts
5
+
6
+ /** The faces the overlay draws with, as `document.fonts.load` names them. */
7
+ export const FACES = {
8
+ heavy: '900 16px "Noto Sans SC"',
9
+ bold: '700 16px "Noto Sans SC"',
10
+ mono: '800 16px "JetBrains Mono"',
11
+ };
12
+
13
+ /** Fixed words the panels draw themselves, so their glyphs are loaded too. */
14
+ export const CHROME_TEXT = "【系统提示】【新任务】进度传说史诗稀有·";
15
+ const DIGITS = "0123456789天:/, ";
16
+
17
+ /**
18
+ * Which characters each face must have loaded before the first frame.
19
+ *
20
+ * The faces are split by unicode range, and a browser fetches a range only
21
+ * when text in it is laid out -- after the frame has been captured. Asking for
22
+ * every character the film will draw, up front, is what makes frame 0 look
23
+ * like frame 1000.
24
+ *
25
+ * @param {{ text: string }[]} cues
26
+ * @param {{ items: { text: string }[] }[]} screenTexts
27
+ */
28
+ export const fontLoadPlan = (cues, screenTexts) => {
29
+ const unique = (/** @type {string} */ text) => [...new Set(Array.from(text))].join("");
30
+ const drawn = unique(
31
+ [...cues.map((cue) => cue.text), ...screenTexts.flatMap((piece) => piece.items.map((item) => item.text)),
32
+ CHROME_TEXT, DIGITS].join(""),
33
+ );
34
+ return [
35
+ { font: FACES.heavy, text: drawn },
36
+ { font: FACES.bold, text: drawn },
37
+ { font: FACES.mono, text: DIGITS },
38
+ ];
39
+ };
40
+
41
+ export const TIMED_OUT = "timed out";
42
+
43
+ /**
44
+ * What is wrong with a finished load, or null when every face is ready.
45
+ *
46
+ * A face that no stylesheet declares loads as an empty list and raises
47
+ * nothing; the browser would substitute and the film would ship in the wrong
48
+ * typeface. So an empty list is a failure, as is a load that never finished.
49
+ *
50
+ * @param {{ font: string, text: string }[]} plan
51
+ * @param {unknown} outcome what `settleWithin(Promise.all(loads))` resolved to
52
+ * @param {(font: string, text: string) => boolean} check `document.fonts.check`
53
+ */
54
+ export const fontLoadProblem = (plan, outcome, check) => {
55
+ if (outcome === TIMED_OUT) return `字体在 ${FONT_WAIT_MS / 1000} 秒内没有加载完:${plan.map((p) => p.font).join("、")}`;
56
+ if (outcome instanceof Error) return `字体加载失败:${outcome.message}`;
57
+ const loaded = /** @type {unknown[][]} */ (outcome);
58
+ const undeclared = plan.filter((_, index) => !loaded[index] || loaded[index].length === 0);
59
+ if (undeclared.length) {
60
+ return `没有声明这些字体(Remotion 工作区的字体包没装?):${undeclared.map((p) => p.font).join("、")}`;
61
+ }
62
+ const pending = plan.filter((p) => !check(p.font, p.text));
63
+ if (pending.length) return `字体还没就绪:${pending.map((p) => p.font).join("、")}`;
64
+ return null;
65
+ };
66
+
67
+ export const FONT_WAIT_MS = 10000;
68
+
69
+ /**
70
+ * Resolves with `promise`'s value, its error, or `TIMED_OUT` after `ms` --
71
+ * whichever comes first -- so a load that never settles cannot hold a frame
72
+ * until the whole render times out, and never resolves as if it had succeeded.
73
+ *
74
+ * @param {Promise<unknown>} promise
75
+ * @param {number} ms
76
+ * @returns {Promise<unknown>}
77
+ */
78
+ export const settleWithin = (promise, ms) =>
79
+ new Promise((resolve) => {
80
+ const timer = setTimeout(() => resolve(TIMED_OUT), ms);
81
+ promise.then(
82
+ (value) => {
83
+ clearTimeout(timer);
84
+ resolve(value);
85
+ },
86
+ (error) => {
87
+ clearTimeout(timer);
88
+ resolve(error instanceof Error ? error : new Error(String(error)));
89
+ },
90
+ );
91
+ });
92
+
93
+ const GENERIC_FAMILIES = new Set([
94
+ "serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "math",
95
+ "emoji", "fangsong", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded",
96
+ ]);
97
+
98
+ /** The named families in a CSS font stack; generic keywords always resolve, so they prove nothing. */
99
+ export const namedFamilies = (/** @type {string} */ stack) =>
100
+ stack
101
+ .split(",")
102
+ .map((part) => part.trim())
103
+ .filter((part) => part && !GENERIC_FAMILIES.has(part.replace(/^["']|["']$/g, "").toLowerCase()));
104
+
105
+ // Latin glyphs in the probe, because a CJK-only probe cannot tell PingFang from
106
+ // "nothing installed" on a machine whose CJK fallback is PingFang itself.
107
+ const LATIN_PROBE = " Hamburgefonstiv 0123456789";
108
+ // Two generic tails that never share Latin glyphs. A family that happens to
109
+ // look like one of them still differs from the other.
110
+ const PROBE_TAILS = ["serif", "monospace"];
111
+
112
+ /**
113
+ * True when none of the stack's named families resolves.
114
+ *
115
+ * `measure(stack, text)` returns the rendered width of `text` in that stack.
116
+ * A family that resolves changes the width against at least one tail; only
117
+ * when the stack measures exactly like both bare tails did nothing resolve.
118
+ *
119
+ * @param {(stack: string, text: string) => number} measure
120
+ * @param {string} stack
121
+ * @param {string} sample
122
+ */
123
+ export const familyIsMissing = (measure, stack, sample) => {
124
+ const named = namedFamilies(stack);
125
+ if (named.length === 0) return false;
126
+ const text = sample + LATIN_PROBE;
127
+ return PROBE_TAILS.every(
128
+ (tail) => measure(`${named.join(", ")}, ${tail}`, text) === measure(tail, text),
129
+ );
130
+ };
131
+
132
+ // ---------------------------------------------------------------- text
133
+
134
+ /** Width of a line in ems: CJK and full-width forms are one, the rest a little over half. */
135
+ export const lineEms = (/** @type {string} */ text) =>
136
+ Array.from(text).reduce((sum, ch) => sum + (ch.charCodeAt(0) > 0x2e7f ? 1 : 0.58), 0);
137
+
138
+ /** Characters of `text` a typewriter shows `elapsed` seconds in, at 35 ms each after `delay`. */
139
+ export const typedLength = (/** @type {number} */ elapsed, delay = 0) =>
140
+ Math.max(0, Math.floor((elapsed - delay) / 0.035 + 1e-9));
141
+
142
+ /**
143
+ * `2 / 1,000,000` → { done: 2, total: 1000000, ratio }, or null when the item is
144
+ * not a progress reading. The ratio is clamped so the bar never overruns.
145
+ *
146
+ * @param {string} text
147
+ */
148
+ export const parseProgress = (text) => {
149
+ const found = text.match(/^\s*([0-9][0-9,]*)\s*\/\s*([0-9][0-9,]*)\s*$/);
150
+ if (!found) return null;
151
+ const done = Number(found[1].replace(/,/g, ""));
152
+ const total = Number(found[2].replace(/,/g, ""));
153
+ if (!(total > 0)) return null;
154
+ return { done, total, ratio: Math.min(1, Math.max(0, done / total)) };
155
+ };
156
+
157
+ // ---------------------------------------------------------------- countdown
158
+
159
+ /**
160
+ * Whole seconds still showing `elapsed` seconds after a countdown that read
161
+ * `countdown` at its first frame. Rounded up, as a timer reads: it shows its
162
+ * starting value for the whole first second. A pure function of the frame, so
163
+ * every render of the same frame shows the same digits.
164
+ *
165
+ * @param {number} countdown
166
+ * @param {number} elapsed
167
+ */
168
+ export const secondsLeft = (countdown, elapsed) =>
169
+ Math.max(0, Math.ceil(countdown - elapsed - 1e-6));
170
+
171
+ /** Seconds since the displayed digit last changed; drives the per-second pulse. */
172
+ export const sinceTick = (/** @type {number} */ countdown, /** @type {number} */ elapsed) => {
173
+ const remaining = countdown - elapsed;
174
+ return Math.ceil(remaining - 1e-6) - remaining;
175
+ };
176
+
177
+ const two = (/** @type {number} */ n) => String(n).padStart(2, "0");
178
+
179
+ /** `4天 23:59:58`, `01:05:00` or `00:42`, by the largest unit still in play. */
180
+ export const formatCountdown = (/** @type {number} */ seconds) => {
181
+ const days = Math.floor(seconds / 86400);
182
+ const hours = Math.floor((seconds % 86400) / 3600);
183
+ const minutes = Math.floor((seconds % 3600) / 60);
184
+ const rest = seconds % 60;
185
+ if (days > 0) return `${days}天 ${two(hours)}:${two(minutes)}:${two(rest)}`;
186
+ if (hours > 0) return `${two(hours)}:${two(minutes)}:${two(rest)}`;
187
+ return `${two(minutes)}:${two(rest)}`;
188
+ };
@@ -1,32 +1,45 @@
1
+ export type CueKind = "line" | "vo" | "system";
2
+
1
3
  export type Cue = {
2
4
  /** Output-time seconds, measured from the rendered segments. */
3
5
  start: number;
4
6
  end: number;
5
- /** The line as written in 剧本.md. Never a transcription. */
7
+ /** One on-screen line of a 剧本.md line in its burned form. */
6
8
  text: string;
9
+ /** Dialogue, an inner or narrating voice, or the system's voice -- read from 剧本.md. */
10
+ kind: CueKind;
11
+ /** Words to highlight, each occurring in `text`. */
12
+ keys: string[];
13
+ };
14
+
15
+ export type ScreenTextStyle = "card" | "system" | "task" | "corner";
16
+ export type Rarity = "传说" | "史诗" | "稀有";
17
+
18
+ export type ScreenText = {
19
+ /** Output-time seconds. */
20
+ start: number;
21
+ end: number;
22
+ style: ScreenTextStyle;
23
+ /** Rows or items, each traced to a [画面文字] line in 剧本.md. */
24
+ items: { text: string; rarity: Rarity | null }[];
25
+ /** Seconds left at `start`, already resolved for 「接续」; null when nothing counts down. */
26
+ countdown: number | null;
7
27
  };
8
28
 
9
- export type SubtitleProps = {
29
+ export type OverlayProps = {
10
30
  cues: Cue[];
31
+ screenTexts: ScreenText[];
11
32
  width: number;
12
33
  height: number;
13
34
  fps: number;
14
35
  durationInSeconds: number;
15
- /** Type size as a fraction of frame height, so one number survives any delivery. */
16
- fontScale: number;
17
- /** Distance from the bottom edge, also a fraction of frame height. */
18
- bottomScale: number;
19
- fontFamily: string;
20
36
  };
21
37
 
22
- export const defaultProps: SubtitleProps = {
38
+ export const defaultProps: OverlayProps = {
23
39
  cues: [],
40
+ screenTexts: [],
24
41
  width: 1080,
25
42
  height: 1920,
26
- fps: 24,
43
+ fps: 30,
27
44
  durationInSeconds: 1,
28
- fontScale: 0.034,
29
- bottomScale: 0.055,
30
- fontFamily:
31
- '"PingFang SC", "Noto Sans CJK SC", "Source Han Sans SC", "Microsoft YaHei", sans-serif',
32
45
  };
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { COLOURS, MONO, SANS, StyleProps, TOP_U, entrance, exit, unit } from "./tokens";
4
+
5
+ /**
6
+ * "主号 2" → name 主号, value 2; "主号 粉丝 2" adds 粉丝 as a small unit before
7
+ * the value. A row with one word is all name.
8
+ */
9
+ const splitRow = (row: string): { name: string; unit: string; value: string } => {
10
+ const words = row.split(/[\s ]+/).filter(Boolean);
11
+ if (words.length < 2) return { name: row, unit: "", value: "" };
12
+ return { name: words[0], unit: words.slice(1, -1).join(" "), value: words[words.length - 1] };
13
+ };
14
+
15
+ /** A clean app dashboard card: white, rounded, a coloured dot per row, big red numbers. */
16
+ export const Card: React.FC<StyleProps> = ({ text }) => {
17
+ const frame = useCurrentFrame();
18
+ const { fps, height, width } = useVideoConfig();
19
+ const u = unit(height);
20
+ const shown = entrance(frame, fps, 0, 15);
21
+ return (
22
+ <AbsoluteFill style={{ alignItems: "center", paddingTop: u * TOP_U.card, opacity: exit(frame, fps, text, 0.15) }}>
23
+ <div
24
+ style={{
25
+ width: width * 0.8,
26
+ background: COLOURS.cardSurface,
27
+ borderRadius: u * 1.6,
28
+ boxShadow: `0 ${u}px ${u * 4}px rgba(0,0,0,0.5)`,
29
+ padding: `${u * 0.7}px ${u * 2.2}px`,
30
+ fontFamily: SANS,
31
+ transform: `translateY(${(1 - shown) * u * 4}px) scale(${0.94 + 0.06 * shown})`,
32
+ opacity: shown,
33
+ }}
34
+ >
35
+ {text.items.map((item, index) => {
36
+ const row = splitRow(item.text);
37
+ return (
38
+ <div
39
+ key={index}
40
+ style={{
41
+ display: "flex",
42
+ alignItems: "center",
43
+ gap: u * 1.2,
44
+ padding: `${u * 1.1}px 0`,
45
+ borderTop: index ? `1px solid ${COLOURS.cardRule}` : "none",
46
+ opacity: entrance(frame, fps, 0.08 + index * 0.07),
47
+ }}
48
+ >
49
+ <span
50
+ style={{
51
+ width: u * 3,
52
+ height: u * 3,
53
+ borderRadius: "50%",
54
+ background: COLOURS.cardDots[index % COLOURS.cardDots.length],
55
+ }}
56
+ />
57
+ <span style={{ flex: 1, fontSize: u * 2.5, fontWeight: 700, color: COLOURS.cardName }}>{row.name}</span>
58
+ {row.unit ? (
59
+ <span style={{ fontSize: u * 1.7, fontWeight: 700, color: COLOURS.cardUnit, marginRight: u * 0.6 }}>
60
+ {row.unit}
61
+ </span>
62
+ ) : null}
63
+ <span style={{ fontFamily: MONO, fontSize: u * 3.6, fontWeight: 800, color: COLOURS.cardValue }}>
64
+ {row.value}
65
+ </span>
66
+ </div>
67
+ );
68
+ })}
69
+ </div>
70
+ </AbsoluteFill>
71
+ );
72
+ };
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { formatCountdown, secondsLeft } from "../rules.mjs";
4
+ import { Frame } from "./chrome";
5
+ import { COLOURS, MONO, StyleProps, TOP_U, entrance, exit, unit } from "./tokens";
6
+
7
+ /** The task chrome in miniature, top right: the label, and the countdown when there is one. */
8
+ export const CornerChip: React.FC<StyleProps> = ({ text }) => {
9
+ const frame = useCurrentFrame();
10
+ const { fps, height } = useVideoConfig();
11
+ const u = unit(height);
12
+ const shown = entrance(frame, fps);
13
+ return (
14
+ <AbsoluteFill
15
+ style={{ alignItems: "flex-end", paddingTop: u * TOP_U.corner, paddingRight: u * 2.4, opacity: exit(frame, fps, text) }}
16
+ >
17
+ <div style={{ opacity: shown, transform: `translateX(${(1 - shown) * u * 6}px)` }}>
18
+ <Frame colour={COLOURS.task} width={u * 24} style={{ padding: `${u * 0.7}px ${u * 1.1}px` }}>
19
+ <div style={{ fontSize: u * 1.35, color: COLOURS.chipLabel, fontWeight: 700, letterSpacing: u * 0.2 }}>
20
+ {text.items.map((item) => item.text).join(" · ")}
21
+ </div>
22
+ {text.countdown === null ? null : (
23
+ <div
24
+ style={{
25
+ fontFamily: MONO,
26
+ fontWeight: 800,
27
+ fontSize: u * 2.4,
28
+ color: COLOURS.alarm,
29
+ textShadow: `0 0 ${u * 0.4}px ${COLOURS.alarm}`,
30
+ }}
31
+ >
32
+ {formatCountdown(secondsLeft(text.countdown, frame / fps))}
33
+ </div>
34
+ )}
35
+ </Frame>
36
+ </div>
37
+ </AbsoluteFill>
38
+ );
39
+ };
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { Sequence, useVideoConfig } from "remotion";
3
+ import { ScreenTextStyle } from "../schema";
4
+ import { assertFamilyResolves } from "../font";
5
+ import { Card } from "./Card";
6
+ import { CornerChip } from "./CornerChip";
7
+ import { SystemPanel } from "./SystemPanel";
8
+ import { TaskPanel } from "./TaskPanel";
9
+ import { SANS, StyleProps } from "./tokens";
10
+
11
+ /** One component per style; swapping a look never touches parsing or timing. */
12
+ const STYLES: Record<ScreenTextStyle, React.FC<StyleProps>> = {
13
+ card: Card,
14
+ system: SystemPanel,
15
+ task: TaskPanel,
16
+ corner: CornerChip,
17
+ };
18
+
19
+ const Checked: React.FC<StyleProps> = ({ text }) => {
20
+ assertFamilyResolves(SANS, text.items.map((item) => item.text).join(""));
21
+ const Style = STYLES[text.style];
22
+ return <Style text={text} />;
23
+ };
24
+
25
+ /** Places one piece in output time; inside it, frame 0 is the piece's first frame. */
26
+ export const ScreenTextLayer: React.FC<StyleProps> = ({ text }) => {
27
+ const { fps } = useVideoConfig();
28
+ const from = Math.round(text.start * fps);
29
+ const until = Math.round(text.end * fps);
30
+ if (until <= from) return null;
31
+ return (
32
+ <Sequence from={from} durationInFrames={until - from} layout="none">
33
+ <Checked text={text} />
34
+ </Sequence>
35
+ );
36
+ };
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { Frame, Header, Reward, Typed } from "./chrome";
4
+ import { COLOURS, StyleProps, TOP_U, entrance, exit, glitch, unit } from "./tokens";
5
+
6
+ const TYPE_DELAY = 0.15;
7
+
8
+ /**
9
+ * 【系统提示】: the first item types out as the status line, the rest arrive as
10
+ * rarity-coloured reward rows once it has finished.
11
+ */
12
+ export const SystemPanel: React.FC<StyleProps> = ({ text }) => {
13
+ const frame = useCurrentFrame();
14
+ const { fps, height, width } = useVideoConfig();
15
+ const u = unit(height);
16
+ const shown = entrance(frame, fps, 0, 13);
17
+ const jitter = glitch(frame);
18
+ const leaving = exit(frame, fps, text);
19
+ const [status, ...rewards] = text.items;
20
+ const rewardsFrom = TYPE_DELAY + Array.from(status.text).length * 0.035 + 0.15;
21
+ return (
22
+ <AbsoluteFill style={{ alignItems: "center", paddingTop: u * TOP_U.system, opacity: leaving * jitter.opacity }}>
23
+ <div style={{ transform: `translate(${jitter.dx}px, ${(1 - leaving) * -u * 2}px) scale(${0.9 + 0.1 * shown})` }}>
24
+ <Frame colour={COLOURS.system} width={width * 0.82}>
25
+ <Header text="【系统提示】" colour={COLOURS.system} />
26
+ <Typed
27
+ text={status.text}
28
+ delay={TYPE_DELAY}
29
+ style={{
30
+ fontSize: u * 4.4,
31
+ fontWeight: 900,
32
+ letterSpacing: u * 0.3,
33
+ textShadow: `0 0 ${u * 1.4}px ${COLOURS.system}`,
34
+ margin: `${u * 0.4}px 0 ${rewards.length ? u * 1.2 : 0}px`,
35
+ }}
36
+ />
37
+ {rewards.map((item, index) => (
38
+ <Reward key={index} name={item.text} rarity={item.rarity} delay={rewardsFrom + index * 0.28} />
39
+ ))}
40
+ </Frame>
41
+ </div>
42
+ </AbsoluteFill>
43
+ );
44
+ };
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { formatCountdown, parseProgress, secondsLeft, sinceTick } from "../rules.mjs";
4
+ import { Frame, Header, RewardTag, Typed } from "./chrome";
5
+ import { COLOURS, MONO, StyleProps, TOP_U, entrance, exit, glitch, unit } from "./tokens";
6
+
7
+ /**
8
+ * 【新任务】: title, the goal typed out, reward tags, an `N / M` item as the
9
+ * 进度 line with its bar, and the countdown pulsing on each second.
10
+ */
11
+ export const TaskPanel: React.FC<StyleProps> = ({ text }) => {
12
+ const frame = useCurrentFrame();
13
+ const { fps, height, width } = useVideoConfig();
14
+ const u = unit(height);
15
+ const shown = entrance(frame, fps, 0, 13);
16
+ const jitter = glitch(frame);
17
+ const leaving = exit(frame, fps, text);
18
+ const [title, goal, ...rest] = text.items;
19
+ const progressItem = rest.find((item) => parseProgress(item.text));
20
+ const progress = progressItem ? parseProgress(progressItem.text) : null;
21
+ const rewards = rest.filter((item) => item !== progressItem);
22
+ const elapsed = frame / fps;
23
+ const beat =
24
+ text.countdown === null ? 1 : 1 + 0.05 * Math.max(0, 1 - sinceTick(text.countdown, elapsed) / 0.15);
25
+ return (
26
+ <AbsoluteFill style={{ alignItems: "center", paddingTop: u * TOP_U.task, opacity: leaving * jitter.opacity }}>
27
+ <div style={{ transform: `translate(${jitter.dx}px, ${(1 - leaving) * -u * 2}px) scale(${0.9 + 0.1 * shown})` }}>
28
+ <Frame colour={COLOURS.task} width={width * 0.86}>
29
+ <Header text="【新任务】" colour={COLOURS.task} />
30
+ <div
31
+ style={{
32
+ fontSize: u * 4.2,
33
+ fontWeight: 900,
34
+ letterSpacing: u * 0.3,
35
+ color: COLOURS.taskTitle,
36
+ textShadow: `0 0 ${u * 1.2}px ${COLOURS.task}`,
37
+ margin: `${u * 0.2}px 0 ${u * 0.8}px`,
38
+ }}
39
+ >
40
+ {title.text}
41
+ </div>
42
+ {goal ? (
43
+ <Typed text={goal.text} delay={0.25} style={{ fontSize: u * 2.35, fontWeight: 700, lineHeight: 1.45 }} />
44
+ ) : null}
45
+ {rewards.length ? (
46
+ <div style={{ marginTop: u * 1.2, display: "flex", flexWrap: "wrap", gap: `${u * 0.6}px ${u}px` }}>
47
+ {rewards.map((item, index) => (
48
+ <RewardTag key={index} name={item.text} rarity={item.rarity} delay={0.5 + index * 0.12} />
49
+ ))}
50
+ </div>
51
+ ) : null}
52
+ {progress && progressItem ? (
53
+ <>
54
+ <div
55
+ style={{
56
+ marginTop: u * 1.4,
57
+ fontSize: u * 1.7,
58
+ fontWeight: 700,
59
+ color: COLOURS.muted,
60
+ display: "flex",
61
+ justifyContent: "space-between",
62
+ }}
63
+ >
64
+ <span>进度</span>
65
+ <span style={{ fontFamily: MONO, fontWeight: 800 }}>{progressItem.text}</span>
66
+ </div>
67
+ <div style={{ height: u * 0.6, background: "#ffffff1c", marginTop: u * 0.4 }}>
68
+ <div
69
+ style={{
70
+ // A sliver stays visible at zero, so the bar reads as a bar.
71
+ width: `${Math.max(1.2, progress.ratio * 100)}%`,
72
+ height: "100%",
73
+ background: COLOURS.alarm,
74
+ boxShadow: `0 0 ${u * 0.6}px ${COLOURS.alarm}`,
75
+ }}
76
+ />
77
+ </div>
78
+ </>
79
+ ) : null}
80
+ {text.countdown === null ? null : (
81
+ <div
82
+ style={{
83
+ marginTop: u * 1.3,
84
+ fontFamily: MONO,
85
+ fontWeight: 800,
86
+ fontSize: u * 4.2,
87
+ color: COLOURS.alarm,
88
+ textShadow: `0 0 ${u}px ${COLOURS.alarm}`,
89
+ transform: `scale(${beat})`,
90
+ transformOrigin: "left center",
91
+ }}
92
+ >
93
+ {formatCountdown(secondsLeft(text.countdown, elapsed))}
94
+ </div>
95
+ )}
96
+ </Frame>
97
+ </div>
98
+ </AbsoluteFill>
99
+ );
100
+ };