stillmade-sdk 0.1.41 → 0.1.43

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 (285) hide show
  1. package/.stillmade/sdk-baseline.json +19 -21
  2. package/AGENTS.md +17 -12
  3. package/FRAME_VIEWS.md +113 -0
  4. package/INTERFACE_DESIGN.md +67 -22
  5. package/MOBILE_UI.md +4 -2
  6. package/MODULES.md +152 -6
  7. package/PASTE_TO_LLM.md +42 -32
  8. package/README.md +3 -2
  9. package/SDK.md +1028 -563
  10. package/UI_AESTHETIC.md +30 -3
  11. package/blocks/generate/package.js +1 -0
  12. package/blocks/generate/versions/1.0.0/package.js +463 -0
  13. package/docs/build/audio-to-editor.md +36 -0
  14. package/docs/build/block-builder-contract.md +150 -0
  15. package/docs/build/comfyui.md +227 -0
  16. package/docs/build/concepts.md +40 -0
  17. package/docs/build/context.md +159 -0
  18. package/docs/build/cookbook.md +57 -0
  19. package/docs/build/custom-interface.md +617 -0
  20. package/docs/build/data-types.md +83 -0
  21. package/docs/build/frame-views.md +259 -0
  22. package/docs/build/generate-media.md +59 -0
  23. package/docs/build/github-applications.md +314 -0
  24. package/docs/build/hosted-images.md +214 -0
  25. package/docs/build/hosted-music.md +39 -0
  26. package/docs/build/hosted-speech.md +157 -0
  27. package/docs/build/hosted-text.md +246 -0
  28. package/docs/build/image-description.md +49 -0
  29. package/docs/build/interface-design.md +343 -0
  30. package/docs/build/javascript.md +63 -0
  31. package/docs/build/local-preview.md +61 -0
  32. package/docs/build/media-analysis.md +27 -0
  33. package/docs/build/media.md +400 -0
  34. package/docs/build/mobile-ui.md +106 -0
  35. package/docs/build/modules.md +487 -0
  36. package/docs/build/original-blocks.md +70 -0
  37. package/docs/build/outside-apis.md +128 -0
  38. package/docs/build/production-context-templates.md +12 -0
  39. package/docs/build/project-storage.md +94 -0
  40. package/docs/build/quickstart.md +48 -0
  41. package/docs/build/recipes.md +103 -0
  42. package/docs/build/script-handoff.md +45 -0
  43. package/docs/build/script-writer.md +34 -0
  44. package/docs/build/state.md +223 -0
  45. package/docs/build/testing.md +112 -0
  46. package/docs/build/timeline-edits.md +116 -0
  47. package/docs/build/timeline-proposals.md +76 -0
  48. package/docs/build/transcription.md +59 -0
  49. package/docs/build/triggers.md +59 -0
  50. package/docs/build/tutorial.md +52 -0
  51. package/docs/build/typescript.md +34 -0
  52. package/docs/build/video-generation.md +61 -0
  53. package/docs/build/web-reading.md +37 -0
  54. package/docs/build/with-your-ai.md +27 -0
  55. package/docs/changelog.md +9 -0
  56. package/docs/creators.md +21 -0
  57. package/docs/distribute/discovery.md +43 -0
  58. package/docs/distribute/imports.md +58 -0
  59. package/docs/distribute/publishing.md +29 -0
  60. package/docs/distribute/sdk-versions.md +36 -0
  61. package/docs/distribute/selling.md +14 -0
  62. package/docs/distribute/teams.md +60 -0
  63. package/docs/distribute/versioning.md +28 -0
  64. package/docs/examples.md +23 -0
  65. package/docs/gallery.md +26 -0
  66. package/docs/index.md +50 -0
  67. package/docs/native-mcp.md +307 -0
  68. package/docs/project-types/automation.md +43 -0
  69. package/docs/project-types/batches.md +50 -0
  70. package/docs/project-types/build.md +42 -0
  71. package/docs/project-types/completion.md +15 -0
  72. package/docs/project-types/conditions.md +21 -0
  73. package/docs/project-types/hosted-review.md +115 -0
  74. package/docs/project-types/onboarding.md +75 -0
  75. package/docs/project-types/running.md +309 -0
  76. package/docs/project-types/testing.md +72 -0
  77. package/docs/reference/application-artifacts.md +87 -0
  78. package/docs/reference/authoring-guide.md +134 -0
  79. package/docs/reference/browser-applications.md +267 -0
  80. package/docs/reference/capabilities.md +45 -0
  81. package/docs/reference/cli.md +71 -0
  82. package/docs/reference/connection-api.md +59 -0
  83. package/docs/reference/cooperative-jobs.md +13 -0
  84. package/docs/reference/desktop-capabilities.md +172 -0
  85. package/docs/reference/desktop-execution.md +77 -0
  86. package/docs/reference/errors.md +233 -0
  87. package/docs/reference/execution.md +800 -0
  88. package/docs/reference/first-party-blocks.md +40 -0
  89. package/docs/reference/generation-models.md +59 -0
  90. package/docs/reference/limits.md +306 -0
  91. package/docs/reference/manifest-kinds-controls.md +36 -0
  92. package/docs/reference/manifest.md +181 -0
  93. package/docs/reference/operations.md +36 -0
  94. package/docs/reference/package-jobs.md +35 -0
  95. package/docs/reference/parity.md +55 -0
  96. package/docs/reference/permissions.md +86 -0
  97. package/docs/reference/ports.md +26 -0
  98. package/docs/reference/types.md +268 -0
  99. package/docs/reference/universal-resolver.md +303 -0
  100. package/docs/reference/view-bridge.md +60 -0
  101. package/docs/reference/view-rules.md +15 -0
  102. package/docs/reference/workspace-sync.md +25 -0
  103. package/docs/sdk.md +34 -0
  104. package/docs/tools/block-remix.md +211 -0
  105. package/docs/tools/llm.md +2213 -0
  106. package/docs/tools/repository-adaptation.md +613 -0
  107. package/examples/check-clip.stillmade.json +8 -1
  108. package/examples/clean-text.stillmade.json +4 -0
  109. package/examples/custom-interface.stillmade.json +4 -0
  110. package/examples/describe-image.stillmade.json +4 -0
  111. package/examples/foley-cue.stillmade.json +4 -0
  112. package/examples/generate-image.stillmade.json +4 -0
  113. package/examples/generate-video.stillmade.json +10 -2
  114. package/examples/module-word-stats/stillmade.block.json +4 -0
  115. package/examples/narrate-script.stillmade.json +4 -0
  116. package/examples/numbered-takes.stillmade.json +4 -0
  117. package/examples/research-topic.stillmade.json +9 -1
  118. package/examples/score-a-scene.stillmade.json +4 -0
  119. package/examples/script-draft.stillmade.json +4 -0
  120. package/examples/summarize-page.stillmade.json +4 -0
  121. package/examples/text-workflow.stillmade.json +7 -32
  122. package/examples/thicken-lines.stillmade.json +4 -0
  123. package/examples/timeline-polish.stillmade.json +15 -3
  124. package/examples/transcribe-audio.stillmade.json +15 -3
  125. package/examples/word-count.stillmade.json +4 -0
  126. package/package.json +1 -1
  127. package/packages/block-cli/cli.js +13 -6
  128. package/packages/block-cli/dev-host/assets/host.js +81 -38
  129. package/packages/block-cli/dev-host/assets/index.css +1 -1
  130. package/packages/block-cli/format.js +1 -1
  131. package/packages/block-cli/ready-gate.js +4 -0
  132. package/packages/block-cli/templates.js +37 -19
  133. package/packages/block-cli/type-packages.js +119 -0
  134. package/packages/block-platform/block-readiness.js +8 -1
  135. package/packages/block-platform/canvas.js +2 -1
  136. package/packages/block-platform/catalog.js +27 -48
  137. package/packages/block-platform/native-host-inventory.js +0 -226
  138. package/packages/block-platform/plain-descriptions.js +1 -25
  139. package/packages/block-platform/project-type-starters.js +13 -53
  140. package/packages/block-platform/script-handoff.js +2 -4
  141. package/packages/block-platform/type-workspace.js +2 -0
  142. package/packages/block-sdk/appearance.js +3 -1
  143. package/packages/block-sdk/audio-generation-example.js +2 -2
  144. package/packages/block-sdk/block-tier.js +1 -1
  145. package/packages/block-sdk/builder-contract.js +13 -11
  146. package/packages/block-sdk/capabilities.js +1 -1
  147. package/packages/block-sdk/capability-example.js +1 -1
  148. package/packages/block-sdk/contracts.js +6 -2
  149. package/packages/block-sdk/examples.js +3 -3
  150. package/packages/block-sdk/first-party-identities.js +1 -84
  151. package/packages/block-sdk/first-party-packages.js +3 -85
  152. package/packages/block-sdk/generation-catalog.js +15 -3
  153. package/packages/block-sdk/image-description-example.js +1 -1
  154. package/packages/block-sdk/image-generation-example.js +1 -1
  155. package/packages/block-sdk/index.d.ts +2 -1
  156. package/packages/block-sdk/media-analysis-example.js +2 -2
  157. package/packages/block-sdk/mock-host.js +7 -1
  158. package/packages/block-sdk/module-example.js +1 -1
  159. package/packages/block-sdk/module-host-calls.js +35 -10
  160. package/packages/block-sdk/module-hosted.js +15 -2
  161. package/packages/block-sdk/module-media-access.js +71 -0
  162. package/packages/block-sdk/module-node.js +1 -1
  163. package/packages/block-sdk/portable-media-actions.js +1 -1
  164. package/packages/block-sdk/runtime.js +1 -1
  165. package/packages/block-sdk/speech-example.js +1 -1
  166. package/packages/block-sdk/state-example.js +1 -1
  167. package/packages/block-sdk/timeline-proposal-example.js +1 -1
  168. package/packages/block-sdk/transcription-example.js +1 -1
  169. package/packages/block-sdk/trigger-examples.js +2 -2
  170. package/packages/block-sdk/video-generation-example.js +1 -1
  171. package/packages/block-sdk/view-bridge.js +4 -1
  172. package/packages/block-sdk/view-design.js +44 -15
  173. package/packages/block-sdk/web-example.js +2 -2
  174. package/packages/design-system/block-kit.css +53 -12
  175. package/packages/design-system/block-kit.html +3 -2
  176. package/blocks/mcp-motion-planner/versions/1.1.0/package.js +0 -97
  177. package/blocks/motion-designer/versions/1.0.1/package.js +0 -173318
  178. package/blocks/motion-designer/versions/1.0.2/package.js +0 -173318
  179. package/blocks/motion-frame-review/versions/1.1.1/package.js +0 -465
  180. package/blocks/motion-frame-review/versions/1.1.2/package.snapshot.js +0 -572
  181. package/blocks/narration/package.js +0 -1
  182. package/blocks/narration/versions/1.0.0/package.js +0 -221
  183. package/blocks/narration/versions/1.0.1/package.js +0 -221
  184. package/blocks/native/sm.animated-scene/versions/2.0.0/package.js +0 -475
  185. package/blocks/native/sm.animated-scene/versions/2.1.0/package.js +0 -498
  186. package/blocks/native/sm.animated-scene/versions/2.2.0/package.js +0 -524
  187. package/blocks/native/sm.animated-scene/versions/2.3.0/package.js +0 -618
  188. package/blocks/native/sm.animated-scene/versions/2.3.1/package.js +0 -618
  189. package/blocks/native/sm.animated-scene/versions/2.3.2/package.js +0 -723
  190. package/blocks/native/sm.animated-scene/versions/2.3.3/package.js +0 -723
  191. package/blocks/native/sm.animated-scene/versions/2.3.4/package.js +0 -676
  192. package/blocks/native/sm.animated-scene/versions/2.3.5/package.js +0 -693
  193. package/blocks/native/sm.animated-scene/versions/2.3.6/package.js +0 -700
  194. package/blocks/native/sm.animated-scene/versions/2.3.7/package.js +0 -747
  195. package/blocks/native/sm.animated-scene/versions/2.3.8/package.js +0 -769
  196. package/blocks/native/sm.animated-scene/versions/2.3.9/package.js +0 -769
  197. package/blocks/native/sm.animated-scene-assistant/versions/1.0.0/package.js +0 -183
  198. package/blocks/native/sm.blueprint/versions/2.0.0/package.js +0 -358
  199. package/blocks/native/sm.blueprint/versions/2.1.0/package.js +0 -358
  200. package/blocks/native/sm.blueprint/versions/2.1.1/package.js +0 -358
  201. package/blocks/native/sm.blueprint/versions/2.1.2/package.js +0 -358
  202. package/blocks/native/sm.blueprint/versions/2.1.3/package.js +0 -381
  203. package/blocks/native/sm.blueprint/versions/2.1.4/package.js +0 -381
  204. package/blocks/native/sm.blueprint/versions/2.1.5/package.js +0 -339
  205. package/blocks/native/sm.blueprint/versions/2.1.6/package.js +0 -356
  206. package/blocks/native/sm.blueprint/versions/2.1.7/package.js +0 -363
  207. package/blocks/native/sm.blueprint/versions/2.1.8/package.js +0 -410
  208. package/blocks/native/sm.blueprint/versions/2.1.9/package.js +0 -422
  209. package/blocks/native/sm.blueprint-assistant/versions/1.0.0/package.js +0 -172
  210. package/blocks/native/sm.chat/versions/2.0.0/package.js +0 -144
  211. package/blocks/native/sm.chat/versions/2.0.1/package.js +0 -97
  212. package/blocks/native/sm.chat/versions/2.0.2/package.js +0 -100
  213. package/blocks/native/sm.chat/versions/2.0.3/package.js +0 -100
  214. package/blocks/native/sm.continue-if/versions/1.0.0/package.js +0 -168
  215. package/blocks/native/sm.describe-media/versions/2.0.0/package.js +0 -153
  216. package/blocks/native/sm.describe-media/versions/2.0.1/package.js +0 -156
  217. package/blocks/native/sm.describe-media/versions/2.1.0/package.js +0 -140
  218. package/blocks/native/sm.image-editor/versions/2.0.0/package.js +0 -183
  219. package/blocks/native/sm.image-editor/versions/2.0.1/package.js +0 -190
  220. package/blocks/native/sm.new-items/versions/1.0.0/package.js +0 -191
  221. package/blocks/native/sm.pick-photos/versions/1.0.0/package.js +0 -334
  222. package/blocks/native/sm.quality-audit/versions/1.0.0/package.js +0 -192
  223. package/blocks/native/sm.quality-audit/versions/1.0.1/package.js +0 -192
  224. package/blocks/native/sm.quality-audit/versions/1.0.2/package.js +0 -195
  225. package/blocks/native/sm.research/versions/2.0.0/package.js +0 -175
  226. package/blocks/native/sm.research/versions/2.0.1/package.js +0 -184
  227. package/blocks/native/sm.research/versions/2.0.2/package.js +0 -187
  228. package/blocks/native/sm.script/versions/2.0.0/package.js +0 -144
  229. package/blocks/native/sm.script/versions/2.1.0/package.js +0 -144
  230. package/blocks/native/sm.script/versions/2.1.1/package.js +0 -144
  231. package/blocks/native/sm.script/versions/2.1.2/package.js +0 -105
  232. package/blocks/native/sm.script/versions/2.1.3/package.js +0 -152
  233. package/blocks/native/sm.script/versions/2.1.4/package.js +0 -156
  234. package/blocks/native/sm.script/versions/2.1.5/package.js +0 -156
  235. package/blocks/native/sm.shot-plan/versions/2.0.0/package.js +0 -301
  236. package/blocks/native/sm.shot-plan/versions/2.1.0/package.js +0 -528
  237. package/blocks/native/sm.shot-plan/versions/2.1.1/package.js +0 -528
  238. package/blocks/native/sm.shot-plan/versions/2.1.2/package.js +0 -528
  239. package/blocks/native/sm.shot-plan/versions/2.1.3/package.js +0 -536
  240. package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +0 -800
  241. package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +0 -920
  242. package/blocks/native/sm.shot-plan-assistant/versions/1.0.0/package.js +0 -173
  243. package/blocks/native/sm.stock-search/versions/2.0.0/package.js +0 -174
  244. package/blocks/native/sm.thumbnail/versions/2.0.0/package.js +0 -137
  245. package/blocks/native/sm.thumbnail/versions/2.0.1/package.js +0 -140
  246. package/blocks/native/sm.transcribe/versions/2.0.0/package.js +0 -163
  247. package/blocks/native/sm.transcribe/versions/2.0.1/package.js +0 -168
  248. package/blocks/native/sm.whiteboard-assistant/versions/1.0.0/package.js +0 -175
  249. package/blocks/native/sm.youtube/versions/2.0.0/package.js +0 -217
  250. package/blocks/occasion-story/package.js +0 -1
  251. package/blocks/occasion-story/versions/1.0.0/package.js +0 -1658
  252. package/blocks/occasion-story/versions/1.0.1/package.js +0 -1699
  253. package/blocks/occasion-story/versions/1.0.2/package.js +0 -1682
  254. package/blocks/photo-reel-composer/versions/1.0.0/package.js +0 -3043
  255. package/blocks/photo-reel-composer/versions/1.0.1/package.js +0 -3026
  256. package/blocks/photo-reel-composer/versions/1.0.2/package.js +0 -4149
  257. package/blocks/product-ad-variants/versions/1.0.0/package.js +0 -2911
  258. package/blocks/product-launch-director/versions/1.0.0/package.js +0 -103
  259. package/blocks/release-visuals/package.js +0 -1
  260. package/blocks/release-visuals/versions/1.0.0/package.js +0 -1492
  261. package/blocks/release-visuals/versions/1.0.1/package.js +0 -1517
  262. package/blocks/release-visuals/versions/1.0.2/package.js +0 -1500
  263. package/blocks/screen-recorder/package.js +0 -1
  264. package/blocks/screen-recorder/versions/1.0.0/package.js +0 -223
  265. package/blocks/screen-recorder/versions/1.0.1/package.js +0 -223
  266. package/blocks/short-form-finish/package.js +0 -1
  267. package/blocks/short-form-finish/versions/1.0.0/package.js +0 -1043
  268. package/blocks/short-form-finish/versions/1.0.1/package.js +0 -1043
  269. package/blocks/short-form-finish/versions/1.0.2/package.js +0 -1043
  270. package/blocks/short-form-finish/versions/1.0.3/package.js +0 -1026
  271. package/examples/saas-product-launch-reviewed-source.stillmade.json +0 -383
  272. package/examples/saas-product-launch.stillmade.json +0 -304
  273. package/packages/block-platform/dealer-inventory-video-project-type.js +0 -38
  274. package/packages/block-platform/first-revenue-project-types.js +0 -46
  275. package/packages/block-platform/fixture-starters.js +0 -13
  276. package/packages/block-platform/launch-project-source.js +0 -10
  277. package/packages/block-platform/launch-project.js +0 -30
  278. package/packages/block-platform/motion-project-types.js +0 -55
  279. package/packages/block-platform/motion-starters.js +0 -14
  280. package/packages/block-platform/product-ad-variants-project-type.js +0 -30
  281. package/packages/block-platform/product-launch-updated.js +0 -90
  282. package/packages/block-platform/revenue-review-project-types.js +0 -36
  283. package/packages/block-platform/revenue-review-starters.js +0 -13
  284. package/packages/block-platform/script-writer.js +0 -29
  285. package/packages/block-platform/website-source-block.js +0 -14
@@ -1,1043 +0,0 @@
1
- // Generated from the owned versioned source.
2
- export default {
3
- "manifest": {
4
- "schemaVersion": 1,
5
- "sdkVersion": "0.1.0",
6
- "id": "stillmade.short-form-finish",
7
- "version": "1.0.2",
8
- "name": "Short-form Finish",
9
- "description": "Turn selected source footage and reviewed subtitles into an editable captioned short with original speech and approved copy.",
10
- "kind": "task",
11
- "runtime": "javascript",
12
- "entry": "src/run.js",
13
- "license": "MIT",
14
- "inputs": {
15
- "assets": {
16
- "type": "video[]",
17
- "primary": true,
18
- "default": [],
19
- "required": true,
20
- "semantic": "legacy.asset-list.assets",
21
- "sources": [
22
- "upstream",
23
- "user"
24
- ],
25
- "description": "One saved video with measured dimensions and duration."
26
- },
27
- "music": {
28
- "type": "audio[]",
29
- "default": [],
30
- "semantic": "legacy.asset-list.assets",
31
- "sources": [
32
- "user"
33
- ],
34
- "description": "Optional licensed audio file covering the full output. Low background level under speech.",
35
- "required": false
36
- },
37
- "audioMode": {
38
- "type": "text",
39
- "default": "original",
40
- "description": "Original sound, or silent footage for a product demonstration only.",
41
- "semantic": "stillmade.short_form.audiomode",
42
- "sources": [
43
- "user",
44
- "default"
45
- ],
46
- "required": false
47
- },
48
- "speechVolume": {
49
- "type": "number",
50
- "default": 100,
51
- "min": 25,
52
- "max": 200,
53
- "description": "Original sound volume percent. Review the result for clipping or distortion.",
54
- "semantic": "stillmade.short_form.speechvolume",
55
- "sources": [
56
- "user",
57
- "default"
58
- ],
59
- "required": false
60
- },
61
- "purpose": {
62
- "type": "text",
63
- "default": "talking-head",
64
- "description": "Talking-head, testimonial, or product demonstration.",
65
- "semantic": "stillmade.short_form.purpose",
66
- "sources": [
67
- "user",
68
- "default"
69
- ],
70
- "required": false
71
- },
72
- "cuts": {
73
- "type": "text",
74
- "default": "",
75
- "description": "Source ranges in seconds, one per line (0-18.5). Blank uses only the first 60 seconds. For full source use 0-measuredDuration. Total must be 5–90 seconds.",
76
- "semantic": "stillmade.short_form.cuts",
77
- "sources": [
78
- "user",
79
- "default"
80
- ],
81
- "required": false
82
- },
83
- "subtitles": {
84
- "type": "text",
85
- "default": "",
86
- "description": "Reviewed SRT subtitles using ORIGINAL source timestamps. Paste text from a caption file. Transcription is not performed by this Block.",
87
- "semantic": "stillmade.short_form.subtitles",
88
- "sources": [
89
- "user",
90
- "default"
91
- ],
92
- "required": false
93
- },
94
- "title": {
95
- "type": "text",
96
- "default": "",
97
- "description": "Exact approved opening hook, up to 64 characters.",
98
- "semantic": "stillmade.short_form.title",
99
- "sources": [
100
- "user",
101
- "default"
102
- ],
103
- "required": false
104
- },
105
- "byline": {
106
- "type": "text",
107
- "default": "",
108
- "description": "Exact speaker or product label, up to 64 characters. Source or music credits belong in disclosure, not this label.",
109
- "semantic": "stillmade.short_form.byline",
110
- "sources": [
111
- "user",
112
- "default"
113
- ],
114
- "required": false
115
- },
116
- "cta": {
117
- "type": "text",
118
- "default": "",
119
- "description": "Optional approved closing call to action, up to 64 characters.",
120
- "semantic": "stillmade.short_form.cta",
121
- "sources": [
122
- "user",
123
- "default"
124
- ],
125
- "required": false
126
- },
127
- "disclosure": {
128
- "type": "text",
129
- "default": "",
130
- "description": "Optional exact source or music credit, or demo disclosure, up to 100 characters. Displays beneath the speaker/product label.",
131
- "semantic": "stillmade.short_form.disclosure",
132
- "sources": [
133
- "user",
134
- "default"
135
- ],
136
- "required": false
137
- },
138
- "captionStyle": {
139
- "type": "text",
140
- "default": "Clean",
141
- "description": "Clean or Emphasis caption treatment.",
142
- "semantic": "stillmade.short_form.captionstyle",
143
- "sources": [
144
- "user",
145
- "default"
146
- ],
147
- "required": false
148
- },
149
- "aspect": {
150
- "type": "text",
151
- "default": "9:16",
152
- "description": "Output frame shape.",
153
- "semantic": "stillmade.short_form.aspect",
154
- "sources": [
155
- "user",
156
- "default"
157
- ],
158
- "required": false
159
- },
160
- "fit": {
161
- "type": "text",
162
- "default": "contain",
163
- "description": "contain preserves the whole source frame; cover crops the source to fill the output frame. Use contain when the owner requires the full frame. Review framing after cropping.",
164
- "semantic": "stillmade.short_form.fit",
165
- "sources": [
166
- "user",
167
- "default"
168
- ],
169
- "required": false
170
- },
171
- "cropX": {
172
- "type": "number",
173
- "default": 0,
174
- "min": -100,
175
- "max": 100,
176
- "description": "Manual horizontal framing adjustment in editor preview units.",
177
- "semantic": "stillmade.short_form.cropx",
178
- "sources": [
179
- "user",
180
- "default"
181
- ],
182
- "required": false
183
- },
184
- "rightsConfirmed": {
185
- "type": "boolean",
186
- "default": false,
187
- "required": true,
188
- "sources": [
189
- "user"
190
- ],
191
- "description": "I have permission to use the source footage and approved statements.",
192
- "semantic": "stillmade.short_form.rightsconfirmed"
193
- }
194
- },
195
- "outputs": {
196
- "timeline": {
197
- "type": "timeline",
198
- "primary": true,
199
- "semantic": "legacy.timeline.updatededitor",
200
- "description": "Editable source cuts, original speech, phrase captions and approved titles."
201
- },
202
- "review": {
203
- "type": "json",
204
- "semantic": "stillmade.short_form.review",
205
- "description": "Source selections, caption count and human review requirements.",
206
- "schema": {
207
- "schemaVersion": "integer",
208
- "state": "string",
209
- "humanApproved": "boolean",
210
- "purpose": "string",
211
- "durationSec": "number",
212
- "source": {
213
- "assetId": "string",
214
- "versionId": "string"
215
- },
216
- "cuts": [
217
- {
218
- "from": "number",
219
- "to": "number"
220
- }
221
- ],
222
- "subtitleCount": "integer",
223
- "providerCalls": "integer",
224
- "warnings": "string[]"
225
- }
226
- }
227
- },
228
- "permissions": {
229
- "project": [
230
- "asset.read"
231
- ],
232
- "network": [],
233
- "filesystem": [],
234
- "secrets": []
235
- },
236
- "ui": [
237
- {
238
- "port": "audioMode",
239
- "control": "select",
240
- "options": [
241
- "original",
242
- "silent"
243
- ]
244
- },
245
- {
246
- "port": "speechVolume",
247
- "control": "slider",
248
- "min": 25,
249
- "max": 200,
250
- "step": 5
251
- },
252
- {
253
- "port": "purpose",
254
- "control": "select",
255
- "options": [
256
- "talking-head",
257
- "testimonial",
258
- "product-demo"
259
- ]
260
- },
261
- {
262
- "port": "captionStyle",
263
- "control": "select",
264
- "options": [
265
- "Clean",
266
- "Emphasis"
267
- ]
268
- },
269
- {
270
- "port": "aspect",
271
- "control": "select",
272
- "options": [
273
- "9:16",
274
- "1:1",
275
- "16:9"
276
- ]
277
- },
278
- {
279
- "port": "fit",
280
- "control": "select",
281
- "options": [
282
- "contain",
283
- "cover"
284
- ]
285
- },
286
- {
287
- "port": "cropX",
288
- "control": "slider",
289
- "min": -100,
290
- "max": 100,
291
- "step": 1
292
- }
293
- ],
294
- "platforms": {
295
- "phone": {
296
- "supported": true
297
- },
298
- "browser": {
299
- "supported": true
300
- },
301
- "desktop": {
302
- "supported": true
303
- }
304
- },
305
- "provenance": {
306
- "notice": "Original source (authorship declared by the packager)\nLicense: MIT\nChanges: Original implementation declared by the packager; no reused repository or dependency code.\nFiles: LICENSE, src/run.js\n\nMIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and \nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including \nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the \nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial \nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT \nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO \nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER \nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE \nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n",
307
- "portableSources": [
308
- {
309
- "kind": "original",
310
- "license": "MIT",
311
- "paths": [
312
- "LICENSE",
313
- "src/run.js"
314
- ],
315
- "evidence": [
316
- "upstream/0/LICENSE",
317
- "upstream/0/src/run.js"
318
- ],
319
- "changes": "Original implementation declared by the packager; no reused repository or dependency code."
320
- }
321
- ]
322
- },
323
- "semantics": [
324
- {
325
- "name": "stillmade.short_form.audiomode",
326
- "description": "Original sound, or silent footage for a product demonstration only.",
327
- "parents": []
328
- },
329
- {
330
- "name": "stillmade.short_form.speechvolume",
331
- "description": "Original sound volume percent. Review the result for clipping or distortion.",
332
- "parents": []
333
- },
334
- {
335
- "name": "stillmade.short_form.purpose",
336
- "description": "Talking-head, testimonial, or product demonstration.",
337
- "parents": []
338
- },
339
- {
340
- "name": "stillmade.short_form.cuts",
341
- "description": "Source ranges in seconds, one per line (0-18.5). Blank uses only the first 60 seconds. For full source use 0-measuredDuration. Total must be 5–90 seconds.",
342
- "parents": []
343
- },
344
- {
345
- "name": "stillmade.short_form.subtitles",
346
- "description": "Reviewed SRT subtitles using ORIGINAL source timestamps. Paste text from a caption file. Transcription is not performed by this Block.",
347
- "parents": []
348
- },
349
- {
350
- "name": "stillmade.short_form.title",
351
- "description": "Exact approved opening hook, up to 64 characters.",
352
- "parents": []
353
- },
354
- {
355
- "name": "stillmade.short_form.byline",
356
- "description": "Exact speaker or product label, up to 64 characters. Source or music credits belong in disclosure, not this label.",
357
- "parents": []
358
- },
359
- {
360
- "name": "stillmade.short_form.cta",
361
- "description": "Optional approved closing call to action, up to 64 characters.",
362
- "parents": []
363
- },
364
- {
365
- "name": "stillmade.short_form.disclosure",
366
- "description": "Optional exact source or music credit, or demo disclosure, up to 100 characters. Displays beneath the speaker/product label.",
367
- "parents": []
368
- },
369
- {
370
- "name": "stillmade.short_form.captionstyle",
371
- "description": "Clean or Emphasis caption treatment.",
372
- "parents": []
373
- },
374
- {
375
- "name": "stillmade.short_form.aspect",
376
- "description": "Output frame shape.",
377
- "parents": []
378
- },
379
- {
380
- "name": "stillmade.short_form.fit",
381
- "description": "contain preserves the whole source frame; cover crops the source to fill the output frame. Use contain when the owner requires the full frame. Review framing after cropping.",
382
- "parents": []
383
- },
384
- {
385
- "name": "stillmade.short_form.cropx",
386
- "description": "Manual horizontal framing adjustment in editor preview units.",
387
- "parents": []
388
- },
389
- {
390
- "name": "stillmade.short_form.rightsconfirmed",
391
- "description": "I have permission to use the source footage and approved statements.",
392
- "parents": []
393
- },
394
- {
395
- "name": "stillmade.short_form.review",
396
- "description": "Source selections, caption count and human review requirements.",
397
- "parents": []
398
- }
399
- ]
400
- },
401
- "code": "// Owned, deterministic editing implementation. No I/O, model or host imports.\nconst copy = value => JSON.parse(JSON.stringify(value));\nconst check = (ok, message) => { if (!ok) throw Error(message); };\nconst round = n => Math.round(n * 1000000) / 1000000;\nconst text = (value, name, max = 80) => {\n check(typeof value === 'string' && value.length <= max && !/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]/.test(value), name + ' must be short plain text.');\n return value.trim();\n};\nconst assets = input.assets;\ncheck(Array.isArray(assets) && assets.length === 1, 'Choose one original video.');\nconst source = assets[0];\ncheck(source.kind === 'video' && typeof source.assetId === 'string' && source.assetId && typeof source.versionId === 'string' && source.versionId, 'The video needs its exact saved asset and version.');\ncheck(typeof source.url === 'string' && /^(https:\\/\\/|http:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?\\/|\\/api\\/media\\/|sm-media:|blob:)/.test(source.url) && !/^https?:\\/\\/[^/?#]*@/.test(source.url), 'Use a saved media reference.');\ncheck(Number.isFinite(source.duration) && source.duration > 0 && Number.isFinite(source.width) && source.width > 0 && Number.isFinite(source.height) && source.height > 0, 'Upload the original video again to measure its duration and dimensions.');\ncheck(input.rightsConfirmed === true, 'Confirm permission to use this video and the exact supplied statements.');\nconst purpose = input.purpose || 'talking-head';\ncheck(['talking-head', 'testimonial', 'product-demo'].includes(purpose), 'Choose a supported production job.');\nconst title = text(input.title || '', 'Opening title', 64);\nconst byline = text(input.byline || '', 'Speaker or product label', 64);\nconst cta = text(input.cta || '', 'Closing call to action', 64);\nconst disclosure = text(input.disclosure || '', 'Disclosure', 100);\nconst aspect = input.aspect || '9:16';\ncheck(['9:16','1:1','16:9'].includes(aspect), 'Choose a supported frame shape.');\nconst style = input.captionStyle || 'Clean';\ncheck(['Clean','Emphasis'].includes(style), 'Choose Clean or Emphasis captions.');\nconst fit = input.fit || 'contain';\ncheck(['contain','cover'].includes(fit), 'Choose full frame or crop to fill.');\nconst cropX = input.cropX === undefined ? 0 : input.cropX;\ncheck(Number.isFinite(cropX) && Math.abs(cropX) <= 100, 'Horizontal framing must be from -100 to 100.');\n// Each selected source interval stays in supplied order. We never invent a\n// quote, remove a qualifying word, or silently select a supposedly viral moment.\nconst rawCuts = (input.cuts || '').trim();\ncheck(rawCuts.length <= 1200, 'Use up to 12 source selections.');\nconst cuts = rawCuts ? rawCuts.split(/\\n/).filter(line => line.trim()).map(line => {\n const match = line.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*[-,]\\s*(\\d+(?:\\.\\d+)?)$/);\n check(match, 'Use one source range per line, in seconds: 0-18.5');\n return {from:Number(match[1]),to:Number(match[2])};\n}) : [{from:0,to:Math.min(source.duration,60)}];\ncheck(cuts.length > 0 && cuts.length <= 12, 'Choose 1–12 source selections.');\nfor (const cut of cuts) check(cut.to > cut.from && cut.from >= 0 && cut.to <= source.duration + .02, 'A selected range falls outside the source video.');\nconst duration = round(cuts.reduce((n,c) => n+c.to-c.from,0));\ncheck(duration >= 5 && duration <= 90, 'The finished clip must be 5–90 seconds.');\nconst srt = input.subtitles || '';\ncheck(typeof srt === 'string' && srt.length <= 60000, 'Supply an SRT transcript, up to 60,000 characters.');\nconst time = stamp => { const m = stamp.match(/^(\\d{1,2}):(\\d{2}):(\\d{2})[,.](\\d{3})$/); check(m && +m[2]<60 && +m[3]<60, 'Invalid subtitle timestamp.'); return +m[1]*3600 + +m[2]*60 + +m[3] + +m[4]/1000; };\nlet cues = [];\nif (srt.trim()) cues = srt.replace(/^\\uFEFF/,'').replace(/\\r/g,'').trim().split(/\\n\\s*\\n/).map(chunk => {\n const lines = chunk.split('\\n'); if (/^\\d+$/.test(lines[0].trim())) lines.shift();\n const timing = lines.shift().trim().match(/^(\\S+)\\s+-->\\s+(\\S+)$/);\n check(timing && lines.length, 'Each subtitle needs a start, end and spoken text.');\n const start = time(timing[1]), end = time(timing[2]);\n check(end > start && end <= source.duration+.05, 'Subtitle timing falls outside the source.');\n const value = text(lines.join(' ').replace(/\\s+/g,' '), 'Subtitle', 180);\n check(value && !/<[^>]*>/.test(value), 'Use plain subtitle text without markup.');\n return {start,end,text:value};\n});\ncheck(cues.length <= 500, 'Select a transcript with at most 500 captions.');\nfor(let i=1;i<cues.length;i++) check(cues[i].start >= cues[i-1].end-.02, 'Subtitle cues must be ordered and must not overlap.');\ncheck(purpose === 'product-demo' || cues.length > 0, 'Paste reviewed SRT subtitles for this clip. Automatic transcription is a separate approved service.');\nconst audioMode=input.audioMode||'original';\ncheck(['original','silent'].includes(audioMode), 'Choose original sound or silent footage.');\ncheck(audioMode==='original'||purpose==='product-demo','Keep the original speaker’s voice for captioned speech and customer stories.');\nconst speechVolume=input.speechVolume===undefined?100:input.speechVolume;\ncheck(Number.isFinite(speechVolume)&&speechVolume>=25&&speechVolume<=200,'Source volume must be 25–200 percent. Listen for distortion.');\nconst music=input.music||[];\ncheck(Array.isArray(music)&&music.length<=1,'Choose at most one music file.');\nif(music.length){const m=music[0];check(m.kind==='audio'&&typeof m.assetId==='string'&&m.assetId&&typeof m.versionId==='string'&&m.versionId&&typeof m.url==='string'&&/^(https:\\/\\/|http:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?\\/|\\/api\\/media\\/|sm-media:|blob:)/.test(m.url)&&!/^https?:\\/\\/[^/?#]*@/.test(m.url)&&Number.isFinite(m.duration)&&m.duration>=duration,'Choose a saved, rights-cleared music file long enough for the full clip.');}\nconst clips=[], audioItems=[], captions=[], overlays=[];\nlet cursor=0;\ncuts.forEach((cut,index)=>{\n const length=round(cut.to-cut.from), id='selection-'+(index+1);\n clips.push({id,kind:'video',trackId:'video',asset:copy(source),assetId:source.assetId,versionId:source.versionId,url:source.url,title:source.name||'Original video',start:round(cursor),duration:length,trimStart:cut.from,speed:1,volume:0,transform:{fit,scale:1,x:cropX,y:0,rotation:0,opacity:1},motion:{type:'none'},transition:{type:'Cut',duration:0}});\n // Dedicated source audio: editing and rendering use the same trimmed interval,\n // and muted visual clips prevent a second embedded copy at normal export.\n if(audioMode==='original')audioItems.push({id:id+'-sound',trackId:'speech',asset:copy(source),assetId:source.assetId,versionId:source.versionId,url:source.url,title:'Original speech',start:round(cursor),duration:length,sourceOffset:cut.from,trimStart:cut.from,volume:speechVolume,rate:1,fadeIn:0,fadeOut:0});\n for (const cue of cues) {\n const from=Math.max(cut.from,cue.start),to=Math.min(cut.to,cue.end);\n if(to<=from)continue;\n // A cut inside a subtitle changes what the words mean. Require reviewed\n // boundaries rather than show an entire quote over a partial source phrase.\n check(cue.start >= cut.from-.05 && cue.end <= cut.to+.05, 'A cut splits a subtitle. Move the cut to a phrase boundary or revise the transcript.');\n captions.push({id:'caption-'+captions.length,trackId:'captions',type:'text',text:cue.text,start:round(cursor+from-cut.from),end:round(cursor+to-cut.from),font:'Hanken Grotesk',size:aspect==='9:16'?15:21,color:style==='Emphasis'?'#F7F0DB':'#FFFFFF',background:'#0A0A0A',backgroundOpacity:75,backgroundPadding:5,backgroundRadius:3,width:.82,x:50,y:76,position:'center',animation:style==='Emphasis'?'pop':'none',animIn:.12,animOut:0});\n }\n cursor += length;\n});\nif(music.length){const m=music[0];audioItems.push({id:'music-bed',trackId:'music',asset:copy(m),assetId:m.assetId,versionId:m.versionId,url:m.url,title:m.name||'Music',start:0,duration,sourceOffset:0,trimStart:0,volume:audioMode==='original'?12:65,rate:1,fadeIn:.3,fadeOut:.8});}\nconst addText=(id,value,start,end,y,size)=>{if(value)overlays.push({id,trackId:'copy',type:'text',text:value,start,end,font:'Hanken Grotesk',size,color:'#F7F0DB',background:'#0A0A0A',backgroundOpacity:80,backgroundPadding:5,width:.82,x:50,y,position:'center',animation:'fade',animIn:.2,animOut:.15});};\naddText('opening',title,0,Math.min(3,duration),16,aspect==='9:16'?16:22);\naddText('byline',byline,0,Math.min(5,duration),90,7);\naddText('cta',cta,Math.max(0,duration-3),duration,17,14);\naddText('disclosure',disclosure,0,duration,95,5.5);\nconst timeline={schemaVersion:1,activeTimelineId:'short-form',name:title||'Captioned clip',aspect,aspectRatio:aspect,previewH:369,backgroundColor:'#0A0A0A',tracks:[{id:'video',type:'video',label:'Source selections'},{id:'speech',type:'audio',label:'Original speech'},{id:'music',type:'audio',label:'Music'},{id:'captions',type:'caption',label:'Reviewed subtitles'},{id:'copy',type:'text',label:'Approved copy'}],trackOrder:['video','speech','music','captions','copy'],clips,audioItems,captions,textOverlays:overlays,stickers:[],effects:[],audio:copy(audioItems),overlays:copy([...captions,...overlays]),targetDuration:duration,exportSettings:{aspect,fps:30,resolution:'1080p',quality:'high'}};\nreturn {timeline,review:{schemaVersion:1,state:'needs-human-review',humanApproved:false,purpose,durationSec:duration,source:{assetId:source.assetId,versionId:source.versionId},cuts:copy(cuts),subtitleCount:captions.length,providerCalls:0,warnings:['Watch and listen to the entire export. Confirm every caption against the actual speech and preserve the speaker’s meaning.',...(fit==='cover'?['Crop-to-fill can hide faces or demonstrations. Review framing at every cut.']:[]),'SRT provides phrase timing. No word-level alignment or automatic transcription is claimed.']}};\n",
402
- "tests": [
403
- {
404
- "name": "Preserve original speech and reviewed source captions",
405
- "input": {
406
- "assets": [
407
- {
408
- "kind": "video",
409
- "assetId": "approved-video",
410
- "versionId": "v1",
411
- "url": "https://media.example/approved.mp4",
412
- "width": 1080,
413
- "height": 1920,
414
- "duration": 12
415
- }
416
- ],
417
- "purpose": "talking-head",
418
- "cuts": "0-12",
419
- "subtitles": "1\n00:00:00,000 --> 00:00:04,000\nThis is the original sentence.\n\n2\n00:00:04,000 --> 00:00:12,000\nKeep the complete thought in the clip.",
420
- "title": "An approved hook",
421
- "rightsConfirmed": true
422
- },
423
- "expected": {
424
- "timeline": {
425
- "schemaVersion": 1,
426
- "activeTimelineId": "short-form",
427
- "name": "An approved hook",
428
- "aspect": "9:16",
429
- "aspectRatio": "9:16",
430
- "previewH": 369,
431
- "backgroundColor": "#0A0A0A",
432
- "tracks": [
433
- {
434
- "id": "video",
435
- "type": "video",
436
- "label": "Source selections"
437
- },
438
- {
439
- "id": "speech",
440
- "type": "audio",
441
- "label": "Original speech"
442
- },
443
- {
444
- "id": "music",
445
- "type": "audio",
446
- "label": "Music"
447
- },
448
- {
449
- "id": "captions",
450
- "type": "caption",
451
- "label": "Reviewed subtitles"
452
- },
453
- {
454
- "id": "copy",
455
- "type": "text",
456
- "label": "Approved copy"
457
- }
458
- ],
459
- "trackOrder": [
460
- "video",
461
- "speech",
462
- "music",
463
- "captions",
464
- "copy"
465
- ],
466
- "clips": [
467
- {
468
- "id": "selection-1",
469
- "kind": "video",
470
- "trackId": "video",
471
- "asset": {
472
- "kind": "video",
473
- "assetId": "approved-video",
474
- "versionId": "v1",
475
- "url": "https://media.example/approved.mp4",
476
- "width": 1080,
477
- "height": 1920,
478
- "duration": 12
479
- },
480
- "assetId": "approved-video",
481
- "versionId": "v1",
482
- "url": "https://media.example/approved.mp4",
483
- "title": "Original video",
484
- "start": 0,
485
- "duration": 12,
486
- "trimStart": 0,
487
- "speed": 1,
488
- "volume": 0,
489
- "transform": {
490
- "fit": "contain",
491
- "scale": 1,
492
- "x": 0,
493
- "y": 0,
494
- "rotation": 0,
495
- "opacity": 1
496
- },
497
- "motion": {
498
- "type": "none"
499
- },
500
- "transition": {
501
- "type": "Cut",
502
- "duration": 0
503
- }
504
- }
505
- ],
506
- "audioItems": [
507
- {
508
- "id": "selection-1-sound",
509
- "trackId": "speech",
510
- "asset": {
511
- "kind": "video",
512
- "assetId": "approved-video",
513
- "versionId": "v1",
514
- "url": "https://media.example/approved.mp4",
515
- "width": 1080,
516
- "height": 1920,
517
- "duration": 12
518
- },
519
- "assetId": "approved-video",
520
- "versionId": "v1",
521
- "url": "https://media.example/approved.mp4",
522
- "title": "Original speech",
523
- "start": 0,
524
- "duration": 12,
525
- "sourceOffset": 0,
526
- "trimStart": 0,
527
- "volume": 100,
528
- "rate": 1,
529
- "fadeIn": 0,
530
- "fadeOut": 0
531
- }
532
- ],
533
- "captions": [
534
- {
535
- "id": "caption-0",
536
- "trackId": "captions",
537
- "type": "text",
538
- "text": "This is the original sentence.",
539
- "start": 0,
540
- "end": 4,
541
- "font": "Hanken Grotesk",
542
- "size": 15,
543
- "color": "#FFFFFF",
544
- "background": "#0A0A0A",
545
- "backgroundOpacity": 75,
546
- "backgroundPadding": 5,
547
- "backgroundRadius": 3,
548
- "width": 0.82,
549
- "x": 50,
550
- "y": 76,
551
- "position": "center",
552
- "animation": "none",
553
- "animIn": 0.12,
554
- "animOut": 0
555
- },
556
- {
557
- "id": "caption-1",
558
- "trackId": "captions",
559
- "type": "text",
560
- "text": "Keep the complete thought in the clip.",
561
- "start": 4,
562
- "end": 12,
563
- "font": "Hanken Grotesk",
564
- "size": 15,
565
- "color": "#FFFFFF",
566
- "background": "#0A0A0A",
567
- "backgroundOpacity": 75,
568
- "backgroundPadding": 5,
569
- "backgroundRadius": 3,
570
- "width": 0.82,
571
- "x": 50,
572
- "y": 76,
573
- "position": "center",
574
- "animation": "none",
575
- "animIn": 0.12,
576
- "animOut": 0
577
- }
578
- ],
579
- "textOverlays": [
580
- {
581
- "id": "opening",
582
- "trackId": "copy",
583
- "type": "text",
584
- "text": "An approved hook",
585
- "start": 0,
586
- "end": 3,
587
- "font": "Hanken Grotesk",
588
- "size": 16,
589
- "color": "#F7F0DB",
590
- "background": "#0A0A0A",
591
- "backgroundOpacity": 80,
592
- "backgroundPadding": 5,
593
- "width": 0.82,
594
- "x": 50,
595
- "y": 16,
596
- "position": "center",
597
- "animation": "fade",
598
- "animIn": 0.2,
599
- "animOut": 0.15
600
- }
601
- ],
602
- "stickers": [],
603
- "effects": [],
604
- "audio": [
605
- {
606
- "id": "selection-1-sound",
607
- "trackId": "speech",
608
- "asset": {
609
- "kind": "video",
610
- "assetId": "approved-video",
611
- "versionId": "v1",
612
- "url": "https://media.example/approved.mp4",
613
- "width": 1080,
614
- "height": 1920,
615
- "duration": 12
616
- },
617
- "assetId": "approved-video",
618
- "versionId": "v1",
619
- "url": "https://media.example/approved.mp4",
620
- "title": "Original speech",
621
- "start": 0,
622
- "duration": 12,
623
- "sourceOffset": 0,
624
- "trimStart": 0,
625
- "volume": 100,
626
- "rate": 1,
627
- "fadeIn": 0,
628
- "fadeOut": 0
629
- }
630
- ],
631
- "overlays": [
632
- {
633
- "id": "caption-0",
634
- "trackId": "captions",
635
- "type": "text",
636
- "text": "This is the original sentence.",
637
- "start": 0,
638
- "end": 4,
639
- "font": "Hanken Grotesk",
640
- "size": 15,
641
- "color": "#FFFFFF",
642
- "background": "#0A0A0A",
643
- "backgroundOpacity": 75,
644
- "backgroundPadding": 5,
645
- "backgroundRadius": 3,
646
- "width": 0.82,
647
- "x": 50,
648
- "y": 76,
649
- "position": "center",
650
- "animation": "none",
651
- "animIn": 0.12,
652
- "animOut": 0
653
- },
654
- {
655
- "id": "caption-1",
656
- "trackId": "captions",
657
- "type": "text",
658
- "text": "Keep the complete thought in the clip.",
659
- "start": 4,
660
- "end": 12,
661
- "font": "Hanken Grotesk",
662
- "size": 15,
663
- "color": "#FFFFFF",
664
- "background": "#0A0A0A",
665
- "backgroundOpacity": 75,
666
- "backgroundPadding": 5,
667
- "backgroundRadius": 3,
668
- "width": 0.82,
669
- "x": 50,
670
- "y": 76,
671
- "position": "center",
672
- "animation": "none",
673
- "animIn": 0.12,
674
- "animOut": 0
675
- },
676
- {
677
- "id": "opening",
678
- "trackId": "copy",
679
- "type": "text",
680
- "text": "An approved hook",
681
- "start": 0,
682
- "end": 3,
683
- "font": "Hanken Grotesk",
684
- "size": 16,
685
- "color": "#F7F0DB",
686
- "background": "#0A0A0A",
687
- "backgroundOpacity": 80,
688
- "backgroundPadding": 5,
689
- "width": 0.82,
690
- "x": 50,
691
- "y": 16,
692
- "position": "center",
693
- "animation": "fade",
694
- "animIn": 0.2,
695
- "animOut": 0.15
696
- }
697
- ],
698
- "targetDuration": 12,
699
- "exportSettings": {
700
- "aspect": "9:16",
701
- "fps": 30,
702
- "resolution": "1080p",
703
- "quality": "high"
704
- }
705
- },
706
- "review": {
707
- "schemaVersion": 1,
708
- "state": "needs-human-review",
709
- "humanApproved": false,
710
- "purpose": "talking-head",
711
- "durationSec": 12,
712
- "source": {
713
- "assetId": "approved-video",
714
- "versionId": "v1"
715
- },
716
- "cuts": [
717
- {
718
- "from": 0,
719
- "to": 12
720
- }
721
- ],
722
- "subtitleCount": 2,
723
- "providerCalls": 0,
724
- "warnings": [
725
- "Watch and listen to the entire export. Confirm every caption against the actual speech and preserve the speaker’s meaning.",
726
- "SRT provides phrase timing. No word-level alignment or automatic transcription is claimed."
727
- ]
728
- }
729
- }
730
- },
731
- {
732
- "name": "Rebase selected phrases without changing their words",
733
- "input": {
734
- "assets": [
735
- {
736
- "kind": "video",
737
- "assetId": "approved-video",
738
- "versionId": "v1",
739
- "url": "https://media.example/approved.mp4",
740
- "width": 1080,
741
- "height": 1920,
742
- "duration": 12
743
- }
744
- ],
745
- "purpose": "talking-head",
746
- "cuts": "4-12",
747
- "subtitles": "1\n00:00:00,000 --> 00:00:04,000\nThis is the original sentence.\n\n2\n00:00:04,000 --> 00:00:12,000\nKeep the complete thought in the clip.",
748
- "title": "An approved hook",
749
- "rightsConfirmed": true
750
- },
751
- "expected": {
752
- "timeline": {
753
- "schemaVersion": 1,
754
- "activeTimelineId": "short-form",
755
- "name": "An approved hook",
756
- "aspect": "9:16",
757
- "aspectRatio": "9:16",
758
- "previewH": 369,
759
- "backgroundColor": "#0A0A0A",
760
- "tracks": [
761
- {
762
- "id": "video",
763
- "type": "video",
764
- "label": "Source selections"
765
- },
766
- {
767
- "id": "speech",
768
- "type": "audio",
769
- "label": "Original speech"
770
- },
771
- {
772
- "id": "music",
773
- "type": "audio",
774
- "label": "Music"
775
- },
776
- {
777
- "id": "captions",
778
- "type": "caption",
779
- "label": "Reviewed subtitles"
780
- },
781
- {
782
- "id": "copy",
783
- "type": "text",
784
- "label": "Approved copy"
785
- }
786
- ],
787
- "trackOrder": [
788
- "video",
789
- "speech",
790
- "music",
791
- "captions",
792
- "copy"
793
- ],
794
- "clips": [
795
- {
796
- "id": "selection-1",
797
- "kind": "video",
798
- "trackId": "video",
799
- "asset": {
800
- "kind": "video",
801
- "assetId": "approved-video",
802
- "versionId": "v1",
803
- "url": "https://media.example/approved.mp4",
804
- "width": 1080,
805
- "height": 1920,
806
- "duration": 12
807
- },
808
- "assetId": "approved-video",
809
- "versionId": "v1",
810
- "url": "https://media.example/approved.mp4",
811
- "title": "Original video",
812
- "start": 0,
813
- "duration": 8,
814
- "trimStart": 4,
815
- "speed": 1,
816
- "volume": 0,
817
- "transform": {
818
- "fit": "contain",
819
- "scale": 1,
820
- "x": 0,
821
- "y": 0,
822
- "rotation": 0,
823
- "opacity": 1
824
- },
825
- "motion": {
826
- "type": "none"
827
- },
828
- "transition": {
829
- "type": "Cut",
830
- "duration": 0
831
- }
832
- }
833
- ],
834
- "audioItems": [
835
- {
836
- "id": "selection-1-sound",
837
- "trackId": "speech",
838
- "asset": {
839
- "kind": "video",
840
- "assetId": "approved-video",
841
- "versionId": "v1",
842
- "url": "https://media.example/approved.mp4",
843
- "width": 1080,
844
- "height": 1920,
845
- "duration": 12
846
- },
847
- "assetId": "approved-video",
848
- "versionId": "v1",
849
- "url": "https://media.example/approved.mp4",
850
- "title": "Original speech",
851
- "start": 0,
852
- "duration": 8,
853
- "sourceOffset": 4,
854
- "trimStart": 4,
855
- "volume": 100,
856
- "rate": 1,
857
- "fadeIn": 0,
858
- "fadeOut": 0
859
- }
860
- ],
861
- "captions": [
862
- {
863
- "id": "caption-0",
864
- "trackId": "captions",
865
- "type": "text",
866
- "text": "Keep the complete thought in the clip.",
867
- "start": 0,
868
- "end": 8,
869
- "font": "Hanken Grotesk",
870
- "size": 15,
871
- "color": "#FFFFFF",
872
- "background": "#0A0A0A",
873
- "backgroundOpacity": 75,
874
- "backgroundPadding": 5,
875
- "backgroundRadius": 3,
876
- "width": 0.82,
877
- "x": 50,
878
- "y": 76,
879
- "position": "center",
880
- "animation": "none",
881
- "animIn": 0.12,
882
- "animOut": 0
883
- }
884
- ],
885
- "textOverlays": [
886
- {
887
- "id": "opening",
888
- "trackId": "copy",
889
- "type": "text",
890
- "text": "An approved hook",
891
- "start": 0,
892
- "end": 3,
893
- "font": "Hanken Grotesk",
894
- "size": 16,
895
- "color": "#F7F0DB",
896
- "background": "#0A0A0A",
897
- "backgroundOpacity": 80,
898
- "backgroundPadding": 5,
899
- "width": 0.82,
900
- "x": 50,
901
- "y": 16,
902
- "position": "center",
903
- "animation": "fade",
904
- "animIn": 0.2,
905
- "animOut": 0.15
906
- }
907
- ],
908
- "stickers": [],
909
- "effects": [],
910
- "audio": [
911
- {
912
- "id": "selection-1-sound",
913
- "trackId": "speech",
914
- "asset": {
915
- "kind": "video",
916
- "assetId": "approved-video",
917
- "versionId": "v1",
918
- "url": "https://media.example/approved.mp4",
919
- "width": 1080,
920
- "height": 1920,
921
- "duration": 12
922
- },
923
- "assetId": "approved-video",
924
- "versionId": "v1",
925
- "url": "https://media.example/approved.mp4",
926
- "title": "Original speech",
927
- "start": 0,
928
- "duration": 8,
929
- "sourceOffset": 4,
930
- "trimStart": 4,
931
- "volume": 100,
932
- "rate": 1,
933
- "fadeIn": 0,
934
- "fadeOut": 0
935
- }
936
- ],
937
- "overlays": [
938
- {
939
- "id": "caption-0",
940
- "trackId": "captions",
941
- "type": "text",
942
- "text": "Keep the complete thought in the clip.",
943
- "start": 0,
944
- "end": 8,
945
- "font": "Hanken Grotesk",
946
- "size": 15,
947
- "color": "#FFFFFF",
948
- "background": "#0A0A0A",
949
- "backgroundOpacity": 75,
950
- "backgroundPadding": 5,
951
- "backgroundRadius": 3,
952
- "width": 0.82,
953
- "x": 50,
954
- "y": 76,
955
- "position": "center",
956
- "animation": "none",
957
- "animIn": 0.12,
958
- "animOut": 0
959
- },
960
- {
961
- "id": "opening",
962
- "trackId": "copy",
963
- "type": "text",
964
- "text": "An approved hook",
965
- "start": 0,
966
- "end": 3,
967
- "font": "Hanken Grotesk",
968
- "size": 16,
969
- "color": "#F7F0DB",
970
- "background": "#0A0A0A",
971
- "backgroundOpacity": 80,
972
- "backgroundPadding": 5,
973
- "width": 0.82,
974
- "x": 50,
975
- "y": 16,
976
- "position": "center",
977
- "animation": "fade",
978
- "animIn": 0.2,
979
- "animOut": 0.15
980
- }
981
- ],
982
- "targetDuration": 8,
983
- "exportSettings": {
984
- "aspect": "9:16",
985
- "fps": 30,
986
- "resolution": "1080p",
987
- "quality": "high"
988
- }
989
- },
990
- "review": {
991
- "schemaVersion": 1,
992
- "state": "needs-human-review",
993
- "humanApproved": false,
994
- "purpose": "talking-head",
995
- "durationSec": 8,
996
- "source": {
997
- "assetId": "approved-video",
998
- "versionId": "v1"
999
- },
1000
- "cuts": [
1001
- {
1002
- "from": 4,
1003
- "to": 12
1004
- }
1005
- ],
1006
- "subtitleCount": 1,
1007
- "providerCalls": 0,
1008
- "warnings": [
1009
- "Watch and listen to the entire export. Confirm every caption against the actual speech and preserve the speaker’s meaning.",
1010
- "SRT provides phrase timing. No word-level alignment or automatic transcription is claimed."
1011
- ]
1012
- }
1013
- }
1014
- }
1015
- ],
1016
- "portable": {
1017
- "format": "stillmade-block/1",
1018
- "dependencies": [],
1019
- "sources": [
1020
- {
1021
- "kind": "original",
1022
- "license": "MIT",
1023
- "paths": [
1024
- "LICENSE",
1025
- "src/run.js"
1026
- ],
1027
- "evidence": [
1028
- "upstream/0/LICENSE",
1029
- "upstream/0/src/run.js"
1030
- ],
1031
- "changes": "Original implementation declared by the packager; no reused repository or dependency code."
1032
- }
1033
- ],
1034
- "files": {
1035
- "upstream/0/LICENSE": "MIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and \nassociated documentation files (the \"Software\"), to deal in the Software without restriction, including \nwithout limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell \ncopies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the \nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial \nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT \nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO \nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER \nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE \nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n",
1036
- "upstream/0/src/run.js": "// Owned, deterministic editing implementation. No I/O, model or host imports.\nconst copy = value => JSON.parse(JSON.stringify(value));\nconst check = (ok, message) => { if (!ok) throw Error(message); };\nconst round = n => Math.round(n * 1000000) / 1000000;\nconst text = (value, name, max = 80) => {\n check(typeof value === 'string' && value.length <= max && !/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]/.test(value), name + ' must be short plain text.');\n return value.trim();\n};\nconst assets = input.assets;\ncheck(Array.isArray(assets) && assets.length === 1, 'Choose one original video.');\nconst source = assets[0];\ncheck(source.kind === 'video' && typeof source.assetId === 'string' && source.assetId && typeof source.versionId === 'string' && source.versionId, 'The video needs its exact saved asset and version.');\ncheck(typeof source.url === 'string' && /^(https:\\/\\/|http:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?\\/|\\/api\\/media\\/|sm-media:|blob:)/.test(source.url) && !/^https?:\\/\\/[^/?#]*@/.test(source.url), 'Use a saved media reference.');\ncheck(Number.isFinite(source.duration) && source.duration > 0 && Number.isFinite(source.width) && source.width > 0 && Number.isFinite(source.height) && source.height > 0, 'Upload the original video again to measure its duration and dimensions.');\ncheck(input.rightsConfirmed === true, 'Confirm permission to use this video and the exact supplied statements.');\nconst purpose = input.purpose || 'talking-head';\ncheck(['talking-head', 'testimonial', 'product-demo'].includes(purpose), 'Choose a supported production job.');\nconst title = text(input.title || '', 'Opening title', 64);\nconst byline = text(input.byline || '', 'Speaker or product label', 64);\nconst cta = text(input.cta || '', 'Closing call to action', 64);\nconst disclosure = text(input.disclosure || '', 'Disclosure', 100);\nconst aspect = input.aspect || '9:16';\ncheck(['9:16','1:1','16:9'].includes(aspect), 'Choose a supported frame shape.');\nconst style = input.captionStyle || 'Clean';\ncheck(['Clean','Emphasis'].includes(style), 'Choose Clean or Emphasis captions.');\nconst fit = input.fit || 'contain';\ncheck(['contain','cover'].includes(fit), 'Choose full frame or crop to fill.');\nconst cropX = input.cropX === undefined ? 0 : input.cropX;\ncheck(Number.isFinite(cropX) && Math.abs(cropX) <= 100, 'Horizontal framing must be from -100 to 100.');\n// Each selected source interval stays in supplied order. We never invent a\n// quote, remove a qualifying word, or silently select a supposedly viral moment.\nconst rawCuts = (input.cuts || '').trim();\ncheck(rawCuts.length <= 1200, 'Use up to 12 source selections.');\nconst cuts = rawCuts ? rawCuts.split(/\\n/).filter(line => line.trim()).map(line => {\n const match = line.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*[-,]\\s*(\\d+(?:\\.\\d+)?)$/);\n check(match, 'Use one source range per line, in seconds: 0-18.5');\n return {from:Number(match[1]),to:Number(match[2])};\n}) : [{from:0,to:Math.min(source.duration,60)}];\ncheck(cuts.length > 0 && cuts.length <= 12, 'Choose 1–12 source selections.');\nfor (const cut of cuts) check(cut.to > cut.from && cut.from >= 0 && cut.to <= source.duration + .02, 'A selected range falls outside the source video.');\nconst duration = round(cuts.reduce((n,c) => n+c.to-c.from,0));\ncheck(duration >= 5 && duration <= 90, 'The finished clip must be 5–90 seconds.');\nconst srt = input.subtitles || '';\ncheck(typeof srt === 'string' && srt.length <= 60000, 'Supply an SRT transcript, up to 60,000 characters.');\nconst time = stamp => { const m = stamp.match(/^(\\d{1,2}):(\\d{2}):(\\d{2})[,.](\\d{3})$/); check(m && +m[2]<60 && +m[3]<60, 'Invalid subtitle timestamp.'); return +m[1]*3600 + +m[2]*60 + +m[3] + +m[4]/1000; };\nlet cues = [];\nif (srt.trim()) cues = srt.replace(/^\\uFEFF/,'').replace(/\\r/g,'').trim().split(/\\n\\s*\\n/).map(chunk => {\n const lines = chunk.split('\\n'); if (/^\\d+$/.test(lines[0].trim())) lines.shift();\n const timing = lines.shift().trim().match(/^(\\S+)\\s+-->\\s+(\\S+)$/);\n check(timing && lines.length, 'Each subtitle needs a start, end and spoken text.');\n const start = time(timing[1]), end = time(timing[2]);\n check(end > start && end <= source.duration+.05, 'Subtitle timing falls outside the source.');\n const value = text(lines.join(' ').replace(/\\s+/g,' '), 'Subtitle', 180);\n check(value && !/<[^>]*>/.test(value), 'Use plain subtitle text without markup.');\n return {start,end,text:value};\n});\ncheck(cues.length <= 500, 'Select a transcript with at most 500 captions.');\nfor(let i=1;i<cues.length;i++) check(cues[i].start >= cues[i-1].end-.02, 'Subtitle cues must be ordered and must not overlap.');\ncheck(purpose === 'product-demo' || cues.length > 0, 'Paste reviewed SRT subtitles for this clip. Automatic transcription is a separate approved service.');\nconst audioMode=input.audioMode||'original';\ncheck(['original','silent'].includes(audioMode), 'Choose original sound or silent footage.');\ncheck(audioMode==='original'||purpose==='product-demo','Keep the original speaker’s voice for captioned speech and customer stories.');\nconst speechVolume=input.speechVolume===undefined?100:input.speechVolume;\ncheck(Number.isFinite(speechVolume)&&speechVolume>=25&&speechVolume<=200,'Source volume must be 25–200 percent. Listen for distortion.');\nconst music=input.music||[];\ncheck(Array.isArray(music)&&music.length<=1,'Choose at most one music file.');\nif(music.length){const m=music[0];check(m.kind==='audio'&&typeof m.assetId==='string'&&m.assetId&&typeof m.versionId==='string'&&m.versionId&&typeof m.url==='string'&&/^(https:\\/\\/|http:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?\\/|\\/api\\/media\\/|sm-media:|blob:)/.test(m.url)&&!/^https?:\\/\\/[^/?#]*@/.test(m.url)&&Number.isFinite(m.duration)&&m.duration>=duration,'Choose a saved, rights-cleared music file long enough for the full clip.');}\nconst clips=[], audioItems=[], captions=[], overlays=[];\nlet cursor=0;\ncuts.forEach((cut,index)=>{\n const length=round(cut.to-cut.from), id='selection-'+(index+1);\n clips.push({id,kind:'video',trackId:'video',asset:copy(source),assetId:source.assetId,versionId:source.versionId,url:source.url,title:source.name||'Original video',start:round(cursor),duration:length,trimStart:cut.from,speed:1,volume:0,transform:{fit,scale:1,x:cropX,y:0,rotation:0,opacity:1},motion:{type:'none'},transition:{type:'Cut',duration:0}});\n // Dedicated source audio: editing and rendering use the same trimmed interval,\n // and muted visual clips prevent a second embedded copy at normal export.\n if(audioMode==='original')audioItems.push({id:id+'-sound',trackId:'speech',asset:copy(source),assetId:source.assetId,versionId:source.versionId,url:source.url,title:'Original speech',start:round(cursor),duration:length,sourceOffset:cut.from,trimStart:cut.from,volume:speechVolume,rate:1,fadeIn:0,fadeOut:0});\n for (const cue of cues) {\n const from=Math.max(cut.from,cue.start),to=Math.min(cut.to,cue.end);\n if(to<=from)continue;\n // A cut inside a subtitle changes what the words mean. Require reviewed\n // boundaries rather than show an entire quote over a partial source phrase.\n check(cue.start >= cut.from-.05 && cue.end <= cut.to+.05, 'A cut splits a subtitle. Move the cut to a phrase boundary or revise the transcript.');\n captions.push({id:'caption-'+captions.length,trackId:'captions',type:'text',text:cue.text,start:round(cursor+from-cut.from),end:round(cursor+to-cut.from),font:'Hanken Grotesk',size:aspect==='9:16'?15:21,color:style==='Emphasis'?'#F7F0DB':'#FFFFFF',background:'#0A0A0A',backgroundOpacity:75,backgroundPadding:5,backgroundRadius:3,width:.82,x:50,y:76,position:'center',animation:style==='Emphasis'?'pop':'none',animIn:.12,animOut:0});\n }\n cursor += length;\n});\nif(music.length){const m=music[0];audioItems.push({id:'music-bed',trackId:'music',asset:copy(m),assetId:m.assetId,versionId:m.versionId,url:m.url,title:m.name||'Music',start:0,duration,sourceOffset:0,trimStart:0,volume:audioMode==='original'?12:65,rate:1,fadeIn:.3,fadeOut:.8});}\nconst addText=(id,value,start,end,y,size)=>{if(value)overlays.push({id,trackId:'copy',type:'text',text:value,start,end,font:'Hanken Grotesk',size,color:'#F7F0DB',background:'#0A0A0A',backgroundOpacity:80,backgroundPadding:5,width:.82,x:50,y,position:'center',animation:'fade',animIn:.2,animOut:.15});};\naddText('opening',title,0,Math.min(3,duration),16,aspect==='9:16'?16:22);\naddText('byline',byline,0,Math.min(5,duration),90,7);\naddText('cta',cta,Math.max(0,duration-3),duration,17,14);\naddText('disclosure',disclosure,0,duration,95,5.5);\nconst timeline={schemaVersion:1,activeTimelineId:'short-form',name:title||'Captioned clip',aspect,aspectRatio:aspect,previewH:369,backgroundColor:'#0A0A0A',tracks:[{id:'video',type:'video',label:'Source selections'},{id:'speech',type:'audio',label:'Original speech'},{id:'music',type:'audio',label:'Music'},{id:'captions',type:'caption',label:'Reviewed subtitles'},{id:'copy',type:'text',label:'Approved copy'}],trackOrder:['video','speech','music','captions','copy'],clips,audioItems,captions,textOverlays:overlays,stickers:[],effects:[],audio:copy(audioItems),overlays:copy([...captions,...overlays]),targetDuration:duration,exportSettings:{aspect,fps:30,resolution:'1080p',quality:'high'}};\nreturn {timeline,review:{schemaVersion:1,state:'needs-human-review',humanApproved:false,purpose,durationSec:duration,source:{assetId:source.assetId,versionId:source.versionId},cuts:copy(cuts),subtitleCount:captions.length,providerCalls:0,warnings:['Watch and listen to the entire export. Confirm every caption against the actual speech and preserve the speaker’s meaning.',...(fit==='cover'?['Crop-to-fill can hide faces or demonstrations. Review framing at every cut.']:[]),'SRT provides phrase timing. No word-level alignment or automatic transcription is claimed.']}};\n"
1037
- },
1038
- "lineage": {
1039
- "parents": [],
1040
- "changes": "Packaged original Block source for StillMade."
1041
- }
1042
- }
1043
- };