stillmade-sdk 0.1.7 → 0.1.9

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 (53) hide show
  1. package/.stillmade/sdk-baseline.json +1 -1
  2. package/AGENTS.md +9 -3
  3. package/DESKTOP_CAPABILITIES.md +1 -1
  4. package/INTERFACE_DESIGN.md +167 -83
  5. package/MODULES.md +1 -1
  6. package/NATIVE_MCP.md +1 -1
  7. package/PASTE_TO_LLM.md +151 -12
  8. package/README.md +36 -0
  9. package/SDK.md +534 -130
  10. package/UI_AESTHETIC.md +64 -0
  11. package/blocks/native/sm.export-video/versions/2.1.0/package.js +580 -0
  12. package/blocks/native/sm.panel-strip/versions/2.4.0/package.js +1176 -0
  13. package/blocks/native/sm.panel-strip/versions/2.4.1/package.js +1316 -0
  14. package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +800 -0
  15. package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +920 -0
  16. package/examples/README.md +10 -0
  17. package/examples/custom-interface.stillmade.json +3 -3
  18. package/package.json +14 -1
  19. package/packages/block-cli/dev-host/assets/host.js +214 -28
  20. package/packages/block-cli/dev-host/assets/index.css +1 -1
  21. package/packages/block-cli/format.js +13 -2
  22. package/packages/block-cli/templates.js +54 -23
  23. package/packages/block-platform/block-readiness.js +5 -0
  24. package/packages/block-platform/brief-ports.js +29 -0
  25. package/packages/block-platform/catalog.js +32 -9
  26. package/packages/block-platform/current-semantic-source.js +7 -3
  27. package/packages/block-platform/flow-inspection.js +14 -4
  28. package/packages/block-platform/plain-descriptions.js +1 -1
  29. package/packages/block-platform/project-context.js +7 -0
  30. package/packages/block-platform/project-input-samples.js +3 -1
  31. package/packages/block-platform/project-inputs.js +11 -2
  32. package/packages/block-platform/project-start-input.js +46 -0
  33. package/packages/block-platform/step-groups.js +2 -0
  34. package/packages/block-platform/steps.js +55 -4
  35. package/packages/block-platform/type-preview.js +19 -3
  36. package/packages/block-platform/workspace-input-readset.js +6 -1
  37. package/packages/block-sdk/builder-contract.js +12 -6
  38. package/packages/block-sdk/collaboration-admission.js +4 -2
  39. package/packages/block-sdk/connection-adapters.js +35 -2
  40. package/packages/block-sdk/contracts.js +5 -2
  41. package/packages/block-sdk/examples.js +3 -3
  42. package/packages/block-sdk/first-party-identities.js +190 -0
  43. package/packages/block-sdk/first-party-packages.js +6 -1
  44. package/packages/block-sdk/index.d.ts +2 -0
  45. package/packages/block-sdk/index.js +1 -1
  46. package/packages/block-sdk/javascript.js +5 -1
  47. package/packages/block-sdk/platform-checks.js +13 -2
  48. package/packages/block-sdk/shared-agent-controls.js +5 -4
  49. package/packages/block-sdk/shared-controls.js +13 -9
  50. package/packages/block-sdk/view-design.js +104 -27
  51. package/packages/block-sdk/view.js +4 -2
  52. package/packages/design-system/block-kit.css +183 -0
  53. package/packages/design-system/block-kit.html +187 -0
package/SDK.md CHANGED
@@ -106,7 +106,7 @@ recipe and JavaScript examples continue to run offline.
106
106
  ## Build your first Block
107
107
 
108
108
  ### Prerequisites
109
- Download and extract the [SDK 0.1.0](/block-sdk/stillmade-sdk-0.1.0.zip). Install Node.js 22 or later. Run commands from the extracted SDK folder. The runtime and its dependencies are bundled; the following example needs no account, API key, npm install, or network request.
109
+ Install Node.js 22 or later. Then either run `npm install --save-dev stillmade-sdk` and type `npx stillmade-block` wherever this page says `node packages/block-cli/cli.js`, or download and extract the [SDK 0.1.0](/block-sdk/stillmade-sdk-0.1.0.zip) and run the commands from the extracted folder. The runtime and its dependencies are bundled; the following example needs no account, API key or network request.
110
110
 
111
111
  ### Create a Block
112
112
  ```sh
@@ -160,7 +160,11 @@ Give your coding agent (Claude Code, Cursor, Codex, Gemini CLI or similar) a lin
160
160
  ### For the coding agent
161
161
  Do every step yourself. Do not ask the person to download, unzip, open a folder or run a command; only ask about what the Block should do.
162
162
 
163
- 1. **Get the SDK.** It is `/block-sdk/stillmade-sdk-0.1.0.zip` on the same site as this page: replace this page's path with that one (for example `https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.zip`, or `http://localhost:3000/block-sdk/stillmade-sdk-0.1.0.zip` when this page came from localhost). If `stillmade-blocks/AGENTS.md` already exists in your working folder, reuse that folder. Otherwise download and extract it into `stillmade-blocks`:
163
+ 1. **Get the SDK.** With Node.js 22 or later and npm, install it in your working folder:
164
+ ```sh
165
+ npm install --save-dev stillmade-sdk
166
+ ```
167
+ Then read `node_modules/stillmade-sdk/AGENTS.md` in full and run every command below as `npx stillmade-block`. Without npm, download the SDK archive instead. It is `/block-sdk/stillmade-sdk-0.1.0.zip` on the same site as this page: replace this page's path with that one (for example `https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.zip`, or `http://localhost:3000/block-sdk/stillmade-sdk-0.1.0.zip` when this page came from localhost). If `stillmade-blocks/AGENTS.md` already exists in your working folder, reuse that folder. Otherwise download and extract it into `stillmade-blocks`:
164
168
  ```sh
165
169
  curl -fsSL "<SDK URL>" -o stillmade-sdk.zip && unzip -q stillmade-sdk.zip -d stillmade-blocks && rm stillmade-sdk.zip
166
170
  ```
@@ -168,17 +172,17 @@ On Windows PowerShell:
168
172
  ```powershell
169
173
  Invoke-WebRequest "<SDK URL>" -OutFile stillmade-sdk.zip; Expand-Archive stillmade-sdk.zip stillmade-blocks; Remove-Item stillmade-sdk.zip
170
174
  ```
171
- The SDK needs Node.js 22 or later and nothing else; its runtime is bundled, so never run `npm install` in it.
172
- 2. **Read `stillmade-blocks/AGENTS.md` in full** before writing code. It is the contract StillMade checks every Block against.
173
- 3. **Build inside `stillmade-blocks`.** Scaffold with `node packages/block-cli/cli.js create <name> javascript` (or `image`, `stateful`, `capability`), then iterate with `node packages/block-cli/cli.js validate <name>` and `test <name>`.
174
- 4. **Run the readiness gate before you call anything ready:** `node packages/block-cli/cli.js gate .` from `stillmade-blocks`. Report the exact status it prints. If it fails, fix the listed checks and run it again. If you cannot fix them, tell the person plainly that the Block is not ready and list the failing checks. A browser application reports NEEDS STILLMADE REVIEW: tell the person to import it so StillMade can run its isolated review.
175
+ The archive needs Node.js 22 or later and nothing else; its runtime is bundled, so never run `npm install` in it. From the archive, run commands from `stillmade-blocks` as `node packages/block-cli/cli.js` where the steps below say `npx stillmade-block`.
176
+ 2. **Read `AGENTS.md` in full** before writing code (`node_modules/stillmade-sdk/AGENTS.md` from npm, `stillmade-blocks/AGENTS.md` from the archive). It is the contract StillMade checks every Block against.
177
+ 3. **Build the Block.** Scaffold with `npx stillmade-block create <name> javascript` (`npx stillmade-block create --list` shows every template, including `text`, `image-generate` and `speech` for hosted generation), then iterate with `npx stillmade-block validate <name>` and `npx stillmade-block test <name>`. Hosted operations need `test <name> --mock`, which uses recorded stand-ins and spends no credits.
178
+ 4. **Run the readiness gate before you call anything ready:** `npx stillmade-block gate .` from the folder that holds the Block. Report the exact status it prints. If it fails, fix the listed checks and run it again. If you cannot fix them, tell the person plainly that the Block is not ready and list the failing checks. A browser application reports NEEDS STILLMADE REVIEW: tell the person to import it so StillMade can run its isolated review.
175
179
  5. **Hand it over.** Give the person the full path of the `<name>` folder. In StillMade they choose **Import folder** and select that folder.
176
180
 
177
181
  `stillmade-blocks` contains hooks that run the gate automatically when a Claude Code or Cursor session started in that folder finishes a turn. In the session that downloaded the SDK those hooks are not active yet, so run the gate yourself as in step 4.
178
182
 
179
183
  ## Block builder contract
180
184
 
181
- Contract `stillmade.block-builder@1.3.0`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
185
+ Contract `stillmade.block-builder@1.5.1`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
182
186
 
183
187
  ### Source of truth
184
188
 
@@ -196,6 +200,8 @@ A Step is one job: the work between two moments where a person or the outside wo
196
200
  2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.
197
201
  3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.
198
202
 
203
+ In a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before; neighbouring workspaces with one main result type are one Step unless another part feeds the second. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
204
+
199
205
  A part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task's own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.
200
206
 
201
207
  Inside one job, each capability stays its own part: a capability Block performs exactly one hosted operation and a connector one app task, so every part keeps its own permission, approval and credit rules, and StillMade groups the parts of one job into one Step (`groups` in the Project Type). Never invent a combined runtime. When one Block can do the whole job with documented primitives (a JavaScript Block doing several local transforms, a workspace owning its whole document), build it as one Block.
@@ -224,7 +230,7 @@ Give the Block a short, plain name of 40 characters or fewer that says what it d
224
230
 
225
231
  ### Interface
226
232
 
227
- If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction genuinely needs one. Expose the capability, not the implementation: meaningful controls, sensible defaults, preview, direct manipulation, progress, clear errors and visible outputs. Do not show raw JSON, internal IDs, filesystem details, provider terminology or developer settings unless the Block is itself a developer tool. When a person chooses among options they need to hear or see (voices, music, sounds, fonts, looks, colors, templates or images), let them preview each option before choosing: a sample to play or a thumbnail, from the Block's own files or the host's free samples, never a paid generation. Leave previews out only when the builder states there is nothing to preview. Choices StillMade owns, such as voices and models in the run review, already show their samples. Follow the host appearance (Light, Dark and White) and the mobile layout contract: one interface for every screen size that fits a 320 CSS-pixel phone without horizontal scrolling.
233
+ If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction genuinely needs one. Build custom views from the Block kit the host injects (`block-kit.css`): the result in `sm-stage`, picks as pictures in `sm-choices`, one `sm-primary` naming the action (Write, Generate; never "Run") and no price (StillMade's run button adds it), extras in a closed `sm-more`; a look the person names wins. Fit one laptop screen without scrolling: label beside control, status in a few words, files as tiles, rare tools as icons. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Expose the capability, not the implementation: meaningful controls, sensible defaults, preview, direct manipulation, progress, clear errors and visible outputs. Do not show raw JSON, internal IDs, filesystem details, provider terminology or developer settings unless the Block is itself a developer tool. When a person chooses among options they need to hear or see (voices, music, sounds, fonts, looks, colors, templates or images), let them preview each option before choosing: a sample to play or a thumbnail, from the Block's own files or the host's free samples, never a paid generation. Leave previews out only when the builder states there is nothing to preview. Choices StillMade owns, such as voices and models in the run review, already show their samples. Follow the host appearance (Light, Dark and White) and the mobile layout contract: one interface for every screen size that fits a 320 CSS-pixel phone without horizontal scrolling.
228
234
 
229
235
  ### Shared editing
230
236
 
@@ -250,6 +256,10 @@ A successful run means more than a returned value. Validate inputs before work;
250
256
 
251
257
  Errors must say what failed, why it likely failed, whether a retry makes sense and what the user can do, so the native project chat and controller can troubleshoot. In JavaScript, throw an `Error` with that message: the host reports it as a `JAVASCRIPT` run failure carrying your message (guest code cannot set its own error code). Reject invalid or oversized input up front with a message naming the input and the limit. Never reduce a failure to "Something went wrong".
252
258
 
259
+ ### Only supplied facts
260
+
261
+ A text Block that writes from supplied facts must not let its prompt template outrank its own "never invent" rule. Every section that needs a specific fact (phone, website, address, price, inspection, certification, warranty, features) is written only when the input supplies that fact; otherwise the section is left out or replaced by a generic line that asserts nothing (for example "Call us today to schedule your test drive."). Never default to "inspected", "certified", invented features or placeholder contact details such as 555 numbers or example websites. Prefer extract-then-generate: first list the facts the input actually contains, then write using only that list. Add a check after generation that rejects or removes sentences naming a claim, number or address not present in the input. Include fixtures with the facts omitted and with them supplied.
262
+
253
263
  ### Adapt to the platform
254
264
 
255
265
  Do not modify StillMade core because a request does not fit immediately. First implement the capability entirely through documented SDK and platform primitives. If that is impossible, classify why: A Block implementation problem (fix it), B missing SDK exposure of an existing host capability, C missing generic platform primitive, D security or permission restriction, E runtime or language incompatibility, F fundamentally incompatible capability. For B or C, name the smallest general primitive that would serve this Block and a meaningful class of future Blocks (shaped like `files.watch()` or `media.transform()`, never `supportThisSpecificBlock()`). Return `{"unsupported":"exact reason","gapClass":"B|C|D|E|F","primitive":"..."}` instead of a package that pretends to work.
@@ -320,6 +330,8 @@ A Step is one job: the work between two moments where a person or the outside wo
320
330
  2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.
321
331
  3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.
322
332
 
333
+ In a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before; neighbouring workspaces with one main result type are one Step unless another part feeds the second. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
334
+
323
335
  A part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task's own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.
324
336
 
325
337
  Inside one job, each capability stays its own part: a capability Block performs exactly one hosted operation and a connector one app task, so every part keeps its own permission, approval and credit rules, and StillMade groups the parts of one job into one Step (`groups` in the Project Type). Never invent a combined runtime. When one Block can do the whole job with documented primitives (a JavaScript Block doing several local transforms, a workspace owning its whole document), build it as one Block.
@@ -879,12 +891,13 @@ StillMade's host image controls remain available with a custom interface.
879
891
  A project lets the user select a compatible authorized image or upload one;
880
892
  connected/context bindings remain read-only. A standalone preview keeps uploaded
881
893
  files local to that preview. Host uploads accept PNG, JPEG, WebP or GIF up to
882
- 12 MB and one megapixel; larger uploads are rejected with a request to resize
883
- a copy. Existing authorized project references can be displayed up to 16
884
- megapixels, but the selected runtime still applies its processing limit. No
885
- automatic processing resize occurs. The host validates permissions and exact media
886
- identity before resolving bytes; no account token, native file handle, or remote
887
- fetch capability enters the iframe.
894
+ 12 MB and one megapixel. Above one megapixel the host makes a one-megapixel
895
+ working copy for the sandbox when the account's media setting is on (its
896
+ default); with the setting off, larger uploads are rejected with a request to
897
+ resize a copy. Existing authorized project references can be displayed up to 16
898
+ megapixels, but the selected runtime still applies its processing limit. The
899
+ host validates permissions and exact media identity before resolving bytes; no
900
+ account token, native file handle, or remote fetch capability enters the iframe.
888
901
 
889
902
  This `view` example assumes one image input and one image output, both
890
903
  named `image`. It shows the actual selected input and processed result
@@ -1083,6 +1096,15 @@ model is told never to treat as instructions, and the review covers that exact
1083
1096
  text. An input that is not listed (one a custom view only displays) is never
1084
1097
  sent.
1085
1098
 
1099
+ Generated `text` and `script` outputs state only what the run was given. Before
1100
+ the result reaches a Step, the host removes each sentence that names a phone
1101
+ number, web address or email, an inspection, certification, warranty or
1102
+ vehicle-history claim, a price or a mileage figure that the run's input does not
1103
+ contain (contact details written in the Block's own instructions count as given;
1104
+ claim words there do not). A removed call to action becomes "Contact us today to
1105
+ learn more." Line breaks are kept, and `object` and `json` outputs are not
1106
+ changed. To keep such a sentence, supply the fact in an input.
1107
+
1086
1108
  #### Conversations
1087
1109
 
1088
1110
  To continue a conversation, declare one optional `json` input for the earlier
@@ -1771,9 +1793,14 @@ const page = await stillmade.connections.call('app:notion', 'NOTION_CREATE_NOTIO
1771
1793
 
1772
1794
  - The first time a person runs the Block, StillMade asks them to connect their
1773
1795
  account for the app (they sign in to the app in a new window) and to allow this
1774
- Block to use it. Then, once per session for each action, it shows what the
1775
- action does (reads, makes changes, sends, publishes or deletes, or that
1776
- StillMade has not reviewed it) and its price.
1796
+ Block to use it. Then, for each action, it shows what the action does (reads,
1797
+ makes changes, sends, publishes or deletes, or that StillMade has not
1798
+ reviewed it) and its price. One approval covers up to 10 paid runs (50 free
1799
+ ones) in the next 10 minutes; after that StillMade asks again, so a Block
1800
+ that loops an action stops. The server enforces this, not the Block.
1801
+ - Blocks can spend at most 300 of a person's credits on app actions in any
1802
+ 24 hours. A Block version StillMade has quarantined runs no app action and
1803
+ no `net.fetch`.
1777
1804
  - The action runs on StillMade's servers with that account. The Block gets the
1778
1805
  checked result; the account and its tokens never reach it. People remove a
1779
1806
  Block's access in its network access panel.
@@ -2058,6 +2085,16 @@ Connections convert between these types with host-owned conversions, version 1:
2058
2085
  A shot list reaches the Shots workspace when the output declares the
2059
2086
  workspace's meaning: `{"type": "shot[]", "semantic": "production_shot_plan"}`.
2060
2087
 
2088
+ Shots 2.2.0 and Image Panels 2.4.0 take narration from an earlier Step. A
2089
+ script whose output declares another meaning (or none), script-named `text`, or
2090
+ a transcript reaches their `narrationScript` and `narrationTiming` inputs
2091
+ through `script-as-narration`, `text-as-narration` and
2092
+ `transcript-as-narration-timing` (version 1, words unchanged; listed in
2093
+ `NARRATION_ADAPTERS`, not `ADAPTERS`). These are never
2094
+ chosen by matching: StillMade pins one on the connection when such a Step is
2095
+ added or replaced, or when the person picks "Use <Step>'s script". Declare
2096
+ `"semantic": "narration_script"` on a narration output to connect without one.
2097
+
2061
2098
  ### Example: PDF to shot list
2062
2099
 
2063
2100
  `examples/module-pdf-shot-list` is a module Block that takes an uploaded PDF,
@@ -2807,7 +2844,7 @@ small fixtures. Production execution uses its normal authorized media storage
2807
2844
  and decoding path instead of these temporary references.
2808
2845
 
2809
2846
  ### Saved media collections
2810
- Use an `asset[]`, `audio[]` or `video[]` input to collect original media with
2847
+ Use an `asset[]`, `audio[]` or `video[]` input to collect media files with
2811
2848
  standard controls. Inside a project, these controls upload, select, order and
2812
2849
  remove files without custom host code. Project uploads are saved before the
2813
2850
  input is published, with exact per-file receipts bound to the Block version.
@@ -5774,8 +5811,21 @@ Supported originals are PNG/JPG/JPEG/WebP/GIF images; MP4/MOV/WebM videos; and
5774
5811
  MP3/WAV/M4A/AAC/OGG audio. Each format specifies the canonical upload MIME type.
5775
5812
  An absent MIME type or `application/octet-stream` may use the recognized extension.
5776
5813
  The existing authenticated media upload endpoint uses the same table and limit,
5777
- preserves original bytes through its signed PUT, and returns a durable media URL.
5778
- No image downscaling is applied to audio or video originals.
5814
+ stores the bytes it is given through its signed PUT, and returns a durable media
5815
+ URL. Before that upload, the host may make a working copy of large media when
5816
+ the account's media setting is on (its default): images over 2048 px or 4 MiB
5817
+ become a 2048 px JPEG (PNG when transparent; GIF and SVG are left alone), audio
5818
+ over 25 MiB becomes 128 kbps MP3, and video over 1080p is re-encoded at 1080p
5819
+ where the device can. The working copy is then the saved value the Block
5820
+ receives, with its own `assetId`, `versionId` and URL; the original stays on the
5821
+ person's device, linked only on host records, and is never substituted at run
5822
+ time. A Block that needs the original asks the person, who can upload it from
5823
+ the input's "Use original" control. With the setting off, originals upload as is.
5824
+ With the account's local-first setting on (its default), a project Step keeps the
5825
+ file in the browser's own storage instead of uploading it: the value's `url` is a
5826
+ durable `sm-media://device/<assetId>/<sha256>` address, the Block reads it on
5827
+ that device, and the host uploads a copy once per version only when a hosted
5828
+ operation, Chat or sharing needs it.
5779
5829
 
5780
5830
  The host's existing Canvas upload adapter accepts optional `signal` and
5781
5831
  `assertCurrent` controls. The callback must throw when the owning session is no
@@ -5905,6 +5955,8 @@ export interface ConnectionAdapterPin { id: string; version: 1; }
5905
5955
  export interface ConnectionCompatibility { compatible: boolean; adapter?: ConnectionAdapterPin|null; requiresValue?: boolean; contextFields?: string[]; reason?: string; }
5906
5956
  export interface ConnectionAdapterOptions { semantics?: import('./semantic-contracts.js').SemanticRegistry; permissions?: string[]; adapter?: ConnectionAdapterPin|null; context?: Record<string, unknown>; }
5907
5957
  export const ADAPTERS: readonly Readonly<ConnectionAdapterPin & {output:string;input:string;contextFields:readonly string[];requiresValue:true}>[];
5958
+ /** Narration conversions (script-as-narration, text-as-narration, transcript-as-narration-timing): valid only where a connection pins them. */
5959
+ export const NARRATION_ADAPTERS: readonly Readonly<ConnectionAdapterPin & {output:string;input:string;targetSemantic:string;pinnedOnly:true;contextFields:readonly string[];requiresValue:true}>[];
5908
5960
  export function connectionCompatibility(output: string|Port, input: string|Port, options?: Pick<ConnectionAdapterOptions,'semantics'>): ConnectionCompatibility;
5909
5961
  export function validateConnectionAdapter(output: string|Port, input: string|Port, options?: Omit<ConnectionAdapterOptions,'context'>): ConnectionCompatibility;
5910
5962
  export function adaptConnectionValue(output: string|Port, input: string|Port, value: unknown, options?: ConnectionAdapterOptions): unknown;
@@ -6587,9 +6639,9 @@ Accepts decoded RGBA. Thickness is an integer from 1 to 8; threshold is from 0 t
6587
6639
 
6588
6640
  ### Run commands
6589
6641
  ```sh
6590
- node packages/block-cli/cli.js <command> <target> [argument] [options]
6642
+ npx stillmade-block <command> <target> [argument] [options]
6591
6643
  ```
6592
- Run from the SDK folder, or add the extracted SDK to a project with `npm install --save-dev <SDK folder>` and call `stillmade-block` from npm scripts. Targets can be authoring folders or packed files, except where a command creates a new target. `--help` prints this list.
6644
+ That is the form after `npm install --save-dev stillmade-sdk`. From a downloaded SDK folder, run `node packages/block-cli/cli.js <command> …` instead. Output is a JSON object when it is piped, which is what coding agents read; `--text` prints readable text and `--json` forces JSON. Targets can be authoring folders or packed files, except where a command creates a new target. `--help` prints this list.
6593
6645
 
6594
6646
  | Command | Argument | Behavior |
6595
6647
  | --- | --- | --- |
@@ -7132,10 +7184,12 @@ The same publishing panel controls public cover media, full description, categor
7132
7184
 
7133
7185
  My builds includes original and forked drafts, imports, installed versions, every own release and private releases. A version can be marked deprecated or delisted, with a public notice and an optional replacement version. It stops appearing in discovery and becomes ineligible for indexing; direct version URLs, source downloads and existing pinned projects remain available unless a separate security revocation applies.
7134
7186
 
7135
- Community publication does not automatically create a curated listing. Set the
7136
- source license in `manifest.license` (or top-level `license` for a project type),
7137
- save/test/sync, and publish an immutable Community version. Submit that released
7138
- version from Block Developer and track the decision in My builds. Supported SPDX
7187
+ A Block published publicly from StillMade is listed in the Marketplace as soon as
7188
+ its automatic checks pass: tests for its exact source, a supported license, its
7189
+ runtime, and no exact duplicate. No one reviews it by hand. A Project Type waits
7190
+ for an admin review. Set the source license in `manifest.license` (or top-level
7191
+ `license` for a project type), save/test/sync, and publish an immutable Community
7192
+ version; track the result in My builds. Supported SPDX
7139
7193
  identifiers for the initial review flow are exported as `MARKETPLACE_LICENSES` and
7140
7194
  listed in `api-index.json`; custom/unknown licensing needs a later review workflow.
7141
7195
  Changing a published version's license requires a new immutable version.
@@ -7154,7 +7208,7 @@ Blocks, Project Types, and creators have their own page URLs. Public pages link
7154
7208
 
7155
7209
  ## Build with an AI coding tool
7156
7210
 
7157
- The fastest route is to send your coding agent [Build a Block with your AI](/docs/build/with-your-ai) with your idea: it downloads the SDK, builds the Block and runs the readiness gate without you running anything.
7211
+ The fastest route is to send your coding agent [Build a Block with your AI](/docs/build/with-your-ai) with your idea: it installs the SDK (`npm install --save-dev stillmade-sdk`), builds the Block and runs the readiness gate without you running anything.
7158
7212
 
7159
7213
  ### Start with the SDK contract
7160
7214
  Use the button below to copy the complete authoring guide, then add a short description such as “Make a Block that converts an image to grayscale while preserving alpha.” The guide includes the package format, supported runtime, typed ports, permissions, fixtures, and a complete example.
@@ -7170,9 +7224,9 @@ Ask the assistant to return source files or one `.stillmade.json` package. Run t
7170
7224
 
7171
7225
  <!-- builder-contract:start (generated by npm run build:sdk; do not edit) -->
7172
7226
 
7173
- ### Block builder contract (stillmade.block-builder@1.3.0)
7227
+ ### Block builder contract (stillmade.block-builder@1.5.1)
7174
7228
 
7175
- Contract `stillmade.block-builder@1.3.0`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
7229
+ Contract `stillmade.block-builder@1.5.1`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
7176
7230
 
7177
7231
  ### Source of truth
7178
7232
 
@@ -7190,6 +7244,8 @@ A Step is one job: the work between two moments where a person or the outside wo
7190
7244
  2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.
7191
7245
  3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.
7192
7246
 
7247
+ In a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before; neighbouring workspaces with one main result type are one Step unless another part feeds the second. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
7248
+
7193
7249
  A part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task's own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.
7194
7250
 
7195
7251
  Inside one job, each capability stays its own part: a capability Block performs exactly one hosted operation and a connector one app task, so every part keeps its own permission, approval and credit rules, and StillMade groups the parts of one job into one Step (`groups` in the Project Type). Never invent a combined runtime. When one Block can do the whole job with documented primitives (a JavaScript Block doing several local transforms, a workspace owning its whole document), build it as one Block.
@@ -7218,7 +7274,7 @@ Give the Block a short, plain name of 40 characters or fewer that says what it d
7218
7274
 
7219
7275
  ### Interface
7220
7276
 
7221
- If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction genuinely needs one. Expose the capability, not the implementation: meaningful controls, sensible defaults, preview, direct manipulation, progress, clear errors and visible outputs. Do not show raw JSON, internal IDs, filesystem details, provider terminology or developer settings unless the Block is itself a developer tool. When a person chooses among options they need to hear or see (voices, music, sounds, fonts, looks, colors, templates or images), let them preview each option before choosing: a sample to play or a thumbnail, from the Block's own files or the host's free samples, never a paid generation. Leave previews out only when the builder states there is nothing to preview. Choices StillMade owns, such as voices and models in the run review, already show their samples. Follow the host appearance (Light, Dark and White) and the mobile layout contract: one interface for every screen size that fits a 320 CSS-pixel phone without horizontal scrolling.
7277
+ If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction genuinely needs one. Build custom views from the Block kit the host injects (`block-kit.css`): the result in `sm-stage`, picks as pictures in `sm-choices`, one `sm-primary` naming the action (Write, Generate; never "Run") and no price (StillMade's run button adds it), extras in a closed `sm-more`; a look the person names wins. Fit one laptop screen without scrolling: label beside control, status in a few words, files as tiles, rare tools as icons. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Expose the capability, not the implementation: meaningful controls, sensible defaults, preview, direct manipulation, progress, clear errors and visible outputs. Do not show raw JSON, internal IDs, filesystem details, provider terminology or developer settings unless the Block is itself a developer tool. When a person chooses among options they need to hear or see (voices, music, sounds, fonts, looks, colors, templates or images), let them preview each option before choosing: a sample to play or a thumbnail, from the Block's own files or the host's free samples, never a paid generation. Leave previews out only when the builder states there is nothing to preview. Choices StillMade owns, such as voices and models in the run review, already show their samples. Follow the host appearance (Light, Dark and White) and the mobile layout contract: one interface for every screen size that fits a 320 CSS-pixel phone without horizontal scrolling.
7222
7278
 
7223
7279
  ### Shared editing
7224
7280
 
@@ -7244,6 +7300,10 @@ A successful run means more than a returned value. Validate inputs before work;
7244
7300
 
7245
7301
  Errors must say what failed, why it likely failed, whether a retry makes sense and what the user can do, so the native project chat and controller can troubleshoot. In JavaScript, throw an `Error` with that message: the host reports it as a `JAVASCRIPT` run failure carrying your message (guest code cannot set its own error code). Reject invalid or oversized input up front with a message naming the input and the limit. Never reduce a failure to "Something went wrong".
7246
7302
 
7303
+ ### Only supplied facts
7304
+
7305
+ A text Block that writes from supplied facts must not let its prompt template outrank its own "never invent" rule. Every section that needs a specific fact (phone, website, address, price, inspection, certification, warranty, features) is written only when the input supplies that fact; otherwise the section is left out or replaced by a generic line that asserts nothing (for example "Call us today to schedule your test drive."). Never default to "inspected", "certified", invented features or placeholder contact details such as 555 numbers or example websites. Prefer extract-then-generate: first list the facts the input actually contains, then write using only that list. Add a check after generation that rejects or removes sentences naming a claim, number or address not present in the input. Include fixtures with the facts omitted and with them supplied.
7306
+
7247
7307
  ### Adapt to the platform
7248
7308
 
7249
7309
  Do not modify StillMade core because a request does not fit immediately. First implement the capability entirely through documented SDK and platform primitives. If that is impossible, classify why: A Block implementation problem (fix it), B missing SDK exposure of an existing host capability, C missing generic platform primitive, D security or permission restriction, E runtime or language incompatibility, F fundamentally incompatible capability. For B or C, name the smallest general primitive that would serve this Block and a meaningful class of future Blocks (shaped like `files.watch()` or `media.transform()`, never `supportThisSpecificBlock()`). Return `{"unsupported":"exact reason","gapClass":"B|C|D|E|F","primitive":"..."}` instead of a package that pretends to work.
@@ -7296,6 +7356,132 @@ A Block is done only when every applicable item holds. Passing a compile, one un
7296
7356
 
7297
7357
  <!-- builder-contract:end -->
7298
7358
 
7359
+ <!-- authoring-guide:start (generated by npm run build:sdk; do not edit) -->
7360
+
7361
+ ### SDK authoring guide
7362
+
7363
+ StillMade's own builders (describe a Block, refine, remix, GitHub adaptation and Project Types) are given these exact sections, rendered from `packages/block-sdk/authoring-guide.js` beside the builder contract. A section marked *When* reaches StillMade's builder only for requests that need it; follow it whenever it applies to your Block.
7364
+
7365
+ ### Remixable source
7366
+
7367
+ Every finished Block must support automatic remixing through the standard SDK contract. Include complete editable runtime source/configuration, typed inputs/outputs, UI controls, fixtures, and a supported permissive manifest.license with its complete real copyright/license notice in manifest.provenance.notice (or verified portable evidence). Do not disable remixing. The code stays private to its creator; others remix only through StillMade’s AI builder. No custom remix endpoint or host callback is needed. Preserve inherited source, notices and lineage. Never invent ownership, copyright holders or permission, or apply a new license to unresolved third-party source; return unsupported with a clear licensing reason instead. The shared SDK admission validator enforces this requirement.
7368
+
7369
+ ### Package format
7370
+
7371
+ A Block package is StillMade SDK 0.1.0 JSON. Prefer {manifest,recipe,tests} for supported operations. Otherwise use {manifest,code,tests} with runtime:"javascript": code is a synchronous function body receiving input and returning declared output bindings, e.g. return {text:input.text.toUpperCase()}. JavaScript runs in isolated QuickJS: no fetch, DOM, Node APIs, modules, dependencies, timers, or host services; no promises, imports, require, eval or Function. Limits: 500 ms execution, 16 MiB heap, 256 KiB code, 4 MiB JSON I/O. Include concrete passing fixtures that reflect the requested behavior, including an edge case; never approximate a hosted model call with fabricated deterministic output. Manifest fields: schemaVersion:1,sdkVersion:"0.1.0",id:"creator.name",version:"1.0.0",name,description,kind:"task",runtime:"recipe", "javascript", "comfyui", or "capability",inputs/outputs as named {type,default?,min?,max?,role?,primary?}. Usually mark one primary input and output with primary:true. Semantic roles such as source_image and character_reference are distinct and must never be guessed,permissions:{project:[],network:[],filesystem:[],secrets:[]},ui:[{control,port}]. Recipe: {schemaVersion:1,steps:[{id,op,args}],outputs}. References: {"$input":"port"}, {"$step":"step-id"}. Supported operations: {"value.copy":{"description":"Copy a JSON value without changing it.","args":["value"]},"text.trim":{"description":"Remove leading and trailing whitespace.","args":["text"]},"text.replace":{"description":"Replace literal text; no regular expressions.","args":["text","find","replacement"]},"text.join":{"description":"Join an array of text values.","args":["items","separator"]},"image.thicken-lines":{"description":"Expand dark outlines while preserving other colors.","args":["image","thickness","threshold"]}}. Types include text,number,integer,boolean,json,image,audio,video,script,text[]. Include test {name,input,expected} where expected is output bindings.
7372
+
7373
+ ### Port meanings
7374
+
7375
+ For each newly created Block, declare the meaning of every input and output in its manifest contract, not just a physical type. Each input needs semantic, required:boolean and a short plain-language description. Each output needs semantic and a short description. Use lowercase snake_case or dotted semantic names, such as company_profile or email_subject; different meanings are not interchangeable merely because both are text. If an existing role is present, semantic must equal that role. Reuse an exact existing meaning only when the information actually has that meaning. Describe structured fields with schema when their shape matters: for example {name:"string",website:"string",industry:"string?"}. Supported schema fields are string/text, number, integer, boolean, object, json, arrays of those types, and optional fields with ?. Declare sources only when a requirement restricts where its answer may come from: upstream, project, adapter, chat, default, user or external. Do not mark a credential as ordinary text or put secrets in defaults. Contracts describe information; they never authorize network access, model calls, project edits, credit spending or new runtime APIs. Keep existing imported, installed and base Block contracts unchanged; do not silently relabel old published contracts to make a connection appear compatible. New contracts must match actual code and fixtures.
7376
+ Custom inheritance is persisted in manifest.semantics, an array of {name,description?,parents?}. Use namespaced custom names, at most 64 definitions per Block, eight parents per definition, and 500 description characters. Never redefine unnamespaced standard meanings, create cycles, or combine conflicting parent sets. Preserve effective inherited meanings of all existing base inputs, outputs and accepts declarations; adding or changing a parent is a contract change even when port labels stay identical. New independent ports may declare new meanings.
7377
+ In isolated computation code, StillMade.emit(outputName,value) collects declared final outputs without a return object; emit each name once and never combine it with a returned output object or job.pending result. StillMade.resolveRequirement(inputName) reads the fixed host-validated declared input snapshot as {resolved:true,requirement,value}, or {resolved:false,requirement} for an absent optional input. StillMade.getProjectContext() returns only context fields explicitly bound in manifest.inputs with their matching context.FIELD.read permissions; getProjectContext(field) reads one declared field and rejects undeclared fields. These snapshot readers do not run the resolver again, request Chat/user answers, read extra project data, expose credentials, write projects or grant permissions. StillMade.validate(value,schema) checks JSON values against SDK semantic schemas and returns a boolean; it does not replace host output validation, prove semantic meaning or authorize execution. These methods are not available in view.javascript.
7378
+
7379
+ ### Hosted text generation
7380
+
7381
+ For hosted text generation use {manifest,capability,tests}, runtime:"capability", entry:"src/capability.json", permissions.capabilities:["text.generate"]. Do not write fetch or async calls in JavaScript. The host owns model, provider, BYOK, credits and explicit run consent; never put those fields or keys in package source. One required primary input text|script|brief (structured values require text), one primary output text|script. capability:{schemaVersion:1,operation:"text.generate",prompt:{"$input":"promptPort"},system:"instructions",maxTokens:1200,output:"outputPort"}. Prompt max 32000 characters, system max 8000, maxTokens integer 1..4096. Include 1..3 tests with concrete input and expectations:{outputPort:{kind:"text" or "script",minLength:1,maxLength:12000,includes:["optional required literal"]}} instead of exact expected text. Hosted behavior tests require a separately confirmed model/payment quote; never claim they ran while authoring. Example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.script-draft","version":"1.0.0","name":"Draft a short script","description":"Turn a production brief into a short narration draft using an approved text-generation call.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"production_brief","required":true,"description":"Production brief that guides the narration draft."}},"outputs":{"script":{"type":"script","primary":true,"semantic":"narration_script","description":"A concise narration script based on the supplied brief."}},"permissions":{"project":[],"capabilities":["text.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"prompt"}]},"capability":{"schemaVersion":1,"operation":"text.generate","prompt":{"$input":"prompt"},"system":"Write a concise narration script. Return only the script text.","maxTokens":1200,"output":"script"},"tests":[{"name":"A short opening narration","input":{"prompt":"Write two narration sentences about a quiet forest at sunrise."},"expectations":{"script":{"kind":"script","minLength":20,"maxLength":6000}}}]}.
7382
+
7383
+ ### Hosted media operations
7384
+
7385
+ For audio transcription, use runtime:"capability", operation:"audio.transcribe" and permissions.capabilities:["audio.transcribe"]. Declare one primary audio input with an asset control, one text language input defaulting to "auto", and one primary transcript output. Bind the two inputs through capability.audio and capability.language. Provider, model, payment and credentials belong to the host. Fixtures use transcript word-count bounds, never invented provider text. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.transcribe-audio","version":"1.0.0","name":"Transcribe audio","description":"Turn one persistent audio file into reviewed text with word timings.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"audio":{"type":"audio","primary":true},"language":{"type":"text","default":"auto"}},"outputs":{"transcript":{"type":"transcript","primary":true}},"permissions":{"project":[],"capabilities":["audio.transcribe"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"audio"},{"control":"select","port":"language","options":["auto","en","es","fr","de","it","pt","ja","ko","zh"]}]},"capability":{"schemaVersion":1,"operation":"audio.transcribe","audio":{"$input":"audio"},"language":{"$input":"language"},"output":"transcript"},"tests":[{"name":"timed speech","input":{"audio":{"kind":"audio","assetId":"audio-1","versionId":"version-1","url":"/api/media/example/audio.mp3","mimeType":"audio/mpeg"},"language":"en"},"expectations":{"transcript":{"kind":"transcript","minWords":1,"maxWords":1000}}}]}.
7386
+ For narration or speech synthesis, use runtime:"capability", operation:"audio.speech" and permissions.capabilities:["audio.speech"]. One required primary text or script input feeds capability.text via {$input:"portName"}; a script value contains schemaVersion:1 and text. One primary audio output is named by capability.output. Text must be at most 3800 characters. The host owns the voice, model, provider, billing and BYOK. Do not put those choices in package code. Tests use audio expectations (WAV, duration and byte bounds), never invented exact audio. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.narrate-script","version":"1.0.0","name":"Narrate a script","description":"Turn a short script into a reviewed speech recording using a host-selected voice.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"script":{"type":"script","primary":true,"semantic":"narration_script","required":true,"description":"Narration script to render as speech."}},"outputs":{"audio":{"type":"audio","primary":true,"semantic":"speech_audio","description":"Speech audio generated from the narration script."}},"permissions":{"project":[],"capabilities":["audio.speech"],"network":[],"filesystem":[],"secrets":[]},"ui":[]},"capability":{"schemaVersion":1,"operation":"audio.speech","text":{"$input":"script"},"output":"audio"},"tests":[{"name":"Read a short welcome","input":{"script":{"schemaVersion":1,"text":"Welcome to the quiet forest."}},"expectations":{"audio":{"kind":"audio","format":"wav","minDuration":0.1,"maxDuration":30,"minBytes":46,"maxBytes":1500000}}}]}.
7387
+ For image generation, use runtime:"capability", operation:"image.generate" and permissions.capabilities:["image.generate"]. One required primary text input feeds capability.prompt via {$input:"portName"}; one primary image output is named by capability.output. Prompt limit 32000 characters. The supported output is PNG 1024x1024, at most 12000000 bytes. Tests use image expectations, never fabricated generated URLs or pixels. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.generate-image","version":"1.0.0","name":"Generate an image","description":"Generate a square PNG from a short visual prompt using an approved host request.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"image_prompt","required":true,"description":"Visual description of the image to generate."}},"outputs":{"image":{"type":"image","primary":true,"semantic":"generated_image","description":"The generated square PNG image."}},"permissions":{"project":[],"capabilities":["image.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"prompt"}]},"capability":{"schemaVersion":1,"operation":"image.generate","prompt":{"$input":"prompt"},"output":"image"},"tests":[{"name":"A simple forest illustration","input":{"prompt":"A simple illustration of a quiet forest with clear dark outlines and soft green colors."},"expectations":{"image":{"kind":"image","format":"png","width":1024,"height":1024,"minBytes":67,"maxBytes":12000000}}}]}.
7388
+ For image analysis, use runtime:"capability", operation:"image.describe" and permissions.capabilities:["image.describe"]. Declare one primary image input (or one image[] input of one to four images to compare together) and one primary text output. capability.image references the input; capability.instruction contains bounded package-owned analysis instructions; capability.maxTokens is 1–4096; capability.output names the text output. The host loads only owned media and controls provider, model, payment and approval. Tests use bounded RGBA pixels and nondeterministic text expectations, never invented live descriptions. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.describe-image","version":"1.0.0","name":"Describe an image","description":"Turn one owned image into concise production-ready visual notes.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"image":{"type":"image","primary":true,"semantic":"source_image","required":true,"description":"Owned image to inspect for visible production details."}},"outputs":{"description":{"type":"text","primary":true,"role":"visual_description","semantic":"visual_description","description":"Concise production notes based only on visible image details."}},"permissions":{"project":[],"capabilities":["image.describe"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"image"}]},"capability":{"schemaVersion":1,"operation":"image.describe","image":{"$input":"image"},"instruction":"Describe only visibly supported subject matter, composition, lighting, color, texture, camera feel and visual style. Return concise production-ready plain text.","maxTokens":700,"output":"description"},"tests":[{"name":"Blue image","input":{"image":{"width":1,"height":1,"data":[20,80,200,255]}},"expectations":{"description":{"kind":"text","minLength":20,"maxLength":32768,"includes":["blue"]}}}]}.
7389
+ For AI-directed editing, use runtime:"capability", operation:"timeline.propose" and permissions.capabilities:["timeline.propose"]. Declare one current timeline input, up to three additional typed context inputs, and one timeline-edit output. Bind every input exactly once through capability.context; declare context.timeline.read and timeline.propose. StillMade controls the provider, strict JSON validation, stale-before checks, preview and Apply. Tests declare timeline-edit command bounds, never fabricated provider results. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.timeline-polish","version":"1.0.0","name":"Timeline polish","description":"Turn a timeline and editing direction into a bounded proposal that remains reviewable before Apply.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","context":"timeline","primary":true},"direction":{"type":"text","default":"Polish this cut conservatively."}},"outputs":{"edit":{"type":"timeline-edit","primary":true}},"permissions":{"project":["context.timeline.read","timeline.propose"],"capabilities":["timeline.propose"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"direction"}]},"capability":{"schemaVersion":1,"operation":"timeline.propose","context":{"timeline":{"$input":"timeline"},"direction":{"$input":"direction"}},"instruction":"Use only supported timeline-edit commands, exact before snapshots, and the current activeTimelineId. Preserve media and locked items. Return strict JSON only.","maxTokens":2000,"output":"edit"},"tests":[{"name":"one restrained edit","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4}],"audioItems":[],"captions":[]},"direction":"Add a soft transition."},"expectations":{"edit":{"kind":"timeline-edit","minCommands":1,"maxCommands":10}}}]}.
7390
+ For video generation use runtime:"capability", operation:"video.generate", permissions.capabilities:["video.generate"], one primary text input and one primary video output. capability.prompt references the input and capability.output names the output. Prompt limit 32000. Tests declare MP4 duration, dimensions and byte bounds; never fabricate video URLs. The enabled host selects its supported model, duration, resolution, payment and approved quote; packages cannot choose provider settings or dispatch jobs. Shipped example: {"manifest":{"schemaVersion":1,"id":"example.generate-video","name":"Generate video","version":"1.0.0","sdkVersion":"0.1.0","description":"Turn a production prompt into a short video using an approved host provider.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"required":true}},"outputs":{"video":{"type":"video","primary":true}},"permissions":{"project":[],"capabilities":["video.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"port":"prompt","control":"text"}]},"capability":{"schemaVersion":1,"operation":"video.generate","prompt":{"$input":"prompt"},"output":"video"},"tests":[{"name":"Produces a bounded MP4 sample","input":{"prompt":"A paper boat floating across a calm blue pond. Gentle camera movement."},"expectations":{"video":{"kind":"video","format":"mp4","minDuration":1,"maxDuration":60,"minBytes":32,"maxBytes":67108864,"minWidth":1,"maxWidth":3840,"minHeight":1,"maxHeight":3840}}}]}.
7391
+ Each capability package performs one supported operation. A job that needs several operations is built from parts: return each operation as its own part Block, and StillMade groups the parts of one job into one Step (One job per Step); never invent a combined runtime. Hosted image, speech and video source can be statically validated while authoring, but real fixture/sample generation requires the user's subsequent model/payment selection and quote confirmation. Do not claim those calls ran. Deterministic image transformations should remain sandboxed recipe or JavaScript code when supported; do not generate a new image as a substitute for processing an existing one.
7392
+
7393
+ ### ComfyUI workflows
7394
+
7395
+ *When:* StillMade's builder adds this section when the request names ComfyUI, a checkpoint, safetensors or KSampler, or refines a ComfyUI Block.
7396
+
7397
+ For an explicitly requested ComfyUI workflow use runtime:"comfyui", entry:"src/comfyui.json", permissions.capabilities:["comfyui.execute"], and package fields {manifest,comfyui,tests,view?}. Reviewed core contracts: {"CheckpointLoaderSimple":{"inputs":{"ckpt_name":"checkpoint"},"outputs":["MODEL","CLIP","VAE"],"outputNode":false},"CLIPTextEncode":{"inputs":{"text":{"type":"STRING","maxLength":4000},"clip":"CLIP"},"outputs":["CONDITIONING"],"outputNode":false},"EmptyLatentImage":{"inputs":{"width":{"type":"INT","min":64,"max":1024,"step":8},"height":{"type":"INT","min":64,"max":1024,"step":8},"batch_size":{"type":"INT","min":1,"max":1,"step":1}},"outputs":["LATENT"],"outputNode":false},"KSampler":{"inputs":{"model":"MODEL","seed":{"type":"INT","min":0,"max":4294967295,"step":1},"steps":{"type":"INT","min":1,"max":40,"step":1},"cfg":{"type":"FLOAT","min":0,"max":20},"sampler_name":["euler","euler_ancestral","dpmpp_2m"],"scheduler":["normal","karras","simple"],"positive":"CONDITIONING","negative":"CONDITIONING","latent_image":"LATENT","denoise":{"type":"FLOAT","min":0,"max":1}},"outputs":["LATENT"],"outputNode":false},"VAEDecode":{"inputs":{"samples":"LATENT","vae":"VAE"},"outputs":["IMAGE"],"outputNode":false},"VAEEncode":{"inputs":{"pixels":"IMAGE","vae":"VAE"},"outputs":["LATENT"],"outputNode":false},"LoadImage":{"inputs":{"image":"upload"},"outputs":["IMAGE","MASK"],"outputNode":false},"ImageInvert":{"inputs":{"image":"IMAGE"},"outputs":["IMAGE"],"outputNode":false},"ImageScale":{"inputs":{"image":"IMAGE","upscale_method":["nearest-exact","bilinear","area","bicubic","lanczos"],"width":"INT","height":"INT","crop":["disabled","center"]},"outputs":["IMAGE"],"outputNode":false},"PreviewImage":{"inputs":{"images":"IMAGE"},"outputs":["IMAGE"],"outputNode":true}}. Object scalar descriptors are internal validation limits; prompt.inputs contains actual scalar values or [nodeId,outputIndex] connections, not those descriptors. Text/integer/number/image ports bind via {node,input,encoding:"scalar" or "uploaded-image"}. Every LoadImage filename is an empty placeholder bound to an image input. Output is PreviewImage images[0], bound by {node,collection:"images",index:0}. No custom nodes, Python, URLs or downloads. One KSampler, one installed safetensors checkpoint basename, batch 1, up to 40 steps, 1024 pixels per generation side, four megapixels across working image/latent nodes. VAEEncode inputs require sides >=64 and multiples of 8; unchanged original media stays intact. Do not invent installed model names: preserve the caller's supplied name, or return unsupported asking for an installed checkpoint name. Example shape only (replace its placeholder with the user's exact installed checkpoint): {"manifest":{"schemaVersion":1,"id":"example.comfy-text-to-image","name":"ComfyUI image generation","version":"1.0.1","sdkVersion":"0.1.0","description":"Generate an image with an installed checkpoint on your reviewed ComfyUI connection.","kind":"task","runtime":"comfyui","entry":"src/comfyui.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"image_prompt","required":true,"description":"Visual description used to generate the image."}},"outputs":{"image":{"type":"image","primary":true,"semantic":"generated_image","description":"The image generated from the prompt."}},"permissions":{"project":[],"capabilities":["comfyui.execute"]},"ui":[{"control":"text","port":"prompt"}]},"comfyui":{"schemaVersion":1,"prompt":{"1":{"class_type":"CheckpointLoaderSimple","inputs":{"ckpt_name":"your-model.safetensors"}},"2":{"class_type":"CLIPTextEncode","inputs":{"text":"","clip":["1",1]}},"3":{"class_type":"CLIPTextEncode","inputs":{"text":"","clip":["1",1]}},"4":{"class_type":"EmptyLatentImage","inputs":{"width":512,"height":512,"batch_size":1}},"5":{"class_type":"KSampler","inputs":{"model":["1",0],"seed":1,"steps":20,"cfg":7,"sampler_name":"euler","scheduler":"normal","positive":["2",0],"negative":["3",0],"latent_image":["4",0],"denoise":1}},"6":{"class_type":"VAEDecode","inputs":{"samples":["5",0],"vae":["1",2]}},"7":{"class_type":"PreviewImage","inputs":{"images":["6",0]}}},"inputs":{"prompt":{"node":"2","input":"text","encoding":"scalar"}},"outputs":{"image":{"node":"7","collection":"images","index":0}}},"tests":[{"name":"Generate one bounded image","input":{"prompt":"A small wooden boat beside a quiet lake."},"expectations":{"image":{"kind":"image","count":1,"width":512,"height":512,"maxBytes":16777216}}}]}. Samples use concrete inputs and expectations:{image:{kind:"image",count:1,width:512,height:512,maxBytes:16777216}}; never invent generated pixels or URLs. Authoring only checks source; fixtures/sample require the account's separately confirmed connection. Never claim backend availability, matching model hashes, GPU readiness or executed tests.
7398
+
7399
+ ### Project context
7400
+
7401
+ Project context is provided through declared input ports, not a global project API. Bind an input with context:"field" and its exact type from {"script":"script","transcript":"transcript","shots":"shot[]","shotPlan":"shot-plan","scenes":"scene[]","characters":"character[]","locations":"location[]","styles":"style[]","assets":"asset[]","files":"file[]","generations":"metadata[]","versions":"metadata[]","timeline":"timeline","editor":"timeline","canvas":"pipeline","blueprint":"bible","panels":"panel-document","board":"board-document","animation":"scene-document","metadata":"metadata"}; declare permissions.project:["context.field.read"] for each bound field (replace field with the actual name). The host supplies the user-selected scope and only authorized context; do not invent context fetch methods. Include concrete context inputs in fixtures. Read-only snapshots cannot be mutated to save project changes. Reuse existing Canvas characters, locations and styles for visual consistency. The SDK also supports optional Step-scoped memory and timeline-edit outputs ; request the corresponding contract only when the capability needs it.
7402
+
7403
+ ### Workspace proposals
7404
+
7405
+ *When:* StillMade's builder adds this section when the request is about a workspace, animation, canvas, blueprint, panels or board, or refines a Block with a workspace-edit output.
7406
+
7407
+ To propose changes to an existing blueprint, panels, board, canvas, or animation workspace, bind its matching context input ({"script":"script","transcript":"transcript","shots":"shot[]","shotPlan":"shot-plan","scenes":"scene[]","characters":"character[]","locations":"location[]","styles":"style[]","assets":"asset[]","files":"file[]","generations":"metadata[]","versions":"metadata[]","timeline":"timeline","editor":"timeline","canvas":"pipeline","blueprint":"bible","panels":"panel-document","board":"board-document","animation":"scene-document","metadata":"metadata"}). Declare context.FIELD.read and workspace.FIELD.propose in permissions.project. Return a primary workspace-edit output: {schemaVersion:1,title,field:"blueprint"|"panels"|"board"|"canvas"|"animation",before:theCompleteUnmodifiedInputDocument,after:theCompleteModifiedCopy}. Preserve unrelated fields and existing identities; Panels and Board retain strip items with panelId, Board also requires each item's type. Fixtures need concrete full before/after documents. These are reviewed proposals; the host checks current source and shared edit permission, then saves using collaboration and undo. Never claim runtime execution already saved it. Canvas proposals retain executable Step instances, host bindings, and connections touching those instances; change Steps through the Project Type builder. Only position, size, title and label may change on programmable nodes, with their host-owned execution state retained. Canvas viewport is local and unchanged. Animation proposals preserve shotId, start/end timing and voiceoverText for every scene record; modify its scene and versions. The collection and narration spine belong to Shots. Starting Chat writes are not supported by this proposal contract.
7408
+
7409
+ ### Step memory
7410
+
7411
+ *When:* StillMade's builder adds this section when the request needs memory, counting or persistence, or refines a stateful Block.
7412
+
7413
+ For memory use runtime:"javascript", manifest.state:{scope:"step",version:1,initial:{...}}, permissions.project including "state.step". Code receives mutable plain-object state separately from input. Mutate state and return only declared output bindings; never return a state envelope as an undeclared output. Each test needs expectedState:{version:1,value:{...}}; a test may supply state:{version:1,value:{...}} to verify continuation. Memory is bounded to 64 KiB and scoped to a placement. The host saves it only with accepted successful output; previews use disposable memory. No localStorage, files, global or account-shared state. Interactive selections support successive memory; unattended future-media execution is not supported for stateful Blocks. Preserve the state schema/version during refinement unless the user requests a schema change. For an explicit upgrade, the new version may declare manifest.state.migrations:[{fromVersion:1,operations:[{op:"rename",from:"oldField",to:"newField"},{op:"default",key:"labels",value:[]}]}], targeting its enclosing state.version (for example 2). Use only ordered top-level rename/default/remove operations; remove uses {op:"remove",key:"field"}. Renames require an existing source and absent destination, defaults never overwrite, removals require an existing field. At most 16 distinct older fromVersion routes, 64 operations each, and 64 KiB combined declarations and intermediate state. No chains, downgrade, arbitrary code or automatic application. Bump the Block version when source or migrations change, retain unrelated state, and never invent an old field layout. Owners preview and explicitly apply migrations to exact archived source-bound state through the project host; authoring does not mutate existing projects. New execution fixtures use the target state version. Exact example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.numbered-takes","version":"1.0.0","name":"Numbered takes","description":"Label successive creative takes with an independent counter for each production placement.","kind":"task","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"text":{"type":"text","primary":true,"semantic":"draft_text","required":true,"description":"Creative take text to label with the next number."}},"outputs":{"text":{"type":"text","primary":true,"semantic":"numbered_take","description":"The supplied take prefixed with its saved sequence number."}},"permissions":{"project":["state.step"]},"state":{"scope":"step","version":1,"initial":{"count":0}},"ui":[{"port":"text","control":"text"}]},"code":"state.count += 1; return {text: \"Take \" + state.count + \": \" + input.text.trim()};","tests":[{"name":"First take","input":{"text":" Opening scene "},"expected":{"text":"Take 1: Opening scene"},"expectedState":{"version":1,"value":{"count":1}}},{"name":"Continues saved numbering","input":{"text":"Alternate ending"},"state":{"version":1,"value":{"count":4}},"expected":{"text":"Take 5: Alternate ending"},"expectedState":{"version":1,"value":{"count":5}}}]}.
7414
+
7415
+ ### Editor timeline proposals
7416
+
7417
+ *When:* StillMade's builder adds this section when the request is about the Editor, timeline, clips, captions, trimming, splitting or volume, or refines a Block with a timeline-edit output.
7418
+
7419
+ For Editor changes return a primary output {type:"timeline-edit"} and declare "timeline.propose" plus "context.timeline.read". Bind timeline input {type:"timeline",context:"timeline"}. A proposal is {schemaVersion:1,title,timelineId:input.timeline.activeTimelineId,commands:[...]}; 1..100 commands, exactly one per target. Command shape is exactly {collection,operation,before,values}. For collections clips or audioItems, before is the complete unmodified source item; operations and exact values are move:{start}, trim:{trimStart,duration}, volume:{volume} (0..200), mute:{muted}, remove:{}, split:{at,newId}. Split at is an absolute timeline time strictly inside the item; newId must be unused. For captions: caption.add has before:null and values:{id,trackId,text,start,end}; caption.update has the complete original before and values:{text,start,end}; remove has complete original before and values:{}. Caption text must be nonempty and at most 5000 characters, end greater than start. Use an unlocked existing caption track or default trackId:"captions" if absent. IDs must be unique across all timeline lanes. Preserve source snapshots, locked tracks, bounds and existing item identities. No arbitrary media, effect insertion, track mutation or direct project writes. The user reviews before/after and applies proposals through Editor undo; do not claim a proposal has already changed the project. Exact examples: [{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.quieter-clips","version":"1.0.0","name":"Quieter clips","description":"Propose a lower volume for existing video clips, then review and apply the changes in the Editor.","kind":"editor-extension","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","primary":true,"context":"timeline","semantic":"timeline","required":true,"description":"Current Editor timeline containing the clips to adjust."},"volume":{"type":"number","min":0,"max":100,"default":50,"semantic":"clip_volume_percent","required":false,"description":"Target volume percentage for each video clip."}},"outputs":{"edit":{"type":"timeline-edit","primary":true,"semantic":"timeline_edit","description":"A reviewable proposal that changes clip volume."}},"permissions":{"project":["context.timeline.read","timeline.propose"]},"ui":[{"port":"volume","control":"slider"}]},"code":"if (!input.timeline.clips.length) throw new Error(\"Add clips in the Editor first\"); return {edit:{schemaVersion:1,title:\"Lower clip volume\",timelineId:input.timeline.activeTimelineId,commands:input.timeline.clips.slice(0,100).map(clip=>({collection:\"clips\",operation:\"volume\",before:clip,values:{volume:input.volume}}))}};","tests":[{"name":"Propose without changing the source","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-01","clips":[{"id":"clip-1","trackId":"video","name":"Opening","start":0,"duration":3,"sourceDuration":3,"volume":100}]},"volume":50},"expected":{"edit":{"schemaVersion":1,"title":"Lower clip volume","timelineId":"timeline-01","commands":[{"collection":"clips","operation":"volume","before":{"id":"clip-1","trackId":"video","name":"Opening","start":0,"duration":3,"sourceDuration":3,"volume":100},"values":{"volume":50}}]}}}]},{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.caption-from-text","version":"1.0.0","name":"Caption from text","description":"Turn a short line of text into a timed caption proposal for the Editor.","kind":"editor-extension","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","context":"timeline","semantic":"timeline","required":true,"description":"Current Editor timeline that will receive the caption."},"text":{"type":"text","primary":true,"semantic":"caption_text","required":true,"description":"Text to place in the new caption."},"start":{"type":"number","min":0,"max":86300,"default":0,"semantic":"start_time_seconds","required":false,"description":"Timeline time in seconds at which the caption begins."},"duration":{"type":"number","min":0.05,"max":100,"default":3,"semantic":"duration_seconds","required":false,"description":"Number of seconds the caption remains visible."}},"outputs":{"edit":{"type":"timeline-edit","primary":true,"semantic":"timeline_edit","description":"A reviewable proposal that adds the timed caption."}},"permissions":{"project":["context.timeline.read","timeline.propose"]},"ui":[{"port":"text","control":"text"},{"port":"start","control":"number"},{"port":"duration","control":"number"}]},"code":"const text=input.text.trim();if(!text)throw new Error(\"Enter caption text\");const ids=new Set([...(input.timeline.clips||[]),...(input.timeline.audioItems||[]),...(input.timeline.captions||[]),...(input.timeline.textOverlays||[]),...(input.timeline.stickers||[]),...(input.timeline.effects||[])].map(item=>item.id));let n=1;while(ids.has(\"block-caption-\"+n))n++;const track=(input.timeline.tracks||[]).find(track=>track.type===\"caption\"&&!track.locked);return {edit:{schemaVersion:1,title:\"Add caption\",timelineId:input.timeline.activeTimelineId,commands:[{collection:\"captions\",operation:\"caption.add\",before:null,values:{id:\"block-caption-\"+n,trackId:track?track.id:\"captions\",text,start:input.start,end:input.start+input.duration}}]}};","tests":[{"name":"Create a timed caption","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-01","clips":[],"captions":[],"tracks":[]},"text":"A new beginning","start":0,"duration":3},"expected":{"edit":{"schemaVersion":1,"title":"Add caption","timelineId":"timeline-01","commands":[{"collection":"captions","operation":"caption.add","before":null,"values":{"id":"block-caption-1","trackId":"captions","text":"A new beginning","start":0,"end":3}}]}}}]}].
7420
+
7421
+ ### Descriptions
7422
+
7423
+ Write the description in third-grade language: one short sentence, ideally 8–14 words and never more than 20. Use common words and say what the person can make or do. Start with an action. Leave out technical terms such as typed ports, scoped context, artifacts, pipeline, runtime, and handoff. Do not list every feature, repeat the name, add marketing claims, or say this Block lets you. Keep essential limits and describe only what the code actually does. For example: Cut clips, add sound and text, then save your video. Apply these No AI Slop editing rules to the description prose only; keep the required response format, code, and identifiers intact. Write for a person choosing a Block or Project Type. Use familiar words, direct verbs, and details supported by the saved code. Preserve useful limits and clear existing sentences. Use “version” or “saved version” instead of “release”; use “Public”, “Private” and “Draft” for visibility and readiness. Call release notes “What changed”. Check each sentence: could it describe almost any app? If so, replace it with a specific supported action or remove it. Never invent examples, numbers, features, reviews, or results to sound more specific. Cut hype, vague benefits, dramatic reveals, fake expert claims, rhetorical questions, introductions that delay the point, and closing lines that repeat the paragraph. Avoid phrases such as game changer, cutting-edge, unlock your potential, seamless, powerful solution, leverage, empower, streamline, and elevate. Say what the person can do. State the useful fact directly instead of using not-X-but-Y contrasts. Use complete sentences, with no decorative dashes, emoji, or emphasis. Keep names consistent; do not cycle through synonyms for a Block. Avoid repeated sentence shapes and forcing every listing into an identical template. Use search terms only where they describe the real task. Do not repeat keywords or stretch the copy to hit a word or sentence count. Before returning the copy, reread it for factual accuracy, simple language, specific detail, natural rhythm, and filler. Fix any failures silently and return only the requested output.
7424
+
7425
+ ### Generated controls first
7426
+
7427
+ *When:* StillMade's builder adds this section when the person did not ask for a custom interface.
7428
+
7429
+ Optional custom view interfaces are supported, but generated manifest.ui controls are sufficient unless the user asks for a custom interface.
7430
+
7431
+ ### Custom interfaces
7432
+
7433
+ *When:* StillMade's builder adds this section when the person asks for an interface, preview or controls, or refines a Block that has one.
7434
+
7435
+ A custom interface is the optional package-level view:{html,css?,themedCss?,javascript?}; when refining, preserve and refine base.view. Design it by StillMade's interface principles, in order: Function first: unless the person asks for a design direction, build from the Block kit (`sm-block`, `sm-stage`, `sm-choices`, `sm-actions`, `sm-more`) and ship little or no CSS of your own. A direction the person names wins over the kit. Show the thing being made: the preview, media or result gets the largest area (`sm-stage`). Never a JSON dump, source editor, manifest or diagnostic panel unless the task is about source. One obvious next action (`sm-primary`); peers are quiet buttons or named icons. Seven or fewer choices in view. The action button names what it does ("Write", "Generate image", "Make video"), never "Run", "Run step", "Submit" or "Review cost". Show no credit numbers: StillMade puts the price beside the verb on its own run button ("Write · 1 credit"), with a chip for the model, quality or count when they can change. Pressing it runs at that price; a confirmation appears only when the server price is higher. Pictures over words: choices people see or hear are thumbnails, swatches, samples or glyphs (`sm-choices`), not word lists. Icons carry an accessible name and tooltip; never emoji. Words only when needed. Drop what nobody needs to finish the task (intros, "how it works", restating the Block name). Words that might help start collapsed (`sm-hint`, `sm-more`). Errors, spending approvals, stale inputs and destructive steps stay visible, in few words. Strong defaults; secondary settings go in `sm-more`, closed. Compact: the whole Block fits one laptop screen without scrolling. A short setting is one row, its label beside the control; no card around a single control. Status is a dot or a few words ("No narration yet"), never a paragraph; the reason sits behind an `sm-hint`. Files are tiles: a saved file is a thumbnail or chip and adding one is a + tile. Never the bare browser file picker, and no size limits in view until a file is too big. Rare tools (update, schedule, history, reload) are named icon buttons beside the action, never sections under the work. One workspace: no cards inside cards, no second title, step counter or Back/Next bar; the host owns navigation. Status sits beside the action it describes (`sm-status`), in a few words. Keep the page in normal scroll flow; no sticky media or control strips unless the editing task needs them. Text scene descriptions are planning notes, never a storyboard or an approved frame. Its DOM controller is separate from manifest runtime code. The view may also declare appearance:"original" or "host". For an imported design, preserve its colors with appearance:"original" and view.css; no themed replacement is required. Preserve base.view.appearance when refining unless the user requests a change. Omitted/host appearance retains the existing Light, Dark and White requirement: supply semantic colors in view.css or a complete replacement view.themedCss. An original-style view may optionally supply valid themedCss for a StillMade appearance preview. Use StillMade variables (--bg,--fg,--fg-muted,--border-tok,--r-3) only when opting into host styling; do not redefine host tokens in the themed variant. Appearance never relaxes sandbox constraints: no CSS escapes/filters/blending, imports/url(), or element.style mutations in JS. Use classes for UI state; media pixels retain their colors. Both CSS fields have the same 32 KiB limit; all existing shared-state, security and permission checks still apply. Declare elements in html, bind with document.getElementById/querySelector and addEventListener; use literal property access, array methods, textContent, input.value, button.disabled and canvas. Initial values: StillMade.onInput(values=>{...}); await StillMade.run(inputBindings) returns validated output bindings from the isolated package runtime; catch errors and show results. No computation in the DOM controller; put it in code/recipe. Disable the action button while pending.
7436
+ Standard custom view code (no `view.config.json` frame runtime) is checked before it runs. These rules come straight from the validator; frame views run in their own sandbox and may build the page with ordinary DOM calls such as `createElement`:
7437
+
7438
+ - **HTML elements:** only `div`, `section`, `article`, `header`, `footer`, `main`, `aside`, `nav`, `h1`, `h2`, `h3`, `h4`, `p`, `span`, `strong`, `em`, `i`, `small`, `br`, `hr`, `label`, `input`, `textarea`, `button`, `select`, `option`, `optgroup`, `fieldset`, `legend`, `output`, `progress`, `meter`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `figure`, `figcaption`, `img`, `video`, `audio`, `source`, `canvas`, `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`, `pre`, `code`, `details`, `summary`. No scripts, iframes, SVG, forms, comments or inline event attributes; media URLs must be `data:` or `blob:`.
7439
+ - **No loops and no function declarations:** view JavaScript may not contain `import()`, `import`, `with`, `this`, `import.meta`/`new.target`, `while`, `do`/`while`, `for`, `for…in`, `for…of`, `function` declarations. Iterate with array `map`/`forEach`/`filter`/`reduce` and write helpers as `const name = (...) => ...`.
7440
+ - **Never use these names in view JavaScript, not even for your own variables:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
7441
+ - **Never use these property names:** `assign`, `values`, `entries`, `fromEntries` (so no `Object.assign`, `Object.values`, `Object.entries`, `Object.fromEntries`; use arrays and `map`/`forEach`).
7442
+ - **These words may not appear anywhere in view JavaScript, even in strings or comments:** `style`, `cssText`, `setProperty`, `removeProperty`, `insertRule`, `deleteRule`, `styleSheets`, `adoptedStyleSheets`, `animate`. Change appearance with CSS classes and `hidden`; put colors in the stylesheet.
7443
+ - **Property access with brackets needs a literal key:** `items[0]` and `record["title"]` are accepted; `items[index]` is rejected. Iterate with `map`/`forEach` or call `items.at(index)`.
7444
+ - **No named recursive functions** and no listeners on the global frame; attach events to declared controls.
7445
+ - **Dynamic elements:** `StillMade.render` can create `div`, `section`, `article`, `header`, `footer`, `p`, `span`, `strong`, `em`, `i`, `small`, `h1`, `h2`, `h3`, `h4`, `label`, `button`, `input`, `textarea`, `select`, `option`, `ul`, `ol`, `li`, `table`, `thead`, `tbody`, `tr`, `th`, `td`, `details`, `summary`, `output`, `progress`, `img`; give repeated items stable IDs derived from their identity. An `img` gets its picture when you set its `src` to the data URL `StillMade.previewImage` returns, after rendering; the renderer keeps Block-owned `src`. `previewImage` works on items of an `image[]` input.
7446
+ - **Shared writes are queued in order:** if one `StillMade.updateShared` write is rejected (conflict, read-only or timeout), the writes queued behind it are rejected too. Send dependent edits one at a time or through `StillMade.scheduleSharedEdit`, keep the person's draft, and retry from the latest `onShared` value. `{merge: true}` merges independent fields of an object value; create an absent field with the precondition `{exists: false}`.
7447
+ - **Limits:** HTML 64 KiB, CSS 32 KiB, JavaScript 64 KiB; 30 runs and 60 image previews per minute.
7448
+ ### Shared project interfaces
7449
+ Every meaningful edit and generated result must be visible to collaborators.
7450
+ Prefer ordinary manifest.ui controls and declared outputs when those express
7451
+ the interaction. Repository-local DOM state is not automatically shared.
7452
+ For a custom view, observe StillMade.onShared(shared => ...) to render saved
7453
+ outputs (including another participant's result), interface state and canEdit.
7454
+ For an ordinary static control, add a unique id and
7455
+ data-stillmade-share="fieldName"; the host owns its complete synchronization
7456
+ adapter. Use StillMade.bindShared(fieldName, elementId) only for a control created
7457
+ or replaced dynamically. Account for every ordinary static control; mark a truly
7458
+ device-only playback or filter control with data-stillmade-local.
7459
+ Use StillMade.setSharedDefaults for input-derived defaults without replacing a
7460
+ dirty draft. Do not overwrite bound control values during remote rendering or
7461
+ replace a bound input during a gesture. For structured interface values, use
7462
+ bounded StillMade.updateShared patches with stable named fields; observe them
7463
+ through onShared instead of keeping the only copy in a private variable.
7464
+ For nested objects, updateShared(values, exactBasePreconditions, {merge:true})
7465
+ can merge independent fields and arrays with stable string item IDs. Always use
7466
+ the actual base used to construct the edit. Overlapping changes still conflict;
7467
+ this does not merge simultaneous text typing or expand the 384 KiB state limit.
7468
+ Requests including before-values are limited to 800,000 UTF-8 bytes; queued and
7469
+ in-flight requests share a 2 MiB budget. Preserve drafts when a limit is reached.
7470
+ Give dynamic rows and controls stable IDs derived from item identity, not array
7471
+ position. StillMade.render preserves matching nodes, focus and selection across
7472
+ updates; it does not merge conflicting values or preserve an unshared draft.
7473
+ Never dispatch a run, click or paid integration in response to a remote update.
7474
+ Respect read-only access, preserve conflicting drafts, and surface write errors.
7475
+ Only explicit user actions may run this Block. Shared acknowledgements are
7476
+ locally queued, not proof of a durable server save. Preview localOnly state is
7477
+ not proof of multiplayer operation. Test two interfaces exchanging actual edits
7478
+ and saved outputs, permission loss, reconnect and conflicts before claiming
7479
+ collaboration support. Never claim arbitrary imported JavaScript state syncs
7480
+ merely because the package passed its computation fixtures.
7481
+ Example view for a package taking and returning text: {"html":"<main class=\"sm-block\"><output class=\"sm-stage\" id=\"result\" aria-live=\"polite\"></output><div class=\"sm-field\"><label for=\"source\">Draft</label><textarea id=\"source\"></textarea></div><div class=\"sm-actions\"><button class=\"sm-primary\" id=\"run\">Clean text</button><p class=\"sm-status\" id=\"status\" role=\"status\"></p></div></main>","css":"#result{place-items:start;min-height:120px;white-space:pre-wrap;overflow-wrap:anywhere}","javascript":"const source=document.getElementById('source'),button=document.getElementById('run'),result=document.getElementById('result'),status=document.getElementById('status');\nlet busy=false;\nStillMade.bindShared('draft','source');\nStillMade.onInput(input=>{StillMade.setSharedDefaults({draft:input.text||''});});\nStillMade.onShared(shared=>{button.disabled=busy||!shared.canEdit;result.textContent=shared.outputs.text||'';});\nbutton.addEventListener('click',async()=>{\n if(busy||!StillMade.getShared().canEdit)return;\n busy=true;button.disabled=true;status.dataset.state='loading';status.textContent='Cleaning';\n try{await StillMade.run({text:source.value});status.dataset.state='success';status.textContent='Ready';}\n catch(error){status.dataset.state='error';status.textContent=error.message||'Could not clean this draft.';}\n finally{busy=false;button.disabled=!StillMade.getShared().canEdit;}\n});"}.
7482
+
7483
+ <!-- authoring-guide:end -->
7484
+
7299
7485
  ### Build new Blocks directly with the SDK
7300
7486
 
7301
7487
  When the user supplies an idea (for example, a photo editor) and this guide, build
@@ -7459,11 +7645,12 @@ those frames. Do not dispatch paid calls or replace pins merely to exercise an u
7459
7645
 
7460
7646
  ### Enforcement versus conventions
7461
7647
 
7462
- The procedure above and `INTERFACE_AUTHORING_RULES` are mandatory instructions for
7463
- authors, not a claim that the SDK can automatically inspect every design choice.
7464
- `INTERFACE_DESIGN_RULES` lists implemented static admission checks separately;
7465
- `INTERFACE_DESIGN_CONVENTIONS` contains recommendations. Passing checks does not
7466
- prove useful visuals, unclipped scrolling, or a working end-to-end workflow.
7648
+ The procedure above and `BLOCK_INTERFACE_PRINCIPLES` (see
7649
+ [Block interface design](/docs/build/interface-design), which starts every interface from
7650
+ the Block kit) are mandatory instructions for authors, not a claim that the SDK
7651
+ can automatically inspect every design choice. `INTERFACE_DESIGN_RULES` lists
7652
+ implemented static admission checks separately. Passing checks does not prove
7653
+ useful visuals, unclipped scrolling, or a working end-to-end workflow.
7467
7654
 
7468
7655
  ### Host-controlled exports
7469
7656
 
@@ -7884,7 +8071,8 @@ exact permission, such as `context.versions.read`.
7884
8071
  ### Controls
7885
8072
 
7886
8073
  `ui` is an array of `{ "control": "...", "port": "inputName" }` objects.
7887
- Control must be `text`, `number`, `slider`, `checkbox`, or `asset`, bound to an
8074
+ Control must be one of `text`, `number`, `slider`, `checkbox`, `asset`, `select`,
8075
+ `multiselect`, `file`, `date`, `color`, `url`, `richtext`, `list` or `grid`, bound to an
7888
8076
  existing input. Labels come from port names/descriptions. For a numeric slider,
7889
8077
  give its input `type`, `min`, `max`, and `default`. Use `asset` for an image input.
7890
8078
  For a custom visual interface, add the optional `view` object below. Do not supply React components, external dependencies, or a hosted UI URL.
@@ -7899,10 +8087,11 @@ fallback controls schema; it is not the custom interface source.
7899
8087
 
7900
8088
  <!-- view-rules:start (generated by npm run build:sdk; do not edit) -->
7901
8089
 
7902
- Custom view code is checked before it runs. These rules come straight from the validator:
8090
+ Standard custom view code (no `view.config.json` frame runtime) is checked before it runs. These rules come straight from the validator; frame views run in their own sandbox and may build the page with ordinary DOM calls such as `createElement`:
7903
8091
 
7904
8092
  - **HTML elements:** only `div`, `section`, `article`, `header`, `footer`, `main`, `aside`, `nav`, `h1`, `h2`, `h3`, `h4`, `p`, `span`, `strong`, `em`, `i`, `small`, `br`, `hr`, `label`, `input`, `textarea`, `button`, `select`, `option`, `optgroup`, `fieldset`, `legend`, `output`, `progress`, `meter`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `figure`, `figcaption`, `img`, `video`, `audio`, `source`, `canvas`, `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`, `pre`, `code`, `details`, `summary`. No scripts, iframes, SVG, forms, comments or inline event attributes; media URLs must be `data:` or `blob:`.
7905
- - **Never use these names in view JavaScript:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
8093
+ - **No loops and no function declarations:** view JavaScript may not contain `import()`, `import`, `with`, `this`, `import.meta`/`new.target`, `while`, `do`/`while`, `for`, `for…in`, `for…of`, `function` declarations. Iterate with array `map`/`forEach`/`filter`/`reduce` and write helpers as `const name = (...) => ...`.
8094
+ - **Never use these names in view JavaScript, not even for your own variables:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
7906
8095
  - **Never use these property names:** `assign`, `values`, `entries`, `fromEntries` (so no `Object.assign`, `Object.values`, `Object.entries`, `Object.fromEntries`; use arrays and `map`/`forEach`).
7907
8096
  - **These words may not appear anywhere in view JavaScript, even in strings or comments:** `style`, `cssText`, `setProperty`, `removeProperty`, `insertRule`, `deleteRule`, `styleSheets`, `adoptedStyleSheets`, `animate`. Change appearance with CSS classes and `hidden`; put colors in the stylesheet.
7908
8097
  - **Property access with brackets needs a literal key:** `items[0]` and `record["title"]` are accepted; `items[index]` is rejected. Iterate with `map`/`forEach` or call `items.at(index)`.
@@ -8417,7 +8606,11 @@ Implement the requested instructions and fixtures, choose a creator namespace,
8417
8606
  and preserve the contract. `capability` has schemaVersion, operation,
8418
8607
  prompt, system, maxTokens, output, and optionally `context` (a list of up to three
8419
8608
  other read-only input names whose values are sent after the prompt as delimited
8420
- data; unlisted inputs are never sent). `operation` is only `text.generate`; `prompt`
8609
+ data; unlisted inputs are never sent). Each `context` input reads a project field
8610
+ (its port declares `"context":"FIELD"`), so it is not something the person types.
8611
+ Text the person types goes in the one prompt input, plus at most one other text
8612
+ input named by `prompt.$instructions`; a view with several fields joins them into
8613
+ the prompt input. `operation` is only `text.generate`; `prompt`
8421
8614
  is one named `$input` reference. One prompt input: text, script, or brief. One output:
8422
8615
  text or script. A script/brief input requires {schemaVersion:1,text}; a generated
8423
8616
  script uses that shape too. Prompt max 32,000 characters, system max 8,000,
@@ -9139,11 +9332,14 @@ Hosted text and speech require their separate [text review](/docs/build/hosted-t
9139
9332
  ## Download the StillMade SDK
9140
9333
 
9141
9334
  ### SDK 0.1.0
9142
- [Download the complete SDK](/block-sdk/stillmade-sdk-0.1.0.zip), or install the same files as an npm package (its dependencies ship inside it, so no registry is needed):
9335
+ Install it from npm into any folder or project (Node.js 22 or later; its dependencies ship inside the package):
9143
9336
  ```sh
9144
- npm install --save-dev https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.tgz
9337
+ npm install --save-dev stillmade-sdk
9145
9338
  npx stillmade-block create --list
9146
9339
  ```
9340
+ Run every CLI command as `npx stillmade-block <command>`. Coding agents should read `node_modules/stillmade-sdk/AGENTS.md` before writing a Block. The npm version can be newer than SDK 0.1.0: patch releases repackage the same SDK, and Blocks still declare `sdkVersion` 0.1.0.
9341
+
9342
+ Without npm, [download the complete SDK](/block-sdk/stillmade-sdk-0.1.0.zip) and run the CLI from the extracted folder as `node packages/block-cli/cli.js <command>`. The same package is also served at `https://stillmade.shop/block-sdk/stillmade-sdk-0.1.0.tgz` for installs that cannot reach the npm registry.
9147
9343
  Includes the validators, recipe interpreter, isolated JavaScript runtime and WASM, pinned runtime dependencies and licenses, CLI, TypeScript declarations, examples, and documentation. Requires Node.js 22 or later for local use. No npm installation is required after extraction.
9148
9344
 
9149
9345
  Every finished Block must pass **Remix readiness**. Include editable source, typed I/O, controls, tests and complete license evidence. Remix works automatically through the same package contract; no custom callback or app wiring is needed. CLI validation, builder completion and import enforce it. See [the required remix contract](/docs/tools/block-remix).
@@ -9301,89 +9497,173 @@ gate; they are not inferred from an author claim.
9301
9497
 
9302
9498
  # Block interface design
9303
9499
 
9304
- Simplicity first: show the work and the next useful action. The app owns project navigation; a Block owns its production surface. Neither needs a second shell.
9305
-
9306
- ### Mandatory authoring rules — for LLMs and creators
9307
-
9308
- These are requirements, not optional conventions. Authors must follow them;
9309
- they are not all machine-enforced. Explicit user requests may authorize the
9310
- named exception, but do not authorize unrelated technical UI.
9311
-
9312
- - Do not add Technical result, Result details, JSON dumps, source editors,
9313
- manifests, debug consoles, or diagnostic panels to a production workspace
9314
- unless the user explicitly requests that surface or the stated task is
9315
- source/debug inspection. A collapsed disclosure is still an added surface.
9316
- - Do not nest decorative cards, bordered panels, or modal shells around the
9317
- same work area. Use one workspace with spacing and labels. Nested containers
9318
- needed for layout are fine; repeated visible framing is not.
9319
- - Do not duplicate the host's step navigation, counters, or workspace identity.
9320
- - Do not append a generic Workflow tools menu to production Blocks. Batch,
9321
- loop, schedule, memory, migration, and diagnostic tools are not default
9322
- workspace chrome; add a specific surface only when explicitly requested.
9323
- - Show the useful output itself, not a technical representation of the output.
9324
- - Keep required approvals, errors, progress, and stale-input warnings visible.
9325
- These are operational feedback, not optional diagnostic panels.
9326
- - Do not lock page scrolling or introduce sticky headers unless the task
9327
- explicitly needs them. A production page must keep all controls reachable.
9328
- The host owns page scrolling; embedded workspaces may scroll their own
9329
- content, but must not trap scrolling or clip controls below the viewport.
9330
- - Do not keep media galleries, asset inputs, or control strips sticky by default.
9331
- They belong to the same scrolling task surface. A fixed timeline/inspector is
9332
- appropriate only when the actual editing task requires that layout, with all
9333
- controls still reachable; it is not a default for every Block.
9334
- - Do not add an ambiguous settings gear for task actions. Show the immediate
9335
- action directly; label secondary task controls by purpose. Do not disguise
9336
- source, wiring, or debug controls as ordinary production settings.
9337
-
9338
- For an existing Block, apply the [SDK update procedure](/block-sdk/PASTE_TO_LLM.md#updating-existing-blocks-through-the-sdk)
9339
- to the actual owned source and a new release/remix. These requirements do not
9340
- authorize rewriting published packages or silently repinning saved projects.
9341
-
9342
- ### Rules — implemented enforcement
9343
-
9344
- These are implementation facts, not aesthetic suggestions.
9345
-
9346
- - The host supplies the Project Type step bar. Production workspaces do not add another step counter or Back/Next bar.
9347
- - For custom views the host omits its duplicate title and outer frame border. Sandbox isolation remains enabled.
9348
- - The host does not append a generic Technical result disclosure to custom workspaces. Stored outputs and required approval actions are preserved.
9349
- - Shared form containment wraps adjacent labeled controls and keeps checkboxes compact.
9350
- - Admission rejects a primary textarea with 12 or more rows when no task presentation is detected. This is a narrow heuristic, not proof of a usable visual workspace: `StillMade.render` can render text cards.
9351
- - HTML, JavaScript, permission, and appearance validation remain separate checks. Simplicity never bypasses safety or approval.
9352
-
9353
- The SDK exports `INTERFACE_DESIGN_RULES` separately from `INTERFACE_DESIGN_CONVENTIONS`. The older `INTERFACE_DESIGN_PRINCIPLES` export is a compatibility alias for conventions, not enforced guarantees.
9354
-
9355
- ### Conventions — design guidance
9356
-
9357
- Static review warns about more than eight visible controls, ungrouped large option sets, and multiple primary headings. Warnings are not hard failures. The following guidance requires design review; it is not automatically enforced.
9358
-
9359
- ### Do not
9360
-
9361
- - Repeat the Block name, step number, or workflow navigation in nested panels.
9362
- - Place a workspace in a card inside another card merely to create hierarchy.
9363
- - Use dialogs as permanent page layout or put a dialog inside another dialog.
9364
- - Give every section a border. Use spacing and short labels.
9365
- - Put twenty controls in the initial view, even if they wrap.
9366
- - Show camera, timing, patch, variant, and export settings before they are needed.
9367
- - Make users read a technical plan to reach the actual work.
9368
- - Call text scene descriptions a visual storyboard, frame review, or motion preview.
9369
- - Present placeholder imagery as the actual approved assets.
9370
- - Lead with JSON, manifests, MCP, transport details, or diagnostics.
9371
- - Add decorative circles around icons, emoji controls, or unlabeled icon buttons.
9372
- - Claim approval, rendering, or successful testing without the actual result.
9373
- - Hide spending approvals, failures, or stale-source warnings for neatness.
9374
- - Discard drafts or selections when collapsing controls or changing layout.
9375
-
9376
- ### Prefer
9377
-
9378
- One obvious primary action, strong defaults, and the controls needed for the current decision. Group advanced adjustments behind labeled disclosures. Use recognizable line icons with accessible names, tooltips, keyboard focus, and generous hit areas; keep text when an icon is ambiguous.
9379
-
9380
- Use StillMade theme tokens, typography, and spacing. Give the preview, timeline, images, or other task object the main surface. Only include source or diagnostic surfaces when authorized under the mandatory authoring rules above; do not add them automatically, even collapsed.
9381
-
9382
- Motion workflows distinguish planning notes from actual frame approval. Frame Review should display the exact images being approved before Motion Designer uses them. Motion preview should display the composition, not just its layer descriptions. These are product acceptance requirements to verify, not guarantees established by static admission.
9383
-
9384
- ### Known coverage gap
9385
-
9386
- Existing pinned Blocks can still contain excessive nested panels and text-only plans. Host containment does not redesign their interfaces. Correct those through new versioned Block releases and explicit upgrades; do not rewrite immutable releases or silently change a saved workflow's pins.
9500
+ A Block page should work at a glance: the thing being made is the biggest
9501
+ thing on the page, there is one obvious next action, choices are pictures, and
9502
+ words appear only when someone needs them. The app owns project navigation;
9503
+ a Block owns its production surface. Neither needs a second shell.
9504
+
9505
+ Most Blocks need no custom view: generated `manifest.ui` controls already
9506
+ follow these rules. Write a custom view only when the core interaction needs
9507
+ one, and start it from the Block kit.
9508
+
9509
+ ### 1. Start from the Block kit
9510
+
9511
+ StillMade adds the Block kit stylesheet (`packages/design-system/block-kit.css`)
9512
+ to every Block view, standard and frame. Use its classes and ship little or no
9513
+ CSS of your own. Every kit rule has zero specificity, so your own CSS always
9514
+ wins. Open `packages/design-system/block-kit.html` in a browser to see every
9515
+ piece in Light, Dark and White.
9516
+
9517
+ ```html
9518
+ <main class="sm-block" data-layout="split">
9519
+ <section class="sm-stage" id="stage" data-ratio="16:9" aria-live="polite">
9520
+ <div class="sm-empty" id="empty">
9521
+ <span class="sm-glyph" data-glyph="video" aria-hidden="true"></span>
9522
+ <p>Drop a clip</p>
9523
+ </div>
9524
+ </section>
9525
+
9526
+ <fieldset class="sm-choices" id="look" data-stillmade-share="look" data-ratio="16:9">
9527
+ <legend>Look</legend>
9528
+ <label><input type="radio" name="look" value="warm" checked><img src="data:..." alt=""><span>Warm</span></label>
9529
+ <label><input type="radio" name="look" value="cool"><img src="data:..." alt=""><span>Cool</span></label>
9530
+ </fieldset>
9531
+
9532
+ <div class="sm-field">
9533
+ <div class="sm-label">
9534
+ <label for="caption">Caption</label>
9535
+ <details class="sm-hint"><summary aria-label="About captions" title="About captions"></summary><p>Shown over the first two seconds.</p></details>
9536
+ </div>
9537
+ <input id="caption" data-stillmade-input="caption" placeholder="Optional">
9538
+ </div>
9539
+
9540
+ <div class="sm-actions">
9541
+ <button type="button" class="sm-primary" id="run" data-stillmade-action="run">Make it</button>
9542
+ <p class="sm-status" id="status" role="status"></p>
9543
+ </div>
9544
+
9545
+ <details class="sm-more">
9546
+ <summary>More</summary>
9547
+ <div class="sm-fields"><!-- secondary settings --></div>
9548
+ </details>
9549
+ </main>
9550
+ ```
9551
+
9552
+ | Class | Use it for |
9553
+ | --- | --- |
9554
+ | `sm-block` | The one workspace. `data-layout="split"` puts the stage beside the controls on wide screens. |
9555
+ | `sm-stage` | The thing being made: preview, media or result. `data-ratio="16:9"`, `"9:16"` or `"1:1"`. |
9556
+ | `sm-empty` | An empty stage: a glyph, at most five words, one action. |
9557
+ | `sm-choices` | Picks shown as pictures (`img`, `.sm-swatch`, `.sm-glyph`) and one or two words. `data-compact` for short text chips. Give the `fieldset` an `id` and `data-stillmade-share` so teammates see the same pick. |
9558
+ | `sm-field`, `sm-fields` | A short label above one control; a responsive group of them. |
9559
+ | `sm-actions` | One `sm-primary`, then `sm-quiet` buttons and `sm-icon` buttons (each with `aria-label` and `title`). |
9560
+ | `sm-status` | Live state beside the action: `data-state="loading"`, `"success"`, `"waiting"` or `"error"`. |
9561
+ | `sm-more` | A closed `<details>` for secondary settings. |
9562
+ | `sm-hint` | A closed `<details>` info toggle for an explanation that might help. |
9563
+ | `sm-gallery` | A grid of results or media. |
9564
+ | `sm-glyph` | A small icon in `currentColor`: `play`, `pause`, `plus`, `close`, `check`, `chevron`, `upload`, `image`, `video`, `audio`, `text`, `spark`, `info`, `warning`, `spinner`. Works in standard views, which cannot use SVG. |
9565
+
9566
+ **Function first.** When the person building a Block does not ask for a look,
9567
+ use the kit as is: they want it to work and be easy to use. When they name a
9568
+ design direction, follow it; the kit is the default, not a ceiling. Original
9569
+ designs keep `appearance: "original"`.
9570
+
9571
+ ### 2. Decide every sentence
9572
+
9573
+ | If the words… | Then |
9574
+ | --- | --- |
9575
+ | aren't needed to finish the task (an intro, "how it works", the Block name again, a description of what is already visible) | Drop them. |
9576
+ | say what a glyph, thumbnail, swatch or sample can show | Show the picture. Keep one or two words under it. |
9577
+ | might help some people (what a setting does, a format limit) | Put them in a closed `sm-hint`, or secondary settings in a closed `sm-more`. |
9578
+ | report an error, a spending approval, a stale input or a destructive step | Keep them visible, in few words. Say what to do next. |
9579
+
9580
+ Buttons say the action in one to three words ("Make it", "Export"). Status says
9581
+ the state in one to four words ("Making", "Ready", "Needs approval").
9582
+
9583
+ The button that makes something names what it makes ("Write", "Generate image",
9584
+ "Make video"), never "Run", "Run step", "Submit" or "Review cost", and says the
9585
+ progress word while it works ("Writing…").
9586
+
9587
+ Credit prices come from StillMade, never from the Block. On a Step, StillMade's
9588
+ run button carries the price beside the verb ("Write · 1 credit"), with a chip
9589
+ before it when the model, quality or count can change; each choice there shows
9590
+ its own price. When a custom view starts paid work, StillMade's run review shows
9591
+ the same single priced button. Pressing a priced button runs at that price; a
9592
+ confirmation appears only when the server's price is higher than the one shown.
9593
+ Do not write credit numbers in the Block.
9594
+
9595
+ ### 3. Rules
9596
+
9597
+ These are requirements for every author, person or model. Not all are checked
9598
+ automatically. An explicit request from the person can authorize a named
9599
+ exception, never unrelated technical UI.
9600
+
9601
+ 1. Show the thing being made, not a technical representation of it. No JSON
9602
+ dumps, source editors, manifests, debug consoles or "Technical result"
9603
+ panels unless the task is about source. A collapsed disclosure is still an
9604
+ added surface.
9605
+ 2. One obvious next action. Seven or fewer choices in view; strong defaults.
9606
+ 3. Choices people see or hear (voices, music, sounds, fonts, looks, colors,
9607
+ templates, images) show a preview of each option: a thumbnail or a sample
9608
+ to play, never a paid generation.
9609
+ 4. One workspace. No cards inside cards, no dialogs as page layout, no second
9610
+ title, step counter, Back/Next bar or generic "Workflow tools" menu.
9611
+ 5. Keep the page in normal scroll flow. No sticky media, headers or control
9612
+ strips unless the editing task needs a fixed timeline or inspector.
9613
+ 6. Name secondary controls by purpose; no ambiguous settings gear.
9614
+ 7. Icons come from the kit glyphs or your own files, with an accessible name
9615
+ and tooltip. Never emoji, never a circle around an icon.
9616
+ 8. Never hide spending approvals, failures or stale-source warnings for
9617
+ neatness, and never discard drafts when collapsing controls.
9618
+ 9. Text scene descriptions are planning notes, never a storyboard, frame review
9619
+ or motion preview. Frame Review shows the exact images being approved.
9620
+ 10. Compact: the whole Block fits one laptop screen (about 720 px tall)
9621
+ without scrolling, and a 320 px phone without sideways scrolling. If it
9622
+ does not fit, fold, shrink or move something; never add a section below.
9623
+ 11. A short setting is one row, its label beside the control (the kit's
9624
+ `sm-field` does this on any view wider than a phone). No card around a
9625
+ single control.
9626
+ 12. Status is a dot or a few words ("No narration yet"), never a paragraph.
9627
+ The reason sits behind an `sm-hint`.
9628
+ 13. Files are tiles: a saved file is a thumbnail or chip, and adding one is a
9629
+ + tile (a `label` around a hidden file input). Never the browser's bare
9630
+ "Choose File / No file chosen" picker, and no size limits in view until
9631
+ a file is too big.
9632
+ 14. Rare tools (update, schedule, history, reload) are named icon buttons
9633
+ beside the action, never sections under the work.
9634
+
9635
+ ### 4. What StillMade checks
9636
+
9637
+ - Admission rejects a primary textarea of 12 or more rows when no task
9638
+ presentation (stage, media, list or `StillMade.render`) is detected
9639
+ (`INTERFACE_DESIGN_RULES`).
9640
+ - Admission warns about more than eight visible controls, more than seven
9641
+ ungrouped options, more than one `h1`, a visible paragraph over twenty words,
9642
+ a word dropdown for something people see or hear, a bare browser file
9643
+ picker, and a size limit printed in view.
9644
+ - Admission also warns, and `stillmade-block ready` fails the "UI works" check
9645
+ (DOD-15), when a button says "Run", "Run step" or "Submit", when the
9646
+ interface shows a credit price, or when it has a page heading
9647
+ (`blockKitProblems`; the failing rules are `BLOCK_KIT_READINESS_RULES`). StillMade's own Block builder also sends back a custom
9648
+ view that is not built from `sm-block`, unless the person asked for their
9649
+ own look (`appearance: "original"`).
9650
+ - The host supplies the Project Type step bar and omits its own title and frame
9651
+ around custom views, and does not add a "Technical result" disclosure.
9652
+ - HTML, JavaScript, permission and appearance validation are separate checks.
9653
+ Simplicity never bypasses safety or approval.
9654
+
9655
+ The SDK exports the ordered principles as `BLOCK_INTERFACE_PRINCIPLES` and the
9656
+ kit vocabulary as `BLOCK_KIT_CLASSES`. `INTERFACE_DESIGN_CONVENTIONS`,
9657
+ `INTERFACE_DESIGN_PRINCIPLES` and `INTERFACE_AUTHORING_RULES` remain as
9658
+ compatibility names for the same list.
9659
+
9660
+ ### Existing Blocks
9661
+
9662
+ Pinned Blocks can still contain nested panels and text-only plans; the kit does
9663
+ not redesign them. Move a Block to the kit through a new versioned release and
9664
+ an explicit upgrade, following the
9665
+ [SDK update procedure](/block-sdk/PASTE_TO_LLM.md#updating-existing-blocks-through-the-sdk).
9666
+ Never rewrite immutable releases or silently change a saved workflow's pins.
9387
9667
 
9388
9668
 
9389
9669
  ## Describe an image
@@ -10401,7 +10681,7 @@ stop.addEventListener('click', async () => {
10401
10681
  });
10402
10682
  ```
10403
10683
 
10404
- `record.stop` returns `{asset,cursor,displays,coordinates}`. `asset` is a canonical media reference with `assetId`, `versionId`, `kind`, `mime`, and a device-local `sm-media:` URL. Cursor samples contain `{x,y,time}`: global device-independent screen coordinates and milliseconds relative to recording start. Multiple monitors can have negative coordinates. These are not automatically converted into cropped-window video coordinates. Share/upload media through the existing host media flow before teammates on other devices can use it.
10684
+ `record.stop` returns `{asset,cursor,displays,coordinates}`. `asset` is a canonical media reference with `assetId`, `versionId`, `kind`, `mime`, and a device-local `sm-media:` URL. Cursor samples contain `{x,y,time}`: global device-independent screen coordinates and milliseconds relative to recording start. Multiple monitors can have negative coordinates. These are not automatically converted into cropped-window video coordinates. Share/upload media through the existing host media flow before teammates on other devices can use it. By default that flow uploads a 1080p working copy of large video (the account's media setting); the original stays in the desktop media library.
10405
10685
 
10406
10686
  `record.cancel` discards an active capture. `session.close` revokes all access and cancels capture. StillMade also renders a host-owned cancel/revoke control outside the Block iframe. Screen source selection happens for every recording. No preview can bypass consent or access the native bridge directly.
10407
10687
 
@@ -11885,7 +12165,7 @@ The latest version of each Block bundled with the SDK, generated from the packag
11885
12165
  | `sm.editor` 2.2.12 | Editor | workspace | `editor` timeline (editor_timeline) · project context only · primary; `assets` asset[] (legacy.asset-list.assets) · project context only; `updatedEditor` timeline (legacy.timeline.updatededitor); `commit` boolean (legacy.boolean.commit); `action` text (legacy.text.action); `renderFps` integer (legacy.integer.renderfps); `renderResolution` integer (legacy.integer.renderresolution); `renderQuality` text (legacy.text.renderquality) | `editor` timeline (editor_timeline) · primary; `edit` workspace-edit (legacy.workspace-edit.edit); `mediaAction` approval (portable_media_action) |
11886
12166
  | `sm.export-audio` 2.0.0 | Export audio | task | `input` timeline (timeline) · primary; `renderFps` text (render_fps); `renderResolution` text (render_resolution); `renderQuality` text (render_quality); `projectTimeline` timeline (timeline) · project context only | `mediaAction` approval (portable_media_action); `output` audio (rendered_audio) |
11887
12167
  | `sm.export-timeline` 2.0.0 | Export timeline | editor-extension | `timeline` timeline · project context only · primary; `assets` asset[] · project context only; `blueprint` bible · project context only; `shotPlan` shot-plan · project context only; `metadata` metadata · project context only; `title` text; `format` text | `download` file-bundle · primary |
11888
- | `sm.export-video` 2.0.0 | Export video | task | `input` timeline (timeline) · primary; `renderFps` text (render_fps); `renderResolution` text (render_resolution); `renderQuality` text (render_quality); `projectTimeline` timeline (timeline) · project context only | `mediaAction` approval (portable_media_action); `output` video (rendered_video) |
12168
+ | `sm.export-video` 2.1.0 | Export video | task | `input` timeline (timeline) · primary; `media` asset[] (project_media); `narration` audio (narration_audio); `renderAspect` text (render_aspect); `renderFps` text (render_fps); `renderResolution` text (render_resolution); `renderQuality` text (render_quality); `projectTimeline` timeline (timeline) · project context only; `projectShotPlan` shot-plan (production_shot_plan) · project context only | `mediaAction` approval (portable_media_action); `output` video (rendered_video) |
11889
12169
  | `sm.final-edit` 2.0.2 | AI Final Edit | task | `timeline` timeline (timeline) · project context only · primary; `direction` text (edit_direction) | `edit` timeline-edit (timeline_edit_proposal) · primary |
11890
12170
  | `sm.generate-image` 2.1.0 | Generate image | task | `input` text · primary; `photos` image[] (reference_image) | `output` image · primary |
11891
12171
  | `sm.generate-video` 2.0.1 | Generate video | task | `prompt` brief (video_generation_brief) · primary | `video` video (generated_video) · primary |
@@ -11896,14 +12176,14 @@ The latest version of each Block bundled with the SDK, generated from the packag
11896
12176
  | `sm.meta-creative` 1.0.0 | Meta ad creative | task | `upload` object (external_data) · primary; `message` text (ad_primary_text); `website_url` text (ad_destination_url); `page_id` text (facebook_page_id); `call_to_action` text (ad_call_to_action) | `creative` object (meta_ad_creative) · primary |
11897
12177
  | `sm.motion-graphics` 2.0.0 | Motion graphics | editor-extension | `timeline` timeline (timeline) · primary; `projectTimeline` timeline (timeline) · project context only; `plan` json (motion_graphics_plan) | `timeline` timeline (timeline) · primary; `edit` workspace-edit (editor_workspace_edit) |
11898
12178
  | `sm.new-items` 1.0.0 | Only new items | task | `items` object (external_data) · primary; `id_field` text (item_id_field) | `item` object (external_data) · primary; `waiting` integer (pending_item_count) |
11899
- | `sm.panel-strip` 2.3.7 | Image Panels | workspace | `panels` panel-document (image_panel_document) · project context only · primary; `transcript` transcript (timed_transcript) · project context only; `script` script (narration_script) · project context only; `blueprint` bible (production_blueprint) · project context only; `updatedPanels` panel-document (image_panel_document); `commit` boolean (apply_changes); `generatedImage` image (image); `targetPanelId` text (panel_id); `attachGeneratedImage` boolean (boolean); `action` text (host_action); `stockQuery` text (stock_query); `generatedImages` image[] (image_list) | `panels` panel-document (image_panel_document) · primary; `edit` workspace-edit (workspace_edit); `stockAction` approval (portable_media_action); `qualityAction` approval (portable_media_action); `renderAction` approval (portable_media_action); `stockAsset` asset (licensed_asset); `stockImage` image (licensed_stock_image); `qualityReport` metadata (quality_report); `generationPlan` metadata (generation_plan); `generateAction` approval (portable_media_action); `generatedImage` image (generated_image); `batchGenerateAction` approval (portable_media_action); `generatedImages` image[] (generated_image_list) |
12179
+ | `sm.panel-strip` 2.4.1 | Image Panels | workspace | `panels` panel-document (image_panel_document) · project context only · primary; `transcript` transcript (timed_transcript) · project context only; `script` script (narration_script) · project context only; `narrationScript` script (narration_script); `narrationTiming` transcript (timed_transcript); `blueprint` bible (production_blueprint) · project context only; `updatedPanels` panel-document (image_panel_document); `commit` boolean (apply_changes); `generatedImage` image (image); `targetPanelId` text (panel_id); `attachGeneratedImage` boolean (boolean); `action` text (host_action); `stockQuery` text (stock_query); `generatedImages` image[] (image_list) | `panels` panel-document (image_panel_document) · primary; `edit` workspace-edit (workspace_edit); `stockAction` approval (portable_media_action); `qualityAction` approval (portable_media_action); `renderAction` approval (portable_media_action); `stockAsset` asset (licensed_asset); `stockImage` image (licensed_stock_image); `qualityReport` metadata (quality_report); `generationPlan` metadata (generation_plan); `generateAction` approval (portable_media_action); `generatedImage` image (generated_image); `batchGenerateAction` approval (portable_media_action); `generatedImages` image[] (generated_image_list) |
11900
12180
  | `sm.pipeline` 2.2.5 | Canvas | workspace | `canvas` pipeline (canvas_document) · project context only · primary; `assets` asset[] (project_assets) · project context only; `versions` metadata[] (asset_versions) · project context only; `updatedCanvas` pipeline (canvas_document); `commit` boolean (canvas_commit_intent); `generatedImage` image (generated_image_version); `addGeneratedImage` boolean (attach_generated_image); `generatedImageTitle` text (generated_image_title); `generatedVideo` video (generated_video_version); `addGeneratedVideo` boolean (attach_generated_video); `generatedVideoTitle` text (generated_video_title); `generatedAudio` audio (generated_audio_version); `addGeneratedAudio` boolean (attach_generated_audio); `generatedAudioTitle` text (generated_audio_title); `connectionProposal` json (context_connection_proposal); `applyContextConnections` boolean (context_connection_intent) | `canvas` pipeline (canvas_document) · primary; `edit` workspace-edit (canvas_edit_proposal) |
11901
12181
  | `sm.quality-audit` 1.0.2 | Quality review plan | task | `assets` asset[] (saved_asset_versions) · project context only · primary | `plan` metadata (visual_quality_review_plan) · primary |
11902
12182
  | `sm.research` 2.0.2 | Research | task | `input` text (research_question) · primary; `sourceTitle` text (source_title); `sourceUrl` text (source_url); `sourceNotes` text (source_notes) | `output` research (research_dossier) · primary |
11903
12183
  | `sm.rough-cut` 2.0.2 | Rough Cut | task | `timeline` timeline (timeline) · project context only · primary; `clipIds` text (timeline_clip_selection); `startAt` number (timeline_start_seconds); `gap` number (timeline_clip_gap_seconds) | `edit` timeline-edit (timeline_edit_proposal) · primary |
11904
12184
  | `sm.score` 2.0.0 | Audio scoring | workspace | `timeline` timeline (timeline) · primary; `projectTimeline` timeline (timeline) · project context only; `audio` audio; `stockAsset` asset (licensed_asset); `audios` audio[]; `plan` json (audio_score_plan) | `timeline` timeline (timeline) · primary; `edit` workspace-edit (editor_workspace_edit) |
11905
12185
  | `sm.script` 2.1.5 | Script Writer | workspace | `brief` brief (project_brief) · primary; `currentScript` script (narration_script) · project context only | `script` script (narration_script) · primary |
11906
- | `sm.shot-plan` 2.1.3 | Shots | workspace | `shotPlan` shot-plan (production_shot_plan) · project context only · primary; `transcript` transcript (timed_transcript) · project context only; `script` script (narration_script) · project context only; `updatedShotPlan` shot-plan (production_shot_plan); `commit` boolean (apply_changes) | `shotPlan` shot-plan (production_shot_plan) · primary; `edit` workspace-edit (workspace_edit) |
12186
+ | `sm.shot-plan` 2.2.1 | Shots | workspace | `shotPlan` shot-plan (production_shot_plan) · project context only · primary; `transcript` transcript (timed_transcript) · project context only; `script` script (narration_script) · project context only; `narrationScript` script (narration_script); `narrationTiming` transcript (timed_transcript); `updatedShotPlan` shot-plan (production_shot_plan); `commit` boolean (apply_changes) | `shotPlan` shot-plan (production_shot_plan) · primary; `edit` workspace-edit (workspace_edit) |
11907
12187
  | `sm.shot-plan-assistant` 1.0.0 | Plan Shots | task | `shotPlan` shot-plan · project context only; `request` text · primary | `edit` workspace-edit · primary |
11908
12188
  | `sm.stock-search` 2.0.0 | Stock media search | task | `query` text (stock_query) · primary; `mediaType` text (stock_media_type) | `stockAction` approval (portable_media_action); `asset` asset (licensed_asset) |
11909
12189
  | `sm.style` 2.0.1 | Style reference | workspace | `input` text (visual_style_direction) · primary; `name` text (style_name); `palette` text (color_palette); `lighting` text (lighting_direction); `camera` text (camera_direction) | `output` style (visual_style_reference) · primary |
@@ -11925,10 +12205,11 @@ The latest version of each Block bundled with the SDK, generated from the packag
11925
12205
  ## Custom view rules
11926
12206
 
11927
12207
  ### Custom view rules
11928
- Custom view code is checked before it runs. These rules come straight from the validator:
12208
+ Standard custom view code (no `view.config.json` frame runtime) is checked before it runs. These rules come straight from the validator; frame views run in their own sandbox and may build the page with ordinary DOM calls such as `createElement`:
11929
12209
 
11930
12210
  - **HTML elements:** only `div`, `section`, `article`, `header`, `footer`, `main`, `aside`, `nav`, `h1`, `h2`, `h3`, `h4`, `p`, `span`, `strong`, `em`, `i`, `small`, `br`, `hr`, `label`, `input`, `textarea`, `button`, `select`, `option`, `optgroup`, `fieldset`, `legend`, `output`, `progress`, `meter`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `figure`, `figcaption`, `img`, `video`, `audio`, `source`, `canvas`, `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`, `pre`, `code`, `details`, `summary`. No scripts, iframes, SVG, forms, comments or inline event attributes; media URLs must be `data:` or `blob:`.
11931
- - **Never use these names in view JavaScript:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
12211
+ - **No loops and no function declarations:** view JavaScript may not contain `import()`, `import`, `with`, `this`, `import.meta`/`new.target`, `while`, `do`/`while`, `for`, `for…in`, `for…of`, `function` declarations. Iterate with array `map`/`forEach`/`filter`/`reduce` and write helpers as `const name = (...) => ...`.
12212
+ - **Never use these names in view JavaScript, not even for your own variables:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
11932
12213
  - **Never use these property names:** `assign`, `values`, `entries`, `fromEntries` (so no `Object.assign`, `Object.values`, `Object.entries`, `Object.fromEntries`; use arrays and `map`/`forEach`).
11933
12214
  - **These words may not appear anywhere in view JavaScript, even in strings or comments:** `style`, `cssText`, `setProperty`, `removeProperty`, `insertRule`, `deleteRule`, `styleSheets`, `adoptedStyleSheets`, `animate`. Change appearance with CSS classes and `hidden`; put colors in the stylesheet.
11934
12215
  - **Property access with brackets needs a literal key:** `items[0]` and `record["title"]` are accepted; `items[index]` is rejected. Iterate with `map`/`forEach` or call `items.at(index)`.
@@ -11937,6 +12218,129 @@ Custom view code is checked before it runs. These rules come straight from the v
11937
12218
  - **Shared writes are queued in order:** if one `StillMade.updateShared` write is rejected (conflict, read-only or timeout), the writes queued behind it are rejected too. Send dependent edits one at a time or through `StillMade.scheduleSharedEdit`, keep the person's draft, and retry from the latest `onShared` value. `{merge: true}` merges independent fields of an object value; create an absent field with the precondition `{exists: false}`.
11938
12219
  - **Limits:** HTML 64 KiB, CSS 32 KiB, JavaScript 64 KiB; 30 runs and 60 image previews per minute.
11939
12220
 
12221
+ ## Authoring guide
12222
+
12223
+ ### Authoring guide
12224
+ StillMade's own builders (describe a Block, refine, remix, GitHub adaptation and Project Types) are given these exact sections, rendered from `packages/block-sdk/authoring-guide.js` beside the builder contract. A section marked *When* reaches StillMade's builder only for requests that need it; follow it whenever it applies to your Block.
12225
+
12226
+ ### Remixable source
12227
+
12228
+ Every finished Block must support automatic remixing through the standard SDK contract. Include complete editable runtime source/configuration, typed inputs/outputs, UI controls, fixtures, and a supported permissive manifest.license with its complete real copyright/license notice in manifest.provenance.notice (or verified portable evidence). Do not disable remixing. The code stays private to its creator; others remix only through StillMade’s AI builder. No custom remix endpoint or host callback is needed. Preserve inherited source, notices and lineage. Never invent ownership, copyright holders or permission, or apply a new license to unresolved third-party source; return unsupported with a clear licensing reason instead. The shared SDK admission validator enforces this requirement.
12229
+
12230
+ ### Package format
12231
+
12232
+ A Block package is StillMade SDK 0.1.0 JSON. Prefer {manifest,recipe,tests} for supported operations. Otherwise use {manifest,code,tests} with runtime:"javascript": code is a synchronous function body receiving input and returning declared output bindings, e.g. return {text:input.text.toUpperCase()}. JavaScript runs in isolated QuickJS: no fetch, DOM, Node APIs, modules, dependencies, timers, or host services; no promises, imports, require, eval or Function. Limits: 500 ms execution, 16 MiB heap, 256 KiB code, 4 MiB JSON I/O. Include concrete passing fixtures that reflect the requested behavior, including an edge case; never approximate a hosted model call with fabricated deterministic output. Manifest fields: schemaVersion:1,sdkVersion:"0.1.0",id:"creator.name",version:"1.0.0",name,description,kind:"task",runtime:"recipe", "javascript", "comfyui", or "capability",inputs/outputs as named {type,default?,min?,max?,role?,primary?}. Usually mark one primary input and output with primary:true. Semantic roles such as source_image and character_reference are distinct and must never be guessed,permissions:{project:[],network:[],filesystem:[],secrets:[]},ui:[{control,port}]. Recipe: {schemaVersion:1,steps:[{id,op,args}],outputs}. References: {"$input":"port"}, {"$step":"step-id"}. Supported operations: {"value.copy":{"description":"Copy a JSON value without changing it.","args":["value"]},"text.trim":{"description":"Remove leading and trailing whitespace.","args":["text"]},"text.replace":{"description":"Replace literal text; no regular expressions.","args":["text","find","replacement"]},"text.join":{"description":"Join an array of text values.","args":["items","separator"]},"image.thicken-lines":{"description":"Expand dark outlines while preserving other colors.","args":["image","thickness","threshold"]}}. Types include text,number,integer,boolean,json,image,audio,video,script,text[]. Include test {name,input,expected} where expected is output bindings.
12233
+
12234
+ ### Port meanings
12235
+
12236
+ For each newly created Block, declare the meaning of every input and output in its manifest contract, not just a physical type. Each input needs semantic, required:boolean and a short plain-language description. Each output needs semantic and a short description. Use lowercase snake_case or dotted semantic names, such as company_profile or email_subject; different meanings are not interchangeable merely because both are text. If an existing role is present, semantic must equal that role. Reuse an exact existing meaning only when the information actually has that meaning. Describe structured fields with schema when their shape matters: for example {name:"string",website:"string",industry:"string?"}. Supported schema fields are string/text, number, integer, boolean, object, json, arrays of those types, and optional fields with ?. Declare sources only when a requirement restricts where its answer may come from: upstream, project, adapter, chat, default, user or external. Do not mark a credential as ordinary text or put secrets in defaults. Contracts describe information; they never authorize network access, model calls, project edits, credit spending or new runtime APIs. Keep existing imported, installed and base Block contracts unchanged; do not silently relabel old published contracts to make a connection appear compatible. New contracts must match actual code and fixtures.
12237
+ Custom inheritance is persisted in manifest.semantics, an array of {name,description?,parents?}. Use namespaced custom names, at most 64 definitions per Block, eight parents per definition, and 500 description characters. Never redefine unnamespaced standard meanings, create cycles, or combine conflicting parent sets. Preserve effective inherited meanings of all existing base inputs, outputs and accepts declarations; adding or changing a parent is a contract change even when port labels stay identical. New independent ports may declare new meanings.
12238
+ In isolated computation code, StillMade.emit(outputName,value) collects declared final outputs without a return object; emit each name once and never combine it with a returned output object or job.pending result. StillMade.resolveRequirement(inputName) reads the fixed host-validated declared input snapshot as {resolved:true,requirement,value}, or {resolved:false,requirement} for an absent optional input. StillMade.getProjectContext() returns only context fields explicitly bound in manifest.inputs with their matching context.FIELD.read permissions; getProjectContext(field) reads one declared field and rejects undeclared fields. These snapshot readers do not run the resolver again, request Chat/user answers, read extra project data, expose credentials, write projects or grant permissions. StillMade.validate(value,schema) checks JSON values against SDK semantic schemas and returns a boolean; it does not replace host output validation, prove semantic meaning or authorize execution. These methods are not available in view.javascript.
12239
+
12240
+ ### Hosted text generation
12241
+
12242
+ For hosted text generation use {manifest,capability,tests}, runtime:"capability", entry:"src/capability.json", permissions.capabilities:["text.generate"]. Do not write fetch or async calls in JavaScript. The host owns model, provider, BYOK, credits and explicit run consent; never put those fields or keys in package source. One required primary input text|script|brief (structured values require text), one primary output text|script. capability:{schemaVersion:1,operation:"text.generate",prompt:{"$input":"promptPort"},system:"instructions",maxTokens:1200,output:"outputPort"}. Prompt max 32000 characters, system max 8000, maxTokens integer 1..4096. Include 1..3 tests with concrete input and expectations:{outputPort:{kind:"text" or "script",minLength:1,maxLength:12000,includes:["optional required literal"]}} instead of exact expected text. Hosted behavior tests require a separately confirmed model/payment quote; never claim they ran while authoring. Example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.script-draft","version":"1.0.0","name":"Draft a short script","description":"Turn a production brief into a short narration draft using an approved text-generation call.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"production_brief","required":true,"description":"Production brief that guides the narration draft."}},"outputs":{"script":{"type":"script","primary":true,"semantic":"narration_script","description":"A concise narration script based on the supplied brief."}},"permissions":{"project":[],"capabilities":["text.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"prompt"}]},"capability":{"schemaVersion":1,"operation":"text.generate","prompt":{"$input":"prompt"},"system":"Write a concise narration script. Return only the script text.","maxTokens":1200,"output":"script"},"tests":[{"name":"A short opening narration","input":{"prompt":"Write two narration sentences about a quiet forest at sunrise."},"expectations":{"script":{"kind":"script","minLength":20,"maxLength":6000}}}]}.
12243
+
12244
+ ### Hosted media operations
12245
+
12246
+ For audio transcription, use runtime:"capability", operation:"audio.transcribe" and permissions.capabilities:["audio.transcribe"]. Declare one primary audio input with an asset control, one text language input defaulting to "auto", and one primary transcript output. Bind the two inputs through capability.audio and capability.language. Provider, model, payment and credentials belong to the host. Fixtures use transcript word-count bounds, never invented provider text. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.transcribe-audio","version":"1.0.0","name":"Transcribe audio","description":"Turn one persistent audio file into reviewed text with word timings.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"audio":{"type":"audio","primary":true},"language":{"type":"text","default":"auto"}},"outputs":{"transcript":{"type":"transcript","primary":true}},"permissions":{"project":[],"capabilities":["audio.transcribe"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"audio"},{"control":"select","port":"language","options":["auto","en","es","fr","de","it","pt","ja","ko","zh"]}]},"capability":{"schemaVersion":1,"operation":"audio.transcribe","audio":{"$input":"audio"},"language":{"$input":"language"},"output":"transcript"},"tests":[{"name":"timed speech","input":{"audio":{"kind":"audio","assetId":"audio-1","versionId":"version-1","url":"/api/media/example/audio.mp3","mimeType":"audio/mpeg"},"language":"en"},"expectations":{"transcript":{"kind":"transcript","minWords":1,"maxWords":1000}}}]}.
12247
+ For narration or speech synthesis, use runtime:"capability", operation:"audio.speech" and permissions.capabilities:["audio.speech"]. One required primary text or script input feeds capability.text via {$input:"portName"}; a script value contains schemaVersion:1 and text. One primary audio output is named by capability.output. Text must be at most 3800 characters. The host owns the voice, model, provider, billing and BYOK. Do not put those choices in package code. Tests use audio expectations (WAV, duration and byte bounds), never invented exact audio. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.narrate-script","version":"1.0.0","name":"Narrate a script","description":"Turn a short script into a reviewed speech recording using a host-selected voice.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"script":{"type":"script","primary":true,"semantic":"narration_script","required":true,"description":"Narration script to render as speech."}},"outputs":{"audio":{"type":"audio","primary":true,"semantic":"speech_audio","description":"Speech audio generated from the narration script."}},"permissions":{"project":[],"capabilities":["audio.speech"],"network":[],"filesystem":[],"secrets":[]},"ui":[]},"capability":{"schemaVersion":1,"operation":"audio.speech","text":{"$input":"script"},"output":"audio"},"tests":[{"name":"Read a short welcome","input":{"script":{"schemaVersion":1,"text":"Welcome to the quiet forest."}},"expectations":{"audio":{"kind":"audio","format":"wav","minDuration":0.1,"maxDuration":30,"minBytes":46,"maxBytes":1500000}}}]}.
12248
+ For image generation, use runtime:"capability", operation:"image.generate" and permissions.capabilities:["image.generate"]. One required primary text input feeds capability.prompt via {$input:"portName"}; one primary image output is named by capability.output. Prompt limit 32000 characters. The supported output is PNG 1024x1024, at most 12000000 bytes. Tests use image expectations, never fabricated generated URLs or pixels. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.generate-image","version":"1.0.0","name":"Generate an image","description":"Generate a square PNG from a short visual prompt using an approved host request.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"image_prompt","required":true,"description":"Visual description of the image to generate."}},"outputs":{"image":{"type":"image","primary":true,"semantic":"generated_image","description":"The generated square PNG image."}},"permissions":{"project":[],"capabilities":["image.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"prompt"}]},"capability":{"schemaVersion":1,"operation":"image.generate","prompt":{"$input":"prompt"},"output":"image"},"tests":[{"name":"A simple forest illustration","input":{"prompt":"A simple illustration of a quiet forest with clear dark outlines and soft green colors."},"expectations":{"image":{"kind":"image","format":"png","width":1024,"height":1024,"minBytes":67,"maxBytes":12000000}}}]}.
12249
+ For image analysis, use runtime:"capability", operation:"image.describe" and permissions.capabilities:["image.describe"]. Declare one primary image input (or one image[] input of one to four images to compare together) and one primary text output. capability.image references the input; capability.instruction contains bounded package-owned analysis instructions; capability.maxTokens is 1–4096; capability.output names the text output. The host loads only owned media and controls provider, model, payment and approval. Tests use bounded RGBA pixels and nondeterministic text expectations, never invented live descriptions. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.describe-image","version":"1.0.0","name":"Describe an image","description":"Turn one owned image into concise production-ready visual notes.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"image":{"type":"image","primary":true,"semantic":"source_image","required":true,"description":"Owned image to inspect for visible production details."}},"outputs":{"description":{"type":"text","primary":true,"role":"visual_description","semantic":"visual_description","description":"Concise production notes based only on visible image details."}},"permissions":{"project":[],"capabilities":["image.describe"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"image"}]},"capability":{"schemaVersion":1,"operation":"image.describe","image":{"$input":"image"},"instruction":"Describe only visibly supported subject matter, composition, lighting, color, texture, camera feel and visual style. Return concise production-ready plain text.","maxTokens":700,"output":"description"},"tests":[{"name":"Blue image","input":{"image":{"width":1,"height":1,"data":[20,80,200,255]}},"expectations":{"description":{"kind":"text","minLength":20,"maxLength":32768,"includes":["blue"]}}}]}.
12250
+ For AI-directed editing, use runtime:"capability", operation:"timeline.propose" and permissions.capabilities:["timeline.propose"]. Declare one current timeline input, up to three additional typed context inputs, and one timeline-edit output. Bind every input exactly once through capability.context; declare context.timeline.read and timeline.propose. StillMade controls the provider, strict JSON validation, stale-before checks, preview and Apply. Tests declare timeline-edit command bounds, never fabricated provider results. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.timeline-polish","version":"1.0.0","name":"Timeline polish","description":"Turn a timeline and editing direction into a bounded proposal that remains reviewable before Apply.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","context":"timeline","primary":true},"direction":{"type":"text","default":"Polish this cut conservatively."}},"outputs":{"edit":{"type":"timeline-edit","primary":true}},"permissions":{"project":["context.timeline.read","timeline.propose"],"capabilities":["timeline.propose"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"text","port":"direction"}]},"capability":{"schemaVersion":1,"operation":"timeline.propose","context":{"timeline":{"$input":"timeline"},"direction":{"$input":"direction"}},"instruction":"Use only supported timeline-edit commands, exact before snapshots, and the current activeTimelineId. Preserve media and locked items. Return strict JSON only.","maxTokens":2000,"output":"edit"},"tests":[{"name":"one restrained edit","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-1","tracks":[{"id":"video"}],"clips":[{"id":"clip-1","trackId":"video","start":0,"duration":4}],"audioItems":[],"captions":[]},"direction":"Add a soft transition."},"expectations":{"edit":{"kind":"timeline-edit","minCommands":1,"maxCommands":10}}}]}.
12251
+ For video generation use runtime:"capability", operation:"video.generate", permissions.capabilities:["video.generate"], one primary text input and one primary video output. capability.prompt references the input and capability.output names the output. Prompt limit 32000. Tests declare MP4 duration, dimensions and byte bounds; never fabricate video URLs. The enabled host selects its supported model, duration, resolution, payment and approved quote; packages cannot choose provider settings or dispatch jobs. Shipped example: {"manifest":{"schemaVersion":1,"id":"example.generate-video","name":"Generate video","version":"1.0.0","sdkVersion":"0.1.0","description":"Turn a production prompt into a short video using an approved host provider.","kind":"task","runtime":"capability","entry":"src/capability.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"required":true}},"outputs":{"video":{"type":"video","primary":true}},"permissions":{"project":[],"capabilities":["video.generate"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"port":"prompt","control":"text"}]},"capability":{"schemaVersion":1,"operation":"video.generate","prompt":{"$input":"prompt"},"output":"video"},"tests":[{"name":"Produces a bounded MP4 sample","input":{"prompt":"A paper boat floating across a calm blue pond. Gentle camera movement."},"expectations":{"video":{"kind":"video","format":"mp4","minDuration":1,"maxDuration":60,"minBytes":32,"maxBytes":67108864,"minWidth":1,"maxWidth":3840,"minHeight":1,"maxHeight":3840}}}]}.
12252
+ Each capability package performs one supported operation. A job that needs several operations is built from parts: return each operation as its own part Block, and StillMade groups the parts of one job into one Step (One job per Step); never invent a combined runtime. Hosted image, speech and video source can be statically validated while authoring, but real fixture/sample generation requires the user's subsequent model/payment selection and quote confirmation. Do not claim those calls ran. Deterministic image transformations should remain sandboxed recipe or JavaScript code when supported; do not generate a new image as a substitute for processing an existing one.
12253
+
12254
+ ### ComfyUI workflows
12255
+
12256
+ *When:* StillMade's builder adds this section when the request names ComfyUI, a checkpoint, safetensors or KSampler, or refines a ComfyUI Block.
12257
+
12258
+ For an explicitly requested ComfyUI workflow use runtime:"comfyui", entry:"src/comfyui.json", permissions.capabilities:["comfyui.execute"], and package fields {manifest,comfyui,tests,view?}. Reviewed core contracts: {"CheckpointLoaderSimple":{"inputs":{"ckpt_name":"checkpoint"},"outputs":["MODEL","CLIP","VAE"],"outputNode":false},"CLIPTextEncode":{"inputs":{"text":{"type":"STRING","maxLength":4000},"clip":"CLIP"},"outputs":["CONDITIONING"],"outputNode":false},"EmptyLatentImage":{"inputs":{"width":{"type":"INT","min":64,"max":1024,"step":8},"height":{"type":"INT","min":64,"max":1024,"step":8},"batch_size":{"type":"INT","min":1,"max":1,"step":1}},"outputs":["LATENT"],"outputNode":false},"KSampler":{"inputs":{"model":"MODEL","seed":{"type":"INT","min":0,"max":4294967295,"step":1},"steps":{"type":"INT","min":1,"max":40,"step":1},"cfg":{"type":"FLOAT","min":0,"max":20},"sampler_name":["euler","euler_ancestral","dpmpp_2m"],"scheduler":["normal","karras","simple"],"positive":"CONDITIONING","negative":"CONDITIONING","latent_image":"LATENT","denoise":{"type":"FLOAT","min":0,"max":1}},"outputs":["LATENT"],"outputNode":false},"VAEDecode":{"inputs":{"samples":"LATENT","vae":"VAE"},"outputs":["IMAGE"],"outputNode":false},"VAEEncode":{"inputs":{"pixels":"IMAGE","vae":"VAE"},"outputs":["LATENT"],"outputNode":false},"LoadImage":{"inputs":{"image":"upload"},"outputs":["IMAGE","MASK"],"outputNode":false},"ImageInvert":{"inputs":{"image":"IMAGE"},"outputs":["IMAGE"],"outputNode":false},"ImageScale":{"inputs":{"image":"IMAGE","upscale_method":["nearest-exact","bilinear","area","bicubic","lanczos"],"width":"INT","height":"INT","crop":["disabled","center"]},"outputs":["IMAGE"],"outputNode":false},"PreviewImage":{"inputs":{"images":"IMAGE"},"outputs":["IMAGE"],"outputNode":true}}. Object scalar descriptors are internal validation limits; prompt.inputs contains actual scalar values or [nodeId,outputIndex] connections, not those descriptors. Text/integer/number/image ports bind via {node,input,encoding:"scalar" or "uploaded-image"}. Every LoadImage filename is an empty placeholder bound to an image input. Output is PreviewImage images[0], bound by {node,collection:"images",index:0}. No custom nodes, Python, URLs or downloads. One KSampler, one installed safetensors checkpoint basename, batch 1, up to 40 steps, 1024 pixels per generation side, four megapixels across working image/latent nodes. VAEEncode inputs require sides >=64 and multiples of 8; unchanged original media stays intact. Do not invent installed model names: preserve the caller's supplied name, or return unsupported asking for an installed checkpoint name. Example shape only (replace its placeholder with the user's exact installed checkpoint): {"manifest":{"schemaVersion":1,"id":"example.comfy-text-to-image","name":"ComfyUI image generation","version":"1.0.1","sdkVersion":"0.1.0","description":"Generate an image with an installed checkpoint on your reviewed ComfyUI connection.","kind":"task","runtime":"comfyui","entry":"src/comfyui.json","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"prompt":{"type":"text","primary":true,"semantic":"image_prompt","required":true,"description":"Visual description used to generate the image."}},"outputs":{"image":{"type":"image","primary":true,"semantic":"generated_image","description":"The image generated from the prompt."}},"permissions":{"project":[],"capabilities":["comfyui.execute"]},"ui":[{"control":"text","port":"prompt"}]},"comfyui":{"schemaVersion":1,"prompt":{"1":{"class_type":"CheckpointLoaderSimple","inputs":{"ckpt_name":"your-model.safetensors"}},"2":{"class_type":"CLIPTextEncode","inputs":{"text":"","clip":["1",1]}},"3":{"class_type":"CLIPTextEncode","inputs":{"text":"","clip":["1",1]}},"4":{"class_type":"EmptyLatentImage","inputs":{"width":512,"height":512,"batch_size":1}},"5":{"class_type":"KSampler","inputs":{"model":["1",0],"seed":1,"steps":20,"cfg":7,"sampler_name":"euler","scheduler":"normal","positive":["2",0],"negative":["3",0],"latent_image":["4",0],"denoise":1}},"6":{"class_type":"VAEDecode","inputs":{"samples":["5",0],"vae":["1",2]}},"7":{"class_type":"PreviewImage","inputs":{"images":["6",0]}}},"inputs":{"prompt":{"node":"2","input":"text","encoding":"scalar"}},"outputs":{"image":{"node":"7","collection":"images","index":0}}},"tests":[{"name":"Generate one bounded image","input":{"prompt":"A small wooden boat beside a quiet lake."},"expectations":{"image":{"kind":"image","count":1,"width":512,"height":512,"maxBytes":16777216}}}]}. Samples use concrete inputs and expectations:{image:{kind:"image",count:1,width:512,height:512,maxBytes:16777216}}; never invent generated pixels or URLs. Authoring only checks source; fixtures/sample require the account's separately confirmed connection. Never claim backend availability, matching model hashes, GPU readiness or executed tests.
12259
+
12260
+ ### Project context
12261
+
12262
+ Project context is provided through declared input ports, not a global project API. Bind an input with context:"field" and its exact type from {"script":"script","transcript":"transcript","shots":"shot[]","shotPlan":"shot-plan","scenes":"scene[]","characters":"character[]","locations":"location[]","styles":"style[]","assets":"asset[]","files":"file[]","generations":"metadata[]","versions":"metadata[]","timeline":"timeline","editor":"timeline","canvas":"pipeline","blueprint":"bible","panels":"panel-document","board":"board-document","animation":"scene-document","metadata":"metadata"}; declare permissions.project:["context.field.read"] for each bound field (replace field with the actual name). The host supplies the user-selected scope and only authorized context; do not invent context fetch methods. Include concrete context inputs in fixtures. Read-only snapshots cannot be mutated to save project changes. Reuse existing Canvas characters, locations and styles for visual consistency. The SDK also supports optional Step-scoped memory and timeline-edit outputs ; request the corresponding contract only when the capability needs it.
12263
+
12264
+ ### Workspace proposals
12265
+
12266
+ *When:* StillMade's builder adds this section when the request is about a workspace, animation, canvas, blueprint, panels or board, or refines a Block with a workspace-edit output.
12267
+
12268
+ To propose changes to an existing blueprint, panels, board, canvas, or animation workspace, bind its matching context input ({"script":"script","transcript":"transcript","shots":"shot[]","shotPlan":"shot-plan","scenes":"scene[]","characters":"character[]","locations":"location[]","styles":"style[]","assets":"asset[]","files":"file[]","generations":"metadata[]","versions":"metadata[]","timeline":"timeline","editor":"timeline","canvas":"pipeline","blueprint":"bible","panels":"panel-document","board":"board-document","animation":"scene-document","metadata":"metadata"}). Declare context.FIELD.read and workspace.FIELD.propose in permissions.project. Return a primary workspace-edit output: {schemaVersion:1,title,field:"blueprint"|"panels"|"board"|"canvas"|"animation",before:theCompleteUnmodifiedInputDocument,after:theCompleteModifiedCopy}. Preserve unrelated fields and existing identities; Panels and Board retain strip items with panelId, Board also requires each item's type. Fixtures need concrete full before/after documents. These are reviewed proposals; the host checks current source and shared edit permission, then saves using collaboration and undo. Never claim runtime execution already saved it. Canvas proposals retain executable Step instances, host bindings, and connections touching those instances; change Steps through the Project Type builder. Only position, size, title and label may change on programmable nodes, with their host-owned execution state retained. Canvas viewport is local and unchanged. Animation proposals preserve shotId, start/end timing and voiceoverText for every scene record; modify its scene and versions. The collection and narration spine belong to Shots. Starting Chat writes are not supported by this proposal contract.
12269
+
12270
+ ### Step memory
12271
+
12272
+ *When:* StillMade's builder adds this section when the request needs memory, counting or persistence, or refines a stateful Block.
12273
+
12274
+ For memory use runtime:"javascript", manifest.state:{scope:"step",version:1,initial:{...}}, permissions.project including "state.step". Code receives mutable plain-object state separately from input. Mutate state and return only declared output bindings; never return a state envelope as an undeclared output. Each test needs expectedState:{version:1,value:{...}}; a test may supply state:{version:1,value:{...}} to verify continuation. Memory is bounded to 64 KiB and scoped to a placement. The host saves it only with accepted successful output; previews use disposable memory. No localStorage, files, global or account-shared state. Interactive selections support successive memory; unattended future-media execution is not supported for stateful Blocks. Preserve the state schema/version during refinement unless the user requests a schema change. For an explicit upgrade, the new version may declare manifest.state.migrations:[{fromVersion:1,operations:[{op:"rename",from:"oldField",to:"newField"},{op:"default",key:"labels",value:[]}]}], targeting its enclosing state.version (for example 2). Use only ordered top-level rename/default/remove operations; remove uses {op:"remove",key:"field"}. Renames require an existing source and absent destination, defaults never overwrite, removals require an existing field. At most 16 distinct older fromVersion routes, 64 operations each, and 64 KiB combined declarations and intermediate state. No chains, downgrade, arbitrary code or automatic application. Bump the Block version when source or migrations change, retain unrelated state, and never invent an old field layout. Owners preview and explicitly apply migrations to exact archived source-bound state through the project host; authoring does not mutate existing projects. New execution fixtures use the target state version. Exact example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.numbered-takes","version":"1.0.0","name":"Numbered takes","description":"Label successive creative takes with an independent counter for each production placement.","kind":"task","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"text":{"type":"text","primary":true,"semantic":"draft_text","required":true,"description":"Creative take text to label with the next number."}},"outputs":{"text":{"type":"text","primary":true,"semantic":"numbered_take","description":"The supplied take prefixed with its saved sequence number."}},"permissions":{"project":["state.step"]},"state":{"scope":"step","version":1,"initial":{"count":0}},"ui":[{"port":"text","control":"text"}]},"code":"state.count += 1; return {text: \"Take \" + state.count + \": \" + input.text.trim()};","tests":[{"name":"First take","input":{"text":" Opening scene "},"expected":{"text":"Take 1: Opening scene"},"expectedState":{"version":1,"value":{"count":1}}},{"name":"Continues saved numbering","input":{"text":"Alternate ending"},"state":{"version":1,"value":{"count":4}},"expected":{"text":"Take 5: Alternate ending"},"expectedState":{"version":1,"value":{"count":5}}}]}.
12275
+
12276
+ ### Editor timeline proposals
12277
+
12278
+ *When:* StillMade's builder adds this section when the request is about the Editor, timeline, clips, captions, trimming, splitting or volume, or refines a Block with a timeline-edit output.
12279
+
12280
+ For Editor changes return a primary output {type:"timeline-edit"} and declare "timeline.propose" plus "context.timeline.read". Bind timeline input {type:"timeline",context:"timeline"}. A proposal is {schemaVersion:1,title,timelineId:input.timeline.activeTimelineId,commands:[...]}; 1..100 commands, exactly one per target. Command shape is exactly {collection,operation,before,values}. For collections clips or audioItems, before is the complete unmodified source item; operations and exact values are move:{start}, trim:{trimStart,duration}, volume:{volume} (0..200), mute:{muted}, remove:{}, split:{at,newId}. Split at is an absolute timeline time strictly inside the item; newId must be unused. For captions: caption.add has before:null and values:{id,trackId,text,start,end}; caption.update has the complete original before and values:{text,start,end}; remove has complete original before and values:{}. Caption text must be nonempty and at most 5000 characters, end greater than start. Use an unlocked existing caption track or default trackId:"captions" if absent. IDs must be unique across all timeline lanes. Preserve source snapshots, locked tracks, bounds and existing item identities. No arbitrary media, effect insertion, track mutation or direct project writes. The user reviews before/after and applies proposals through Editor undo; do not claim a proposal has already changed the project. Exact examples: [{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.quieter-clips","version":"1.0.0","name":"Quieter clips","description":"Propose a lower volume for existing video clips, then review and apply the changes in the Editor.","kind":"editor-extension","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","primary":true,"context":"timeline","semantic":"timeline","required":true,"description":"Current Editor timeline containing the clips to adjust."},"volume":{"type":"number","min":0,"max":100,"default":50,"semantic":"clip_volume_percent","required":false,"description":"Target volume percentage for each video clip."}},"outputs":{"edit":{"type":"timeline-edit","primary":true,"semantic":"timeline_edit","description":"A reviewable proposal that changes clip volume."}},"permissions":{"project":["context.timeline.read","timeline.propose"]},"ui":[{"port":"volume","control":"slider"}]},"code":"if (!input.timeline.clips.length) throw new Error(\"Add clips in the Editor first\"); return {edit:{schemaVersion:1,title:\"Lower clip volume\",timelineId:input.timeline.activeTimelineId,commands:input.timeline.clips.slice(0,100).map(clip=>({collection:\"clips\",operation:\"volume\",before:clip,values:{volume:input.volume}}))}};","tests":[{"name":"Propose without changing the source","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-01","clips":[{"id":"clip-1","trackId":"video","name":"Opening","start":0,"duration":3,"sourceDuration":3,"volume":100}]},"volume":50},"expected":{"edit":{"schemaVersion":1,"title":"Lower clip volume","timelineId":"timeline-01","commands":[{"collection":"clips","operation":"volume","before":{"id":"clip-1","trackId":"video","name":"Opening","start":0,"duration":3,"sourceDuration":3,"volume":100},"values":{"volume":50}}]}}}]},{"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.caption-from-text","version":"1.0.0","name":"Caption from text","description":"Turn a short line of text into a timed caption proposal for the Editor.","kind":"editor-extension","runtime":"javascript","license":"MIT","provenance":{"notice":"MIT License\n\nCopyright (c) 2026 StillMade SDK contributors\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"},"inputs":{"timeline":{"type":"timeline","context":"timeline","semantic":"timeline","required":true,"description":"Current Editor timeline that will receive the caption."},"text":{"type":"text","primary":true,"semantic":"caption_text","required":true,"description":"Text to place in the new caption."},"start":{"type":"number","min":0,"max":86300,"default":0,"semantic":"start_time_seconds","required":false,"description":"Timeline time in seconds at which the caption begins."},"duration":{"type":"number","min":0.05,"max":100,"default":3,"semantic":"duration_seconds","required":false,"description":"Number of seconds the caption remains visible."}},"outputs":{"edit":{"type":"timeline-edit","primary":true,"semantic":"timeline_edit","description":"A reviewable proposal that adds the timed caption."}},"permissions":{"project":["context.timeline.read","timeline.propose"]},"ui":[{"port":"text","control":"text"},{"port":"start","control":"number"},{"port":"duration","control":"number"}]},"code":"const text=input.text.trim();if(!text)throw new Error(\"Enter caption text\");const ids=new Set([...(input.timeline.clips||[]),...(input.timeline.audioItems||[]),...(input.timeline.captions||[]),...(input.timeline.textOverlays||[]),...(input.timeline.stickers||[]),...(input.timeline.effects||[])].map(item=>item.id));let n=1;while(ids.has(\"block-caption-\"+n))n++;const track=(input.timeline.tracks||[]).find(track=>track.type===\"caption\"&&!track.locked);return {edit:{schemaVersion:1,title:\"Add caption\",timelineId:input.timeline.activeTimelineId,commands:[{collection:\"captions\",operation:\"caption.add\",before:null,values:{id:\"block-caption-\"+n,trackId:track?track.id:\"captions\",text,start:input.start,end:input.start+input.duration}}]}};","tests":[{"name":"Create a timed caption","input":{"timeline":{"schemaVersion":1,"activeTimelineId":"timeline-01","clips":[],"captions":[],"tracks":[]},"text":"A new beginning","start":0,"duration":3},"expected":{"edit":{"schemaVersion":1,"title":"Add caption","timelineId":"timeline-01","commands":[{"collection":"captions","operation":"caption.add","before":null,"values":{"id":"block-caption-1","trackId":"captions","text":"A new beginning","start":0,"end":3}}]}}}]}].
12281
+
12282
+ ### Descriptions
12283
+
12284
+ Write the description in third-grade language: one short sentence, ideally 8–14 words and never more than 20. Use common words and say what the person can make or do. Start with an action. Leave out technical terms such as typed ports, scoped context, artifacts, pipeline, runtime, and handoff. Do not list every feature, repeat the name, add marketing claims, or say this Block lets you. Keep essential limits and describe only what the code actually does. For example: Cut clips, add sound and text, then save your video. Apply these No AI Slop editing rules to the description prose only; keep the required response format, code, and identifiers intact. Write for a person choosing a Block or Project Type. Use familiar words, direct verbs, and details supported by the saved code. Preserve useful limits and clear existing sentences. Use “version” or “saved version” instead of “release”; use “Public”, “Private” and “Draft” for visibility and readiness. Call release notes “What changed”. Check each sentence: could it describe almost any app? If so, replace it with a specific supported action or remove it. Never invent examples, numbers, features, reviews, or results to sound more specific. Cut hype, vague benefits, dramatic reveals, fake expert claims, rhetorical questions, introductions that delay the point, and closing lines that repeat the paragraph. Avoid phrases such as game changer, cutting-edge, unlock your potential, seamless, powerful solution, leverage, empower, streamline, and elevate. Say what the person can do. State the useful fact directly instead of using not-X-but-Y contrasts. Use complete sentences, with no decorative dashes, emoji, or emphasis. Keep names consistent; do not cycle through synonyms for a Block. Avoid repeated sentence shapes and forcing every listing into an identical template. Use search terms only where they describe the real task. Do not repeat keywords or stretch the copy to hit a word or sentence count. Before returning the copy, reread it for factual accuracy, simple language, specific detail, natural rhythm, and filler. Fix any failures silently and return only the requested output.
12285
+
12286
+ ### Generated controls first
12287
+
12288
+ *When:* StillMade's builder adds this section when the person did not ask for a custom interface.
12289
+
12290
+ Optional custom view interfaces are supported, but generated manifest.ui controls are sufficient unless the user asks for a custom interface.
12291
+
12292
+ ### Custom interfaces
12293
+
12294
+ *When:* StillMade's builder adds this section when the person asks for an interface, preview or controls, or refines a Block that has one.
12295
+
12296
+ A custom interface is the optional package-level view:{html,css?,themedCss?,javascript?}; when refining, preserve and refine base.view. Design it by StillMade's interface principles, in order: Function first: unless the person asks for a design direction, build from the Block kit (`sm-block`, `sm-stage`, `sm-choices`, `sm-actions`, `sm-more`) and ship little or no CSS of your own. A direction the person names wins over the kit. Show the thing being made: the preview, media or result gets the largest area (`sm-stage`). Never a JSON dump, source editor, manifest or diagnostic panel unless the task is about source. One obvious next action (`sm-primary`); peers are quiet buttons or named icons. Seven or fewer choices in view. The action button names what it does ("Write", "Generate image", "Make video"), never "Run", "Run step", "Submit" or "Review cost". Show no credit numbers: StillMade puts the price beside the verb on its own run button ("Write · 1 credit"), with a chip for the model, quality or count when they can change. Pressing it runs at that price; a confirmation appears only when the server price is higher. Pictures over words: choices people see or hear are thumbnails, swatches, samples or glyphs (`sm-choices`), not word lists. Icons carry an accessible name and tooltip; never emoji. Words only when needed. Drop what nobody needs to finish the task (intros, "how it works", restating the Block name). Words that might help start collapsed (`sm-hint`, `sm-more`). Errors, spending approvals, stale inputs and destructive steps stay visible, in few words. Strong defaults; secondary settings go in `sm-more`, closed. Compact: the whole Block fits one laptop screen without scrolling. A short setting is one row, its label beside the control; no card around a single control. Status is a dot or a few words ("No narration yet"), never a paragraph; the reason sits behind an `sm-hint`. Files are tiles: a saved file is a thumbnail or chip and adding one is a + tile. Never the bare browser file picker, and no size limits in view until a file is too big. Rare tools (update, schedule, history, reload) are named icon buttons beside the action, never sections under the work. One workspace: no cards inside cards, no second title, step counter or Back/Next bar; the host owns navigation. Status sits beside the action it describes (`sm-status`), in a few words. Keep the page in normal scroll flow; no sticky media or control strips unless the editing task needs them. Text scene descriptions are planning notes, never a storyboard or an approved frame. Its DOM controller is separate from manifest runtime code. The view may also declare appearance:"original" or "host". For an imported design, preserve its colors with appearance:"original" and view.css; no themed replacement is required. Preserve base.view.appearance when refining unless the user requests a change. Omitted/host appearance retains the existing Light, Dark and White requirement: supply semantic colors in view.css or a complete replacement view.themedCss. An original-style view may optionally supply valid themedCss for a StillMade appearance preview. Use StillMade variables (--bg,--fg,--fg-muted,--border-tok,--r-3) only when opting into host styling; do not redefine host tokens in the themed variant. Appearance never relaxes sandbox constraints: no CSS escapes/filters/blending, imports/url(), or element.style mutations in JS. Use classes for UI state; media pixels retain their colors. Both CSS fields have the same 32 KiB limit; all existing shared-state, security and permission checks still apply. Declare elements in html, bind with document.getElementById/querySelector and addEventListener; use literal property access, array methods, textContent, input.value, button.disabled and canvas. Initial values: StillMade.onInput(values=>{...}); await StillMade.run(inputBindings) returns validated output bindings from the isolated package runtime; catch errors and show results. No computation in the DOM controller; put it in code/recipe. Disable the action button while pending.
12297
+ Standard custom view code (no `view.config.json` frame runtime) is checked before it runs. These rules come straight from the validator; frame views run in their own sandbox and may build the page with ordinary DOM calls such as `createElement`:
12298
+
12299
+ - **HTML elements:** only `div`, `section`, `article`, `header`, `footer`, `main`, `aside`, `nav`, `h1`, `h2`, `h3`, `h4`, `p`, `span`, `strong`, `em`, `i`, `small`, `br`, `hr`, `label`, `input`, `textarea`, `button`, `select`, `option`, `optgroup`, `fieldset`, `legend`, `output`, `progress`, `meter`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `figure`, `figcaption`, `img`, `video`, `audio`, `source`, `canvas`, `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`, `pre`, `code`, `details`, `summary`. No scripts, iframes, SVG, forms, comments or inline event attributes; media URLs must be `data:` or `blob:`.
12300
+ - **No loops and no function declarations:** view JavaScript may not contain `import()`, `import`, `with`, `this`, `import.meta`/`new.target`, `while`, `do`/`while`, `for`, `for…in`, `for…of`, `function` declarations. Iterate with array `map`/`forEach`/`filter`/`reduce` and write helpers as `const name = (...) => ...`.
12301
+ - **Never use these names in view JavaScript, not even for your own variables:** `eval`, `Function`, `AsyncFunction`, `GeneratorFunction`, `require`, `importScripts`, `process`, `globalThis`, `window`, `self`, `top`, `parent`, `opener`, `frames`, `location`, `navigation`, `history`, `navigator`, `localStorage`, `sessionStorage`, `indexedDB`, `caches`, `cookieStore`, `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`, `SharedWorker`, `ServiceWorker`, `WebAssembly`, `Deno`, `Bun`, `Reflect`, `Proxy`, `DOMParser`, `MutationObserver`, `postMessage`, `open`, `close`, `print`, `alert`, `confirm`, `prompt`, `setInterval`, `setTimeout`, `queueMicrotask`, `arguments`, `constructor`, `__proto__`, `prototype`, `__lookupGetter__`, `__lookupSetter__`, `__defineGetter__`, `__defineSetter__`, `getPrototypeOf`, `setPrototypeOf`, `getOwnPropertyDescriptor`, `getOwnPropertyDescriptors`, `getOwnPropertyNames`, `getOwnPropertySymbols`, `defineProperty`, `defineProperties`, `defaultView`, `ownerGlobal`, `contentWindow`, `contentDocument`, `cookie`, `domain`, `documentURI`, `URL`, `baseURI`, `referrer`, `write`, `writeln`, `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `createElement`, `createElementNS`, `createContextualFragment`, `createRange`, `setAttribute`, `setAttributeNS`, `attributes`, `attributeStyleMap`, `adoptNode`, `importNode`, `execCommand`, `replaceChildren`, `setHTML`, `setHTMLUnsafe`, `parseHTML`, `parseHTMLUnsafe`, `showSaveFilePicker`, `showDirectoryPicker`, `saveAs`, `FileSystemWritableFileStream`, `createWritable`, `msSaveBlob`, `msSaveOrOpenBlob`, `download`.
12302
+ - **Never use these property names:** `assign`, `values`, `entries`, `fromEntries` (so no `Object.assign`, `Object.values`, `Object.entries`, `Object.fromEntries`; use arrays and `map`/`forEach`).
12303
+ - **These words may not appear anywhere in view JavaScript, even in strings or comments:** `style`, `cssText`, `setProperty`, `removeProperty`, `insertRule`, `deleteRule`, `styleSheets`, `adoptedStyleSheets`, `animate`. Change appearance with CSS classes and `hidden`; put colors in the stylesheet.
12304
+ - **Property access with brackets needs a literal key:** `items[0]` and `record["title"]` are accepted; `items[index]` is rejected. Iterate with `map`/`forEach` or call `items.at(index)`.
12305
+ - **No named recursive functions** and no listeners on the global frame; attach events to declared controls.
12306
+ - **Dynamic elements:** `StillMade.render` can create `div`, `section`, `article`, `header`, `footer`, `p`, `span`, `strong`, `em`, `i`, `small`, `h1`, `h2`, `h3`, `h4`, `label`, `button`, `input`, `textarea`, `select`, `option`, `ul`, `ol`, `li`, `table`, `thead`, `tbody`, `tr`, `th`, `td`, `details`, `summary`, `output`, `progress`, `img`; give repeated items stable IDs derived from their identity. An `img` gets its picture when you set its `src` to the data URL `StillMade.previewImage` returns, after rendering; the renderer keeps Block-owned `src`. `previewImage` works on items of an `image[]` input.
12307
+ - **Shared writes are queued in order:** if one `StillMade.updateShared` write is rejected (conflict, read-only or timeout), the writes queued behind it are rejected too. Send dependent edits one at a time or through `StillMade.scheduleSharedEdit`, keep the person's draft, and retry from the latest `onShared` value. `{merge: true}` merges independent fields of an object value; create an absent field with the precondition `{exists: false}`.
12308
+ - **Limits:** HTML 64 KiB, CSS 32 KiB, JavaScript 64 KiB; 30 runs and 60 image previews per minute.
12309
+ ### Shared project interfaces
12310
+ Every meaningful edit and generated result must be visible to collaborators.
12311
+ Prefer ordinary manifest.ui controls and declared outputs when those express
12312
+ the interaction. Repository-local DOM state is not automatically shared.
12313
+ For a custom view, observe StillMade.onShared(shared => ...) to render saved
12314
+ outputs (including another participant's result), interface state and canEdit.
12315
+ For an ordinary static control, add a unique id and
12316
+ data-stillmade-share="fieldName"; the host owns its complete synchronization
12317
+ adapter. Use StillMade.bindShared(fieldName, elementId) only for a control created
12318
+ or replaced dynamically. Account for every ordinary static control; mark a truly
12319
+ device-only playback or filter control with data-stillmade-local.
12320
+ Use StillMade.setSharedDefaults for input-derived defaults without replacing a
12321
+ dirty draft. Do not overwrite bound control values during remote rendering or
12322
+ replace a bound input during a gesture. For structured interface values, use
12323
+ bounded StillMade.updateShared patches with stable named fields; observe them
12324
+ through onShared instead of keeping the only copy in a private variable.
12325
+ For nested objects, updateShared(values, exactBasePreconditions, {merge:true})
12326
+ can merge independent fields and arrays with stable string item IDs. Always use
12327
+ the actual base used to construct the edit. Overlapping changes still conflict;
12328
+ this does not merge simultaneous text typing or expand the 384 KiB state limit.
12329
+ Requests including before-values are limited to 800,000 UTF-8 bytes; queued and
12330
+ in-flight requests share a 2 MiB budget. Preserve drafts when a limit is reached.
12331
+ Give dynamic rows and controls stable IDs derived from item identity, not array
12332
+ position. StillMade.render preserves matching nodes, focus and selection across
12333
+ updates; it does not merge conflicting values or preserve an unshared draft.
12334
+ Never dispatch a run, click or paid integration in response to a remote update.
12335
+ Respect read-only access, preserve conflicting drafts, and surface write errors.
12336
+ Only explicit user actions may run this Block. Shared acknowledgements are
12337
+ locally queued, not proof of a durable server save. Preview localOnly state is
12338
+ not proof of multiplayer operation. Test two interfaces exchanging actual edits
12339
+ and saved outputs, permission loss, reconnect and conflicts before claiming
12340
+ collaboration support. Never claim arbitrary imported JavaScript state syncs
12341
+ merely because the package passed its computation fixtures.
12342
+ Example view for a package taking and returning text: {"html":"<main class=\"sm-block\"><output class=\"sm-stage\" id=\"result\" aria-live=\"polite\"></output><div class=\"sm-field\"><label for=\"source\">Draft</label><textarea id=\"source\"></textarea></div><div class=\"sm-actions\"><button class=\"sm-primary\" id=\"run\">Clean text</button><p class=\"sm-status\" id=\"status\" role=\"status\"></p></div></main>","css":"#result{place-items:start;min-height:120px;white-space:pre-wrap;overflow-wrap:anywhere}","javascript":"const source=document.getElementById('source'),button=document.getElementById('run'),result=document.getElementById('result'),status=document.getElementById('status');\nlet busy=false;\nStillMade.bindShared('draft','source');\nStillMade.onInput(input=>{StillMade.setSharedDefaults({draft:input.text||''});});\nStillMade.onShared(shared=>{button.disabled=busy||!shared.canEdit;result.textContent=shared.outputs.text||'';});\nbutton.addEventListener('click',async()=>{\n if(busy||!StillMade.getShared().canEdit)return;\n busy=true;button.disabled=true;status.dataset.state='loading';status.textContent='Cleaning';\n try{await StillMade.run({text:source.value});status.dataset.state='success';status.textContent='Ready';}\n catch(error){status.dataset.state='error';status.textContent=error.message||'Could not clean this draft.';}\n finally{busy=false;button.disabled=!StillMade.getShared().canEdit;}\n});"}.
12343
+
11940
12344
  ## Port fields
11941
12345
 
11942
12346
  ### Port fields