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
@@ -0,0 +1,487 @@
1
+ # Module Blocks: JavaScript and WebAssembly
2
+
3
+ # Module Blocks: modern JavaScript and WebAssembly
4
+
5
+ A module Block (`"runtime": "module"`) is an ordinary ES module. It can use
6
+ everything a modern browser Worker offers: `async`/`await`, timers, typed
7
+ arrays, `WebAssembly`, `OffscreenCanvas`, `createImageBitmap`, WebCodecs, and
8
+ any npm package you bundle into it, including C and C++ libraries compiled to
9
+ WebAssembly (OpenCV, libvips builds, codecs, solvers), threaded WebAssembly
10
+ (see Threads) and live WebSocket connections to declared origins (see Live
11
+ connections). Use it when a recipe or the QuickJS `javascript` runtime is too
12
+ small for the job. (StillMade's in-app builder writes `main.js` as one file that
13
+ imports nothing and uses no browser APIs; that single-file rule is the builder's,
14
+ not the runtime's. A module Block you build with the SDK can use all of this.)
15
+
16
+ ## Folder layout
17
+
18
+ ```
19
+ my-block/
20
+ stillmade.block.json manifest with "runtime": "module"
21
+ module/ everything the Block runs
22
+ main.js the entry (bundle npm dependencies into it)
23
+ filters.wasm optional WebAssembly and data files
24
+ module.json optional: {"entry": "other.js", "threads": true}
25
+ tests/fixtures.json [{name, input, expected, mocks?}]
26
+ ```
27
+
28
+ Start one with `stillmade-block create my-block module` (`module-hosted` for
29
+ one that calls hosted operations). `stillmade-block` is the SDK's command line:
30
+ `npx stillmade-block` with the npm package `stillmade-sdk`, or
31
+ `node <sdk>/packages/block-cli/cli.js` where `<sdk>` is the extracted
32
+ `/block-sdk/stillmade-sdk-0.1.0.zip`. Bundle npm
33
+ dependencies into `module/main.js` with any ES module bundler (esbuild,
34
+ rolldown, Vite). A module holds up to 64 files, 64 MiB each and 128 MiB in
35
+ total.
36
+
37
+ ## The entry
38
+
39
+ ```js
40
+ export default async function run(input, stillmade) {
41
+ stillmade.progress(0.1, 'Loading');
42
+ const { instance } = await WebAssembly.instantiate(stillmade.asset('filters.wasm'));
43
+ const bytes = await stillmade.files.read(input.photo); // a saved image the Block received
44
+ // ... process ...
45
+ const saved = await stillmade.files.write(pngBlob, { mediaType: 'image/png', name: 'result.png' });
46
+ return { result: saved };
47
+ }
48
+ ```
49
+
50
+ Return an object whose keys are your declared outputs. Outputs are checked
51
+ against the manifest's port types.
52
+
53
+ ## The `stillmade` host API
54
+
55
+ | Member | What it does |
56
+ | --- | --- |
57
+ | `input` | The validated input values (the same object as the first argument). |
58
+ | `signal` | An `AbortSignal` that fires when the person cancels. |
59
+ | `progress(value, label)` | Reports progress from 0 to 1 with a short label. Each report extends the run limit. |
60
+ | `log(...values)` | Adds a line to the run log (up to 500 lines). |
61
+ | `asset(path)` | A copy of one of the module's own files, such as a `.wasm` build, as an `ArrayBuffer`. |
62
+ | `files.read(ref)` | The bytes, as an `ArrayBuffer`, of a saved image, video, audio, asset or file (a PDF, text, CSV, Markdown or JSON file on a `file` input) the Block received as input, or that StillMade made in this run: files it wrote, transform results and generated media. Media the person kept on their device (a `sm-media://device/...` address in a project Step) reads the same way, without leaving the device. Other files cannot be read, and transforms, hosted calls and actions accept only these references too; a hosted call or action gets a cloud copy of device media, made once per version. A reference is the file its `kind`, `assetId`, `versionId` and `url` name, so `{kind, assetId, versionId, url}` rebuilt from a `media.generate` result item (without its `key`) is the same file; any other file is refused with `MODULE_FILE_READ`, by `stillmade-block test --mock`, `ready` and `gate` as by StillMade. |
63
+ | `files.write(data, {mediaType, name})` | Saves PNG, JPEG, WebP, GIF, MP4, WebM, MP3, WAV, M4A or OGG media, or a PDF, TXT, CSV, Markdown, JSON or ZIP file (`application/pdf`, `text/plain`, `text/csv`, `text/markdown`, `application/json`, `application/zip`; a `{kind: 'file'}` reference), to the runner's library and returns a reference you can return as an output. At most 64 files per run. |
64
+ | `media.transform(request)` | On-device media work done by StillMade: decode audio to samples, extract or loudness-normalise audio, cut, join or re-encode video, and take thumbnails. Results are saved like `files.write`. See [Media processing](#media-processing). |
65
+ | `actions.run(action)` | Runs a declared portable action. `media.generate` opens one approval for its whole batch, naming the most it can cost in StillMade credits; `stock.search` runs directly. |
66
+ | `hosted.run(operation, request)` | Calls a hosted operation the manifest lists in `permissions.capabilities` (for example `text.generate`, `image.generate`, `audio.speech`, `web.research`). Each call is quoted and approved like a capability Block and paid with StillMade credits. |
67
+ | `net.fetch(url, {method, headers, body})` | Calls one of the origins declared in `permissions.network` through StillMade, which adds the person's own key or OAuth token. Returns a standard `Response`. See [Outside APIs](outside-apis.md). |
68
+ | `net.connect(url, {protocols})` | Opens a live WebSocket to the `wss://` form of a declared origin and resolves with the open socket. No key is added. See [Live connections](#live-connections). |
69
+ | `connections.call(appId, operationId, input)` | Calls an operation of a published adapter declared in `permissions.connections`, with the person's own account. Returns the checked JSON result. |
70
+ | `storage.get/set/remove/list/info(...)` | The Block's own cloud storage in the current project: JSON values up to 1 MiB under folder-like keys. Kept across runs, devices and the Block's versions. See [Project storage, reads and assets](project-storage.md). |
71
+ | `storage.putFile(key, data, {mediaType})` / `storage.getFile(key)` | Files up to 64 MiB in the same storage; `getFile` returns `{key, mediaType, bytes, data}` with `data` as an `ArrayBuffer`, or `null`. |
72
+ | `project.read(fields?)` | A copy of the current project limited to the fields the manifest may read (`project.read` plus `context.<field>.read`), such as `timeline`, `shots` or `assets`. |
73
+ | `assets.create(file, {name})` / `assets.appendVersion(asset, file)` | Adds a saved image, video or audio file to the project's media as a new asset (`asset.create`), or as the newest version of an asset the Block received, read or created (`asset.version.append`). |
74
+
75
+ Hosted calls take the operation's request fields, for example:
76
+
77
+ ```js
78
+ const plan = await stillmade.hosted.run('text.generate', {
79
+ prompt: `Write a 20-second podcast intro about ${input.topic}.`,
80
+ system: 'Warm, short sentences.', maxTokens: 400,
81
+ schema: { type: 'object', required: ['script'], properties: { script: { type: 'string' } } },
82
+ });
83
+ const voice = await stillmade.hosted.run('audio.speech', { text: plan.script, settings: { voice: 'adam' } });
84
+ ```
85
+
86
+ `image.describe`, `media.analyze` and `audio.transcribe` take saved references
87
+ (`image`/`images`, `media`, `audio`). `audio.transcribe` takes a saved audio or a
88
+ saved video, whose own sound it transcribes, so there is no need to extract the
89
+ audio first. `media.analyze` returns `{schemaVersion: 1, items}`, one item per
90
+ picture or video, and for one video takes an optional `range: {start, end}` in
91
+ seconds, at most 12 apart, to look closely at that part. `image.generate` takes `prompt` and optional
92
+ `references`, one to eight saved images the new picture keeps. `video.generate` takes `prompt` and optional
93
+ `firstFrame` (a saved image the video starts on, for image-to-video), `lastFrame` (one it ends on; needs
94
+ `firstFrame`), `negativePrompt` (up to 2,000 characters) and `settings`; a model named in `settings` must take
95
+ those frames (Kling 3.0 Turbo and Wan 2.6 Flash take no last frame; Wan 2.6 Flash and Wan 2.7 need a first frame).
96
+ For a vertical or square clip, set `settings.aspectRatio` (`'9:16'`, `'1:1'`) and name no model: the default
97
+ model (Vidu Q3 Turbo) takes no aspect ratio, so StillMade uses the cheapest model that makes that shape with the
98
+ frames and `duration` given (Wan 2.6 Flash with a first frame, else Veo 3.1 Lite, else Seedance 2.0 Mini; see
99
+ [Generation models](../reference/generation-models.md)). A model you name is kept, and one that cannot make the shape
100
+ is refused. Look at pictures with `media.analyze`:
101
+ StillMade credits pay for it, while `image.describe` runs only on the person's
102
+ own OpenRouter key. `web.fetch` takes `url` and `instruction`;
103
+ `web.research` takes `topic`; music and sound effects take `prompt` and
104
+ `durationSec`.
105
+
106
+ ### Actions (`permissions.actions`)
107
+
108
+ `actions.run` runs only the actions listed in `permissions.actions`, the same list a
109
+ JavaScript Block declares: `"actions": ["media.generate"]` (or `"stock.search"`). A
110
+ module that runs actions and calls no hosted operation leaves
111
+ `permissions.capabilities` out; an empty list means the same.
112
+
113
+ ```js
114
+ const { value, failures } = await stillmade.actions.run({
115
+ schemaVersion: 1, kind: 'stillmade.media-action', operation: 'media.generate',
116
+ resultPort: null,
117
+ request: { items: [
118
+ { key: 'front', operation: 'image', prompt: `The front of ${input.address} at dusk`,
119
+ settings: { model: 'nano-banana-pro', aspectRatio: '16:9' } },
120
+ { key: 'tour', operation: 'speech', prompt: input.script },
121
+ ] },
122
+ });
123
+ ```
124
+
125
+ The action is the object a JavaScript Block returns, with every key present; its
126
+ items, settings and limits are in [Generate media from
127
+ JavaScript](generate-media.md). Pass `resultPort: null`: no
128
+ output port receives the media, `actions.run` returns it to your code and `run()`
129
+ returns what the Block outputs. Before anything is generated StillMade opens one
130
+ approval for the whole batch with the most it can cost in StillMade credits; credits
131
+ the batch does not use come back when it closes. Each `actions.run` call is its own
132
+ approval, so put every item one run needs in one batch: one clip per photo, the
133
+ voice and the music are one `media.generate` call of up to 50 items (StillMade makes
134
+ four at a time), never a call per photo, and never `hosted.run('video.generate')` once
135
+ per photo, which asks again from the second call. Video items take the same
136
+ `settings` as `video.generate` above, `aspectRatio` included. `actions.run` resolves with:
137
+
138
+ - `value`: the items that were made, in request order, each
139
+ `{key, kind, assetId, versionId, url, duration?}` with `kind` `image`, `video` or `audio`
140
+ (speech, music and sound effects). `duration` is the length in seconds of a
141
+ video, music or sound effect item: the length it asked for (`settings.duration`,
142
+ `settings.durationSec`, or the model's default). A speech item has none, since
143
+ its length is what the words take: decode it with
144
+ `media.transform({operation: 'audio.decode', source})`, whose result has
145
+ `duration`, before timing a cut to it. Return them as outputs, or pass them to
146
+ `files.read`, `media.transform` or a hosted call.
147
+ - `failures`: one `{key, message}` for each item that was not made; those items are
148
+ not in `value`.
149
+
150
+ It throws when no item was made, when the person declines (`MODULE_ACTION_DECLINED`)
151
+ and when the action is not declared (`MODULE_PERMISSION`). `stock.search` resolves
152
+ with `{results}`. With `--mock` or a `tests/mocks.json`, `media.generate` answers
153
+ with stand-in media and no failures; record your own answer under
154
+ `actions["media.generate"]`.
155
+
156
+ ## A check, then the expensive run, on one page
157
+
158
+ A job the person fills in and then gets a result from is one Block on one page
159
+ (builder contract, "One job per Step"): the inputs, a cheap check before the
160
+ expensive run, and the result. A listing video writes the script and voices a
161
+ few seconds of it first, and makes the video only once the person has read the
162
+ script and heard the voice. Build that as one module Block with a custom view,
163
+ never as two Blocks or two Steps.
164
+
165
+ The manifest takes the mode and the checked values as optional inputs, and the
166
+ outputs the check leaves out are optional (a run may leave out any output with
167
+ `"required": false`). `action` names the main button, the expensive run:
168
+
169
+ ```json
170
+ "inputs": {
171
+ "listingPhotos": { "type": "image[]", "primary": true, "description": "Photos of the home, in the order of the tour." },
172
+ "homeFacts": { "type": "text", "description": "Address, rooms and what stands out." },
173
+ "make": { "type": "text", "required": false, "default": "video", "description": "check: the script and a voice sample; video: the tour." },
174
+ "script": { "type": "script", "required": false, "description": "The checked script, as the person left it." }
175
+ },
176
+ "outputs": {
177
+ "tour": { "type": "video", "primary": true, "required": false },
178
+ "script": { "type": "script", "required": false },
179
+ "voiceSample": { "type": "audio", "required": false }
180
+ },
181
+ "action": { "label": "Generate tour", "running": "Generating tour…" },
182
+ "permissions": { "capabilities": ["text.generate", "audio.speech"] }
183
+ ```
184
+
185
+ The module does only the cheap work when asked for the check:
186
+
187
+ ```js
188
+ export default async function run(input, stillmade) {
189
+ const write = async () => (await stillmade.hosted.run('text.generate', {
190
+ prompt: `A 40-second listing tour script from these facts: ${input.homeFacts}`,
191
+ schema: { type: 'object', required: ['script'], properties: { script: { type: 'string' } } },
192
+ })).script;
193
+ if (input.make === 'check') {
194
+ const script = await write();
195
+ const voiceSample = await stillmade.hosted.run('audio.speech', { text: script.split(/(?<=[.!?])\s/)[0] });
196
+ return { script, voiceSample };
197
+ }
198
+ const script = input.script || await write();
199
+ const voice = await stillmade.hosted.run('audio.speech', { text: script });
200
+ const { duration } = await stillmade.media.transform({ operation: 'audio.decode', source: voice });
201
+ const each = duration / input.listingPhotos.length;
202
+ const tour = await stillmade.media.transform({
203
+ operation: 'timeline.render', aspect: '9:16',
204
+ clips: input.listingPhotos.map((photo, index) => ({ source: photo, start: index * each, duration: each, motion: 'zoom-in' })),
205
+ audio: [{ source: voice, start: 0, duration }],
206
+ });
207
+ return { tour, script };
208
+ }
209
+ ```
210
+
211
+ The view runs it twice with `StillMade.run`, which takes the inputs the view
212
+ sets (StillMade keeps them on the Step and fills the rest): `{make: 'check'}`
213
+ first ("Write the script"), then `{make: 'video', script}` with the script the
214
+ person checked or edited. One `sm-primary` at a time: the check until there is
215
+ a script, then the expensive run, labelled with `manifest.action`, while
216
+ "Write again" stays a quiet button. The result's place
217
+ (`sm-stage data-ratio="9:16"`) is drawn before either run; the script and the sample sit in it or beside the inputs
218
+ until the video replaces them. Each run starts empty: return the script again
219
+ with the video so it stays on the Step. StillMade saves the inputs a view sets,
220
+ so the mode last pressed stays on the Step; until the view first sets it, a run
221
+ started elsewhere (Chat, an automation) takes the default and makes the video.
222
+
223
+ Each run is priced and approved by StillMade on its own. Beside the Block's
224
+ page StillMade shows what each hosted call costs at the Step's saved or default
225
+ model ("Write · 1 credit · Make voiceover · 3 credits"), with an (i) saying that
226
+ pressing the Block's button is the approval. A run started by the person's own
227
+ click or key in the view then runs each hosted operation once without a dialog,
228
+ up to that price. StillMade checks the price with the provider first and opens
229
+ its approval dialog, with the quote, only when the quote is higher, when the run
230
+ calls the same operation a second time, or when the run did not come from the
231
+ person's press (in a preview outside a project every call shows the dialog;
232
+ `stillmade-block dev` answers with stand-ins and spends nothing). The view shows
233
+ no prices. `media.transform`, `timeline.render` included, runs on the device
234
+ and costs nothing; `actions.run` `media.generate` always asks first.
235
+
236
+ ## Media processing
237
+
238
+ `stillmade.media.transform(request)` asks StillMade to do heavy media work on
239
+ the person's device: Web Audio for audio, and the same renderer as Final
240
+ Render for video (WebCodecs in the browser, the native renderer on desktop).
241
+ It never runs on StillMade's servers and costs no credits. A transform reads
242
+ only files the Block received, wrote or generated in this run (including media
243
+ from `actions.run` `media.generate`; see `files.read` above), and saves its
244
+ result to the runner's library, so you can return it as an output or pass it
245
+ to the next transform.
246
+
247
+ | Request | Result |
248
+ | --- | --- |
249
+ | `{operation: 'audio.decode', source, sampleRate?}` | `{sampleRate, duration, samples}`: mono `Float32Array` PCM for analysis (default 16 kHz; 8000 to 48000). |
250
+ | `{operation: 'audio.extract', source, format?}` | A saved WAV (16-bit, 48 kHz) or MP3 of the audio track. |
251
+ | `{operation: 'audio.normalize', source, targetLufs?, format?}` | A saved WAV or MP3 at the target integrated loudness (default -16 LUFS, peaks at most -1 dBFS). |
252
+ | `{operation: 'video.cut', source, keep, settings?}` | A saved MP4 with only the `keep` ranges (`[{start, end}]` seconds, ordered, up to 500). |
253
+ | `{operation: 'video.concat', sources, settings?}` | A saved MP4 of 2 to 50 videos in order. |
254
+ | `{operation: 'video.resize', source, settings}` | A saved MP4 re-encoded with new settings. |
255
+ | `{operation: 'image.thumbnail', source, at?, width?}` | A saved PNG of one frame (default the first frame, 1280 px wide). |
256
+ | `{operation: 'document.text', source}` | `{text, pages}`: all the words of a PDF or text file the person gave, and `pages`, a list holding each page's words as one string (up to 500 pages). A file with no words (a scanned PDF) is refused. |
257
+ | `{operation: 'image.compose', width, height, layers, background?, format?, text?}` | A saved PNG (or JPEG) of saved images joined into one picture: side by side, a grid, a contact sheet, a poster with a title. |
258
+ | `{operation: 'timeline.render', clips, kind?, aspect?, audio?, captions?, settings?}` | A saved MP4 (or, with `kind: 'audio'`, an MP3) of your own timeline, rendered the way the Editor exports. |
259
+
260
+ `timeline.render` is how a Block exports: there is no separate Export, Captions,
261
+ Transition or Color grade Block to call. Each clip is `{source, start, duration,
262
+ from?, transition?: {type, duration?}, look?, mute?, motion?, fit?, focus?}` (a saved
263
+ image or video; a video keeps its own sound unless `mute`; `motion` is a slow
264
+ camera move over the clip: `zoom-in`, `zoom-out`, `pan-left`, `pan-right`,
265
+ `pan-up` or `pan-down`, which always keeps the picture filling the frame), each
266
+ audio item `{source, start, duration, from?, volume?}` (`volume` is a percentage of the
267
+ recording's own level, 0 to 200: 100, the default, plays it as recorded, 50 at half
268
+ the level, 0 silent; put music under a voice at about 20 to 30), and each caption
269
+ `{text, start, end, words?, position?, highlight?, animation?, size?}`, where
270
+ `words` (`[{text, start, end}]`) highlights each word as it is spoken and `size`
271
+ is the text height in pixels of the rendered frame (16 to 200; default 44, so
272
+ give a 1080 by 1920 vertical video about 80). Times are seconds on the timeline: each
273
+ clip starts where the one before it ends, less the transition on that earlier
274
+ clip, which blends it into the next one; clips that overlap more are refused.
275
+ `aspect` is `16:9`, `9:16`, `1:1` or `4:5`. A clip of another shape than the
276
+ frame (a wide sermon in a `9:16` clip) is cropped to fill it, around its middle
277
+ or around `focus: {x, y}`, the point to keep in view as fractions 0 to 1 across
278
+ the picture (`{x: 0.3}` keeps a speaker standing left of centre), and
279
+ `focus.scale` (1 to 2.5, instead of a motion) holds the clip that much closer on
280
+ that point; `fit: 'contain'` fits it whole with bars instead. Looks, transitions and caption
281
+ animations use the Editor's names (`TIMELINE_LOOKS`, `TIMELINE_TRANSITIONS`,
282
+ `CAPTION_ANIMATIONS`). The render follows the person's export plan, like any
283
+ export from StillMade.
284
+
285
+ `image.compose` draws on a canvas `width` by `height` pixels (16 to 4096),
286
+ filled with `background` (`#rrggbb`, default white). Each layer is `{source, x,
287
+ y, width, height, fit?}`: a saved image placed in that box, in order, cropped
288
+ to fill it (`fit: 'cover'`, the default) or fitted whole inside it (`'contain'`).
289
+ Each piece of text is `{text, x, y, size?, color?, background?, align?,
290
+ weight?, width?}`, drawn on top: `y` is the top of the line, `size` is 8 to 400
291
+ pixels (default 48), `color` defaults to black, `background` puts a box behind it
292
+ (for a Before label over a photo; `color` and `background` are `#rrggbb`, or `#rrggbbaa`
293
+ or `rgba()` for a see-through box such as `#00000099`), `align` is `left`, `center` or `right` around
294
+ `x`, and `weight` is `bold` (default) or `normal`. `width` wraps the words onto
295
+ lines no wider than it, 1.2 times `size` apart, breaking a word wider than a line
296
+ (or a caption with no spaces) between its characters: give a caption under a
297
+ frame the frame's width so it never runs into the next one. Up to 64 layers and 64 pieces of
298
+ text; `format` is `png` (default) or `jpeg`. A grid is arithmetic in your code:
299
+ for 9 photos on a 1800 by 1800 sheet, three columns of 600 pixels.
300
+
301
+ `source`/`sources` are saved references (`{kind, assetId, versionId, url}`).
302
+ Render `settings` are `{fps: 24|30|60, resolution: 720|1080|2160, quality:
303
+ 'low'|'medium'|'high'}`; the default is 30 fps, 1080, high. `video.cut`,
304
+ `video.concat` and `video.resize` keep the source's aspect ratio;
305
+ `timeline.render` makes the `aspect` it is given. Rendering follows the person's export plan, like any
306
+ export.
307
+
308
+ Pure helpers ship as `@stillmade/block-sdk/media` for you to bundle (the file is
309
+ `stillmade-sdk/packages/block-sdk/media-transform.js` in the npm package and
310
+ `<sdk>/packages/block-sdk/media-transform.js` in the extracted download; alias
311
+ the name in your bundler or import that path):
312
+ `silentRanges(samples, sampleRate, {thresholdDb, minSilence})`,
313
+ `keepRanges(silent, duration, {padding})`, `integratedLoudness(channels,
314
+ sampleRate)` (ITU-R BS.1770), `loudnessGain`, `encodeWav` and `decodeWav`.
315
+
316
+ ```js
317
+ import { silentRanges, keepRanges } from '@stillmade/block-sdk/media';
318
+
319
+ export default async function run(input, stillmade) {
320
+ const audio = await stillmade.media.transform({ operation: 'audio.decode', source: input.video });
321
+ const keep = keepRanges(silentRanges(audio.samples, audio.sampleRate), audio.duration);
322
+ const trimmed = await stillmade.media.transform({ operation: 'video.cut', source: input.video, keep });
323
+ return { trimmed };
324
+ }
325
+ ```
326
+
327
+ For anything else, decode and encode in the Block itself: the Worker has
328
+ WebCodecs (`VideoDecoder`, `VideoEncoder`, `AudioDecoder`), `OffscreenCanvas`
329
+ and `createImageBitmap`, and you can bundle `mp4-muxer`, `mp4box` or a
330
+ WebAssembly codec. Images arrive as saved references: read the bytes with
331
+ `files.read` and decode them with `createImageBitmap`, at full resolution.
332
+
333
+ ## Sandbox and limits
334
+
335
+ - The module runs in one dedicated Worker inside a response-sandboxed frame
336
+ with an opaque origin. It has no network of its own (`fetch`, WebSocket and
337
+ remote imports fail; `net.fetch` and `net.connect` go through StillMade), no
338
+ storage of the app, no page or DOM access, and cannot read another Block's
339
+ files.
340
+ - Every file is fetched from StillMade's store and checked against its SHA-256
341
+ before anything runs.
342
+ - A run may take 2 minutes; each progress report allows 60 more seconds. Time
343
+ StillMade spends on a host call (rendering a video, waiting for the person to
344
+ approve credits) does not count. Every run stops after 30 minutes in total.
345
+ When a limit passes, or the person cancels, the frame is removed and the
346
+ Worker stops immediately, even inside an endless loop.
347
+ - Generating code at run time (`new Function`, used by Emscripten bindings) is
348
+ allowed inside the sandbox.
349
+
350
+ Module Blocks run in StillMade on the person's device: in the browser while
351
+ StillMade is open, or in StillMade Desktop. StillMade has no server sandbox for
352
+ them. A schedule or event that hands browser-only Steps to the owner's StillMade
353
+ Desktop ("Run ... on my StillMade Desktop") pauses at a module Step until the
354
+ owner's desktop app, open with "Run my automations on this computer" on, runs it
355
+ with the Step's exact inputs; the workflow then continues from its result. Module
356
+ Steps that call paid hosted operations, generate media or need a device grant
357
+ still wait for the owner. Without that choice a background run stops before a
358
+ module Step and the person runs it in the project. Marketplace listings show
359
+ module Blocks as **Browser only**.
360
+
361
+ ## Live connections
362
+
363
+ `stillmade.net.connect(url, {protocols})` opens a WebSocket to the `wss://` form
364
+ of an origin in `permissions.network` (declare `https://stream.example.org` and
365
+ connect to `wss://stream.example.org/...`) and resolves once it is open:
366
+
367
+ ```js
368
+ const socket = await stillmade.net.connect('wss://stream.example.org/v1/live', { protocols: ['json.v1'] });
369
+ await socket.send(JSON.stringify({ subscribe: input.channel }));
370
+ for await (const data of socket) { // text arrives as strings, binary as ArrayBuffers
371
+ stillmade.progress(0.5, 'Listening'); // each progress report extends the run limit
372
+ if (JSON.parse(data).done) break; // leaving the loop closes the connection (code 1000)
373
+ }
374
+ ```
375
+
376
+ The socket also has `onmessage`, `onclose`, `addEventListener`, `readyState`,
377
+ `protocol`, `close(code, reason)` (1000 or 3000 to 4999) and `closed`, a promise of
378
+ `{code, reason, wasClean}`. `for await` yields every message not yet read, also ones
379
+ that arrived before the loop began (up to 1,024 messages or 16 MiB; older ones are
380
+ dropped). The module's own frame still has no network: StillMade
381
+ checks each URL (`wss://`, the standard port, a declared origin, valid subprotocol
382
+ names) and opens the connection in a separate relay frame that runs no Block code,
383
+ then relays the messages. A run keeps at most 4 connections, sends or receives at
384
+ most 1 MiB a message and 256 MiB in all, and every connection closes when the run
385
+ ends. The service sees an opaque `Origin` (`null`). StillMade adds no key to a live
386
+ connection: for a service that needs one, get a short-lived token with `net.fetch`
387
+ first and pass it as the service documents.
388
+
389
+ ## Threads (SharedArrayBuffer)
390
+
391
+ A module that runs its own Workers with shared memory (Emscripten pthreads,
392
+ threaded WebAssembly builds) declares it in `module.json`:
393
+
394
+ ```json
395
+ { "entry": "main.js", "threads": true }
396
+ ```
397
+
398
+ It then runs in a Worker that can share `SharedArrayBuffer` and
399
+ `WebAssembly.Memory({shared: true})` with the Workers it starts from `blob:` URLs
400
+ (module Workers and remote scripts stay unavailable, so bundle the thread code).
401
+ That Worker is not a secure context, so it has no WebCodecs, WebGPU,
402
+ `crypto.subtle` or `crypto.randomUUID`; a module without `threads` keeps those and
403
+ cannot share memory between Workers. Threads work in Chrome and Edge (version 137
404
+ or later) and in StillMade Desktop; in other browsers `self.crossOriginIsolated` is
405
+ false and `SharedArrayBuffer` is missing, so check it and fall back to one thread.
406
+ `files.write` and `storage.putFile` accept bytes in shared memory (they are copied).
407
+
408
+ ## Test, pack and import
409
+
410
+ ```sh
411
+ stillmade-block test my-block # runs fixtures in a local Node worker
412
+ stillmade-block preview my-block input.json
413
+ stillmade-block dev my-block # the interface in your browser, reloaded on save
414
+ stillmade-block gate my-block # StillMade's readiness check
415
+ stillmade-block pack my-block # writes <id>-<version>.stillmade-module.json
416
+ ```
417
+
418
+ `dev` serves the Block's real controls or view at `http://127.0.0.1:4800`
419
+ (`--port` picks another) and answers hosted calls with stand-ins, never credits.
420
+ The Block is not ready until `gate` passes. Name the Block's own folder: `gate .`
421
+ in a folder that also holds the extracted SDK checks the SDK's examples too.
422
+ `pack my-block` is the only packing step for a module Block, and it never takes
423
+ `--original` (original `.stillmade-block` archives hold recipe, JavaScript,
424
+ capability and ComfyUI source only and refuse a module Block).
425
+
426
+ `test` and `preview` run your code on your machine in a disposable Node worker
427
+ with the same host API and run limits (not a security sandbox; it is your own
428
+ code). `files.read`, `files.write`, `media.transform`, actions and hosted calls need StillMade, or
429
+ a host you pass to `createNodeModuleExecutor` in your own tests. The Node worker has no
430
+ `OffscreenCanvas` or `createImageBitmap`: a Block that decodes or draws images there
431
+ needs its own fallback for tests, or tests only what it does without drawing.
432
+
433
+ A fixture's `files` mock is a path inside the Block folder (local runs only: it does not
434
+ travel in the pack), `{"text": "..."}` for a text file, or `{"base64": "..."}` for a small
435
+ binary file (at most 256 KB), which travels in the pack and the import review. Text can
436
+ never hold a NUL character (`\u0000`): StillMade cannot store it, and import refuses it.
437
+
438
+ A fixture's photo, recording, clip or file can be one of StillMade's example files,
439
+ named by address: `/api/media/example/photo-1.jpg` to `photo-4.jpg`, `sheet.png`,
440
+ `room.png`, `kitchen.wav`, `hall.wav`, `click-120.wav`, `audio.mp3`, `clip.mp4`,
441
+ `notes.txt`, `sales.csv` and `lesson.pdf`, a two-page PDF of words (sizes in
442
+ PASTE_TO_LLM.md). `test` reads their real bytes
443
+ when the fixture gives no mock for them, and StillMade, its preview and its phone and
444
+ browser check serve the same files, so the example a person sees is the one you tested.
445
+ Point what a fixture's mocks answer at them too (`/api/media/example/clip.mp4` for a
446
+ rendered clip, `kitchen.wav` for speech): `dev` then shows and plays the result, where a
447
+ made-up address shows an empty player.
448
+
449
+ A fixture can record what the host answers, so a test of a Block that calls
450
+ hosted operations costs nothing and gives the same result every run:
451
+
452
+ ```json
453
+ {
454
+ "name": "Finds John 3:16",
455
+ "input": { "sermon": { "kind": "audio", "assetId": "a1", "versionId": "v1", "url": "https://example.com/sermon.wav" } },
456
+ "mocks": {
457
+ "hosted": {
458
+ "audio.transcribe": { "schemaVersion": 1, "text": "Turn to John 3:16", "words": [{ "text": "John", "start": 0.6, "end": 0.9 }] },
459
+ "text.generate": { "verses": [{ "reference": "John 3:16", "word": 0 }] }
460
+ }
461
+ },
462
+ "expected": { "verses": [{ "reference": "John 3:16", "start": 0.6 }] }
463
+ }
464
+ ```
465
+
466
+ `mocks` has the shape of `tests/mocks.json` (`hosted`, `transforms`, `actions`,
467
+ `files`, `storage`, `project`, `connections`, `network`); a list answers
468
+ successive calls in order. `files` maps an input's asset ID to a file in the Block
469
+ folder, or to `{"text": "..."}` for a text file, so a Block that reads a notes or
470
+ CSV file the person gives it is tested without a folder (StillMade's builder has none). A fixture's own `mocks` take the place of
471
+ `tests/mocks.json` for that fixture. StillMade's own builder writes module
472
+ Blocks the same way and runs their tests before anyone sees them.
473
+
474
+ Import the `.stillmade-module.json` in StillMade (Create, then Import).
475
+ StillMade verifies every file digest, uploads each file to its
476
+ content-addressed store, and saves the Block. Fixtures then run in your
477
+ browser's module sandbox.
478
+
479
+ Uploaded module files count against your cloud storage. A digest alone never
480
+ grants access: a person can read a module file only when they uploaded those
481
+ exact bytes or can open a saved Block that names it (yours, an installed or
482
+ community version, or one shared with their team).
483
+
484
+ See `examples/module-opencv/` for a Block that runs OpenCV on a 12-megapixel
485
+ photo, `examples/module-silence-trimmer/` for one that cuts the pauses out of a
486
+ talking video, and `examples/module-audio-extract/` for one that saves a
487
+ video's audio as WAV.
@@ -0,0 +1,70 @@
1
+ # Build an original SDK folder
2
+
3
+ Download `/block-sdk/stillmade-sdk-0.1.0.zip` (the same files as `npm install --save-dev stillmade-sdk`) and extract it into a development directory. `stillmade-block` in every StillMade guide is its command line: `node <sdk>/packages/block-cli/cli.js`, where `<sdk>` is the extracted folder (run from inside it, as below), or `npx stillmade-block` after the npm install. Then run:
4
+
5
+ ```sh
6
+ node packages/block-cli/cli.js create my-block javascript
7
+ node packages/block-cli/cli.js validate my-block
8
+ node packages/block-cli/cli.js test my-block
9
+ node packages/block-cli/cli.js dev my-block
10
+ node packages/block-cli/cli.js gate my-block
11
+ node packages/block-cli/cli.js pack my-block
12
+ ```
13
+
14
+ `dev` shows the Block's interface in your browser and reloads it on every save.
15
+ `gate` is the readiness check StillMade runs; the Block is not ready until it
16
+ passes. Name the Block's own folder: `gate .` in a folder that also holds the
17
+ extracted SDK checks the SDK's examples too. `pack` runs the tests again and
18
+ writes the file to import, named by the Block's runtime:
19
+
20
+ | Runtime | `pack my-block` writes |
21
+ | --- | --- |
22
+ | `module`, or a frame view with files in `src/view-files/` | `<id>-<version>.stillmade-module.json` |
23
+ | `recipe`, `javascript`, `capability`, `comfyui` | `<id>-<version>.stillmade.json` |
24
+
25
+ `create` supplies an editable starter. Replace its identity, task behavior, controls
26
+ and fixtures with your Block's requirements, retaining applicable SDK license
27
+ notices. The directory layout is:
28
+
29
+ ```text
30
+ my-block/
31
+ stillmade.block.json
32
+ src/run.js
33
+ src/view.html optional custom interface
34
+ src/view.css optional styles
35
+ src/view.js optional interface behavior
36
+ tests/fixtures.json
37
+ ```
38
+
39
+ `src/run.js` is an isolated function body operating on `input`; it returns declared
40
+ outputs. The custom interface uses the supplied `StillMade` bridge. A small image
41
+ tool can read typed image pixels (at most one megapixel, as RGBA arrays), apply its
42
+ edits in `run.js`, show before/after images through `StillMade.previewImage`, and call
43
+ `StillMade.run` from its controls; a photo editor that keeps full-size photos is a
44
+ `module` Block with a frame view (MODULES.md).
45
+ Author those files directly under the SDK contract; external framework packages,
46
+ Node services and a separate standalone website are not this runtime's source.
47
+ No build/install command executes during import.
48
+
49
+ Select **my-block** with Import folder (or drop the folder on the Import page) to
50
+ import that source unchanged. The app reads the folder with the same reader as
51
+ `pack`, so module Blocks (their `module/` files and `.wasm`) and frame views with
52
+ `src/view-files/` import as they pack; module files upload by SHA-256 before review.
53
+ `node_modules`, `.git` and other files outside the Block layout are ignored, so a
54
+ folder Codex or Claude Code built with its own tools imports as it is. Select the
55
+ Block directory, not an SDK checkout containing several example builds. Use at least two fixtures (normal and edge cases).
56
+ A new Block needs no `.stillmade-block` archive: `pack <folder> <file>.stillmade-block --original`
57
+ writes one only for your own recipe, JavaScript, capability or ComfyUI code
58
+ ([PORTABLE.md](https://stillmade.shop/block-sdk/PORTABLE.md)) and refuses a module Block. Direct folder
59
+ import and packed-file import retain the same runtime and interface. Security, source-rights confirmation and hosted execution approvals still
60
+ use the ordinary review path. They do not adapt the Block.
61
+
62
+ This is the implemented SDK, not the full future runtime catalog. It supports
63
+ data-only recipes, isolated JavaScript, reviewed hosted text and speech generation, and a reviewed ComfyUI core-image adapter,
64
+ typed inputs/outputs, schema-driven controls,
65
+ optional sandboxed HTML interfaces,
66
+ behavior fixtures, package validation, standalone execution, and app import.
67
+ Python source, external modules, native dependencies, custom ComfyUI nodes and
68
+ per-frame editor renderers are not executable through this SDK version. ComfyUI
69
+ requires an explicitly selected account connection and a real remote review;
70
+ recipe and JavaScript examples continue to run offline.