@oh-story/dsh 0.1.9 → 0.1.11

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 (529) hide show
  1. package/README.md +14 -14
  2. package/lib/client.js +209 -73
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +248 -168
  5. package/lib/drama/skills/short-drama/SKILL.md +16 -4
  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/creator_markdown_check.py +14 -0
  10. package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
  11. package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
  12. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
  13. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
  14. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
  15. package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
  16. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  17. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
  18. package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
  19. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
  20. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
  21. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
  22. package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
  23. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
  24. 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
  25. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
  26. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
  27. 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
  28. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
  29. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
  30. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
  31. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
  32. package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
  33. package/lib/drama/skills/short-drama-edit/SKILL.md +65 -44
  34. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
  35. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
  36. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
  37. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
  38. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
  39. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
  40. package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
  41. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
  42. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
  43. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
  44. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
  45. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
  46. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
  47. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
  48. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
  49. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
  50. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +6 -4
  51. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +26 -67
  52. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +31 -56
  53. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +42 -78
  54. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +44 -92
  55. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +14 -16
  56. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1272 -125
  57. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +113 -9
  58. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
  59. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
  60. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
  61. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
  62. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
  63. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
  64. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
  65. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
  66. package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
  67. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
  68. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
  69. package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  70. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
  71. package/lib/drama/skills/short-drama-review/SKILL.md +4 -5
  72. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
  73. package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
  74. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
  75. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
  76. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +24 -35
  77. package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
  78. package/lib/drama/skills/short-drama-storyboard/SKILL.md +23 -11
  79. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
  80. package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  81. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
  82. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +139 -34
  83. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +5 -3
  84. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +15 -12
  85. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +29 -0
  86. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
  87. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
  88. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +52 -39
  89. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -21
  90. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +9 -23
  91. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +14 -26
  92. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
  93. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
  94. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
  95. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -2
  96. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
  97. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
  98. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
  99. package/lib/drama/skills/short-drama-write/SKILL.md +10 -7
  100. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
  101. package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
  102. package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
  103. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
  104. package/lib/index.js +346 -98
  105. package/lib/index.js.map +4 -4
  106. package/lib/novel-to-game/README.md +299 -59
  107. package/lib/novel-to-game/README_ZH.md +299 -56
  108. package/lib/novel-to-game/VERSION +1 -1
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
  118. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
  119. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
  120. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
  121. package/lib/novel-to-game/manifest.json +77 -87
  122. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
  123. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
  124. package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
  125. package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
  126. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
  127. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
  128. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
  129. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
  130. package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
  131. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
  132. package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
  133. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
  134. package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
  135. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
  136. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
  137. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
  138. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
  139. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
  140. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
  141. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
  142. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
  143. package/lib/oh-story/manifest.json +607 -327
  144. package/lib/oh-story/roles/chapter-extractor.md +177 -237
  145. package/lib/oh-story/roles/character-designer.md +1 -2
  146. package/lib/oh-story/roles/consistency-checker.md +15 -13
  147. package/lib/oh-story/roles/narrative-writer.md +36 -38
  148. package/lib/oh-story/roles/story-architect.md +9 -61
  149. package/lib/oh-story/roles/story-explorer.md +20 -125
  150. package/lib/oh-story/roles/story-researcher.md +0 -1
  151. package/lib/oh-story/skills/story/SKILL.md +15 -9
  152. package/lib/oh-story/skills/story/VERSION +1 -1
  153. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
  154. package/lib/oh-story/skills/story/references/author-memory.md +46 -94
  155. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
  156. package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
  157. package/lib/oh-story/skills/story-deslop/SKILL.md +59 -73
  158. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
  159. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +12 -11
  160. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  161. package/lib/oh-story/skills/story-deslop/references/author-memory.md +46 -94
  162. package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
  163. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +22 -5
  164. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
  165. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
  166. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
  167. package/lib/oh-story/skills/story-import/SKILL.md +64 -584
  168. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
  169. package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
  170. package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
  171. package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
  172. package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
  173. package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
  174. package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
  175. package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
  176. package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
  177. package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +98 -92
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
  180. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
  181. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +15 -11
  182. package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
  183. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +132 -27
  184. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +478 -15
  185. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +182 -23
  186. package/lib/oh-story/skills/story-long-analyze/SKILL.md +81 -270
  187. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +252 -0
  188. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +3 -3
  189. package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
  190. package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
  191. package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
  192. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +138 -64
  193. package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  194. package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  195. package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  196. package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  197. package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  198. package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
  199. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +55 -128
  200. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +6 -6
  201. package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  202. package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  203. package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  204. package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  205. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
  206. package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  207. package/lib/oh-story/skills/story-long-scan/SKILL.md +45 -194
  208. package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  209. package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
  210. package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  211. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
  212. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
  213. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
  214. package/lib/oh-story/skills/story-long-write/SKILL.md +49 -81
  215. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
  216. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +12 -11
  217. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +24 -202
  218. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  219. package/lib/oh-story/skills/story-long-write/references/author-memory.md +46 -94
  220. package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
  221. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +7 -11
  222. package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
  223. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
  224. package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
  225. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
  226. package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
  227. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
  228. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
  229. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  230. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +7 -15
  231. package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
  232. package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
  233. package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
  234. package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
  235. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
  236. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
  237. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
  238. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
  239. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
  240. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
  241. package/lib/oh-story/skills/story-long-write/references/project-files.md +19 -13
  242. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +13 -5
  243. package/lib/oh-story/skills/story-long-write/references/reference-index.md +5 -6
  244. package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
  245. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
  246. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  247. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
  248. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
  249. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +15 -11
  250. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +81 -121
  251. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +41 -139
  252. package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
  253. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +57 -67
  254. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -253
  255. package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
  256. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +6 -4
  257. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +19 -65
  258. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
  259. package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  260. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +226 -25
  261. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
  262. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +132 -27
  263. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
  264. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +7 -4
  265. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +304 -46
  266. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +478 -15
  267. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +182 -23
  268. package/lib/oh-story/skills/story-review/SKILL.md +31 -302
  269. package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
  270. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +12 -11
  271. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
  272. package/lib/oh-story/skills/story-review/references/author-memory.md +46 -94
  273. package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
  274. package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
  275. package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
  276. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
  277. package/lib/oh-story/skills/story-review/references/review-quality.md +6 -11
  278. package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
  279. package/lib/oh-story/skills/story-review/references/solo.md +52 -0
  280. package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
  281. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
  282. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +15 -11
  283. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
  284. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
  285. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +478 -15
  286. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +182 -23
  287. package/lib/oh-story/skills/story-setup/SKILL.md +71 -452
  288. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
  289. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +12 -11
  290. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
  291. package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  292. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
  293. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +22 -5
  294. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
  295. package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  296. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
  297. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
  298. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  299. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
  300. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
  301. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
  302. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
  303. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
  304. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
  305. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
  306. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
  307. package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
  308. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
  309. package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
  310. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
  311. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  312. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
  313. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +19 -65
  314. package/lib/oh-story/skills/story-setup/references/diagnostics.md +4 -4
  315. package/lib/oh-story/skills/story-short-analyze/SKILL.md +56 -110
  316. package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
  317. package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
  318. package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
  319. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
  320. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
  321. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
  322. package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  323. package/lib/oh-story/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
  324. package/lib/oh-story/skills/story-short-scan/SKILL.md +35 -65
  325. package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
  326. package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  327. package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
  328. package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
  329. package/lib/oh-story/skills/story-short-write/SKILL.md +23 -175
  330. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
  331. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  332. package/lib/oh-story/skills/story-short-write/references/author-memory.md +46 -94
  333. package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
  334. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
  335. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
  336. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
  337. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
  338. package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
  339. package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -47
  340. package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
  341. package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
  342. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
  343. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
  344. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
  345. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
  346. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
  347. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
  348. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +21 -13
  349. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +116 -0
  350. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +99 -0
  351. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
  352. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
  353. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
  354. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
  355. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  356. package/lib/types/client/file-activity.d.ts +19 -1
  357. package/lib/types/client/file-activity.d.ts.map +1 -1
  358. package/lib/types/client/index.d.ts.map +1 -1
  359. package/lib/types/client/production-prompts.d.ts +7 -0
  360. package/lib/types/client/production-prompts.d.ts.map +1 -1
  361. package/lib/types/client/production-runtime.d.ts +16 -0
  362. package/lib/types/client/production-runtime.d.ts.map +1 -1
  363. package/lib/types/client/video-studio.d.ts.map +1 -1
  364. package/lib/types/drama-adapters.d.ts +5 -3
  365. package/lib/types/drama-adapters.d.ts.map +1 -1
  366. package/lib/types/native-hooks.d.ts +53 -1
  367. package/lib/types/native-hooks.d.ts.map +1 -1
  368. package/lib/types/role-identity.d.ts +17 -0
  369. package/lib/types/role-identity.d.ts.map +1 -0
  370. package/lib/types/role-tool.d.ts.map +1 -1
  371. package/lib/types/skill-provider.d.ts.map +1 -1
  372. package/lib/types/video-project.d.ts.map +1 -1
  373. package/lib/types/workspace-route.d.ts.map +1 -1
  374. package/lib/video-recap/CHANGELOG.md +60 -1
  375. package/lib/video-recap/README.en.md +348 -137
  376. package/lib/video-recap/README.md +340 -136
  377. package/lib/video-recap/manifest.json +406 -251
  378. package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
  379. package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
  380. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  381. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
  382. package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
  383. package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
  384. package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
  385. package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
  386. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
  387. package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  388. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  389. package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  390. package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  391. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  392. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  393. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
  394. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
  395. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
  396. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
  397. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
  398. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
  399. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
  400. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
  401. package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  402. package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
  403. package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
  404. package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
  405. package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
  406. package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
  407. package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
  408. package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
  409. package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
  410. package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
  411. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
  412. package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
  413. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
  414. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
  415. package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
  416. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
  417. package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  418. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
  419. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
  420. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
  421. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  422. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
  423. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
  424. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
  425. package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
  426. package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
  427. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
  428. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
  429. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
  430. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
  431. package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
  432. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
  433. package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
  434. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
  435. package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
  436. package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
  437. package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
  438. package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
  439. package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
  440. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
  441. package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
  442. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
  443. package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
  444. package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
  445. package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
  446. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
  447. package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
  448. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
  449. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
  450. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
  451. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
  452. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
  453. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
  454. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
  455. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
  456. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
  457. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
  458. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
  459. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
  460. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
  461. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
  462. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
  463. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
  464. package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
  465. package/lib/video-recap/skills/video-script/SKILL.md +16 -13
  466. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
  467. package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
  468. package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
  469. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
  470. package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
  471. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
  472. package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
  473. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
  474. package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
  475. package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
  476. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
  477. package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
  478. package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
  479. package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
  480. package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
  481. package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
  482. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  483. package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
  484. package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  485. package/lib/video-recap/skills/{video-script/scripts/agent_brief.py → video-understanding/scripts/briefing/builder.py} +38 -35
  486. package/lib/video-recap/skills/video-understanding/scripts/{brief_context.py → briefing/context.py} +4 -35
  487. package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
  488. package/lib/video-recap/skills/{video-script/scripts/brief_timeline.py → video-understanding/scripts/briefing/timeline.py} +5 -6
  489. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
  490. package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
  491. package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
  492. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
  493. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
  494. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
  495. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
  496. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
  497. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
  498. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
  499. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
  500. package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
  501. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
  502. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  503. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
  504. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
  505. package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  506. package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  507. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
  508. package/package.json +45 -45
  509. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
  510. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
  511. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
  512. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -530
  513. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -559
  514. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
  515. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
  516. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +0 -678
  517. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
  518. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
  519. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
  520. package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
  521. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
  522. package/lib/video-recap/skills/video-script/scripts/brief_context.py +0 -348
  523. package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
  524. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +0 -399
  525. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
  526. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +0 -327
  527. /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
  528. /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
  529. /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
@@ -1,87 +1,86 @@
1
- import React, { useMemo } from "react";
2
- import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
3
- import { Cue, SubtitleProps } from "./schema";
4
- import { assertFamilyResolves, waitForFonts } from "./font";
1
+ import React from "react";
2
+ import { AbsoluteFill, spring, useCurrentFrame, useVideoConfig } from "remotion";
3
+ import { Cue } from "./schema";
4
+ import { assertFamilyResolves } from "./font";
5
+ import { lineEms } from "./rules.mjs";
6
+ import { SANS, SUBTITLE } from "./screen/tokens";
5
7
 
6
- waitForFonts();
8
+ /** Splits a line into plain and highlighted runs, earliest keyword first. */
9
+ const runs = (text: string, keys: string[]): { text: string; key: boolean }[] => {
10
+ const out: { text: string; key: boolean }[] = [];
11
+ let rest = text;
12
+ while (rest) {
13
+ const hit = keys
14
+ .map((key) => ({ key, at: rest.indexOf(key) }))
15
+ .filter((found) => found.at >= 0)
16
+ .sort((a, b) => a.at - b.at)[0];
17
+ if (!hit) {
18
+ out.push({ text: rest, key: false });
19
+ break;
20
+ }
21
+ if (hit.at) out.push({ text: rest.slice(0, hit.at), key: false });
22
+ out.push({ text: hit.key, key: true });
23
+ rest = rest.slice(hit.at + hit.key.length);
24
+ }
25
+ return out;
26
+ };
7
27
 
8
28
  /**
9
- * A transparent subtitle layer, composited onto untouched picture.
10
- *
11
- * Everything here is expressed as a fraction of frame height, so the same
12
- * numbers hold for 768×1344 and 1080×1920. That is the whole reason this file
13
- * exists in pixels rather than in an ASS style: the subtitle renderer's own
14
- * `PlayRes` scaling was applied twice once already, and type three times too
15
- * large shipped in a sample film.
29
+ * The subtitle layer: Noto Sans SC 900, white or voice-coloured fill over a
30
+ * dark rim drawn beneath it, baseline about a quarter up the frame so the
31
+ * platform's own UI never covers it. Sizes are fractions of frame height.
16
32
  */
17
- export const Subtitles: React.FC<SubtitleProps> = ({
18
- cues,
19
- fontScale,
20
- bottomScale,
21
- fontFamily,
22
- }) => {
33
+ export const Subtitles: React.FC<{ cues: Cue[] }> = ({ cues }) => {
23
34
  const frame = useCurrentFrame();
24
35
  const { fps, height, width } = useVideoConfig();
25
36
  const now = frame / fps;
26
- const active = cues.find((cue: Cue) => now >= cue.start && now < cue.end);
27
-
28
- const fontSize = height * fontScale;
29
- // A dark rim on every side keeps white type legible over a bright frame
30
- // without a caption box, which is what vertical drama expects. It depends
31
- // only on the frame size, so it is built once — and it stays above the early
32
- // return, because a hook may not be skipped on the frames with no line.
33
- const shadow = useMemo(() => {
34
- const rim = Math.max(2, height * 0.0024);
35
- return [
36
- `0 0 ${rim * 3}px rgba(0,0,0,0.85)`,
37
- `${rim}px ${rim}px 0 rgba(0,0,0,0.92)`,
38
- `-${rim}px ${rim}px 0 rgba(0,0,0,0.92)`,
39
- `${rim}px -${rim}px 0 rgba(0,0,0,0.92)`,
40
- `-${rim}px -${rim}px 0 rgba(0,0,0,0.92)`,
41
- ].join(", ");
42
- }, [height]);
43
-
44
- // Checked against a line about to be filmed, so a missing font stops the
37
+ const cue = cues.find((item) => now >= item.start && now < item.end);
38
+ // Checked against a line about to be filmed, so a missing face stops the
45
39
  // render on the first subtitle frame rather than after the whole pass.
46
- assertFamilyResolves(fontFamily, active?.text ?? "");
47
- if (!active) return null;
48
-
49
- // A short lift on entry reads as the line arriving with the delivery. It is
50
- // over well before the first syllable ends, so it never delays reading.
51
- const age = now - active.start;
52
- const lift = Math.min(1, age / 0.12);
53
- const eased = 1 - Math.pow(1 - lift, 3);
40
+ assertFamilyResolves(SANS, cue?.text ?? "");
41
+ if (!cue) return null;
54
42
 
43
+ const u = height / 100;
44
+ // One line: long lines are split upstream; what still does not fit shrinks.
45
+ const size = Math.min(u * 4.3, (width * 0.9) / (lineEms(cue.text) * 1.04));
46
+ const age = now - cue.start;
47
+ const pop = spring({ frame: Math.round(age * fps), fps, config: { damping: 12, stiffness: 260, mass: 0.5 } });
55
48
  return (
56
- <AbsoluteFill
57
- style={{
58
- justifyContent: "flex-end",
59
- alignItems: "center",
60
- paddingBottom: height * bottomScale,
61
- paddingLeft: width * 0.06,
62
- paddingRight: width * 0.06,
63
- }}
64
- >
49
+ <AbsoluteFill style={{ justifyContent: "flex-end", alignItems: "center", paddingBottom: u * 24 }}>
65
50
  <div
66
51
  style={{
67
- fontFamily,
68
- fontSize,
69
- fontWeight: 700,
70
- color: "#ffffff",
52
+ fontFamily: SANS,
53
+ fontWeight: 900,
54
+ fontSize: size,
55
+ color: SUBTITLE.fill[cue.kind],
56
+ letterSpacing: size * 0.04,
71
57
  textAlign: "center",
72
- lineHeight: 1.28,
73
- letterSpacing: fontSize * 0.02,
74
- textShadow: shadow,
75
- opacity: eased,
76
- transform: `translateY(${(1 - eased) * fontSize * 0.25}px)`,
77
- // A long line wraps rather than running off the frame — the failure
78
- // that made the first shipped version unreadable.
79
- maxWidth: "100%",
80
- wordBreak: "break-word",
81
- whiteSpace: "pre-wrap",
58
+ lineHeight: 1.2,
59
+ whiteSpace: "nowrap",
60
+ WebkitTextStroke: `${u * 0.42}px ${SUBTITLE.rim}`,
61
+ paintOrder: "stroke fill",
62
+ textShadow: `0 ${u * 0.25}px ${u * 0.6}px rgba(0,0,0,0.55)`,
63
+ opacity: Math.min(1, age / 0.06),
64
+ transform: `scale(${0.94 + 0.06 * pop})`,
82
65
  }}
83
66
  >
84
- {active.text}
67
+ {runs(cue.text, cue.keys).map((run, index) =>
68
+ run.key ? (
69
+ <span
70
+ key={index}
71
+ style={{
72
+ color: SUBTITLE.keyword,
73
+ display: "inline-block",
74
+ // A 120 ms pop as the word lands.
75
+ transform: `scale(${1.08 + 0.15 * Math.max(0, 1 - age / 0.12)})`,
76
+ }}
77
+ >
78
+ {run.text}
79
+ </span>
80
+ ) : (
81
+ <span key={index}>{run.text}</span>
82
+ ),
83
+ )}
85
84
  </div>
86
85
  </AbsoluteFill>
87
86
  );
@@ -1,46 +1,56 @@
1
- import { continueRender, delayRender } from "remotion";
1
+ import { useEffect, useState } from "react";
2
+ import { cancelRender, continueRender, delayRender } from "remotion";
3
+ import { FONT_WAIT_MS, familyIsMissing, fontLoadProblem, settleWithin } from "./rules.mjs";
4
+
5
+ type Plan = { font: string; text: string }[];
2
6
 
3
7
  /**
4
- * Remotion captures a frame as soon as React has painted, so a font still being
5
- * fetched is simply absent from it. Nothing downstream reports that: the film
6
- * measures correct and reads wrong.
8
+ * Holds the first frame until every face in `plan` has the glyphs it will draw.
9
+ *
10
+ * Registered per mount rather than at module load, the way Remotion expects;
11
+ * bounded by `FONT_WAIT_MS`; and a face that is undeclared, fails, or never
12
+ * finishes cancels the render with the reason instead of shipping a fallback.
13
+ * Returns false until then, so nothing is drawn -- or measured -- early; the
14
+ * frame is released only after the ready tree has been committed.
7
15
  */
8
- export const waitForFonts = (): void => {
9
- const handle = delayRender("等待字体就绪");
10
- document.fonts.ready.then(
11
- () => continueRender(handle),
12
- () => continueRender(handle),
13
- );
16
+ export const useFaces = (plan: Plan): boolean => {
17
+ const [handle] = useState(() => delayRender("加载字体"));
18
+ const [ready, setReady] = useState(false);
19
+ useEffect(() => {
20
+ const loads = Promise.all(plan.map((entry) => document.fonts.load(entry.font, entry.text)));
21
+ settleWithin(loads, FONT_WAIT_MS).then((outcome) => {
22
+ const problem = fontLoadProblem(plan, outcome, (font, text) => document.fonts.check(font, text));
23
+ if (problem) cancelRender(new Error(problem));
24
+ else setReady(true);
25
+ });
26
+ }, [plan]);
27
+ useEffect(() => {
28
+ if (ready) continueRender(handle);
29
+ }, [ready, handle]);
30
+ return ready;
14
31
  };
15
32
 
16
33
  /**
17
- * `document.fonts.ready` promises that loading finished, not that the requested
18
- * family exists — a missing face raises nothing, the browser substitutes, and
19
- * the film ships in the wrong typeface. Measuring is the only reliable test:
20
- * identical widths against a family that cannot exist mean nothing resolved.
34
+ * A second guard at draw time: measuring is the only test of what the frame
35
+ * actually uses, so a stack whose families all fall back stops the render.
21
36
  */
22
- let familyChecked = "";
37
+ const checked = new Set<string>();
23
38
 
24
39
  export const assertFamilyResolves = (fontFamily: string, sample: string): void => {
25
40
  // Runs on every frame, so measure once per family.
26
- if (!sample || familyChecked === fontFamily) return;
27
- const canvas = document.createElement("canvas");
28
- const context = canvas.getContext("2d");
41
+ if (!sample || checked.has(fontFamily)) return;
42
+ const context = document.createElement("canvas").getContext("2d");
29
43
  if (!context) return;
30
44
 
31
- const measure = (family: string): number => {
32
- context.font = `700 64px ${family}`;
33
- return context.measureText(sample).width;
45
+ const measure = (stack: string, text: string): number => {
46
+ context.font = `700 64px ${stack}`;
47
+ return context.measureText(text).width;
34
48
  };
35
- // A name no font can carry, so it always lands on the browser's last resort.
36
- const fallbackOnly = measure('"__no_such_family__"');
37
- const requested = measure(`${fontFamily}, "__no_such_family__"`);
38
- if (requested === fallbackOnly) {
49
+ if (familyIsMissing(measure, fontFamily, sample)) {
39
50
  throw new Error(
40
- `字幕字体 ${fontFamily} 在渲染环境里一个都没装上,` +
41
- "画面会落到浏览器的兜底字体。装一款其中的字体," +
42
- "或给 render 传一个本机确实有的字体族。",
51
+ `字体 ${fontFamily} 在渲染环境里一个都没有,画面会落到浏览器的兜底字体。` +
52
+ "在 Remotion 工作区运行 npm install 装上字体包。",
43
53
  );
44
54
  }
45
- familyChecked = fontFamily;
55
+ checked.add(fontFamily);
46
56
  };
@@ -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
+ };