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.
- package/.stillmade/sdk-baseline.json +1 -1
- package/AGENTS.md +9 -3
- package/DESKTOP_CAPABILITIES.md +1 -1
- package/INTERFACE_DESIGN.md +167 -83
- package/MODULES.md +1 -1
- package/NATIVE_MCP.md +1 -1
- package/PASTE_TO_LLM.md +151 -12
- package/README.md +36 -0
- package/SDK.md +534 -130
- package/UI_AESTHETIC.md +64 -0
- package/blocks/native/sm.export-video/versions/2.1.0/package.js +580 -0
- package/blocks/native/sm.panel-strip/versions/2.4.0/package.js +1176 -0
- package/blocks/native/sm.panel-strip/versions/2.4.1/package.js +1316 -0
- package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +800 -0
- package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +920 -0
- package/examples/README.md +10 -0
- package/examples/custom-interface.stillmade.json +3 -3
- package/package.json +14 -1
- package/packages/block-cli/dev-host/assets/host.js +214 -28
- package/packages/block-cli/dev-host/assets/index.css +1 -1
- package/packages/block-cli/format.js +13 -2
- package/packages/block-cli/templates.js +54 -23
- package/packages/block-platform/block-readiness.js +5 -0
- package/packages/block-platform/brief-ports.js +29 -0
- package/packages/block-platform/catalog.js +32 -9
- package/packages/block-platform/current-semantic-source.js +7 -3
- package/packages/block-platform/flow-inspection.js +14 -4
- package/packages/block-platform/plain-descriptions.js +1 -1
- package/packages/block-platform/project-context.js +7 -0
- package/packages/block-platform/project-input-samples.js +3 -1
- package/packages/block-platform/project-inputs.js +11 -2
- package/packages/block-platform/project-start-input.js +46 -0
- package/packages/block-platform/step-groups.js +2 -0
- package/packages/block-platform/steps.js +55 -4
- package/packages/block-platform/type-preview.js +19 -3
- package/packages/block-platform/workspace-input-readset.js +6 -1
- package/packages/block-sdk/builder-contract.js +12 -6
- package/packages/block-sdk/collaboration-admission.js +4 -2
- package/packages/block-sdk/connection-adapters.js +35 -2
- package/packages/block-sdk/contracts.js +5 -2
- package/packages/block-sdk/examples.js +3 -3
- package/packages/block-sdk/first-party-identities.js +190 -0
- package/packages/block-sdk/first-party-packages.js +6 -1
- package/packages/block-sdk/index.d.ts +2 -0
- package/packages/block-sdk/index.js +1 -1
- package/packages/block-sdk/javascript.js +5 -1
- package/packages/block-sdk/platform-checks.js +13 -2
- package/packages/block-sdk/shared-agent-controls.js +5 -4
- package/packages/block-sdk/shared-controls.js +13 -9
- package/packages/block-sdk/view-design.js +104 -27
- package/packages/block-sdk/view.js +4 -2
- package/packages/design-system/block-kit.css +183 -0
- 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
|
-
|
|
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.**
|
|
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
|
|
172
|
-
2. **Read `
|
|
173
|
-
3. **Build
|
|
174
|
-
4. **Run the readiness gate before you call anything ready:** `
|
|
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.
|
|
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
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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,
|
|
1775
|
-
|
|
1776
|
-
|
|
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
|
|
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
|
-
|
|
5778
|
-
|
|
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
|
-
|
|
6642
|
+
npx stillmade-block <command> <target> [argument] [options]
|
|
6591
6643
|
```
|
|
6592
|
-
|
|
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
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
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
|
|
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.
|
|
7227
|
+
### Block builder contract (stillmade.block-builder@1.5.1)
|
|
7174
7228
|
|
|
7175
|
-
Contract `stillmade.block-builder@1.
|
|
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 `
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
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`,
|
|
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
|
-
|
|
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
|
-
- **
|
|
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). `
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
-
|
|
9350
|
-
|
|
9351
|
-
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
-
|
|
9362
|
-
-
|
|
9363
|
-
-
|
|
9364
|
-
-
|
|
9365
|
-
-
|
|
9366
|
-
-
|
|
9367
|
-
-
|
|
9368
|
-
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
- **
|
|
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
|