stillmade-sdk 0.1.36 → 0.1.38
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/AGENTS.md +8 -8
- package/IMAGE_DESCRIPTION.md +2 -0
- package/MODULES.md +68 -10
- package/NATIVE_MCP.md +18 -7
- package/PASTE_TO_LLM.md +121 -50
- package/PORTABLE.md +7 -3
- package/README.md +19 -6
- package/REMIX.md +6 -0
- package/SDK.md +226 -103
- package/TRANSCRIPTION.md +4 -2
- package/UI_AESTHETIC.md +2 -0
- package/api-index.json +1 -1
- package/blocks/narration/package.js +1 -1
- package/blocks/narration/versions/1.0.1/package.js +221 -0
- package/blocks/native/sm.animated-scene/versions/2.3.9/package.js +769 -0
- package/blocks/native/sm.board-editor/versions/2.2.11/package.js +551 -0
- package/blocks/native/sm.describe-media/versions/2.1.0/package.js +140 -0
- package/blocks/native/sm.panel-strip/versions/2.4.2/package.js +1316 -0
- package/blocks/native/sm.photo-notes/versions/1.1.0/package.js +170 -0
- package/blocks/native/sm.pipeline/versions/2.2.6/package.js +1095 -0
- package/blocks/screen-recorder/package.js +1 -1
- package/blocks/screen-recorder/versions/1.0.1/package.js +223 -0
- package/package.json +1 -1
- package/packages/block-cli/cli.js +91 -35
- package/packages/block-cli/dev-host/assets/host.js +35 -32
- package/packages/block-cli/dev-host/assets/index.css +1 -1
- package/packages/block-cli/dev-server.js +4 -0
- package/packages/block-cli/ready-gate.js +19 -4
- package/packages/block-cli/templates.js +33 -21
- package/packages/block-platform/block-readiness.js +10 -1
- package/packages/block-platform/catalog.js +14 -3
- package/packages/block-platform/chat-packages.js +6 -0
- package/packages/block-platform/flow-inspection.js +3 -1
- package/packages/block-platform/project-target.js +57 -0
- package/packages/block-platform/project-type-composer.js +4 -3
- package/packages/block-platform/project-types.js +5 -2
- package/packages/block-sdk/admission.js +4 -1
- package/packages/block-sdk/block-tier.js +23 -0
- package/packages/block-sdk/builder-contract.js +12 -10
- package/packages/block-sdk/capabilities.js +23 -10
- package/packages/block-sdk/collaboration-admission.js +3 -0
- package/packages/block-sdk/contracts.js +13 -1
- package/packages/block-sdk/example-media-data.js +16 -0
- package/packages/block-sdk/example-media.js +42 -0
- package/packages/block-sdk/first-party-identities.js +8 -0
- package/packages/block-sdk/first-party-packages.js +11 -1
- package/packages/block-sdk/index.d.ts +6 -2
- package/packages/block-sdk/local-admission.js +6 -5
- package/packages/block-sdk/media-transform.js +59 -3
- package/packages/block-sdk/mock-host.js +23 -2
- package/packages/block-sdk/mocks.js +4 -1
- package/packages/block-sdk/module-hosted.js +12 -2
- package/packages/block-sdk/port-types.d.ts +6 -0
- package/packages/block-sdk/port-types.js +32 -10
- package/packages/block-sdk/shared-agent-controls.js +25 -2
- package/packages/block-sdk/shared-controls.js +17 -3
- package/packages/block-sdk/testing.js +46 -3
- package/packages/block-sdk/view-design.js +67 -0
- package/packages/block-sdk/view-renderer.js +50 -8
- package/packages/design-system/block-kit.css +6 -4
package/SDK.md
CHANGED
|
@@ -53,15 +53,28 @@ Read [Blocks, Steps, and Project Types](/docs/build/concepts) before designing a
|
|
|
53
53
|
|
|
54
54
|
## Build an original SDK folder
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Download `/block-sdk/stillmade-sdk-0.1.0.zip` (the same files as `npm install --save-dev stillmade-sdk`) and extract it into a development directory. `stillmade-block` in every StillMade guide is its command line: `node <sdk>/packages/block-cli/cli.js`, where `<sdk>` is the extracted folder (run from inside it, as below), or `npx stillmade-block` after the npm install. Then run:
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
59
|
node packages/block-cli/cli.js create my-block javascript
|
|
60
60
|
node packages/block-cli/cli.js validate my-block
|
|
61
61
|
node packages/block-cli/cli.js test my-block
|
|
62
|
-
node packages/block-cli/cli.js
|
|
62
|
+
node packages/block-cli/cli.js dev my-block
|
|
63
|
+
node packages/block-cli/cli.js gate my-block
|
|
64
|
+
node packages/block-cli/cli.js pack my-block
|
|
63
65
|
```
|
|
64
66
|
|
|
67
|
+
`dev` shows the Block's interface in your browser and reloads it on every save.
|
|
68
|
+
`gate` is the readiness check StillMade runs; the Block is not ready until it
|
|
69
|
+
passes. Name the Block's own folder: `gate .` in a folder that also holds the
|
|
70
|
+
extracted SDK checks the SDK's examples too. `pack` runs the tests again and
|
|
71
|
+
writes the file to import, named by the Block's runtime:
|
|
72
|
+
|
|
73
|
+
| Runtime | `pack my-block` writes |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `module`, or a frame view with files in `src/view-files/` | `<id>-<version>.stillmade-module.json` |
|
|
76
|
+
| `recipe`, `javascript`, `capability`, `comfyui` | `<id>-<version>.stillmade.json` |
|
|
77
|
+
|
|
65
78
|
`create` supplies an editable starter. Replace its identity, task behavior, controls
|
|
66
79
|
and fixtures with your Block's requirements, retaining applicable SDK license
|
|
67
80
|
notices. The directory layout is:
|
|
@@ -77,9 +90,11 @@ my-block/
|
|
|
77
90
|
```
|
|
78
91
|
|
|
79
92
|
`src/run.js` is an isolated function body operating on `input`; it returns declared
|
|
80
|
-
outputs. The custom interface uses the supplied `StillMade` bridge. A
|
|
81
|
-
can read typed image pixels
|
|
82
|
-
|
|
93
|
+
outputs. The custom interface uses the supplied `StillMade` bridge. A small image
|
|
94
|
+
tool can read typed image pixels (at most one megapixel, as RGBA arrays), apply its
|
|
95
|
+
edits in `run.js`, show before/after images through `StillMade.previewImage`, and call
|
|
96
|
+
`StillMade.run` from its controls; a photo editor that keeps full-size photos is a
|
|
97
|
+
`module` Block with a frame view (MODULES.md).
|
|
83
98
|
Author those files directly under the SDK contract; external framework packages,
|
|
84
99
|
Node services and a separate standalone website are not this runtime's source.
|
|
85
100
|
No build/install command executes during import.
|
|
@@ -90,11 +105,11 @@ import that source unchanged. The app reads the folder with the same reader as
|
|
|
90
105
|
`src/view-files/` import as they pack; module files upload by SHA-256 before review.
|
|
91
106
|
`node_modules`, `.git` and other files outside the Block layout are ignored, so a
|
|
92
107
|
folder Codex or Claude Code built with its own tools imports as it is. Select the
|
|
93
|
-
Block directory, not an SDK checkout containing several example builds. Use at least two fixtures (normal and edge cases)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
Block directory, not an SDK checkout containing several example builds. Use at least two fixtures (normal and edge cases).
|
|
109
|
+
A new Block needs no `.stillmade-block` archive: `pack <folder> <file>.stillmade-block --original`
|
|
110
|
+
writes one only for your own recipe, JavaScript, capability or ComfyUI code
|
|
111
|
+
([PORTABLE.md](/block-sdk/PORTABLE.md)) and refuses a module Block. Direct folder
|
|
112
|
+
import and packed-file import retain the same runtime and interface. Security, source-rights confirmation and hosted execution approvals still
|
|
98
113
|
use the ordinary review path. They do not adapt the Block.
|
|
99
114
|
|
|
100
115
|
This is the implemented SDK, not the full future runtime catalog. It supports
|
|
@@ -144,11 +159,11 @@ Validation checks package structure. Tests execute every fixture through the iso
|
|
|
144
159
|
Open [Create import](/create/import), choose **Choose folder**, and select `my-block`. Its manifest, implementation, fixtures and optional `src/view.html`, `src/view.css`, `src/view.js` interface load unchanged. Review checks and the actual preview, then confirm import after the required source-rights and device review. The SDK contracts are the foundation from the first development step; there is no source adaptation phase.
|
|
145
160
|
|
|
146
161
|
### Package an alternate delivery file
|
|
147
|
-
|
|
162
|
+
Retain complete license notices and at least two fixtures (normal and edge cases), then run:
|
|
148
163
|
```sh
|
|
149
|
-
node packages/block-cli/cli.js pack ./my-block
|
|
164
|
+
node packages/block-cli/cli.js pack ./my-block
|
|
150
165
|
```
|
|
151
|
-
|
|
166
|
+
`pack` reruns the fixtures and writes `<id>-<version>.stillmade.json` for this JavaScript Block (a module Block, or a frame view with `src/view-files/`, writes `<id>-<version>.stillmade-module.json`). In StillMade choose Create, then Import, and pick that file for the same source and interface. Packaging does not rewrite the implementation. Output files are never silently overwritten. A new Block needs no `.stillmade-block` archive: `--original` writes one only for your own recipe, JavaScript, capability or ComfyUI code ([portable packages](/block-sdk/PORTABLE.md)) and refuses a module Block. Use repository evidence instead of `--original` when reusing third-party code.
|
|
152
167
|
|
|
153
168
|
### Photo-editor folder example
|
|
154
169
|
The extracted SDK contains `examples/original-photo-editor/`, including its actual custom interface, pixel processing and fixtures. Select that folder directly or try [its packaged original](/block-sdk/examples/original-photo-editor.stillmade-block). This bounded example adjusts brightness/color and rotates pixels with transparency. See [the supported original-authoring contract](/docs/build/original-blocks).
|
|
@@ -179,14 +194,14 @@ Invoke-WebRequest "<SDK URL>" -OutFile stillmade-sdk.zip; Expand-Archive stillma
|
|
|
179
194
|
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`.
|
|
180
195
|
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.
|
|
181
196
|
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.
|
|
182
|
-
4. **Run the readiness gate before you call anything ready:** `npx stillmade-block gate
|
|
197
|
+
4. **Run the readiness gate before you call anything ready:** `npx stillmade-block gate <name>` on the Block's own folder (`gate .` in a folder that also holds the extracted SDK checks the SDK's examples too). 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.
|
|
183
198
|
5. **Hand it over.** Give the person the full path of the `<name>` folder. In StillMade they choose **Import folder** and select that folder.
|
|
184
199
|
|
|
185
200
|
`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.
|
|
186
201
|
|
|
187
202
|
## Block builder contract
|
|
188
203
|
|
|
189
|
-
Contract `stillmade.block-builder@1.
|
|
204
|
+
Contract `stillmade.block-builder@1.11.0`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
|
|
190
205
|
|
|
191
206
|
### Source of truth
|
|
192
207
|
|
|
@@ -194,7 +209,7 @@ Treat the current SDK (`stillmade.block.json` manifest, `packages/block-sdk` con
|
|
|
194
209
|
|
|
195
210
|
### Reuse first
|
|
196
211
|
|
|
197
|
-
Before building, check what
|
|
212
|
+
Before building, check what exists (`find_existing_blocks` over MCP, or the Marketplace and your library) by the goal's inputs, results and operations. Only a `likely` or `verified-contract` match (a Block or a chain of two) does the job: use it. `partial` is a close Block to look at: remix it (license, notices and credit stay attached) or build only the missing step. Never republish another creator's source without remix lineage: public copies are refused. Build new when nothing does the job or the person wants their own, and name similar Blocks you considered.
|
|
198
213
|
|
|
199
214
|
### Three sizes of Block
|
|
200
215
|
|
|
@@ -208,11 +223,11 @@ Pick the runtime from the job. No hosted AI: a recipe or isolated `javascript`,
|
|
|
208
223
|
|
|
209
224
|
A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.
|
|
210
225
|
|
|
211
|
-
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval).
|
|
226
|
+
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). So Bible, Canvas and Editor are separate Steps after Chat.
|
|
212
227
|
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.
|
|
213
228
|
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.
|
|
214
229
|
|
|
215
|
-
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
|
|
230
|
+
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. A Block with its own interface (a workspace, editor or picker) is always its own Step; two such Steps are never one. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
|
|
216
231
|
|
|
217
232
|
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.
|
|
218
233
|
|
|
@@ -226,7 +241,7 @@ Translate the request into an explicit capability contract before implementation
|
|
|
226
241
|
|
|
227
242
|
### Blocks compose
|
|
228
243
|
|
|
229
|
-
A Block is one job: not a mini-app of many jobs and not a fragment of one. Its outputs become other Blocks' inputs and its inputs come from other Blocks, project context or the user. Use the typed port system: shared StillMade types (`image`, `video`, `audio`, `asset`, `script`, `shot[]`, ...), a `
|
|
244
|
+
A Block is one job: not a mini-app of many jobs and not a fragment of one. Its outputs become other Blocks' inputs and its inputs come from other Blocks, project context or the user. Use the typed port system: shared StillMade types (`image`, `video`, `audio`, `asset`, `script`, `shot[]`, ...), `required`, a short `description`, and a `semantic` only where it narrows what fits. Mark exactly one primary input and one primary output so Project Type Steps connect automatically, and use `kind: "task"`: a `workspace` Block owns a project document and is never connected automatically. Inputs marked `context` read the project and cannot receive a connection from another Step. Output reusable StillMade values: media references (`{kind,assetId,versionId,url}`) and structured production values with `schemaVersion: 1`, preserving provenance, versions and relationships downstream Blocks need. Never make ordinary users wire JSON, copy IDs, understand internal paths or translate one Block's output into another: `createUniversalResolver()` resolves inputs from compatible upstream outputs, permitted project context and, for optional non-context inputs, `StillMade.requestFromUser`.
|
|
230
245
|
|
|
231
246
|
### Feature map
|
|
232
247
|
|
|
@@ -246,7 +261,7 @@ Give the Block a short, plain name of 40 characters or fewer that says what it d
|
|
|
246
261
|
|
|
247
262
|
### Interface
|
|
248
263
|
|
|
249
|
-
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
|
|
264
|
+
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 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, (i) on unclear controls. These compact rules are for a job or an ingredient. Size the view to the Block's tier (block-tier.js): an app, a workspace a person works in for a while (video editor, 3D configurator, CAD or drawing tool), looks and works like that kind of app instead of a compact page: its canvas or stage fills the view, with the panels, toolbars, inspector or timeline such an app has, direct manipulation (select, drag, zoom), keyboard shortcuts and undo; it keeps the host appearance and Block kit controls, its panels may scroll, and on a phone its tools fold into sheets while the canvas stays usable. Never shrink an app into a form of fields and one button. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action="what it does"` and label each field: Chat presses and fills them. Expose the capability, not the implementation: sensible defaults, previews, progress, clear errors, visible outputs. Show no raw JSON, internal IDs, filesystem details, provider terms or developer settings unless the Block is a developer tool. When a person chooses among options they need to hear or see (voices, music, fonts, looks, templates, images), let them preview each option before choosing: a sample or thumbnail from the Block's own files or the host's free samples, never a paid generation. Skip previews only when there is nothing to preview. Choices StillMade owns (voices, models in the run review) already show samples. Follow the host appearance (Light, Dark and White) and one elastic layout: stacked on a 320px phone with no sideways scroll (result first, action pinned), and from 860px the controls left (≤380px), the result filling the right. Short settings sit side by side, each label beside its control (`sm-field`, `sm-settings`; three or four short options as one `data-segmented` row); many items are one line each (`sm-list`) that open in place; input files are small tiles (`sm-gallery data-size="small"`); on a phone chip rows scroll sideways and long lists show three. Compacting moves controls, never deletes them.
|
|
250
265
|
|
|
251
266
|
### Shared editing
|
|
252
267
|
|
|
@@ -329,7 +344,7 @@ A Block is done only when every applicable item holds. Passing a compile, one un
|
|
|
329
344
|
## Blocks, Steps, and Project Types
|
|
330
345
|
|
|
331
346
|
### Block
|
|
332
|
-
A reusable production workspace or capability. The built-in catalog consists of full screens: Script Writer, Voiceover, Shots, Blueprint, Canvas, Editor, Image Panels, White Board Motion, and Assets. Chat is the
|
|
347
|
+
A reusable production workspace or capability. The built-in catalog consists of full screens: Script Writer, Voiceover, Shots, Blueprint, Canvas, Editor, Image Panels, White Board Motion, and Assets. Chat is not a Block: it is the project's own conversation, which every Project Type starts with. Internal canvas nodes and individual editor controls are implementation details inside their workspace, not separate built-in Blocks.
|
|
333
348
|
|
|
334
349
|
External processing Blocks, such as Thicken Line Art or lip sync, add a capability to the workflow. A Block declares input and output ports, permissions, controls, and an implementation runtime.
|
|
335
350
|
|
|
@@ -342,11 +357,11 @@ An ordered production workflow. Its persisted `stages` array stores Steps for co
|
|
|
342
357
|
### One job per Step
|
|
343
358
|
A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.
|
|
344
359
|
|
|
345
|
-
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval).
|
|
360
|
+
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). So Bible, Canvas and Editor are separate Steps after Chat.
|
|
346
361
|
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.
|
|
347
362
|
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.
|
|
348
363
|
|
|
349
|
-
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
|
|
364
|
+
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. A Block with its own interface (a workspace, editor or picker) is always its own Step; two such Steps are never one. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
|
|
350
365
|
|
|
351
366
|
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.
|
|
352
367
|
|
|
@@ -360,9 +375,9 @@ An image-producing Block can connect to an image-consuming Block. Primary ports
|
|
|
360
375
|
See [types and compatibility](/docs/reference/types), [context](/docs/build/context), and [Project Type construction](/docs/project-types/build).
|
|
361
376
|
|
|
362
377
|
### Complete workspace Blocks
|
|
363
|
-
Built-in Blocks are the existing complete production screens. A Guided Project Type
|
|
378
|
+
Built-in Blocks are the existing complete production screens. A Guided Project Type starts with Chat, the project's conversation, then Voiceover → Shots → Blueprint → Canvas → Editor; export belongs to Editor. The reusable Script Writer is the full Narrative Compiler; it and the legacy Script adapter do not replace Chat. Canvas remains the home of persistent visual references and character consistency.
|
|
364
379
|
|
|
365
|
-
The Block boundary follows the complete user-placeable Step. Package and remix the complete workspace, including its related tools. For example, Motion Graphics remains inside the Editor; Image Panels, Blueprint, and the Whiteboard Step are each one Block. The White Board Motion Project Type
|
|
380
|
+
The Block boundary follows the complete user-placeable Step. Package and remix the complete workspace, including its related tools. For example, Motion Graphics remains inside the Editor; Image Panels, Blueprint, and the Whiteboard Step are each one Block. The White Board Motion Project Type starts with Chat, then three complete Blocks: Voiceover, Whiteboard, and Editor, with no extra Project-Type Block. Historical component IDs may remain resolvable for saved projects without becoming discoverable Blocks.
|
|
366
381
|
|
|
367
382
|
A source snapshot or host alias alone is not portable. A portable workspace owns its interface and executes through an installable SDK runtime. The first-party sm.blueprint@2.0.0, sm.board-editor@2.0.0, sm.editor@2.0.0, and sm.pipeline@2.0.0 packages in the downloadable SDK demonstrate complete sandboxed workspaces using typed documents and the public before/after workspace proposal contract. Whiteboard retains all boards, placed media, timing, transitions, motion settings, and custom fields as one remixable Block. Editor retains Motion Graphics and every other editing tool inside the Editor Block. Canvas retains its native content and layout while the Project Type builder owns Step placement and wiring. Package-supplied React bundles are not exposed; use [custom sandboxed HTML interfaces](/docs/build/custom-interface).
|
|
368
383
|
|
|
@@ -904,6 +919,10 @@ the workspace cancel unfinished display requests. Use a local revision counter
|
|
|
904
919
|
in asynchronous UI code so a late result cannot repaint an earlier selection.
|
|
905
920
|
|
|
906
921
|
StillMade's host image controls remain available with a custom interface.
|
|
922
|
+
On a project Step the custom interface fills the Step: the host's file inputs and "Apply to"
|
|
923
|
+
open from the Step's inputs button, and while a required file is missing a "Choose <input>"
|
|
924
|
+
button sits above the interface. Show your own empty state
|
|
925
|
+
in the view ("Add a photo") and never describe where the host's controls are.
|
|
907
926
|
A project lets the user select a compatible authorized image or upload one;
|
|
908
927
|
connected/context bindings remain read-only. A standalone preview keeps uploaded
|
|
909
928
|
files local to that preview. Host uploads accept PNG, JPEG, WebP or GIF up to
|
|
@@ -2141,7 +2160,7 @@ my-block/
|
|
|
2141
2160
|
main.js the entry (bundle npm dependencies into it)
|
|
2142
2161
|
filters.wasm optional WebAssembly and data files
|
|
2143
2162
|
module.json optional: {"entry": "other.js", "threads": true}
|
|
2144
|
-
tests/fixtures.json [{name, input, expected}]
|
|
2163
|
+
tests/fixtures.json [{name, input, expected, mocks?}]
|
|
2145
2164
|
```
|
|
2146
2165
|
|
|
2147
2166
|
Start one with `stillmade-block create my-block module`. Bundle npm
|
|
@@ -2331,9 +2350,15 @@ false and `SharedArrayBuffer` is missing, so check it and fall back to one threa
|
|
|
2331
2350
|
```sh
|
|
2332
2351
|
stillmade-block test my-block # runs fixtures in a local Node worker
|
|
2333
2352
|
stillmade-block preview my-block input.json
|
|
2353
|
+
stillmade-block dev my-block # the interface in your browser, reloaded on save
|
|
2354
|
+
stillmade-block gate my-block # StillMade's readiness check
|
|
2334
2355
|
stillmade-block pack my-block # writes <id>-<version>.stillmade-module.json
|
|
2335
2356
|
```
|
|
2336
2357
|
|
|
2358
|
+
`pack my-block` is the only packing step for a module Block, and it never takes
|
|
2359
|
+
`--original` (original `.stillmade-block` archives hold recipe, JavaScript,
|
|
2360
|
+
capability and ComfyUI source only and refuse a module Block).
|
|
2361
|
+
|
|
2337
2362
|
`test` and `preview` run your code on your machine in a disposable Node worker
|
|
2338
2363
|
with the same host API and run limits (not a security sandbox; it is your own
|
|
2339
2364
|
code). `files.read`, `files.write`, `media.transform`, actions and hosted calls need StillMade, or
|
|
@@ -4420,6 +4445,11 @@ Every number below is read from the SDK constant named under its heading, the sa
|
|
|
4420
4445
|
| Decoded samples | 201,326,592 |
|
|
4421
4446
|
| Decode sample rates | 8,000, 16,000, 22,050, 24,000, 32,000, 44,100, 48,000 |
|
|
4422
4447
|
| Thumbnail width (pixels) | 4,096 |
|
|
4448
|
+
| Joined image side (pixels) | 4,096 |
|
|
4449
|
+
| Images in one joined image | 64 |
|
|
4450
|
+
| Text pieces in one joined image | 64 |
|
|
4451
|
+
| Document pages read | 500 |
|
|
4452
|
+
| Document text (characters) | 4,000,000 |
|
|
4423
4453
|
|
|
4424
4454
|
### Files, documents and tables
|
|
4425
4455
|
|
|
@@ -4599,7 +4629,7 @@ stand-ins:
|
|
|
4599
4629
|
| `actions` | `stillmade.actions.run`, by operation | The action result (`{value, failures}` for `media.generate`) |
|
|
4600
4630
|
| `network` | `stillmade.net.fetch` | Recorded responses: method, url (a trailing `*` matches the rest), status, headers and one of `json`, `text` or `base64` |
|
|
4601
4631
|
| `connections` | `stillmade.connections.call`, by `appId/operation` | The JSON result |
|
|
4602
|
-
| `files` | `files.read`, by asset ID or URL | A path inside the Block folder |
|
|
4632
|
+
| `files` | `files.read`, by asset ID or URL | A path inside the Block folder, or `{"text": "..."}` with a text file's contents |
|
|
4603
4633
|
| `storage` | `stillmade.storage` | Values stored before the run |
|
|
4604
4634
|
| `project` | `stillmade.project.read` | A project snapshot |
|
|
4605
4635
|
| `transforms` | `stillmade.media.transform`, by operation | The transform result (`audio.decode` on a WAV file decodes it for real) |
|
|
@@ -5832,7 +5862,7 @@ Declare an output with `type: "file-bundle"` (or `file-bundle[]`). The shared Bl
|
|
|
5832
5862
|
preview automatically offers Download, including when the Block has a custom
|
|
5833
5863
|
view. A file result is inert data: it grants no filesystem access and never mounts
|
|
5834
5864
|
HTML, runs code or triggers a download before the user clicks. This output type
|
|
5835
|
-
is separate from the
|
|
5865
|
+
is separate from the file `pack` writes to install the Block itself.
|
|
5836
5866
|
|
|
5837
5867
|
```json
|
|
5838
5868
|
{
|
|
@@ -6029,6 +6059,8 @@ export function testPackage(pkg: BlockPackage, options?: { timeoutMs?: number; c
|
|
|
6029
6059
|
export const SDK_VERSION: '0.1.0';
|
|
6030
6060
|
export const ROOT_CHAT_ID: 'stillmade.starting-chat';
|
|
6031
6061
|
export function stableStringify(value: unknown): string;
|
|
6062
|
+
/** Throws when any text or field name in a package holds the NUL character (\u0000), which StillMade cannot store; the message names where it is (tests.1.mocks.files.tiny-1.text). Import review, validate, test, pack and ready run it. */
|
|
6063
|
+
export function assertStorableText(value: unknown): void;
|
|
6032
6064
|
export function validateManifest(value: unknown): { valid: boolean; errors: { path: string; message: string }[] };
|
|
6033
6065
|
export function validateRecipe(recipe: BlockPackage['recipe']): true;
|
|
6034
6066
|
export interface RecipeProgress {stepId:string;complete:number;total:number}
|
|
@@ -6655,9 +6687,11 @@ export type MediaTransformRequest =
|
|
|
6655
6687
|
| {operation:'video.cut'; source:SavedMediaRef; keep:TimeRange[]; settings?:RenderSettings}
|
|
6656
6688
|
| {operation:'video.concat'; sources:SavedMediaRef[]; settings?:RenderSettings}
|
|
6657
6689
|
| {operation:'video.resize'; source:SavedMediaRef; settings:RenderSettings}
|
|
6658
|
-
| {operation:'image.thumbnail'; source:SavedMediaRef; at?:number; width?:number}
|
|
6690
|
+
| {operation:'image.thumbnail'; source:SavedMediaRef; at?:number; width?:number}
|
|
6691
|
+
| {operation:'document.text'; source:SavedMediaRef}
|
|
6692
|
+
| {operation:'image.compose'; width:number; height:number; background?:string; format?:'png'|'jpeg'; layers:{source:SavedMediaRef; x:number; y:number; width:number; height:number; fit?:'cover'|'contain'}[]; text?:{text:string; x:number; y:number; size?:number; color?:string; background?:string; align?:'left'|'center'|'right'; weight?:'normal'|'bold'; width?:number}[]};
|
|
6659
6693
|
export type DecodedAudio = {sampleRate:number; duration:number; samples:Float32Array};
|
|
6660
|
-
export const MEDIA_TRANSFORM_LIMITS: Readonly<{ranges:500;sources:50;durationSeconds:21600;decodeSamples:201326592;sampleRates:readonly number[];thumbnailWidth:4096}>;
|
|
6694
|
+
export const MEDIA_TRANSFORM_LIMITS: Readonly<{ranges:500;sources:50;durationSeconds:21600;decodeSamples:201326592;sampleRates:readonly number[];thumbnailWidth:4096;composeSide:4096;composeLayers:64;composeText:64;documentPages:500;documentCharacters:4000000}>;
|
|
6661
6695
|
export const MEDIA_TRANSFORM_OPERATIONS: readonly MediaTransformRequest['operation'][];
|
|
6662
6696
|
export function mediaTransformRequest(value: MediaTransformRequest): Required<MediaTransformRequest>;
|
|
6663
6697
|
export function mediaTransformSources(request: MediaTransformRequest): SavedMediaRef[];
|
|
@@ -6775,7 +6809,8 @@ That is the form after `npm install --save-dev stillmade-sdk`. From a downloaded
|
|
|
6775
6809
|
| run target | Optional input.json, --mock; Blocks also accept --state file and --save-state new-file | Execute once and print the outputs (preview is the same command) |
|
|
6776
6810
|
| dev folder | Optional --port 4800, --build "npm run build", and --live (with --site) to review each saved version in your StillMade account | [Preview the interface with hot reload](/docs/build/local-preview) |
|
|
6777
6811
|
| ready target | None | Run the [Block builder contract](/docs/build/block-builder-contract) readiness check: fixtures, bad input, cancellation, reopen, composition with real catalog Blocks; exits non-zero when FAILED or BLOCKED |
|
|
6778
|
-
|
|
|
6812
|
+
| gate folder | None | Run ready on every new or edited Block under the folder; a Block is not ready until it passes. Name the Block's own folder: `gate .` in a folder that also holds the extracted SDK checks the SDK's examples too |
|
|
6813
|
+
| pack target | Optional output path. A module Block (or a frame view with src/view-files/) writes `<id>-<version>.stillmade-module.json`, other runtimes `<id>-<version>.stillmade.json`; --original with an output.stillmade-block path writes an archive of your own recipe, JavaScript, capability or ComfyUI code and refuses a module Block | Require passing tests, then write the file to import |
|
|
6779
6814
|
| remix target | Optional output path | Write an editable draft copy with retained license and pinned lineage; nothing is executed |
|
|
6780
6815
|
|
|
6781
6816
|
### Templates
|
|
@@ -6797,7 +6832,7 @@ That is the form after `npm install --save-dev stillmade-sdk`. From a downloaded
|
|
|
6797
6832
|
| music | capability | Hosted music generation (audio.music). |
|
|
6798
6833
|
| sfx | capability | Hosted sound effects (audio.sfx). |
|
|
6799
6834
|
| transcribe | capability | Hosted transcription with word timings (audio.transcribe). |
|
|
6800
|
-
| describe-image | capability | Describe or compare up to four images (image.describe). |
|
|
6835
|
+
| describe-image | capability | Describe or compare up to four images (image.describe) on the person's own OpenRouter key; analyze-media runs on StillMade credits. |
|
|
6801
6836
|
| analyze-media | capability | Visual evidence from images or a video (media.analyze). |
|
|
6802
6837
|
| web-fetch | capability | Answer a question from one web page (web.fetch). |
|
|
6803
6838
|
| web-research | capability | Research a topic with sources (web.research). |
|
|
@@ -6858,7 +6893,7 @@ A portable image reference is not decoded pixel data. The host must resolve it b
|
|
|
6858
6893
|
5. Test actual supported media in the host workspace.
|
|
6859
6894
|
|
|
6860
6895
|
### Every SDK error code
|
|
6861
|
-
Generated from the SDK source (
|
|
6896
|
+
Generated from the SDK source (199 codes). The message is one example; the actual message names the exact field or limit.
|
|
6862
6897
|
|
|
6863
6898
|
| Code | Example message | Raised in |
|
|
6864
6899
|
| --- | --- | --- |
|
|
@@ -6973,6 +7008,7 @@ Generated from the SDK source (198 codes). The message is one example; the actua
|
|
|
6973
7008
|
| MODULE_BUNDLE | Only module Blocks and frame views with assets are bundled this way | `module-bundle` |
|
|
6974
7009
|
| MODULE_ERROR | The module stopped without returning: it waits on something that never finishes. | `module-quickjs` |
|
|
6975
7010
|
| MODULE_FILE | … is missing from the local files | `module-node`, `module-quickjs`, `module-runtime` |
|
|
7011
|
+
| MODULE_FILE_READ | The {base64} file for … must be plain base64 of at most 256 KB. | `mock-host` |
|
|
6976
7012
|
| MODULE_HOST_UNAVAILABLE | Local files need a file reader. | `mock-host` |
|
|
6977
7013
|
| MODULE_HOSTED | A module Block identity is required | `module-hosted` |
|
|
6978
7014
|
| MODULE_PERMISSION | This Block did not declare … in permissions.capabilities | `module-hosted` |
|
|
@@ -7076,7 +7112,7 @@ Generated from the SDK source (198 codes). The message is one example; the actua
|
|
|
7076
7112
|
| Hosted image.generate | One prompt and optional reference images to a stored PNG with any catalog image model and its settings; StillMade credits or account key, and actual image review |
|
|
7077
7113
|
| Hosted video.generate | One prompt and optional first/last frames to a measured stored video clip with any catalog video model and its settings; StillMade credits and actual video review |
|
|
7078
7114
|
| Hosted image.describe | One to four owned images to bounded production notes; host-selected model/payment and actual description review |
|
|
7079
|
-
| Hosted media.analyze | One owned video (optionally one time range), image or up to four images to structured visual evidence: summary, timeline, observations, on-screen text and issues, with StillMade credits |
|
|
7115
|
+
| Hosted media.analyze | One owned video (optionally one time range), image or up to four images to structured visual evidence: summary, timeline, observations, on-screen text and issues, with StillMade credits; detail "check" gives one to four photos a short answer each at one price per call |
|
|
7080
7116
|
| Hosted web.fetch | One public web page read by the host and answered against the Block's instruction, with StillMade credits; no cookies, keys or private addresses |
|
|
7081
7117
|
| Hosted web.research | One topic searched on the web and summarized with cited sources, with StillMade credits |
|
|
7082
7118
|
| Hosted timeline.propose | Bounded typed context to strict timeline-edit JSON; host-selected model/payment, stale-before checks, preview and Apply |
|
|
@@ -7168,7 +7204,9 @@ available alternatives.
|
|
|
7168
7204
|
|
|
7169
7205
|
## Versions, checkpoints, and remixes
|
|
7170
7206
|
|
|
7171
|
-
Fixtures are `{name,input,expected}`
|
|
7207
|
+
Fixtures are `{name,input,expected}` for recipe and JavaScript Blocks, `{name,input,expected,mocks}`
|
|
7208
|
+
(`mocks` optional) for module Blocks and `{name,input,expectations}` for capability and
|
|
7209
|
+
ComfyUI Blocks; expected equals the full output object.
|
|
7172
7210
|
Include 1–50 tests. `testPackage()` returns a SHA-256 content digest, pass/fail and
|
|
7173
7211
|
structured results. Changing source, manifest or fixtures invalidates that report.
|
|
7174
7212
|
CLI `pack` requires passing tests. Releases are immutable and require tests against
|
|
@@ -7347,9 +7385,9 @@ Ask the assistant to return source files or one `.stillmade.json` package. Run t
|
|
|
7347
7385
|
|
|
7348
7386
|
<!-- builder-contract:start (generated by npm run build:sdk; do not edit) -->
|
|
7349
7387
|
|
|
7350
|
-
### Block builder contract (stillmade.block-builder@1.
|
|
7388
|
+
### Block builder contract (stillmade.block-builder@1.11.0)
|
|
7351
7389
|
|
|
7352
|
-
Contract `stillmade.block-builder@1.
|
|
7390
|
+
Contract `stillmade.block-builder@1.11.0`. Humans, coding agents, imported source, GitHub repositories and generated code all adapt to the Block platform; anything that becomes a StillMade Block obeys this contract.
|
|
7353
7391
|
|
|
7354
7392
|
### Source of truth
|
|
7355
7393
|
|
|
@@ -7357,7 +7395,7 @@ Treat the current SDK (`stillmade.block.json` manifest, `packages/block-sdk` con
|
|
|
7357
7395
|
|
|
7358
7396
|
### Reuse first
|
|
7359
7397
|
|
|
7360
|
-
Before building, check what
|
|
7398
|
+
Before building, check what exists (`find_existing_blocks` over MCP, or the Marketplace and your library) by the goal's inputs, results and operations. Only a `likely` or `verified-contract` match (a Block or a chain of two) does the job: use it. `partial` is a close Block to look at: remix it (license, notices and credit stay attached) or build only the missing step. Never republish another creator's source without remix lineage: public copies are refused. Build new when nothing does the job or the person wants their own, and name similar Blocks you considered.
|
|
7361
7399
|
|
|
7362
7400
|
### Three sizes of Block
|
|
7363
7401
|
|
|
@@ -7371,11 +7409,11 @@ Pick the runtime from the job. No hosted AI: a recipe or isolated `javascript`,
|
|
|
7371
7409
|
|
|
7372
7410
|
A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.
|
|
7373
7411
|
|
|
7374
|
-
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval).
|
|
7412
|
+
1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). So Bible, Canvas and Editor are separate Steps after Chat.
|
|
7375
7413
|
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.
|
|
7376
7414
|
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.
|
|
7377
7415
|
|
|
7378
|
-
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
|
|
7416
|
+
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. A Block with its own interface (a workspace, editor or picker) is always its own Step; two such Steps are never one. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.
|
|
7379
7417
|
|
|
7380
7418
|
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.
|
|
7381
7419
|
|
|
@@ -7389,7 +7427,7 @@ Translate the request into an explicit capability contract before implementation
|
|
|
7389
7427
|
|
|
7390
7428
|
### Blocks compose
|
|
7391
7429
|
|
|
7392
|
-
A Block is one job: not a mini-app of many jobs and not a fragment of one. Its outputs become other Blocks' inputs and its inputs come from other Blocks, project context or the user. Use the typed port system: shared StillMade types (`image`, `video`, `audio`, `asset`, `script`, `shot[]`, ...), a `
|
|
7430
|
+
A Block is one job: not a mini-app of many jobs and not a fragment of one. Its outputs become other Blocks' inputs and its inputs come from other Blocks, project context or the user. Use the typed port system: shared StillMade types (`image`, `video`, `audio`, `asset`, `script`, `shot[]`, ...), `required`, a short `description`, and a `semantic` only where it narrows what fits. Mark exactly one primary input and one primary output so Project Type Steps connect automatically, and use `kind: "task"`: a `workspace` Block owns a project document and is never connected automatically. Inputs marked `context` read the project and cannot receive a connection from another Step. Output reusable StillMade values: media references (`{kind,assetId,versionId,url}`) and structured production values with `schemaVersion: 1`, preserving provenance, versions and relationships downstream Blocks need. Never make ordinary users wire JSON, copy IDs, understand internal paths or translate one Block's output into another: `createUniversalResolver()` resolves inputs from compatible upstream outputs, permitted project context and, for optional non-context inputs, `StillMade.requestFromUser`.
|
|
7393
7431
|
|
|
7394
7432
|
### Feature map
|
|
7395
7433
|
|
|
@@ -7409,7 +7447,7 @@ Give the Block a short, plain name of 40 characters or fewer that says what it d
|
|
|
7409
7447
|
|
|
7410
7448
|
### Interface
|
|
7411
7449
|
|
|
7412
|
-
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
|
|
7450
|
+
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 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, (i) on unclear controls. These compact rules are for a job or an ingredient. Size the view to the Block's tier (block-tier.js): an app, a workspace a person works in for a while (video editor, 3D configurator, CAD or drawing tool), looks and works like that kind of app instead of a compact page: its canvas or stage fills the view, with the panels, toolbars, inspector or timeline such an app has, direct manipulation (select, drag, zoom), keyboard shortcuts and undo; it keeps the host appearance and Block kit controls, its panels may scroll, and on a phone its tools fold into sheets while the canvas stays usable. Never shrink an app into a form of fields and one button. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action="what it does"` and label each field: Chat presses and fills them. Expose the capability, not the implementation: sensible defaults, previews, progress, clear errors, visible outputs. Show no raw JSON, internal IDs, filesystem details, provider terms or developer settings unless the Block is a developer tool. When a person chooses among options they need to hear or see (voices, music, fonts, looks, templates, images), let them preview each option before choosing: a sample or thumbnail from the Block's own files or the host's free samples, never a paid generation. Skip previews only when there is nothing to preview. Choices StillMade owns (voices, models in the run review) already show samples. Follow the host appearance (Light, Dark and White) and one elastic layout: stacked on a 320px phone with no sideways scroll (result first, action pinned), and from 860px the controls left (≤380px), the result filling the right. Short settings sit side by side, each label beside its control (`sm-field`, `sm-settings`; three or four short options as one `data-segmented` row); many items are one line each (`sm-list`) that open in place; input files are small tiles (`sm-gallery data-size="small"`); on a phone chip rows scroll sideways and long lists show three. Compacting moves controls, never deletes them.
|
|
7413
7451
|
|
|
7414
7452
|
### Shared editing
|
|
7415
7453
|
|
|
@@ -7503,7 +7541,7 @@ Every finished Block must support automatic remixing through the standard SDK co
|
|
|
7503
7541
|
|
|
7504
7542
|
### Package format
|
|
7505
7543
|
|
|
7506
|
-
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"]}}.
|
|
7544
|
+
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"]}}. Port types: text, number, integer, boolean, json, object, image, video, audio, asset, script, transcript, brief, bible, shot-plan, panel-document, board-document, scene-document, timeline, timeline-edit, file-bundle, workspace-edit, timeline-range, research, character, location, style, pipeline, approval, scene, shot, mask, depth, pose, metadata, project-context, file, document, table, url, date, color. A value is exactly: text: a string; number: a number; integer: a whole number; boolean: true or false; json: any JSON value; object: a plain JSON object; image: {kind: 'image'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; width?: number; height?: number}; video: {kind: 'video'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; width?: number; height?: number; duration?: number}; audio: {kind: 'audio'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; duration?: number; sampleRate?: number; channels?: number}; asset: {kind: 'image' | 'video' | 'audio' | 'asset'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string}; script: {schemaVersion: 1; text: string; [key: string]: unknown}; transcript: {schemaVersion: 1; text: string; words: Array<{text: string; start: number; end: number}>}; file: {kind: 'file'; assetId: string; versionId: string; mimeType: 'application/pdf' | 'text/plain' | 'text/csv' | 'text/markdown' | 'application/json' | 'application/zip'; url?: string; name?: string; bytes?: number}; document: {schemaVersion: 1; format: 'markdown' | 'plain'; text: string; title?: string}; table: {schemaVersion?: 1; columns: string[]; rows: Array<Record<string, string | number | boolean | null>>}; url: an absolute http or https address string; date: an ISO 8601 date string ('2026-10-09' or '2026-10-09T14:30'); color: a hex color string ('#ff8800' or '#ff8800cc'). A file value is a saved file: one the person gives the Block (a module Block reads its bytes with stillmade.files.read) or what stillmade.files.write returns in a module Block; an image may also be decoded RGBA pixels {width, height, data} in recipe and JavaScript Blocks, which hold only 4 MiB of JSON (a picture of about 300 by 300 pixels), so a Block that joins photos into one picture (side by side, a grid, a contact sheet, a poster) is a module Block calling media.transform image.compose. Every other type is {schemaVersion: 1; [key: string]: unknown} with the fields its own section gives. A type ending in [] is a plain array of those values (audio[] is a list of audio references, never objects that wrap them). Include test {name,input,expected} where expected is output bindings.
|
|
7507
7545
|
|
|
7508
7546
|
### Feature map
|
|
7509
7547
|
|
|
@@ -7511,7 +7549,7 @@ Every Block lists its separate tools in manifest.features, one entry per tool or
|
|
|
7511
7549
|
|
|
7512
7550
|
### Port meanings
|
|
7513
7551
|
|
|
7514
|
-
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
|
|
7552
|
+
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 required:boolean, a short plain-language description and a semantic (import refuses an input without one, and gate checks it). An input accepts values declared with its meaning or a narrower one: an input for any photo a Step hands on uses source_image, as StillMade's Image editor does (generated_image and processed_image are kinds of source_image), and a photo editor's result is processed_image. 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, and always on a json or object output (import refuses one without it): 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 ?. A value of a structured type (transcript, script, brief, shot-plan and the like) is an object with schemaVersion:1, in code and fixtures; a transcript as the Transcription Block hands it on is {schemaVersion:1,text:"...",words:[{text:"Today",start:0,end:0.4}]}, times in seconds. 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.
|
|
7515
7553
|
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.
|
|
7516
7554
|
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.
|
|
7517
7555
|
|
|
@@ -7524,14 +7562,14 @@ For hosted text generation use {manifest,capability,tests}, runtime:"capability"
|
|
|
7524
7562
|
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}}}]}.
|
|
7525
7563
|
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}}}]}.
|
|
7526
7564
|
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}}}]}.
|
|
7527
|
-
For image analysis, use runtime:"capability", operation:"
|
|
7565
|
+
For image or video analysis (what a photo shows, which photos show different angles, the words on a menu or sign), use runtime:"capability", operation:"media.analyze" and permissions.capabilities:["media.analyze"]: StillMade credits pay for it, while image.describe runs only on the person's own OpenRouter key, so never use it. Declare one primary image input, one image[] input of one to four images, or one video input (with optional start and end number inputs, bound through capability.range:{start:{"$input":"start"},end:{"$input":"end"}}), and one primary text or json output. capability:{schemaVersion:1,operation:"media.analyze",media:{"$input":"photo"},question:"what to look for, at most 2,000 characters",output:"notes"}; a text output gets each picture's summary, a json output {schemaVersion:1,items}. For a quick rating of photos (sorting or picking), add detail:"check": images only, one short answer each, one credit per call of up to four photos instead of four credits per picture. The host loads only owned media and controls provider, model, payment and approval. Tests give saved references and expect json that includes summary, or text length bounds, never invented live answers. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.check-clip","version":"1.0.0","name":"Check a clip","description":"Answer a production question about a saved video clip, with timestamps.","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":{"clip":{"type":"video","primary":true,"semantic":"source_video","required":true,"description":"Saved video to check."},"start":{"type":"number","required":false,"min":0,"semantic":"range_start","description":"Optional start of the range to look at closely, in seconds."},"end":{"type":"number","required":false,"min":0,"semantic":"range_end","description":"Optional end of the range, in seconds (at most 12 seconds after the start)."}},"outputs":{"report":{"type":"json","primary":true,"semantic":"media_findings","description":"Summary, timeline, on-screen text and issues found."}},"permissions":{"project":[],"capabilities":["media.analyze"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"clip"},{"control":"number","port":"start"},{"control":"number","port":"end"}]},"capability":{"schemaVersion":1,"operation":"media.analyze","media":{"$input":"clip"},"question":"Is the product logo clearly visible, and when? List any on-screen text and visible defects.","range":{"start":{"$input":"start"},"end":{"$input":"end"}},"output":"report"},"tests":[{"name":"A saved clip","input":{"clip":{"kind":"video","assetId":"clip-1","versionId":"v1","url":"/api/media/owner/clip.mp4"}},"expectations":{"report":{"kind":"json","includes":["summary"]}}}]}.
|
|
7528
7566
|
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}}}]}.
|
|
7529
7567
|
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}}}]}.
|
|
7530
7568
|
A capability package performs exactly one supported operation. A job that needs several (transcribe a recording, then find what is said in it; write words, then voice them), or code between two operations, is one module Block that calls each with stillmade.hosted.run (Module Blocks); never split one job into part Blocks, and never invent a combined capability 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.
|
|
7531
7569
|
|
|
7532
7570
|
### Module Blocks
|
|
7533
7571
|
|
|
7534
|
-
A job that needs several hosted operations, or code around one (reading a transcript's words and times, shaping a list), is a module Block: {manifest,moduleSource,tests}, runtime:"module", moduleSource:{"main.js":"<the code>"}. StillMade names main.js by its digest and adds module; give no other files. main.js is plain JavaScript that imports nothing and uses no browser APIs (no fetch, TextEncoder, timers or WebCodecs): export default async function run(input, stillmade) returns the declared outputs. It calls each hosted operation with await stillmade.hosted.run(operation, request), listed in manifest.permissions.capabilities: text.generate, image.generate, video.generate, audio.speech, audio.music, audio.sfx, audio.transcribe, image.describe, media.analyze, web.fetch, web.research. Requests, and what each call returns (a test's mocks record exactly that value): text.generate {prompt, system, maxTokens} returns the text as a string, and with schema (a JSON schema object) returns that object, so ask for JSON with schema; audio.transcribe {audio: the input's saved reference, language:"auto"} returns {schemaVersion:1,text,words:[{text,start,end}]}; image.generate {prompt} and audio.speech {text} return a saved image or audio reference; image.describe {image, instruction} and web.fetch {url, instruction} return a string; media.analyze {media, question} returns {items:[...]}; web.research {topic} returns {summary,sources}. A call that fails throws: let it fail the run rather than catching it and returning an empty result. Media work on the person's device (cut, join or resize video, decode or extract audio, thumbnails) is await stillmade.media.transform(request); exporting is too: {operation:"timeline.render",kind:"video" or "audio",aspect:"9:16",clips:[{source,start,duration,from,transition:{type:"Fade"},look:"Filmic"}],audio:[{source,start,duration}],captions:[{text,start,end,words:[{text,start,end}],highlight:"#ffd400"}]} renders the Block's own timeline with the Editor's transitions, looks and word-by-word captions under the person's export plan, so never ask for a separate export, captions or color Block; stillmade.log and stillmade.progress(fraction, label) report. One press of the Step's button runs every call at the price shown beside it. A module Block runs in the person's browser while StillMade is open, never on a schedule or event. Tests are {name,input,expected,mocks}: mocks records what each call answers, as {hosted:{"audio.transcribe":{schemaVersion:1,text:"...",words:[...]},"text.generate":{...}},transforms:{...}} (a list answers successive calls in order), and expected is the exact output main.js returns for those answers. StillMade runs every test before anyone sees the Block, without spending credits. With the SDK instead, the same code is module/main.js in a Block folder and the same tests are tests/fixtures.json; stillmade-block pack writes the file to import (MODULES.md).
|
|
7572
|
+
A job that needs several hosted operations, or code around one (reading a transcript's words and times, shaping a list), or media work on the person's device even with no hosted operation (rendering clips and photos into one video, joining pictures into one), is a module Block: {manifest,moduleSource,tests}, runtime:"module", moduleSource:{"main.js":"<the code>"}. StillMade names main.js by its digest and adds module. StillMade's in-app builder takes one file: give no other files, and main.js is plain JavaScript that imports nothing and uses no browser APIs (no fetch, TextEncoder, timers or WebCodecs). That single-file rule is the in-app builder's; a module Block built with the SDK may use timers, WebCodecs, bundled npm packages and .wasm files as MODULES.md describes. main.js: export default async function run(input, stillmade) returns the declared outputs. It calls each hosted operation with await stillmade.hosted.run(operation, request), listed in manifest.permissions.capabilities: text.generate, image.generate, video.generate, audio.speech, audio.music, audio.sfx, audio.transcribe, image.describe, media.analyze, web.fetch, web.research. Requests, and what each call returns (a test's mocks record exactly that value): text.generate {prompt, system, maxTokens} returns the text as a string, and with schema (a JSON schema object) returns that object, so ask for JSON with schema; one text.generate prompt holds at most 32,000 characters, so a longer file or transcript is read in parts (one call per part of about 25,000 characters, then one call that joins what they found), never cut off, and when the result is the whole text again (a cleaned transcript, a translation) each part's answer is kept and joined in order in main.js, since one answer holds at most 4,096 tokens; audio.transcribe {audio: the input's saved reference, language:"auto"} returns {schemaVersion:1,text,words:[{text,start,end}]}; image.generate {prompt, references?: one to eight saved images the new picture keeps, such as the product, person or place in the person's photos} and audio.speech {text} return a saved image or audio reference; web.fetch {url, instruction} returns a string; media.analyze {media: one to four saved images or one saved video, question} returns {items:[{kind,summary,observations,detectedText}]}, one item per image, and is how a Block looks at pictures (picking the photos that show different angles, reading a menu off a photo): StillMade credits pay for it; image.describe {image, instruction} returns a string but runs only on the person's own OpenRouter key, so never use it; web.research {topic} returns {summary,sources}. A file the person gives the Block (a text, CSV, Markdown or JSON file on a file input) is read with await stillmade.files.read(input.<port>), which returns its bytes; turn them into text in main.js as the worked file input below does, and give the test's input file an assetId whose text mocks.files records as {files:{"<assetId>":{text:"..."}}}. A Block that reads a file the person gives it is a module Block, never a text.generate capability package. A PDF (lesson slides, a lease, a contract) is read with await stillmade.media.transform({operation:"document.text",source:input.<port>}), which returns {text, pages}, the words of each page, on the person's device and without credits (a text file reads the same way); a scanned PDF has no words to read. A call that fails throws: let it fail the run rather than catching it and returning an empty result. Media work on the person's device (cut, join or resize video, decode or extract audio, thumbnails, joining images) is await stillmade.media.transform(request). Joining saved images into one picture (two photos side by side with Before and After labels, a grid or collage, a contact sheet, a storyboard sheet, a poster with a title) is {operation:"image.compose",width,height,background:"#ffffff",layers:[{source,x,y,width,height,fit:"cover" or "contain"}],text:[{text,x,y,size,color,background,align:"left" or "center" or "right",width}]}: a canvas up to 4096 pixels a side, positions in pixels worked out in main.js, layers drawn in order and text on top (y is the top of the line, background puts a box behind it, colours are #rrggbb or, see-through, #rrggbbaa such as #00000099 for a caption box, width wraps the words onto lines no wider than it: give a caption under a frame the frame's width so it never runs into the next one); it returns the saved image, and main.js never decodes pixels itself. Exporting is a transform too: {operation:"timeline.render",kind:"video" or "audio",aspect:"9:16",clips:[{source,start,duration,from,transition:{type:"Fade"},look:"Filmic"}],audio:[{source,start,duration}],captions:[{text,start,end,words:[{text,start,end}],highlight:"#ffd400"}]} renders the Block's own timeline with the Editor's transitions, looks and word-by-word captions under the person's export plan (start is a clip's place on that timeline, so each clip starts where the one before it ends, less the transition on the clip before it, which blends that clip into the next, and from is where it begins in its own file; overlapping clips are refused), so never ask for a separate export, captions or color Block; stillmade.log and stillmade.progress(fraction, label) report. One press of the Step's button approves each hosted operation once at the price shown beside it, and media.analyze for every picture or video the Step is given, across as many calls as that takes; any further call asks the person first. A module Block runs in the person's browser while StillMade is open, never on a schedule or event. Tests are {name,input,expected,mocks} and compare outputs, so every test is a case that runs to its outputs; an input the Block refuses (it throws a plain Error with a message for the person, such as a clip with no duration) is never a test. mocks records what each call answers, as {hosted:{"audio.transcribe":{schemaVersion:1,text:"...",words:[...]},"text.generate":{...}},transforms:{...}} (a list answers successive calls in order), and expected is the exact output main.js returns for those answers: exact values of each output's type, never capability bounds such as minDuration or maxBytes. In a module test, mock each media call with its complete saved reference (kind, assetId, versionId, url, mimeType and its measured fields: duration, bytes, sampleRate, channels for audio; width, height, bytes for images) and repeat those references unchanged in expected; names or labels go in their own output. Worked outputs, each a passing test. A document output is the document object on a document port (not text): {"outputs":{"sheet":{"type":"document"}},"main.js":"export default async function run(input, stillmade) { const text = await stillmade.hosted.run('text.generate', {prompt: 'A Markdown care sheet for ' + input.plant, maxTokens: 800}); return {sheet: {schemaVersion: 1, format: 'markdown', text, title: input.plant + ' care'}}; }","test":{"name":"Fern","input":{"plant":"Fern"},"mocks":{"hosted":{"text.generate":"## Light\nBright, indirect light."}},"expected":{"sheet":{"schemaVersion":1,"format":"markdown","text":"## Light\nBright, indirect light.","title":"Fern care"}}}}. A file output is the reference await stillmade.files.write(data, {mediaType, name}) returns, handed on unchanged; mediaType is one of application/pdf, text/plain, text/csv, text/markdown, application/json, application/zip: {"outputs":{"report":{"type":"file"}},"main.js":"export default async function run(input, stillmade) { const csv = 'Room\\n' + input.rooms.join('\\n'); return {report: await stillmade.files.write(csv, {mediaType: 'text/csv', name: 'rooms.csv'})}; }","test":{"name":"Two rooms","input":{"rooms":["Kitchen","Hall"]},"expected":{"report":{"kind":"file","mimeType":"text/csv","name":"rooms.csv"}}}} (in a test, files.write answers with fields the host makes: assetId local-written-file-N, versionId, url /api/media/stillmade-mock/written-file-N.csv with the extension of its mediaType, and bytes; expected gives that reference as {kind:"file", mimeType, name} and it matches when those three match). An audio[] output is the list of references audio.speech returned, unchanged; its mock is a list answering successive calls, and the labels go in a separate text[] output: {"outputs":{"tracks":{"type":"audio[]"},"names":{"type":"text[]"}},"main.js":"export default async function run(input, stillmade) { const tracks = []; for (const room of input.rooms) tracks.push(await stillmade.hosted.run('audio.speech', {text: 'Welcome to the ' + room + '.'})); return {tracks, names: input.rooms}; }","test":{"name":"Two rooms","input":{"rooms":["Kitchen","Hall"]},"mocks":{"hosted":{"audio.speech":[{"kind":"audio","assetId":"speech-1","versionId":"v1","url":"/api/media/example/kitchen.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1},{"kind":"audio","assetId":"speech-2","versionId":"v1","url":"/api/media/example/hall.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1}]}},"expected":{"tracks":[{"kind":"audio","assetId":"speech-1","versionId":"v1","url":"/api/media/example/kitchen.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1},{"kind":"audio","assetId":"speech-2","versionId":"v1","url":"/api/media/example/hall.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1}],"names":["Kitchen","Hall"]}}}. A file input read as text: {"outputs":{"guide":{"type":"text"}},"main.js":"function textOf(buffer) { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.length; i += 8192) binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 8192)); try { return decodeURIComponent(escape(binary)); } catch (error) { return binary; } }\nexport default async function run(input, stillmade) { const notes = textOf(await stillmade.files.read(input.notes)); const guide = await stillmade.hosted.run('text.generate', {prompt: 'Rewrite these notes as numbered steps, keeping every number as written:\\n' + notes, maxTokens: 1200}); return {guide}; }","test":{"name":"Two notes","input":{"notes":{"kind":"file","assetId":"notes-1","versionId":"v1","url":"/api/media/example/notes.txt","mimeType":"text/plain","name":"notes.txt","bytes":31}},"mocks":{"files":{"notes-1":{"text":"pull the probe\nwipe the tines"}},"hosted":{"text.generate":"1. Pull the probe.\n2. Wipe the tines."}},"expected":{"guide":"1. Pull the probe.\n2. Wipe the tines."}}}. Photos joined into one image, its transform mocked with the saved image it returns: {"outputs":{"sheet":{"type":"image"}},"main.js":"export default async function run(input, stillmade) { const photos = input.photos; if (!photos.length) throw new Error('Add at least one photo.'); const columns = Math.ceil(Math.sqrt(photos.length)), rows = Math.ceil(photos.length / columns), cell = 400, gap = 20, top = 160; const width = columns * cell + (columns + 1) * gap, height = top + rows * cell + (rows + 1) * gap; const layers = photos.map((source, i) => ({source, x: gap + (i % columns) * (cell + gap), y: top + gap + Math.floor(i / columns) * (cell + gap), width: cell, height: cell, fit: 'cover'})); const sheet = await stillmade.media.transform({operation: 'image.compose', width, height, background: '#ffffff', layers, text: [{text: input.title, x: width / 2, y: 50, size: 64, align: 'center', color: '#111111'}]}); return {sheet}; }","test":{"name":"Four photos","input":{"photos":[{"kind":"image","assetId":"photo-1","versionId":"v1","url":"/api/media/example/photo-1.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-2","versionId":"v1","url":"/api/media/example/photo-2.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-3","versionId":"v1","url":"/api/media/example/photo-3.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-4","versionId":"v1","url":"/api/media/example/photo-4.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000}],"title":"Saturday games"},"mocks":{"transforms":{"image.compose":{"kind":"image","assetId":"sheet-1","versionId":"v1","url":"/api/media/example/sheet.png","mimeType":"image/png","width":1300,"height":1440,"bytes":1820000}}},"expected":{"sheet":{"kind":"image","assetId":"sheet-1","versionId":"v1","url":"/api/media/example/sheet.png","mimeType":"image/png","width":1300,"height":1440,"bytes":1820000}}}}. StillMade runs every test before anyone sees the Block, without spending credits. With the SDK instead, the same code is module/main.js in a Block folder and the same tests are tests/fixtures.json; stillmade-block pack writes the file to import (MODULES.md).
|
|
7535
7573
|
|
|
7536
7574
|
### ComfyUI workflows
|
|
7537
7575
|
|
|
@@ -7579,7 +7617,7 @@ Optional custom view interfaces are supported, but generated manifest.ui control
|
|
|
7579
7617
|
|
|
7580
7618
|
*When:* StillMade's builder adds this section when the person asks for an interface, preview or controls, or refines a Block that has one.
|
|
7581
7619
|
|
|
7582
|
-
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. An (i) beside anything a newcomer could misread: a setting, status or number whose effect is not obvious from its label gets a closed `sm-hint` with one or two sentences. Give every input that needs one a `description`; StillMade shows it as that (i). Obvious controls get none. 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. Use the width: from 860px wide the kit puts the controls on the left at a reading width (300–380px) and the result (`sm-stage`) fills the right, in its real shape; under that everything stacks with the result first and the action within reach. Extra width goes to the result, never to wider text fields. Before there is a result, the stage previews what will be made from the inputs. `data-layout="stack"` opts out. Settings sit side by side (`sm-settings`), next to what they change. Three or four short options are one segmented row (`data-segmented`), not a row of big tiles. Lists fill the width at a small size: input files are small tiles (`sm-gallery data-size="small"`), many items are one line each (`sm-list`): a thumbnail, one line of text, a short fact. Tapping an item opens all of its fields in place. Extra width means more items per row, never bigger cards. On a phone chip and segment rows scroll sideways instead of wrapping; long lists show their first three with "N more" (`data-peek`) and long text five lines until tapped (`sm-clamp`). Nothing is lost when a Block gets smaller: every control stays on the screen, behind an (i), in `sm-more`, or inside the item it belongs to. Compacting moves controls; it never deletes 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.
|
|
7620
|
+
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. An (i) beside anything a newcomer could misread: a setting, status or number whose effect is not obvious from its label gets a closed `sm-hint` with one or two sentences. Give every input that needs one a `description`; StillMade shows it as that (i). Obvious controls get none. 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. Use the width: from 860px wide the kit puts the controls on the left at a reading width (300–380px) and the result (`sm-stage`) fills the right, in its real shape; under that everything stacks with the result first and the action within reach. Extra width goes to the result, never to wider text fields. Before there is a result, the stage previews what will be made from the inputs. `data-layout="stack"` opts out. Size with the kit, never fixed sizes: no fixed pixel widths or heights for the workspace or stage, no 100vh, no position:fixed panels. StillMade zooms a Block's screen to fill the space it has (up to 150%, never down to a phone layout on a computer), so a Block laid out with the kit's spacing fills its Step without empty space. Settings sit side by side (`sm-settings`), next to what they change. Three or four short options are one segmented row (`data-segmented`), not a row of big tiles. Lists fill the width at a small size: input files are small tiles (`sm-gallery data-size="small"`), many items are one line each (`sm-list`): a thumbnail, one line of text, a short fact. Tapping an item opens all of its fields in place. Extra width means more items per row, never bigger cards. On a phone chip and segment rows scroll sideways instead of wrapping; long lists show their first three with "N more" (`data-peek`) and long text five lines until tapped (`sm-clamp`). Nothing is lost when a Block gets smaller: every control stays on the screen, behind an (i), in `sm-more`, or inside the item it belongs to. Compacting moves controls; it never deletes 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.
|
|
7583
7621
|
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`:
|
|
7584
7622
|
|
|
7585
7623
|
- **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:`.
|
|
@@ -7634,34 +7672,69 @@ Example view for a package taking and returning text: {"html":"<main class=\"sm-
|
|
|
7634
7672
|
When the user supplies an idea (for example, a photo editor) and this guide, build
|
|
7635
7673
|
an original StillMade Block from scratch. A GitHub repository is not required.
|
|
7636
7674
|
Create the SDK folder as the working product from the first iteration:
|
|
7637
|
-
`stillmade.block.json`, `src/run.js` (
|
|
7638
|
-
`tests/fixtures.json`, and optional
|
|
7675
|
+
`stillmade.block.json`, `src/run.js` (`module/main.js` for a module Block, or the
|
|
7676
|
+
documented declarative runtime file), `tests/fixtures.json`, and optional
|
|
7677
|
+
`src/view.html`, `src/view.css`, `src/view.js`.
|
|
7639
7678
|
Implement the task through typed SDK inputs/outputs and the `StillMade` UI bridge.
|
|
7640
7679
|
The exact finished folder must import with **Choose folder** without source edits
|
|
7641
7680
|
or adaptation. Keep development tools and extracted SDK examples outside it.
|
|
7642
7681
|
|
|
7643
|
-
|
|
7644
|
-
|
|
7682
|
+
The SDK is one download, `/block-sdk/stillmade-sdk-0.1.0.zip` on the StillMade
|
|
7683
|
+
site, with the same files as `npm install --save-dev stillmade-sdk`.
|
|
7684
|
+
`stillmade-block` in every StillMade guide is its command line: `npx stillmade-block`
|
|
7685
|
+
after the npm install, or `node <sdk>/packages/block-cli/cli.js` where `<sdk>` is
|
|
7686
|
+
the folder you extracted the download into. `stillmade-block create my-block
|
|
7687
|
+
javascript` scaffolds an isolated-JavaScript Block (`create --list` shows a
|
|
7645
7688
|
template for every runtime and hosted operation). Edit that source,
|
|
7646
7689
|
then run `validate my-block` and `test my-block` with the same CLI; add `--json`
|
|
7647
7690
|
for machine-readable results. Blocks that use hosted operations, files, network,
|
|
7648
7691
|
connections or storage test offline with `test my-block --mock` (recorded
|
|
7649
7692
|
stand-ins, or answers recorded in `tests/mocks.json`); a stand-in pass is never a
|
|
7650
|
-
live verification.
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7693
|
+
live verification. `stillmade-block dev my-block` shows the Block's real interface
|
|
7694
|
+
in a browser (see "Start a local preview early, then iterate").
|
|
7695
|
+
|
|
7696
|
+
Before calling the Block ready, run `stillmade-block gate my-block` on the Block's
|
|
7697
|
+
own folder: it runs StillMade's readiness check, including the admission
|
|
7698
|
+
requirements (explicit semantic/required declarations) that `validate` does not
|
|
7699
|
+
cover. When the Block sits outside the extracted SDK folder, always name its
|
|
7700
|
+
folder: `gate .` in a folder that also holds the SDK checks the SDK's examples too.
|
|
7701
|
+
Then `stillmade-block pack my-block` runs the tests again and writes the one file
|
|
7702
|
+
to import, named by the Block's runtime:
|
|
7703
|
+
|
|
7704
|
+
| Runtime | `pack my-block` writes |
|
|
7705
|
+
| --- | --- |
|
|
7706
|
+
| `module`, or a frame view with files in `src/view-files/` | `<id>-<version>.stillmade-module.json` |
|
|
7707
|
+
| `recipe`, `javascript`, `capability`, `comfyui` | `<id>-<version>.stillmade.json` |
|
|
7708
|
+
|
|
7709
|
+
Deliver that file (Create, then Create new, New Block, Import Block in StillMade) or the Block folder.
|
|
7710
|
+
|
|
7711
|
+
A test or example that needs a real photo, recording, clip or file can name one of
|
|
7712
|
+
StillMade's example files instead of carrying its own: `/api/media/example/photo-1.jpg`
|
|
7713
|
+
to `photo-4.jpg` (1600×1200, `photo-2.jpg` 1200×1600), `sheet.png` (1300×1440),
|
|
7714
|
+
`room.png` (1200×900), `kitchen.wav`, `hall.wav` and `click-120.wav` (2 s, 24 kHz mono),
|
|
7715
|
+
`audio.mp3` (2 s), `clip.mp4` (640×360, 2 s), `notes.txt` and `sales.csv`, for example
|
|
7716
|
+
`{"kind":"image","assetId":"photo-1","versionId":"v1","url":"/api/media/example/photo-1.jpg","mimeType":"image/jpeg","width":1600,"height":1200}`.
|
|
7717
|
+
StillMade serves them, its preview and phone and browser check open them, and `test`
|
|
7718
|
+
reads them when the test gives no mock for them. A Block whose interface needs a photo
|
|
7719
|
+
before its controls work needs such an example: the phone and browser check opens the
|
|
7720
|
+
interface with the first test's input and fails when it shows no usable control.
|
|
7721
|
+
Never add `--original` to a module Block: original `.stillmade-block` archives take
|
|
7722
|
+
only recipe, JavaScript, capability and ComfyUI source (PORTABLE.md), and a new
|
|
7723
|
+
Block does not need one. An already-portable folder (with `portable.json`) uses
|
|
7724
|
+
ordinary `pack`, which writes its `.stillmade-block`; keep its existing evidence aligned with its source. Include normal and edge-case fixtures and complete applicable license notices.
|
|
7658
7725
|
The folder remains a supported direct import; packaging is an alternate delivery
|
|
7659
7726
|
format, not a conversion to another implementation. Portable folders may use the
|
|
7660
7727
|
same split interface files or `src/view.json`, never both.
|
|
7661
7728
|
|
|
7662
|
-
For a photo editor
|
|
7663
|
-
|
|
7664
|
-
`
|
|
7729
|
+
For a photo editor that keeps the photo's full size, build a `module` Block with a
|
|
7730
|
+
frame view: `module/main.js` reads the photo with `stillmade.files.read`, edits its
|
|
7731
|
+
pixels with `OffscreenCanvas` in the module's Worker and writes the result with
|
|
7732
|
+
`stillmade.files.write`. The `javascript` runtime's `src/run.js` takes images only
|
|
7733
|
+
as RGBA arrays of at most one megapixel, so it suits small images only. "Full size"
|
|
7734
|
+
is the size of the photo the Block receives: with the account's media setting on (its
|
|
7735
|
+
default), a photo over 2048 px or 4 MiB arrives as a 2048 px working copy. Either way,
|
|
7736
|
+
return typed image outputs and show images through `StillMade.previewImage`. The UI
|
|
7737
|
+
calls `StillMade.run` and shares edits using the documented shared-interface contract.
|
|
7665
7738
|
Observe saved image outputs through `onShared`; display them with
|
|
7666
7739
|
`StillMade.previewImage` just as you display run results. In saved Steps, shared
|
|
7667
7740
|
write promises wait for an acknowledged save before the run flush barrier proceeds.
|
|
@@ -7928,7 +8001,7 @@ contract, explain the specific gap early and work with them on a supported optio
|
|
|
7928
8001
|
Never silently replace their goal with an easier example.
|
|
7929
8002
|
|
|
7930
8003
|
When the user is happy with the result or asks to finalize, prepare the importable
|
|
7931
|
-
|
|
8004
|
+
file `stillmade-block pack` writes from the source they just reviewed. Perform the checks permitted
|
|
7932
8005
|
by the environment and the user's instructions; report any unfinished or unverified
|
|
7933
8006
|
behavior honestly. Deliver the Block for use in StillMade, not just a preview URL
|
|
7934
8007
|
or instructions to rebuild it themselves. The single-artifact final-delivery rule
|
|
@@ -7945,7 +8018,16 @@ Do not wait until packaging, or make the user start the servers manually when yo
|
|
|
7945
8018
|
can do it. Open the browser/IDE preview if supported and give the user the working
|
|
7946
8019
|
local URL. Keep the processes running while the user tries it and asks for edits.
|
|
7947
8020
|
|
|
7948
|
-
|
|
8021
|
+
Start the preview with `stillmade-block dev my-block`, the SDK's browser preview.
|
|
8022
|
+
It serves the Block's real generated controls or custom view on loopback
|
|
8023
|
+
(`http://127.0.0.1:4800`; `--port` picks another), reloads on every save
|
|
8024
|
+
(`--build "npm run build"` reruns a bundler first), runs the Block with an
|
|
8025
|
+
example's input or the user's, and answers hosted operations, files and
|
|
8026
|
+
connections with stand-ins (from `tests/mocks.json` when present), never with
|
|
8027
|
+
credits. Show its URL in the coding tool's preview where it has one. With `--live`
|
|
8028
|
+
it also prints a link that offers each saved version to the user's signed-in
|
|
8029
|
+
StillMade account for the usual import review, where runs are real and paid steps
|
|
8030
|
+
ask first. Only when `dev` cannot show what the Block needs,
|
|
7949
8031
|
create a small development-only preview harness around the actual Block source,
|
|
7950
8032
|
its declared input controls, and its outputs. Use the supplied SDK sandbox and
|
|
7951
8033
|
view bridge for execution and custom interfaces; do not run Block code with eval,
|
|
@@ -7955,9 +8037,9 @@ A browser-only Block does not need a backend merely for appearance. If one is
|
|
|
7955
8037
|
needed to host the SDK or serve the preview, start it together with the frontend
|
|
7956
8038
|
using one documented development command. Keep that command reproducible.
|
|
7957
8039
|
|
|
7958
|
-
|
|
7959
|
-
runs a sample and returns JSON
|
|
7960
|
-
|
|
8040
|
+
`stillmade-block run <package> <input.json>` (`preview` is the same command)
|
|
8041
|
+
runs a sample once and returns JSON; it is not a browser server. Do not invent
|
|
8042
|
+
other SDK commands such as `serve`. A separate preview
|
|
7961
8043
|
harness is development tooling; keep it outside the final portable Block package.
|
|
7962
8044
|
For a custom view, render the actual authored view through the SDK's existing
|
|
7963
8045
|
sandboxed view contract, with controls connected to real validated inputs/outputs.
|
|
@@ -7981,7 +8063,8 @@ feedback to the same Block source and keep the preview available. If the user
|
|
|
7981
8063
|
already requested a final artifact without an interactive review, proceed after
|
|
7982
8064
|
available checks. Otherwise, package when they say it is ready to import. Then run
|
|
7983
8065
|
the permitted SDK validation, sandbox fixtures, and packaging checks against that
|
|
7984
|
-
same final source and return the single
|
|
8066
|
+
same final source and return the single file `pack` writes for its runtime
|
|
8067
|
+
(the table under "Build new Blocks directly with the SDK"). The rule to
|
|
7985
8068
|
return one artifact applies to final delivery, not progress messages or preview URLs.
|
|
7986
8069
|
|
|
7987
8070
|
If the environment cannot run servers or open a browser, explain the precise
|
|
@@ -8012,7 +8095,10 @@ Do not implement a new application shell or require manual connection wiring.
|
|
|
8012
8095
|
|
|
8013
8096
|
### Exact package shape
|
|
8014
8097
|
|
|
8015
|
-
|
|
8098
|
+
For runtime `javascript` (a standalone JavaScript Block) the package is below. A
|
|
8099
|
+
module Block is a folder instead: `module/main.js` exporting
|
|
8100
|
+
`async function run(input, stillmade)`, with module fixtures (see "Module Blocks"
|
|
8101
|
+
and MODULES.md).
|
|
8016
8102
|
|
|
8017
8103
|
```json
|
|
8018
8104
|
{
|
|
@@ -8066,6 +8152,8 @@ any restrictions on distributing source to a team.
|
|
|
8066
8152
|
|
|
8067
8153
|
### Runtime constraints
|
|
8068
8154
|
|
|
8155
|
+
These constraints are for runtime `javascript`; a module Block runs as an ES module
|
|
8156
|
+
in a browser Worker and can use what MODULES.md lists.
|
|
8069
8157
|
JavaScript runs in isolated QuickJS, not Node.js or the browser's JavaScript engine.
|
|
8070
8158
|
Ordinary synchronous JavaScript, arrays, objects, strings, and `Math` are available.
|
|
8071
8159
|
No network, DOM, files, modules, shell, timers, promises, or installed dependencies.
|
|
@@ -8116,8 +8204,8 @@ pretending those APIs exist or returning a successful-looking placeholder.
|
|
|
8116
8204
|
Each input/output is a named port. Names start with a lowercase letter and contain
|
|
8117
8205
|
only letters, numbers and underscores. Every input of a new Block needs `semantic`,
|
|
8118
8206
|
`required` and `description`; every output needs `semantic` and `description`.
|
|
8119
|
-
`validate` and `test` check structure and fixtures only; `
|
|
8120
|
-
|
|
8207
|
+
`validate` and `test` check structure and fixtures only; `ready` and `gate` run
|
|
8208
|
+
full admission, which rejects a port missing these fields.
|
|
8121
8209
|
|
|
8122
8210
|
<!-- port-fields:start (generated by npm run build:sdk; do not edit) -->
|
|
8123
8211
|
|
|
@@ -8438,13 +8526,18 @@ the workspace cancel unfinished display requests. Use a local revision counter
|
|
|
8438
8526
|
in asynchronous UI code so a late result cannot repaint an earlier selection.
|
|
8439
8527
|
|
|
8440
8528
|
StillMade's host image controls remain available with a custom interface.
|
|
8529
|
+
On a project Step the custom interface fills the Step: the host's file inputs and "Apply to"
|
|
8530
|
+
open from the Step's inputs button, and while a required file is missing a "Choose <input>"
|
|
8531
|
+
button sits above the interface. Show your own empty state
|
|
8532
|
+
in the view ("Add a photo") and never describe where the host's controls are.
|
|
8441
8533
|
A project lets the user select a compatible authorized image or upload one;
|
|
8442
8534
|
connected/context bindings remain read-only. A standalone preview keeps uploaded
|
|
8443
8535
|
files local to that preview. Host uploads accept PNG, JPEG, WebP or GIF up to
|
|
8444
|
-
12 MB and one megapixel
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8536
|
+
12 MB and one megapixel. Above one megapixel the host makes a one-megapixel
|
|
8537
|
+
working copy for the sandbox when the account's media setting is on (its
|
|
8538
|
+
default); with the setting off, larger uploads are rejected with a request to
|
|
8539
|
+
resize a copy. Existing authorized project references can be displayed up to 16
|
|
8540
|
+
megapixels, but the selected runtime still applies its processing limit. The host validates permissions and exact media
|
|
8448
8541
|
identity before resolving bytes; no account token, native file handle, or remote
|
|
8449
8542
|
fetch capability enters the iframe.
|
|
8450
8543
|
|
|
@@ -8567,9 +8660,16 @@ inside the iframe; larger media should use the existing native Editor workspace.
|
|
|
8567
8660
|
|
|
8568
8661
|
### Tests and interconnection
|
|
8569
8662
|
|
|
8570
|
-
Include 2–6 small deterministic fixtures covering normal behavior and edge cases
|
|
8571
|
-
|
|
8572
|
-
|
|
8663
|
+
Include 2–6 small deterministic fixtures covering normal behavior and edge cases
|
|
8664
|
+
(a capability Block takes 1–3). The fields of each fixture depend on the runtime:
|
|
8665
|
+
|
|
8666
|
+
| Runtime | Each fixture |
|
|
8667
|
+
| --- | --- |
|
|
8668
|
+
| `recipe`, `javascript` | `{ "name", "input", "expected" }`; a Block with `manifest.state` adds `expectedState` and may give a starting `state` |
|
|
8669
|
+
| `module` | `{ "name", "input", "expected", "mocks" }`; `mocks` is optional and records what each host call answers (MODULES.md) |
|
|
8670
|
+
| `capability`, `comfyui` | `{ "name", "input", "expectations" }`: bounded checks of each output, never `expected` |
|
|
8671
|
+
|
|
8672
|
+
`expected` is the **output bindings object**, not one bare value and not
|
|
8573
8673
|
`{"outputs": ...}`. Fixture comparison is exact JSON, including numeric values,
|
|
8574
8674
|
array lengths and output keys. Compute expected values carefully; do not weaken a
|
|
8575
8675
|
test merely to make code pass. Test defaults by omitting a defaulted input in at
|
|
@@ -8585,6 +8685,8 @@ should have defaults when practical.
|
|
|
8585
8685
|
|
|
8586
8686
|
### Deliver, test, and import
|
|
8587
8687
|
|
|
8688
|
+
For a new Block, deliver the one file `stillmade-block pack` writes for its runtime
|
|
8689
|
+
(the table under "Build new Blocks directly with the SDK") after `gate` passes.
|
|
8588
8690
|
For repository adaptations, deliver one `.stillmade-block` archive using the
|
|
8589
8691
|
[portable packaging commands](/block-sdk/PORTABLE.md). The agent runs validation,
|
|
8590
8692
|
fixtures and packaging. The recipient uploads the file; StillMade repeats source,
|
|
@@ -8597,8 +8699,6 @@ changed source. For a remix, use a new ID and preserve
|
|
|
8597
8699
|
`manifest.provenance.remixedFrom` with the source ID, version and SHA-256 digest
|
|
8598
8700
|
when available. Never guess a digest or erase attribution.
|
|
8599
8701
|
|
|
8600
|
-
Now inspect the supplied GitHub URL and produce the portable artifact using this contract.
|
|
8601
|
-
|
|
8602
8702
|
|
|
8603
8703
|
### Optional ComfyUI implementation
|
|
8604
8704
|
|
|
@@ -8826,7 +8926,11 @@ When a Block needs npm libraries, WebAssembly (including C/C++ libraries such as
|
|
|
8826
8926
|
OpenCV), async work, timers, OffscreenCanvas or WebCodecs, use
|
|
8827
8927
|
`"runtime": "module"`. The folder holds `stillmade.block.json`, `module/main.js`
|
|
8828
8928
|
(an ES module that bundles its npm dependencies), optional `.wasm` or data files
|
|
8829
|
-
in `module/`, and `tests/fixtures.json` with `{name,input,expected}
|
|
8929
|
+
in `module/`, and `tests/fixtures.json` with `{name,input,expected,mocks}` (`mocks`
|
|
8930
|
+
optional). The rule under "SDK authoring guide", "Module Blocks" above, that main.js
|
|
8931
|
+
imports nothing and uses no browser APIs (no fetch, TextEncoder, timers or
|
|
8932
|
+
WebCodecs), is the single-file rule of StillMade's in-app builder; a module Block
|
|
8933
|
+
built with the SDK bundles what it imports and can use everything MODULES.md lists. The entry is
|
|
8830
8934
|
`export default async function run(input, stillmade)` and returns the declared
|
|
8831
8935
|
outputs. Use `stillmade.progress(value,label)`, `stillmade.log()`,
|
|
8832
8936
|
`stillmade.signal`, `stillmade.asset(path)` for the module's own files,
|
|
@@ -8836,7 +8940,9 @@ outputs. Use `stillmade.progress(value,label)`, `stillmade.log()`,
|
|
|
8836
8940
|
to mono PCM samples, `audio.extract`, `audio.normalize`, `video.cut` with
|
|
8837
8941
|
ordered `keep` ranges, `video.concat`, `video.resize`, `image.thumbnail`; the
|
|
8838
8942
|
results are saved references), with helpers such as `silentRanges`,
|
|
8839
|
-
`keepRanges` and `integratedLoudness` from `@stillmade/block-sdk/media
|
|
8943
|
+
`keepRanges` and `integratedLoudness` from `@stillmade/block-sdk/media` (bundle
|
|
8944
|
+
`stillmade-sdk/packages/block-sdk/media-transform.js` from the npm package, or
|
|
8945
|
+
`<sdk>/packages/block-sdk/media-transform.js` from the extracted download),
|
|
8840
8946
|
`stillmade.actions.run(action)` for declared actions such as `media.generate`,
|
|
8841
8947
|
and `stillmade.hosted.run(operation,request)` for hosted operations listed in
|
|
8842
8948
|
`permissions.capabilities`. There is no network, DOM or app storage. Never
|
|
@@ -8868,7 +8974,9 @@ Markdown, JSON or ZIP: `{kind:"file",assetId,versionId,mimeType,url,name,bytes}`
|
|
|
8868
8974
|
`document`, `chart` (`x`, `y`, `chart:"bar"|"line"`), `code`, `player` or
|
|
8869
8975
|
`download`. A module Block reads an uploaded file with
|
|
8870
8976
|
`await stillmade.files.read(input.file)` and builds values with `createTable`
|
|
8871
|
-
and `createDocument` from `@stillmade/block-sdk/data
|
|
8977
|
+
and `createDocument` from `@stillmade/block-sdk/data` (bundle
|
|
8978
|
+
`stillmade-sdk/packages/block-sdk/data-values.js` from the npm package, or
|
|
8979
|
+
`<sdk>/packages/block-sdk/data-values.js` from the extracted download). To hand shots to the
|
|
8872
8980
|
Shots workspace, output `{"type":"shot[]","semantic":"production_shot_plan"}`
|
|
8873
8981
|
where each shot has `schemaVersion:1` and a unique `id`.
|
|
8874
8982
|
|
|
@@ -8919,7 +9027,8 @@ output, and
|
|
|
8919
9027
|
A video Block may add `range:{start:{$input:"start"},end:{$input:"end"}}` bound
|
|
8920
9028
|
to two optional `number` inputs (at most 12 seconds apart). A json output
|
|
8921
9029
|
receives `{schemaVersion:1,items:[{kind,summary,timeline,observations,detectedText,issues,confidence}]}`.
|
|
8922
|
-
Each analyzed item is one StillMade-credit call.
|
|
9030
|
+
Each analyzed item is one StillMade-credit call. An `image` or `image[]` Block that only rates photos may add
|
|
9031
|
+
`detail:"check"`: each photo is read smaller and gets one short `summary`, and a call of up to four photos costs one photo check.
|
|
8923
9032
|
|
|
8924
9033
|
### Optional web reading
|
|
8925
9034
|
|
|
@@ -9174,7 +9283,7 @@ bytes in this state. External hosts pass `{state:{version:1,value:{count:0}}}` t
|
|
|
9174
9283
|
|
|
9175
9284
|
|
|
9176
9285
|
Offline Project Type packaging also supports ComfyUI-only and mixed ComfyUI,
|
|
9177
|
-
hosted-capability, and local packages. Use `
|
|
9286
|
+
hosted-capability, and local packages. Use `stillmade-block pack
|
|
9178
9287
|
workflow.json workflow.stillmade.json`. This statically checks every embedded
|
|
9179
9288
|
package but executes no fixtures or providers; its report must stay unverified
|
|
9180
9289
|
until StillMade runs the import review and the user confirms installation.
|
|
@@ -9857,6 +9966,8 @@ Never rewrite immutable releases or silently change a saved workflow's pins.
|
|
|
9857
9966
|
|
|
9858
9967
|
Use `runtime: "capability"` with `operation: "image.describe"` when a Block must inspect one image and return plain text. This is the portable boundary for visual analysis: the Block declares its image input, analysis instructions, output, tests, and license; StillMade owns image loading, provider credentials, model/payment selection, approval, accounting, and the retained execution record.
|
|
9859
9968
|
|
|
9969
|
+
**StillMade credits do not pay for `image.describe`** (since September 13, 2026). A Block that calls it runs only for people who added their own OpenRouter key in Settings → API keys, and stops for everyone else. For a Block anyone can run on StillMade credits, use `media.analyze` instead (`stillmade-block create my-block analyze-media`): it takes one image or one to four images, answers one question about each, and is priced per image. The built-in Describe media 2.1.0 and Photo notes 1.1.0 moved to it; their earlier releases stay for projects pinned to them.
|
|
9970
|
+
|
|
9860
9971
|
The downloaded SDK includes `examples/describe-image.stillmade.json` and `packages/block-sdk/image-description-example.js`.
|
|
9861
9972
|
|
|
9862
9973
|
```json
|
|
@@ -10032,10 +10143,12 @@ The downloaded SDK includes `packages/block-sdk/transcription-example.js` and `e
|
|
|
10032
10143
|
|
|
10033
10144
|
```sh
|
|
10034
10145
|
node packages/block-cli/cli.js validate examples/transcribe-audio.stillmade.json
|
|
10035
|
-
node packages/block-cli/cli.js pack examples/transcribe-audio.stillmade.json
|
|
10036
|
-
node packages/block-cli/cli.js validate transcribe-audio.stillmade
|
|
10146
|
+
node packages/block-cli/cli.js pack examples/transcribe-audio.stillmade.json
|
|
10147
|
+
node packages/block-cli/cli.js validate example.transcribe-audio-1.0.0.stillmade.json
|
|
10037
10148
|
```
|
|
10038
10149
|
|
|
10150
|
+
`pack` writes `<id>-<version>.stillmade.json`, the file to import in StillMade (Create, then Import Block).
|
|
10151
|
+
|
|
10039
10152
|
Before returning an artifact, confirm that it has one exact operation, usable generated controls, realistic fixtures, complete attribution, no embedded credentials or provider choices, no ambient permissions, and passes the same packaged validator used by StillMade.
|
|
10040
10153
|
|
|
10041
10154
|
|
|
@@ -11441,6 +11554,12 @@ attribution; removing it requires a separate rights review, not a source edit.
|
|
|
11441
11554
|
|
|
11442
11555
|
### Outside StillMade
|
|
11443
11556
|
|
|
11557
|
+
Outside StillMade you remix only your own Blocks: one you imported or built, including a
|
|
11558
|
+
newer version you edited in StillMade and downloaded from your versions (**Download
|
|
11559
|
+
source + SDK**) to take back to your coding agent. Another person's Block is remixed only
|
|
11560
|
+
in StillMade, as above: StillMade offers no download of it, and an import whose original
|
|
11561
|
+
is another person's Block is refused with "Their Blocks can be remixed only in StillMade".
|
|
11562
|
+
|
|
11444
11563
|
Use the CLI shipped in the downloadable SDK:
|
|
11445
11564
|
|
|
11446
11565
|
```sh
|
|
@@ -11977,8 +12096,9 @@ control, context binding or default. Use one `primary:true` port per direction
|
|
|
11977
12096
|
when the capability has a primary value; declare semantic roles only when true.
|
|
11978
12097
|
The host presents typed outputs and supplies supported connections automatically.
|
|
11979
12098
|
Inputs must be ordinary user task data, never setup scripts or manifest edits.
|
|
11980
|
-
|
|
11981
|
-
stateful Blocks
|
|
12099
|
+
Recipe and JavaScript fixtures have `{name,input,expected}` plus the existing state
|
|
12100
|
+
fields for stateful Blocks; capability and ComfyUI fixtures have
|
|
12101
|
+
`{name,input,expectations}`. Expected outputs must satisfy the declared types exactly.
|
|
11982
12102
|
|
|
11983
12103
|
Archive `portable.json` (replace the illustrative values with real evidence):
|
|
11984
12104
|
|
|
@@ -12130,7 +12250,10 @@ a dependency list, instructions, SDK archive or a promise to finish later.
|
|
|
12130
12250
|
### Original implementations in the same archive
|
|
12131
12251
|
|
|
12132
12252
|
For code you authored without reused repository or dependency code, use the same
|
|
12133
|
-
canonical archive with an explicit authorship declaration
|
|
12253
|
+
canonical archive with an explicit authorship declaration. Original archives take
|
|
12254
|
+
recipe, JavaScript, declarative capability and ComfyUI source. A module Block is
|
|
12255
|
+
packed with `pack <folder>` alone, which writes `<id>-<version>.stillmade-module.json`
|
|
12256
|
+
(the file to import); `--original` refuses it with `RUNTIME_RESTRICTED`.
|
|
12134
12257
|
|
|
12135
12258
|
```sh
|
|
12136
12259
|
node packages/block-cli/cli.js pack working.stillmade.json creator.my-block-1.0.0.stillmade-block --original
|
|
@@ -12421,11 +12544,11 @@ The latest version of each Block bundled with the SDK, generated from the packag
|
|
|
12421
12544
|
| --- | --- | --- | --- | --- |
|
|
12422
12545
|
| `sm.ai-decide` 1.0.0 | AI decide | task | `data` text (external_data) · primary; `rules` text (decision_rules) | `decision` object (ai_decision) · primary |
|
|
12423
12546
|
| `sm.ai-write` 1.0.0 | AI write | task | `data` text (external_data) · primary; `instructions` text (writing_instructions) | `text` text (written_message) · primary |
|
|
12424
|
-
| `sm.animated-scene` 2.3.
|
|
12547
|
+
| `sm.animated-scene` 2.3.9 | Animated Scene | workspace | `animation` scene-document (animated_scene_document) · project context only · primary; `shotPlan` shot-plan (production_shot_plan) · project context only; `updatedAnimation` scene-document (animated_scene_document); `commit` boolean (apply_changes); `generatedImage` image (image); `targetShotId` text (target_shot); `targetLayerId` text (target_layer); `attachGeneratedImage` boolean (attach_image); `action` text (animated_scene_action); `versions` metadata[] (revision_history) · project context only; `presence` metadata[] (collaborator_presence); `assets` asset[] (project_media) · project context only | `animation` scene-document (animated_scene_document) · primary; `edit` workspace-edit (workspace_edit); `qualityAction` approval (portable_media_action); `renderAction` approval (portable_media_action); `qualityReport` metadata (quality_report); `editorTimeline` timeline (editor_handoff); `revision` metadata (revision_event) |
|
|
12425
12548
|
| `sm.animated-scene-assistant` 1.0.0 | Plan Animated Scene | task | `animation` scene-document · project context only; `request` text · primary | `edit` workspace-edit · primary |
|
|
12426
12549
|
| `sm.blueprint` 2.1.9 | Blueprint | workspace | `blueprint` bible (production_blueprint) · project context only · primary; `updatedBlueprint` bible (production_blueprint); `commit` boolean (apply_changes); `assistantEdit` workspace-edit (workspace_edit); `versions` metadata[] (revision_history) · project context only; `presence` metadata[] (collaborator_presence); `assets` asset[] (project_media) · project context only | `blueprint` bible (production_blueprint) · primary; `edit` workspace-edit (workspace_edit); `shotPlan` shot-plan (shot_plan_handoff); `revision` metadata (revision_event) |
|
|
12427
12550
|
| `sm.blueprint-assistant` 1.0.0 | Improve Blueprint | task | `blueprint` bible · project context only; `request` text · primary | `edit` workspace-edit · primary |
|
|
12428
|
-
| `sm.board-editor` 2.2.
|
|
12551
|
+
| `sm.board-editor` 2.2.11 | Whiteboard | workspace | `board` board-document (whiteboard_document) · project context only · primary; `updatedBoard` board-document (whiteboard_document); `commit` boolean (apply_changes); `action` text (whiteboard_action); `presenterPrompt` text (presenter_prompt); `generatedImage` image (image); `versions` metadata[] (revision_history) · project context only; `presence` metadata[] (collaborator_presence); `assets` asset[] (project_media) · project context only | `board` board-document (whiteboard_document) · primary; `edit` workspace-edit (workspace_edit); `presenterAction` approval (portable_media_action); `qualityAction` approval (portable_media_action); `renderAction` approval (portable_media_action); `actionEdit` workspace-edit (workspace_edit); `qualityReport` metadata (quality_report); `editorTimeline` timeline (editor_handoff); `revision` metadata (revision_event) |
|
|
12429
12552
|
| `sm.captions` 2.0.0 | Captions | editor-extension | `transcript` transcript · primary; `projectTranscript` transcript · project context only; `timeline` timeline · project context only; `text` text; `start` number; `duration` number; `highlightColor` text; `animation` text | `edit` timeline-edit · primary |
|
|
12430
12553
|
| `sm.character` 2.0.1 | Character reference | workspace | `input` text (character_description) · primary; `name` text (character_name); `role` text (character_role); `appearance` text (character_appearance); `personality` text (character_personality) | `output` character (character_reference) · primary |
|
|
12431
12554
|
| `sm.chat` 2.0.3 | Production chat | workspace | `brief` brief (production_brief) · primary | `script` script (narration_script) · primary |
|
|
@@ -12433,7 +12556,7 @@ The latest version of each Block bundled with the SDK, generated from the packag
|
|
|
12433
12556
|
| `sm.connect-app` 1.0.0 | Connect an app | task | `app` text (connected_app) · primary | `result` object (external_data) · primary |
|
|
12434
12557
|
| `sm.contact-fields` 1.0.0 | Contact fields | task | `record` object (external_data) · primary | `email` text (contact_email) · primary; `first_name` text (contact_first_name); `last_name` text (contact_last_name); `full_name` text (contact_full_name); `phone` text (contact_phone); `company` text (contact_company); `city` text (contact_city); `other_answers` text (contact_other_answers) |
|
|
12435
12558
|
| `sm.continue-if` 1.0.0 | Continue only if | task | `decision` object (ai_decision) · primary; `item` object (external_data); `minimum_score` integer (minimum_score) | `item` object (external_data) · primary |
|
|
12436
|
-
| `sm.describe-media` 2.0
|
|
12559
|
+
| `sm.describe-media` 2.1.0 | Describe media | task | `image` image (source_image) · primary | `description` text (visual_description) · primary |
|
|
12437
12560
|
| `sm.edit-assistant` 2.0.2 | Edit Assistant | task | `timeline` timeline (timeline) · project context only · primary; `selection` timeline-range (timeline_range); `request` text (edit_request) | `edit` timeline-edit (timeline_edit_proposal) · primary |
|
|
12438
12561
|
| `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) |
|
|
12439
12562
|
| `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) |
|
|
@@ -12449,10 +12572,10 @@ The latest version of each Block bundled with the SDK, generated from the packag
|
|
|
12449
12572
|
| `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 |
|
|
12450
12573
|
| `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) |
|
|
12451
12574
|
| `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) |
|
|
12452
|
-
| `sm.panel-strip` 2.4.
|
|
12453
|
-
| `sm.photo-notes` 1.
|
|
12575
|
+
| `sm.panel-strip` 2.4.2 | 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) |
|
|
12576
|
+
| `sm.photo-notes` 1.1.0 | Photo notes | task | `photos` image[] (photo_set) · primary | `notes` text (photo_notes) · primary |
|
|
12454
12577
|
| `sm.pick-photos` 1.0.0 | Pick best photos | task | `photos` image[] (image) · primary; `keep` integer (photo_keep_count); `instruction` text (photo_selection_instruction); `notes` json (photo_notes) | `photos` image[] (image) · primary; `reasons` text (plain_text) |
|
|
12455
|
-
| `sm.pipeline` 2.2.
|
|
12578
|
+
| `sm.pipeline` 2.2.6 | 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) |
|
|
12456
12579
|
| `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 |
|
|
12457
12580
|
| `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 |
|
|
12458
12581
|
| `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 |
|
|
@@ -12471,12 +12594,12 @@ The latest version of each Block bundled with the SDK, generated from the packag
|
|
|
12471
12594
|
| `sm.whiteboard-assistant` 1.0.0 | Build Whiteboard | task | `board` board-document · project context only; `request` text · primary | `edit` workspace-edit · primary |
|
|
12472
12595
|
| `sm.write-summary` 1.0.0 | Write summary | task | `data` text (external_data) · primary | `summary` text (summary_report) · primary |
|
|
12473
12596
|
| `sm.youtube` 2.0.0 | YouTube publication | task | `video` video (finished_video) · primary; `title` text; `description` text; `tags` text; `visibility` text; `publishAt` text; `madeForKids` boolean; `aiGenerated` boolean | `uploadAction` approval (portable_media_action); `publication` json (youtube_publication) |
|
|
12474
|
-
| `stillmade.narration` 1.0.
|
|
12597
|
+
| `stillmade.narration` 1.0.1 | Narration | workspace | `brief` brief (project_brief) · primary; `script` script (narration_script); `tone` text (narration_tone); `rules` text (writing_rules); `voice` text (speech_voice); `speed` number (speech_speed); `take` audio (narration_audio) | `script` script (narration_script); `audio` audio (narration_audio) · primary |
|
|
12475
12598
|
| `stillmade.occasion-story` 1.0.2 | Occasion Story | task | `assets` asset[] (legacy.asset-list.assets) · primary; `occasion` text (stillmade.occasion_story.occasion); `title` text (stillmade.occasion_story.title); `ending` text (stillmade.occasion_story.ending); `notes` text (stillmade.occasion_story.notes); `selections` text (stillmade.occasion_story.selections); `music` audio[] (stillmade.occasion_story.music); `musicVolume` number (stillmade.occasion_story.musicvolume); `muteVideoAudio` boolean (stillmade.occasion_story.mutevideoaudio); `aspect` text (stillmade.occasion_story.aspect); `disclosure` text (stillmade.occasion_story.disclosure) | `timeline` timeline (legacy.timeline.updatededitor) · primary; `review` json (stillmade.occasion_story.review) |
|
|
12476
12599
|
| `stillmade.photo-reel-composer` 1.0.2 | Photo Reel Composer | task | `assets` asset[] (legacy.asset-list.assets) · primary; `purpose` text (stillmade.photo_reel.purpose); `title` text (stillmade.photo_reel.title); `captions` text (stillmade.photo_reel.captions); `cta` text (stillmade.photo_reel.cta); `contact` text (stillmade.photo_reel.contact); `demoDisclosure` text (stillmade.photo_reel.demodisclosure); `duration` number (stillmade.photo_reel.duration); `aspect` text (stillmade.photo_reel.aspect); `logo` asset[] (stillmade.photo_reel.logo); `music` asset[] (stillmade.photo_reel.music) | `timeline` timeline (legacy.timeline.updatededitor) · primary; `review` json (stillmade.photo_reel.review); `provenance` json (stillmade.photo_reel.provenance) |
|
|
12477
12600
|
| `stillmade.product-ad-variants` 1.0.0 | Product Ad Variants | task | `timeline` timeline (legacy.timeline.updatededitor) · primary; `hookA` text (stillmade.product_ad_variants.hooka); `hookB` text (stillmade.product_ad_variants.hookb); `hookC` text (stillmade.product_ad_variants.hookc); `openingA` number (stillmade.product_ad_variants.openinga); `openingB` number (stillmade.product_ad_variants.openingb); `openingC` number (stillmade.product_ad_variants.openingc); `openingDuration` number (stillmade.product_ad_variants.openingduration); `copyApproved` boolean (stillmade.product_ad_variants.copyapproved) | `variantA` timeline (legacy.timeline.updatededitor) · primary; `variantB` timeline (legacy.timeline.updatededitor); `variantC` timeline (legacy.timeline.updatededitor); `review` json (stillmade.product_ad_variants.review) |
|
|
12478
12601
|
| `stillmade.release-visuals` 1.0.2 | Release Visuals | task | `assets` asset[] (legacy.asset-list.assets) · primary; `purpose` text (stillmade.release_visuals.purpose); `title` text (stillmade.release_visuals.title); `artist` text (stillmade.release_visuals.artist); `cta` text (stillmade.release_visuals.cta); `duration` number (stillmade.release_visuals.duration); `songVolume` number (stillmade.release_visuals.songvolume); `offset` number (stillmade.release_visuals.offset); `lyrics` text (stillmade.release_visuals.lyrics); `aspect` text (stillmade.release_visuals.aspect); `disclosure` text (stillmade.release_visuals.disclosure) | `timeline` timeline (legacy.timeline.updatededitor) · primary; `review` json (stillmade.release_visuals.review) |
|
|
12479
|
-
| `stillmade.screen-recorder` 1.0.
|
|
12602
|
+
| `stillmade.screen-recorder` 1.0.1 | Screen Recorder | task | `video` video (source_video) · primary; `original` video (source_video) | `video` video (edited_video) · primary; `original` video (source_video) |
|
|
12480
12603
|
| `stillmade.short-form-finish` 1.0.3 | Short-form Finish | task | `assets` video[] (legacy.asset-list.assets) · primary; `music` audio[] (legacy.asset-list.assets); `audioMode` text (stillmade.short_form.audiomode); `speechVolume` number (stillmade.short_form.speechvolume); `purpose` text (stillmade.short_form.purpose); `cuts` text (stillmade.short_form.cuts); `subtitles` text (stillmade.short_form.subtitles); `title` text (stillmade.short_form.title); `byline` text (stillmade.short_form.byline); `cta` text (stillmade.short_form.cta); `disclosure` text (stillmade.short_form.disclosure); `captionStyle` text (stillmade.short_form.captionstyle); `aspect` text (stillmade.short_form.aspect); `fit` text (stillmade.short_form.fit); `cropX` number (stillmade.short_form.cropx) | `timeline` timeline (legacy.timeline.updatededitor) · primary; `review` json (stillmade.short_form.review) |
|
|
12481
12604
|
|
|
12482
12605
|
## Custom view rules
|
|
@@ -12506,7 +12629,7 @@ Every finished Block must support automatic remixing through the standard SDK co
|
|
|
12506
12629
|
|
|
12507
12630
|
### Package format
|
|
12508
12631
|
|
|
12509
|
-
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"]}}.
|
|
12632
|
+
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"]}}. Port types: text, number, integer, boolean, json, object, image, video, audio, asset, script, transcript, brief, bible, shot-plan, panel-document, board-document, scene-document, timeline, timeline-edit, file-bundle, workspace-edit, timeline-range, research, character, location, style, pipeline, approval, scene, shot, mask, depth, pose, metadata, project-context, file, document, table, url, date, color. A value is exactly: text: a string; number: a number; integer: a whole number; boolean: true or false; json: any JSON value; object: a plain JSON object; image: {kind: 'image'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; width?: number; height?: number}; video: {kind: 'video'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; width?: number; height?: number; duration?: number}; audio: {kind: 'audio'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string; duration?: number; sampleRate?: number; channels?: number}; asset: {kind: 'image' | 'video' | 'audio' | 'asset'; assetId: string; versionId: string; url: string; mimeType?: string; bytes?: number; name?: string; role?: string}; script: {schemaVersion: 1; text: string; [key: string]: unknown}; transcript: {schemaVersion: 1; text: string; words: Array<{text: string; start: number; end: number}>}; file: {kind: 'file'; assetId: string; versionId: string; mimeType: 'application/pdf' | 'text/plain' | 'text/csv' | 'text/markdown' | 'application/json' | 'application/zip'; url?: string; name?: string; bytes?: number}; document: {schemaVersion: 1; format: 'markdown' | 'plain'; text: string; title?: string}; table: {schemaVersion?: 1; columns: string[]; rows: Array<Record<string, string | number | boolean | null>>}; url: an absolute http or https address string; date: an ISO 8601 date string ('2026-10-09' or '2026-10-09T14:30'); color: a hex color string ('#ff8800' or '#ff8800cc'). A file value is a saved file: one the person gives the Block (a module Block reads its bytes with stillmade.files.read) or what stillmade.files.write returns in a module Block; an image may also be decoded RGBA pixels {width, height, data} in recipe and JavaScript Blocks, which hold only 4 MiB of JSON (a picture of about 300 by 300 pixels), so a Block that joins photos into one picture (side by side, a grid, a contact sheet, a poster) is a module Block calling media.transform image.compose. Every other type is {schemaVersion: 1; [key: string]: unknown} with the fields its own section gives. A type ending in [] is a plain array of those values (audio[] is a list of audio references, never objects that wrap them). Include test {name,input,expected} where expected is output bindings.
|
|
12510
12633
|
|
|
12511
12634
|
### Feature map
|
|
12512
12635
|
|
|
@@ -12514,7 +12637,7 @@ Every Block lists its separate tools in manifest.features, one entry per tool or
|
|
|
12514
12637
|
|
|
12515
12638
|
### Port meanings
|
|
12516
12639
|
|
|
12517
|
-
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
|
|
12640
|
+
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 required:boolean, a short plain-language description and a semantic (import refuses an input without one, and gate checks it). An input accepts values declared with its meaning or a narrower one: an input for any photo a Step hands on uses source_image, as StillMade's Image editor does (generated_image and processed_image are kinds of source_image), and a photo editor's result is processed_image. 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, and always on a json or object output (import refuses one without it): 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 ?. A value of a structured type (transcript, script, brief, shot-plan and the like) is an object with schemaVersion:1, in code and fixtures; a transcript as the Transcription Block hands it on is {schemaVersion:1,text:"...",words:[{text:"Today",start:0,end:0.4}]}, times in seconds. 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.
|
|
12518
12641
|
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.
|
|
12519
12642
|
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.
|
|
12520
12643
|
|
|
@@ -12527,14 +12650,14 @@ For hosted text generation use {manifest,capability,tests}, runtime:"capability"
|
|
|
12527
12650
|
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}}}]}.
|
|
12528
12651
|
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}}}]}.
|
|
12529
12652
|
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}}}]}.
|
|
12530
|
-
For image analysis, use runtime:"capability", operation:"
|
|
12653
|
+
For image or video analysis (what a photo shows, which photos show different angles, the words on a menu or sign), use runtime:"capability", operation:"media.analyze" and permissions.capabilities:["media.analyze"]: StillMade credits pay for it, while image.describe runs only on the person's own OpenRouter key, so never use it. Declare one primary image input, one image[] input of one to four images, or one video input (with optional start and end number inputs, bound through capability.range:{start:{"$input":"start"},end:{"$input":"end"}}), and one primary text or json output. capability:{schemaVersion:1,operation:"media.analyze",media:{"$input":"photo"},question:"what to look for, at most 2,000 characters",output:"notes"}; a text output gets each picture's summary, a json output {schemaVersion:1,items}. For a quick rating of photos (sorting or picking), add detail:"check": images only, one short answer each, one credit per call of up to four photos instead of four credits per picture. The host loads only owned media and controls provider, model, payment and approval. Tests give saved references and expect json that includes summary, or text length bounds, never invented live answers. Shipped example: {"manifest":{"schemaVersion":1,"sdkVersion":"0.1.0","id":"example.check-clip","version":"1.0.0","name":"Check a clip","description":"Answer a production question about a saved video clip, with timestamps.","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":{"clip":{"type":"video","primary":true,"semantic":"source_video","required":true,"description":"Saved video to check."},"start":{"type":"number","required":false,"min":0,"semantic":"range_start","description":"Optional start of the range to look at closely, in seconds."},"end":{"type":"number","required":false,"min":0,"semantic":"range_end","description":"Optional end of the range, in seconds (at most 12 seconds after the start)."}},"outputs":{"report":{"type":"json","primary":true,"semantic":"media_findings","description":"Summary, timeline, on-screen text and issues found."}},"permissions":{"project":[],"capabilities":["media.analyze"],"network":[],"filesystem":[],"secrets":[]},"ui":[{"control":"asset","port":"clip"},{"control":"number","port":"start"},{"control":"number","port":"end"}]},"capability":{"schemaVersion":1,"operation":"media.analyze","media":{"$input":"clip"},"question":"Is the product logo clearly visible, and when? List any on-screen text and visible defects.","range":{"start":{"$input":"start"},"end":{"$input":"end"}},"output":"report"},"tests":[{"name":"A saved clip","input":{"clip":{"kind":"video","assetId":"clip-1","versionId":"v1","url":"/api/media/owner/clip.mp4"}},"expectations":{"report":{"kind":"json","includes":["summary"]}}}]}.
|
|
12531
12654
|
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}}}]}.
|
|
12532
12655
|
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}}}]}.
|
|
12533
12656
|
A capability package performs exactly one supported operation. A job that needs several (transcribe a recording, then find what is said in it; write words, then voice them), or code between two operations, is one module Block that calls each with stillmade.hosted.run (Module Blocks); never split one job into part Blocks, and never invent a combined capability 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.
|
|
12534
12657
|
|
|
12535
12658
|
### Module Blocks
|
|
12536
12659
|
|
|
12537
|
-
A job that needs several hosted operations, or code around one (reading a transcript's words and times, shaping a list), is a module Block: {manifest,moduleSource,tests}, runtime:"module", moduleSource:{"main.js":"<the code>"}. StillMade names main.js by its digest and adds module; give no other files. main.js is plain JavaScript that imports nothing and uses no browser APIs (no fetch, TextEncoder, timers or WebCodecs): export default async function run(input, stillmade) returns the declared outputs. It calls each hosted operation with await stillmade.hosted.run(operation, request), listed in manifest.permissions.capabilities: text.generate, image.generate, video.generate, audio.speech, audio.music, audio.sfx, audio.transcribe, image.describe, media.analyze, web.fetch, web.research. Requests, and what each call returns (a test's mocks record exactly that value): text.generate {prompt, system, maxTokens} returns the text as a string, and with schema (a JSON schema object) returns that object, so ask for JSON with schema; audio.transcribe {audio: the input's saved reference, language:"auto"} returns {schemaVersion:1,text,words:[{text,start,end}]}; image.generate {prompt} and audio.speech {text} return a saved image or audio reference; image.describe {image, instruction} and web.fetch {url, instruction} return a string; media.analyze {media, question} returns {items:[...]}; web.research {topic} returns {summary,sources}. A call that fails throws: let it fail the run rather than catching it and returning an empty result. Media work on the person's device (cut, join or resize video, decode or extract audio, thumbnails) is await stillmade.media.transform(request); exporting is too: {operation:"timeline.render",kind:"video" or "audio",aspect:"9:16",clips:[{source,start,duration,from,transition:{type:"Fade"},look:"Filmic"}],audio:[{source,start,duration}],captions:[{text,start,end,words:[{text,start,end}],highlight:"#ffd400"}]} renders the Block's own timeline with the Editor's transitions, looks and word-by-word captions under the person's export plan, so never ask for a separate export, captions or color Block; stillmade.log and stillmade.progress(fraction, label) report. One press of the Step's button runs every call at the price shown beside it. A module Block runs in the person's browser while StillMade is open, never on a schedule or event. Tests are {name,input,expected,mocks}: mocks records what each call answers, as {hosted:{"audio.transcribe":{schemaVersion:1,text:"...",words:[...]},"text.generate":{...}},transforms:{...}} (a list answers successive calls in order), and expected is the exact output main.js returns for those answers. StillMade runs every test before anyone sees the Block, without spending credits. With the SDK instead, the same code is module/main.js in a Block folder and the same tests are tests/fixtures.json; stillmade-block pack writes the file to import (MODULES.md).
|
|
12660
|
+
A job that needs several hosted operations, or code around one (reading a transcript's words and times, shaping a list), or media work on the person's device even with no hosted operation (rendering clips and photos into one video, joining pictures into one), is a module Block: {manifest,moduleSource,tests}, runtime:"module", moduleSource:{"main.js":"<the code>"}. StillMade names main.js by its digest and adds module. StillMade's in-app builder takes one file: give no other files, and main.js is plain JavaScript that imports nothing and uses no browser APIs (no fetch, TextEncoder, timers or WebCodecs). That single-file rule is the in-app builder's; a module Block built with the SDK may use timers, WebCodecs, bundled npm packages and .wasm files as MODULES.md describes. main.js: export default async function run(input, stillmade) returns the declared outputs. It calls each hosted operation with await stillmade.hosted.run(operation, request), listed in manifest.permissions.capabilities: text.generate, image.generate, video.generate, audio.speech, audio.music, audio.sfx, audio.transcribe, image.describe, media.analyze, web.fetch, web.research. Requests, and what each call returns (a test's mocks record exactly that value): text.generate {prompt, system, maxTokens} returns the text as a string, and with schema (a JSON schema object) returns that object, so ask for JSON with schema; one text.generate prompt holds at most 32,000 characters, so a longer file or transcript is read in parts (one call per part of about 25,000 characters, then one call that joins what they found), never cut off, and when the result is the whole text again (a cleaned transcript, a translation) each part's answer is kept and joined in order in main.js, since one answer holds at most 4,096 tokens; audio.transcribe {audio: the input's saved reference, language:"auto"} returns {schemaVersion:1,text,words:[{text,start,end}]}; image.generate {prompt, references?: one to eight saved images the new picture keeps, such as the product, person or place in the person's photos} and audio.speech {text} return a saved image or audio reference; web.fetch {url, instruction} returns a string; media.analyze {media: one to four saved images or one saved video, question} returns {items:[{kind,summary,observations,detectedText}]}, one item per image, and is how a Block looks at pictures (picking the photos that show different angles, reading a menu off a photo): StillMade credits pay for it; image.describe {image, instruction} returns a string but runs only on the person's own OpenRouter key, so never use it; web.research {topic} returns {summary,sources}. A file the person gives the Block (a text, CSV, Markdown or JSON file on a file input) is read with await stillmade.files.read(input.<port>), which returns its bytes; turn them into text in main.js as the worked file input below does, and give the test's input file an assetId whose text mocks.files records as {files:{"<assetId>":{text:"..."}}}. A Block that reads a file the person gives it is a module Block, never a text.generate capability package. A PDF (lesson slides, a lease, a contract) is read with await stillmade.media.transform({operation:"document.text",source:input.<port>}), which returns {text, pages}, the words of each page, on the person's device and without credits (a text file reads the same way); a scanned PDF has no words to read. A call that fails throws: let it fail the run rather than catching it and returning an empty result. Media work on the person's device (cut, join or resize video, decode or extract audio, thumbnails, joining images) is await stillmade.media.transform(request). Joining saved images into one picture (two photos side by side with Before and After labels, a grid or collage, a contact sheet, a storyboard sheet, a poster with a title) is {operation:"image.compose",width,height,background:"#ffffff",layers:[{source,x,y,width,height,fit:"cover" or "contain"}],text:[{text,x,y,size,color,background,align:"left" or "center" or "right",width}]}: a canvas up to 4096 pixels a side, positions in pixels worked out in main.js, layers drawn in order and text on top (y is the top of the line, background puts a box behind it, colours are #rrggbb or, see-through, #rrggbbaa such as #00000099 for a caption box, width wraps the words onto lines no wider than it: give a caption under a frame the frame's width so it never runs into the next one); it returns the saved image, and main.js never decodes pixels itself. Exporting is a transform too: {operation:"timeline.render",kind:"video" or "audio",aspect:"9:16",clips:[{source,start,duration,from,transition:{type:"Fade"},look:"Filmic"}],audio:[{source,start,duration}],captions:[{text,start,end,words:[{text,start,end}],highlight:"#ffd400"}]} renders the Block's own timeline with the Editor's transitions, looks and word-by-word captions under the person's export plan (start is a clip's place on that timeline, so each clip starts where the one before it ends, less the transition on the clip before it, which blends that clip into the next, and from is where it begins in its own file; overlapping clips are refused), so never ask for a separate export, captions or color Block; stillmade.log and stillmade.progress(fraction, label) report. One press of the Step's button approves each hosted operation once at the price shown beside it, and media.analyze for every picture or video the Step is given, across as many calls as that takes; any further call asks the person first. A module Block runs in the person's browser while StillMade is open, never on a schedule or event. Tests are {name,input,expected,mocks} and compare outputs, so every test is a case that runs to its outputs; an input the Block refuses (it throws a plain Error with a message for the person, such as a clip with no duration) is never a test. mocks records what each call answers, as {hosted:{"audio.transcribe":{schemaVersion:1,text:"...",words:[...]},"text.generate":{...}},transforms:{...}} (a list answers successive calls in order), and expected is the exact output main.js returns for those answers: exact values of each output's type, never capability bounds such as minDuration or maxBytes. In a module test, mock each media call with its complete saved reference (kind, assetId, versionId, url, mimeType and its measured fields: duration, bytes, sampleRate, channels for audio; width, height, bytes for images) and repeat those references unchanged in expected; names or labels go in their own output. Worked outputs, each a passing test. A document output is the document object on a document port (not text): {"outputs":{"sheet":{"type":"document"}},"main.js":"export default async function run(input, stillmade) { const text = await stillmade.hosted.run('text.generate', {prompt: 'A Markdown care sheet for ' + input.plant, maxTokens: 800}); return {sheet: {schemaVersion: 1, format: 'markdown', text, title: input.plant + ' care'}}; }","test":{"name":"Fern","input":{"plant":"Fern"},"mocks":{"hosted":{"text.generate":"## Light\nBright, indirect light."}},"expected":{"sheet":{"schemaVersion":1,"format":"markdown","text":"## Light\nBright, indirect light.","title":"Fern care"}}}}. A file output is the reference await stillmade.files.write(data, {mediaType, name}) returns, handed on unchanged; mediaType is one of application/pdf, text/plain, text/csv, text/markdown, application/json, application/zip: {"outputs":{"report":{"type":"file"}},"main.js":"export default async function run(input, stillmade) { const csv = 'Room\\n' + input.rooms.join('\\n'); return {report: await stillmade.files.write(csv, {mediaType: 'text/csv', name: 'rooms.csv'})}; }","test":{"name":"Two rooms","input":{"rooms":["Kitchen","Hall"]},"expected":{"report":{"kind":"file","mimeType":"text/csv","name":"rooms.csv"}}}} (in a test, files.write answers with fields the host makes: assetId local-written-file-N, versionId, url /api/media/stillmade-mock/written-file-N.csv with the extension of its mediaType, and bytes; expected gives that reference as {kind:"file", mimeType, name} and it matches when those three match). An audio[] output is the list of references audio.speech returned, unchanged; its mock is a list answering successive calls, and the labels go in a separate text[] output: {"outputs":{"tracks":{"type":"audio[]"},"names":{"type":"text[]"}},"main.js":"export default async function run(input, stillmade) { const tracks = []; for (const room of input.rooms) tracks.push(await stillmade.hosted.run('audio.speech', {text: 'Welcome to the ' + room + '.'})); return {tracks, names: input.rooms}; }","test":{"name":"Two rooms","input":{"rooms":["Kitchen","Hall"]},"mocks":{"hosted":{"audio.speech":[{"kind":"audio","assetId":"speech-1","versionId":"v1","url":"/api/media/example/kitchen.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1},{"kind":"audio","assetId":"speech-2","versionId":"v1","url":"/api/media/example/hall.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1}]}},"expected":{"tracks":[{"kind":"audio","assetId":"speech-1","versionId":"v1","url":"/api/media/example/kitchen.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1},{"kind":"audio","assetId":"speech-2","versionId":"v1","url":"/api/media/example/hall.wav","mimeType":"audio/wav","duration":2,"bytes":96044,"sampleRate":24000,"channels":1}],"names":["Kitchen","Hall"]}}}. A file input read as text: {"outputs":{"guide":{"type":"text"}},"main.js":"function textOf(buffer) { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.length; i += 8192) binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 8192)); try { return decodeURIComponent(escape(binary)); } catch (error) { return binary; } }\nexport default async function run(input, stillmade) { const notes = textOf(await stillmade.files.read(input.notes)); const guide = await stillmade.hosted.run('text.generate', {prompt: 'Rewrite these notes as numbered steps, keeping every number as written:\\n' + notes, maxTokens: 1200}); return {guide}; }","test":{"name":"Two notes","input":{"notes":{"kind":"file","assetId":"notes-1","versionId":"v1","url":"/api/media/example/notes.txt","mimeType":"text/plain","name":"notes.txt","bytes":31}},"mocks":{"files":{"notes-1":{"text":"pull the probe\nwipe the tines"}},"hosted":{"text.generate":"1. Pull the probe.\n2. Wipe the tines."}},"expected":{"guide":"1. Pull the probe.\n2. Wipe the tines."}}}. Photos joined into one image, its transform mocked with the saved image it returns: {"outputs":{"sheet":{"type":"image"}},"main.js":"export default async function run(input, stillmade) { const photos = input.photos; if (!photos.length) throw new Error('Add at least one photo.'); const columns = Math.ceil(Math.sqrt(photos.length)), rows = Math.ceil(photos.length / columns), cell = 400, gap = 20, top = 160; const width = columns * cell + (columns + 1) * gap, height = top + rows * cell + (rows + 1) * gap; const layers = photos.map((source, i) => ({source, x: gap + (i % columns) * (cell + gap), y: top + gap + Math.floor(i / columns) * (cell + gap), width: cell, height: cell, fit: 'cover'})); const sheet = await stillmade.media.transform({operation: 'image.compose', width, height, background: '#ffffff', layers, text: [{text: input.title, x: width / 2, y: 50, size: 64, align: 'center', color: '#111111'}]}); return {sheet}; }","test":{"name":"Four photos","input":{"photos":[{"kind":"image","assetId":"photo-1","versionId":"v1","url":"/api/media/example/photo-1.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-2","versionId":"v1","url":"/api/media/example/photo-2.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-3","versionId":"v1","url":"/api/media/example/photo-3.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000},{"kind":"image","assetId":"photo-4","versionId":"v1","url":"/api/media/example/photo-4.jpg","mimeType":"image/jpeg","width":1600,"height":1200,"bytes":412000}],"title":"Saturday games"},"mocks":{"transforms":{"image.compose":{"kind":"image","assetId":"sheet-1","versionId":"v1","url":"/api/media/example/sheet.png","mimeType":"image/png","width":1300,"height":1440,"bytes":1820000}}},"expected":{"sheet":{"kind":"image","assetId":"sheet-1","versionId":"v1","url":"/api/media/example/sheet.png","mimeType":"image/png","width":1300,"height":1440,"bytes":1820000}}}}. StillMade runs every test before anyone sees the Block, without spending credits. With the SDK instead, the same code is module/main.js in a Block folder and the same tests are tests/fixtures.json; stillmade-block pack writes the file to import (MODULES.md).
|
|
12538
12661
|
|
|
12539
12662
|
### ComfyUI workflows
|
|
12540
12663
|
|
|
@@ -12582,7 +12705,7 @@ Optional custom view interfaces are supported, but generated manifest.ui control
|
|
|
12582
12705
|
|
|
12583
12706
|
*When:* StillMade's builder adds this section when the person asks for an interface, preview or controls, or refines a Block that has one.
|
|
12584
12707
|
|
|
12585
|
-
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. An (i) beside anything a newcomer could misread: a setting, status or number whose effect is not obvious from its label gets a closed `sm-hint` with one or two sentences. Give every input that needs one a `description`; StillMade shows it as that (i). Obvious controls get none. 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. Use the width: from 860px wide the kit puts the controls on the left at a reading width (300–380px) and the result (`sm-stage`) fills the right, in its real shape; under that everything stacks with the result first and the action within reach. Extra width goes to the result, never to wider text fields. Before there is a result, the stage previews what will be made from the inputs. `data-layout="stack"` opts out. Settings sit side by side (`sm-settings`), next to what they change. Three or four short options are one segmented row (`data-segmented`), not a row of big tiles. Lists fill the width at a small size: input files are small tiles (`sm-gallery data-size="small"`), many items are one line each (`sm-list`): a thumbnail, one line of text, a short fact. Tapping an item opens all of its fields in place. Extra width means more items per row, never bigger cards. On a phone chip and segment rows scroll sideways instead of wrapping; long lists show their first three with "N more" (`data-peek`) and long text five lines until tapped (`sm-clamp`). Nothing is lost when a Block gets smaller: every control stays on the screen, behind an (i), in `sm-more`, or inside the item it belongs to. Compacting moves controls; it never deletes 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.
|
|
12708
|
+
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. An (i) beside anything a newcomer could misread: a setting, status or number whose effect is not obvious from its label gets a closed `sm-hint` with one or two sentences. Give every input that needs one a `description`; StillMade shows it as that (i). Obvious controls get none. 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. Use the width: from 860px wide the kit puts the controls on the left at a reading width (300–380px) and the result (`sm-stage`) fills the right, in its real shape; under that everything stacks with the result first and the action within reach. Extra width goes to the result, never to wider text fields. Before there is a result, the stage previews what will be made from the inputs. `data-layout="stack"` opts out. Size with the kit, never fixed sizes: no fixed pixel widths or heights for the workspace or stage, no 100vh, no position:fixed panels. StillMade zooms a Block's screen to fill the space it has (up to 150%, never down to a phone layout on a computer), so a Block laid out with the kit's spacing fills its Step without empty space. Settings sit side by side (`sm-settings`), next to what they change. Three or four short options are one segmented row (`data-segmented`), not a row of big tiles. Lists fill the width at a small size: input files are small tiles (`sm-gallery data-size="small"`), many items are one line each (`sm-list`): a thumbnail, one line of text, a short fact. Tapping an item opens all of its fields in place. Extra width means more items per row, never bigger cards. On a phone chip and segment rows scroll sideways instead of wrapping; long lists show their first three with "N more" (`data-peek`) and long text five lines until tapped (`sm-clamp`). Nothing is lost when a Block gets smaller: every control stays on the screen, behind an (i), in `sm-more`, or inside the item it belongs to. Compacting moves controls; it never deletes 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.
|
|
12586
12709
|
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`:
|
|
12587
12710
|
|
|
12588
12711
|
- **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:`.
|