stillmade-sdk 0.1.42 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.stillmade/sdk-baseline.json +6 -8
- package/AGENTS.md +15 -10
- package/FRAME_VIEWS.md +113 -0
- package/INTERFACE_DESIGN.md +62 -22
- package/MOBILE_UI.md +4 -2
- package/MODULES.md +152 -6
- package/PASTE_TO_LLM.md +35 -25
- package/README.md +3 -2
- package/SDK.md +994 -550
- package/UI_AESTHETIC.md +22 -2
- package/blocks/generate/package.js +1 -0
- package/blocks/generate/versions/1.0.0/package.js +463 -0
- package/docs/build/audio-to-editor.md +36 -0
- package/docs/build/block-builder-contract.md +150 -0
- package/docs/build/comfyui.md +227 -0
- package/docs/build/concepts.md +40 -0
- package/docs/build/context.md +159 -0
- package/docs/build/cookbook.md +57 -0
- package/docs/build/custom-interface.md +617 -0
- package/docs/build/data-types.md +83 -0
- package/docs/build/frame-views.md +259 -0
- package/docs/build/generate-media.md +59 -0
- package/docs/build/github-applications.md +314 -0
- package/docs/build/hosted-images.md +214 -0
- package/docs/build/hosted-music.md +39 -0
- package/docs/build/hosted-speech.md +157 -0
- package/docs/build/hosted-text.md +246 -0
- package/docs/build/image-description.md +49 -0
- package/docs/build/interface-design.md +343 -0
- package/docs/build/javascript.md +63 -0
- package/docs/build/local-preview.md +61 -0
- package/docs/build/media-analysis.md +27 -0
- package/docs/build/media.md +400 -0
- package/docs/build/mobile-ui.md +106 -0
- package/docs/build/modules.md +487 -0
- package/docs/build/original-blocks.md +70 -0
- package/docs/build/outside-apis.md +128 -0
- package/docs/build/production-context-templates.md +12 -0
- package/docs/build/project-storage.md +94 -0
- package/docs/build/quickstart.md +48 -0
- package/docs/build/recipes.md +103 -0
- package/docs/build/script-handoff.md +45 -0
- package/docs/build/script-writer.md +34 -0
- package/docs/build/state.md +223 -0
- package/docs/build/testing.md +112 -0
- package/docs/build/timeline-edits.md +116 -0
- package/docs/build/timeline-proposals.md +76 -0
- package/docs/build/transcription.md +59 -0
- package/docs/build/triggers.md +59 -0
- package/docs/build/tutorial.md +52 -0
- package/docs/build/typescript.md +34 -0
- package/docs/build/video-generation.md +61 -0
- package/docs/build/web-reading.md +37 -0
- package/docs/build/with-your-ai.md +27 -0
- package/docs/changelog.md +9 -0
- package/docs/creators.md +21 -0
- package/docs/distribute/discovery.md +43 -0
- package/docs/distribute/imports.md +58 -0
- package/docs/distribute/publishing.md +29 -0
- package/docs/distribute/sdk-versions.md +36 -0
- package/docs/distribute/selling.md +14 -0
- package/docs/distribute/teams.md +60 -0
- package/docs/distribute/versioning.md +28 -0
- package/docs/examples.md +23 -0
- package/docs/gallery.md +26 -0
- package/docs/index.md +50 -0
- package/docs/native-mcp.md +307 -0
- package/docs/project-types/automation.md +43 -0
- package/docs/project-types/batches.md +50 -0
- package/docs/project-types/build.md +42 -0
- package/docs/project-types/completion.md +15 -0
- package/docs/project-types/conditions.md +21 -0
- package/docs/project-types/hosted-review.md +115 -0
- package/docs/project-types/onboarding.md +75 -0
- package/docs/project-types/running.md +309 -0
- package/docs/project-types/testing.md +72 -0
- package/docs/reference/application-artifacts.md +87 -0
- package/docs/reference/authoring-guide.md +134 -0
- package/docs/reference/browser-applications.md +267 -0
- package/docs/reference/capabilities.md +45 -0
- package/docs/reference/cli.md +71 -0
- package/docs/reference/connection-api.md +59 -0
- package/docs/reference/cooperative-jobs.md +13 -0
- package/docs/reference/desktop-capabilities.md +172 -0
- package/docs/reference/desktop-execution.md +77 -0
- package/docs/reference/errors.md +233 -0
- package/docs/reference/execution.md +800 -0
- package/docs/reference/first-party-blocks.md +40 -0
- package/docs/reference/generation-models.md +59 -0
- package/docs/reference/limits.md +306 -0
- package/docs/reference/manifest-kinds-controls.md +36 -0
- package/docs/reference/manifest.md +181 -0
- package/docs/reference/operations.md +36 -0
- package/docs/reference/package-jobs.md +35 -0
- package/docs/reference/parity.md +55 -0
- package/docs/reference/permissions.md +86 -0
- package/docs/reference/ports.md +26 -0
- package/docs/reference/types.md +268 -0
- package/docs/reference/universal-resolver.md +303 -0
- package/docs/reference/view-bridge.md +60 -0
- package/docs/reference/view-rules.md +15 -0
- package/docs/reference/workspace-sync.md +25 -0
- package/docs/sdk.md +34 -0
- package/docs/tools/block-remix.md +211 -0
- package/docs/tools/llm.md +2213 -0
- package/docs/tools/repository-adaptation.md +613 -0
- package/examples/check-clip.stillmade.json +4 -1
- package/examples/generate-video.stillmade.json +6 -2
- package/examples/research-topic.stillmade.json +5 -1
- package/examples/text-workflow.stillmade.json +3 -32
- package/examples/timeline-polish.stillmade.json +11 -3
- package/examples/transcribe-audio.stillmade.json +11 -3
- package/package.json +1 -1
- package/packages/block-cli/cli.js +13 -6
- package/packages/block-cli/dev-host/assets/host.js +76 -38
- package/packages/block-cli/dev-host/assets/index.css +1 -1
- package/packages/block-cli/format.js +1 -1
- package/packages/block-cli/templates.js +31 -13
- package/packages/block-cli/type-packages.js +119 -0
- package/packages/block-platform/block-readiness.js +5 -0
- package/packages/block-platform/canvas.js +2 -1
- package/packages/block-platform/catalog.js +27 -48
- package/packages/block-platform/native-host-inventory.js +0 -226
- package/packages/block-platform/plain-descriptions.js +1 -25
- package/packages/block-platform/project-type-starters.js +13 -53
- package/packages/block-platform/script-handoff.js +2 -4
- package/packages/block-platform/type-workspace.js +2 -0
- package/packages/block-sdk/appearance.js +3 -1
- package/packages/block-sdk/block-tier.js +1 -1
- package/packages/block-sdk/builder-contract.js +10 -8
- package/packages/block-sdk/capabilities.js +1 -1
- package/packages/block-sdk/contracts.js +6 -2
- package/packages/block-sdk/first-party-identities.js +1 -84
- package/packages/block-sdk/first-party-packages.js +3 -85
- package/packages/block-sdk/generation-catalog.js +15 -3
- package/packages/block-sdk/index.d.ts +2 -1
- package/packages/block-sdk/media-analysis-example.js +1 -1
- package/packages/block-sdk/mock-host.js +7 -1
- package/packages/block-sdk/module-host-calls.js +35 -10
- package/packages/block-sdk/module-hosted.js +15 -2
- package/packages/block-sdk/module-media-access.js +71 -0
- package/packages/block-sdk/module-node.js +1 -1
- package/packages/block-sdk/portable-media-actions.js +1 -1
- package/packages/block-sdk/runtime.js +1 -1
- package/packages/block-sdk/timeline-proposal-example.js +1 -1
- package/packages/block-sdk/transcription-example.js +1 -1
- package/packages/block-sdk/video-generation-example.js +1 -1
- package/packages/block-sdk/view-bridge.js +4 -1
- package/packages/block-sdk/view-design.js +17 -14
- package/packages/block-sdk/web-example.js +1 -1
- package/packages/design-system/block-kit.css +48 -12
- package/packages/design-system/block-kit.html +3 -2
- package/blocks/mcp-motion-planner/versions/1.1.0/package.js +0 -97
- package/blocks/motion-designer/versions/1.0.1/package.js +0 -173318
- package/blocks/motion-designer/versions/1.0.2/package.js +0 -173318
- package/blocks/motion-frame-review/versions/1.1.1/package.js +0 -465
- package/blocks/motion-frame-review/versions/1.1.2/package.snapshot.js +0 -572
- package/blocks/narration/package.js +0 -1
- package/blocks/narration/versions/1.0.0/package.js +0 -221
- package/blocks/narration/versions/1.0.1/package.js +0 -221
- package/blocks/native/sm.animated-scene/versions/2.0.0/package.js +0 -475
- package/blocks/native/sm.animated-scene/versions/2.1.0/package.js +0 -498
- package/blocks/native/sm.animated-scene/versions/2.2.0/package.js +0 -524
- package/blocks/native/sm.animated-scene/versions/2.3.0/package.js +0 -618
- package/blocks/native/sm.animated-scene/versions/2.3.1/package.js +0 -618
- package/blocks/native/sm.animated-scene/versions/2.3.2/package.js +0 -723
- package/blocks/native/sm.animated-scene/versions/2.3.3/package.js +0 -723
- package/blocks/native/sm.animated-scene/versions/2.3.4/package.js +0 -676
- package/blocks/native/sm.animated-scene/versions/2.3.5/package.js +0 -693
- package/blocks/native/sm.animated-scene/versions/2.3.6/package.js +0 -700
- package/blocks/native/sm.animated-scene/versions/2.3.7/package.js +0 -747
- package/blocks/native/sm.animated-scene/versions/2.3.8/package.js +0 -769
- package/blocks/native/sm.animated-scene/versions/2.3.9/package.js +0 -769
- package/blocks/native/sm.animated-scene-assistant/versions/1.0.0/package.js +0 -183
- package/blocks/native/sm.blueprint/versions/2.0.0/package.js +0 -358
- package/blocks/native/sm.blueprint/versions/2.1.0/package.js +0 -358
- package/blocks/native/sm.blueprint/versions/2.1.1/package.js +0 -358
- package/blocks/native/sm.blueprint/versions/2.1.2/package.js +0 -358
- package/blocks/native/sm.blueprint/versions/2.1.3/package.js +0 -381
- package/blocks/native/sm.blueprint/versions/2.1.4/package.js +0 -381
- package/blocks/native/sm.blueprint/versions/2.1.5/package.js +0 -339
- package/blocks/native/sm.blueprint/versions/2.1.6/package.js +0 -356
- package/blocks/native/sm.blueprint/versions/2.1.7/package.js +0 -363
- package/blocks/native/sm.blueprint/versions/2.1.8/package.js +0 -410
- package/blocks/native/sm.blueprint/versions/2.1.9/package.js +0 -422
- package/blocks/native/sm.blueprint-assistant/versions/1.0.0/package.js +0 -172
- package/blocks/native/sm.chat/versions/2.0.0/package.js +0 -144
- package/blocks/native/sm.chat/versions/2.0.1/package.js +0 -97
- package/blocks/native/sm.chat/versions/2.0.2/package.js +0 -100
- package/blocks/native/sm.chat/versions/2.0.3/package.js +0 -100
- package/blocks/native/sm.continue-if/versions/1.0.0/package.js +0 -168
- package/blocks/native/sm.describe-media/versions/2.0.0/package.js +0 -153
- package/blocks/native/sm.describe-media/versions/2.0.1/package.js +0 -156
- package/blocks/native/sm.describe-media/versions/2.1.0/package.js +0 -140
- package/blocks/native/sm.image-editor/versions/2.0.0/package.js +0 -183
- package/blocks/native/sm.image-editor/versions/2.0.1/package.js +0 -190
- package/blocks/native/sm.new-items/versions/1.0.0/package.js +0 -191
- package/blocks/native/sm.pick-photos/versions/1.0.0/package.js +0 -334
- package/blocks/native/sm.quality-audit/versions/1.0.0/package.js +0 -192
- package/blocks/native/sm.quality-audit/versions/1.0.1/package.js +0 -192
- package/blocks/native/sm.quality-audit/versions/1.0.2/package.js +0 -195
- package/blocks/native/sm.research/versions/2.0.0/package.js +0 -175
- package/blocks/native/sm.research/versions/2.0.1/package.js +0 -184
- package/blocks/native/sm.research/versions/2.0.2/package.js +0 -187
- package/blocks/native/sm.script/versions/2.0.0/package.js +0 -144
- package/blocks/native/sm.script/versions/2.1.0/package.js +0 -144
- package/blocks/native/sm.script/versions/2.1.1/package.js +0 -144
- package/blocks/native/sm.script/versions/2.1.2/package.js +0 -105
- package/blocks/native/sm.script/versions/2.1.3/package.js +0 -152
- package/blocks/native/sm.script/versions/2.1.4/package.js +0 -156
- package/blocks/native/sm.script/versions/2.1.5/package.js +0 -156
- package/blocks/native/sm.shot-plan/versions/2.0.0/package.js +0 -301
- package/blocks/native/sm.shot-plan/versions/2.1.0/package.js +0 -528
- package/blocks/native/sm.shot-plan/versions/2.1.1/package.js +0 -528
- package/blocks/native/sm.shot-plan/versions/2.1.2/package.js +0 -528
- package/blocks/native/sm.shot-plan/versions/2.1.3/package.js +0 -536
- package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +0 -800
- package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +0 -920
- package/blocks/native/sm.shot-plan-assistant/versions/1.0.0/package.js +0 -173
- package/blocks/native/sm.stock-search/versions/2.0.0/package.js +0 -174
- package/blocks/native/sm.thumbnail/versions/2.0.0/package.js +0 -137
- package/blocks/native/sm.thumbnail/versions/2.0.1/package.js +0 -140
- package/blocks/native/sm.transcribe/versions/2.0.0/package.js +0 -163
- package/blocks/native/sm.transcribe/versions/2.0.1/package.js +0 -168
- package/blocks/native/sm.whiteboard-assistant/versions/1.0.0/package.js +0 -175
- package/blocks/native/sm.youtube/versions/2.0.0/package.js +0 -217
- package/blocks/occasion-story/package.js +0 -1
- package/blocks/occasion-story/versions/1.0.0/package.js +0 -1658
- package/blocks/occasion-story/versions/1.0.1/package.js +0 -1699
- package/blocks/occasion-story/versions/1.0.2/package.js +0 -1682
- package/blocks/photo-reel-composer/versions/1.0.0/package.js +0 -3043
- package/blocks/photo-reel-composer/versions/1.0.1/package.js +0 -3026
- package/blocks/photo-reel-composer/versions/1.0.2/package.js +0 -4149
- package/blocks/product-ad-variants/versions/1.0.0/package.js +0 -2911
- package/blocks/product-launch-director/versions/1.0.0/package.js +0 -103
- package/blocks/release-visuals/package.js +0 -1
- package/blocks/release-visuals/versions/1.0.0/package.js +0 -1492
- package/blocks/release-visuals/versions/1.0.1/package.js +0 -1517
- package/blocks/release-visuals/versions/1.0.2/package.js +0 -1500
- package/blocks/screen-recorder/package.js +0 -1
- package/blocks/screen-recorder/versions/1.0.0/package.js +0 -223
- package/blocks/screen-recorder/versions/1.0.1/package.js +0 -223
- package/blocks/short-form-finish/package.js +0 -1
- package/blocks/short-form-finish/versions/1.0.0/package.js +0 -1043
- package/blocks/short-form-finish/versions/1.0.1/package.js +0 -1043
- package/blocks/short-form-finish/versions/1.0.2/package.js +0 -1043
- package/blocks/short-form-finish/versions/1.0.3/package.js +0 -1026
- package/examples/saas-product-launch-reviewed-source.stillmade.json +0 -383
- package/examples/saas-product-launch.stillmade.json +0 -304
- package/packages/block-platform/dealer-inventory-video-project-type.js +0 -38
- package/packages/block-platform/first-revenue-project-types.js +0 -46
- package/packages/block-platform/fixture-starters.js +0 -13
- package/packages/block-platform/launch-project-source.js +0 -10
- package/packages/block-platform/launch-project.js +0 -30
- package/packages/block-platform/motion-project-types.js +0 -55
- package/packages/block-platform/motion-starters.js +0 -14
- package/packages/block-platform/product-ad-variants-project-type.js +0 -30
- package/packages/block-platform/product-launch-updated.js +0 -90
- package/packages/block-platform/revenue-review-project-types.js +0 -36
- package/packages/block-platform/revenue-review-starters.js +0 -13
- package/packages/block-platform/script-writer.js +0 -29
- package/packages/block-platform/website-source-block.js +0 -14
|
@@ -1,58 +1,20 @@
|
|
|
1
|
-
import {REVENUE_REVIEW_STARTERS} from './revenue-review-starters.js';
|
|
2
1
|
import {CURRENT_BUILTIN_TYPES,WORKSPACE_BLOCKS} from './catalog.js';
|
|
3
2
|
|
|
4
|
-
//
|
|
5
|
-
// Motion Designer packages
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const MOTION_STARTER_IDS=Object.freeze(['motion-graphics','saas-launch','screenshot-motion','product-photo-ad']);
|
|
10
|
-
// The fixture Types (fixture-starters.js: the revenue-review Types, Product Ad Variants, the photo reels) embed exact
|
|
11
|
-
// Block releases too; they resolve the same way, by id, for the acceptance drivers.
|
|
12
|
-
export const FIXTURE_STARTER_IDS=Object.freeze([...REVENUE_REVIEW_STARTERS.map(item=>item.id),'product-ad-variants','property-listing-reel','product-promo-reel']);
|
|
13
|
-
export const ON_DEMAND_STARTER_IDS=Object.freeze([...MOTION_STARTER_IDS,...FIXTURE_STARTER_IDS]);
|
|
3
|
+
// November 4, 2026 (owner): the Motion starters (Motion Graphics, Product Launch Video, Screenshot to Motion, Product
|
|
4
|
+
// Photo Ad) and their Motion Designer packages were deleted. No starter loads Blocks on demand any more; the names
|
|
5
|
+
// stay so a later on-demand starter plugs in the same way.
|
|
6
|
+
export const MOTION_STARTER_IDS=Object.freeze([]);
|
|
7
|
+
export const ON_DEMAND_STARTER_IDS=Object.freeze([...MOTION_STARTER_IDS]);
|
|
14
8
|
|
|
15
9
|
// These are editable starting configurations of the existing production Blocks,
|
|
16
10
|
// not separate generators or new marketplace identities.
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// resolves them by id for those drivers and for the creator experiments.
|
|
11
|
+
// November 4, 2026 (owner): the Guided-style video starters, the revenue-review, ad-variant and photo-reel
|
|
12
|
+
// fixtures and the Blocks they used (Script Writer, Shots, Blueprint and the rest) were deleted.
|
|
20
13
|
export const PROJECT_TYPE_STARTERS=Object.freeze([
|
|
21
|
-
{id:'faceless-youtube',name:'Faceless YouTube',description:'Write a narrated story, plan the visuals, and assemble a complete video.',blocks:['script','voiceover','shot-plan','blueprint','pipeline','editor'],brief:'Create a faceless YouTube video. Start with a clear hook, develop the story with concrete visual beats, and end with a useful conclusion. Ask for the topic, audience, target duration and sources. Do not invent factual claims.'},
|
|
22
|
-
{id:'short-form',name:'Short-form vertical',description:'Build a concise story with a strong opening and visuals for a vertical screen.',blocks:['script','voiceover','shot-plan','pipeline','editor'],brief:'Create a short-form vertical video. Ask for the subject and intended platform. Keep narration concise, place the hook immediately, and plan readable 9:16 compositions with room for captions. Confirm the target length before writing.'},
|
|
23
|
-
{id:'ai-commercial',name:'AI commercial',description:'Develop a campaign idea, maintain its visual direction, and edit the final spot.',blocks:['script','voiceover','shot-plan','blueprint','pipeline','editor'],brief:'Develop an AI commercial from the supplied campaign brief. Establish the audience, promise, tone, product proof and call to action. Ask for brand assets and target duration. Use only supported product claims; preserve a consistent visual direction.'},
|
|
24
|
-
{id:'anime-episode',name:'Anime episode',description:'Develop the episode, cast and locations, then produce its shots and final cut.',blocks:['script','voiceover','blueprint','shot-plan','pipeline','editor'],brief:'Develop an anime episode with clear scene progression and character motivations. Ask for the premise, cast, intended duration and visual references. Prepare dialogue that can be reviewed for voice production. Maintain existing character and location references in Blueprint and Canvas.'},
|
|
25
|
-
{id:'product-ad',name:'Product ad',description:'Show a product’s value with a focused script, visual proof and a clear call to action.',blocks:['script','voiceover','shot-plan','blueprint','pipeline','editor'],brief:'Create a product advertisement using supplied product facts and assets. Ask for the audience, channel, duration and call to action. Lead with a concrete customer problem, demonstrate verified features and benefits, and avoid invented testimonials, statistics or prices.'},
|
|
26
|
-
{id:'music-video',name:'Music video',description:'Bring your track, plan a visual sequence, and cut the images and clips to the music.',blocks:['blueprint','shot-plan','pipeline','editor']},
|
|
27
|
-
{id:'documentary',name:'Documentary',description:'Research and write the story, organize its visual references, and build the film.',blocks:['script','voiceover','blueprint','shot-plan','pipeline','editor'],brief:'Develop a documentary from the supplied subject and sources. Separate verified facts, attributed statements and open questions. Ask about the intended audience, length and available footage. Structure an evidence-based narrative and identify the visuals required for each section.'},
|
|
28
14
|
{id:'empty',name:'Empty',description:'Start with protected Chat and add the Blocks your workflow needs.',blocks:[]},
|
|
29
|
-
{id:'motion-graphics',name:'Motion Graphics',description:'Direct an idea, approve styleframes, and build editable coded motion scenes and a finished export.'},
|
|
30
|
-
{id:'saas-launch',name:'Product Launch Video',description:'Turn product evidence and real UI into approved styleframes, an editable coded launch video, and a finished export.'},
|
|
31
|
-
{id:'screenshot-motion',name:'Screenshot to Motion',description:'Turn exact product screenshots into an editable motion walkthrough without inventing product behavior.'},
|
|
32
|
-
{id:'product-photo-ad',name:'Product Photo Ad',description:'Turn rights-cleared product photos and verified offer facts into an editable short advertisement.'},
|
|
33
15
|
]);
|
|
34
16
|
|
|
35
|
-
//
|
|
36
|
-
// Selecting one prepares a private experiment draft; it never publishes,
|
|
37
|
-
// spends credits, or upgrades its demand evidence label.
|
|
38
|
-
export const REVENUE_LOOP_CANDIDATES=Object.freeze([
|
|
39
|
-
['captioned-business-clip','Service owners and consultants with an unposted recording','Finish a selected recording with accurate captions and personal first-project help.'],
|
|
40
|
-
['customer-story-clip','Businesses with an authorized customer interview','Preserve a real customer quote and finish a captioned excerpt.'],
|
|
41
|
-
['product-demonstration-clip','Small brands with real demonstration footage','Show the actual product in use with approved copy and a clear next step.'],
|
|
42
|
-
['farewell-story','Teams with already-collected farewell messages','Assemble supplied memories and messages into a reviewed farewell video.'],
|
|
43
|
-
['birthday-story','Gift buyers with collected photos and messages','Make a personal birthday story from their supplied memories.'],
|
|
44
|
-
['anniversary-story','Couples with supplied photos and messages','Arrange real memories and exact dedications into a short anniversary story.'],
|
|
45
|
-
['single-release-teaser','Artists with finished audio and cover artwork','Finish a short release visual using the original track and artwork.'],
|
|
46
|
-
['lyric-excerpt','Artists with approved lyrics and phrase timings','Create a short lyric visual with the exact supplied song and text.'],
|
|
47
|
-
].map(([id,buyer,job],index)=>{
|
|
48
|
-
const starter=REVENUE_REVIEW_STARTERS.find(item=>item.id===id);
|
|
49
|
-
return {id,starterId:id,rank:index+1,name:starter.name,buyer,job,promise:starter.description,evidence:'hypothesis',
|
|
50
|
-
hypothesis:'A reviewed output and bounded first-project help can earn a genuine external payment. Demand and acquisition cost remain unverified.',
|
|
51
|
-
primaryMetric:'Paid and delivered customer orders',
|
|
52
|
-
decisionRule:'Approve the representative result and verify payment and export before acquisition. Test one offer within the agreed budget. Gift and music purchases need an explicit decision on one-project versus recurring payment.'};
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
// `onDemand` holds the loaded definitions of motion and fixture starters by id (MOTION_STARTERS, FIXTURE_STARTERS).
|
|
17
|
+
// `onDemand` holds the loaded definitions of on-demand starters by id.
|
|
56
18
|
export function newProjectTypeFromStarter(starter,id,onDemand=null){
|
|
57
19
|
const selected=PROJECT_TYPE_STARTERS.find(item=>item.id===starter);
|
|
58
20
|
if(ON_DEMAND_STARTER_IDS.includes(starter)){
|
|
@@ -61,23 +23,21 @@ export function newProjectTypeFromStarter(starter,id,onDemand=null){
|
|
|
61
23
|
return {...structuredClone(definition),id};
|
|
62
24
|
}
|
|
63
25
|
if(!selected){
|
|
64
|
-
const base=structuredClone(CURRENT_BUILTIN_TYPES.find(type=>type.legacyMode==='
|
|
26
|
+
const base=structuredClone(CURRENT_BUILTIN_TYPES.find(type=>type.legacyMode==='blank')||CURRENT_BUILTIN_TYPES[0]);
|
|
65
27
|
return {...base,id,name:'Untitled Project Type',version:'0.1.0',description:'A production workflow you can customize.'};
|
|
66
28
|
}
|
|
67
29
|
const stages=selected.blocks.map(key=>{
|
|
68
30
|
const block=WORKSPACE_BLOCKS.find(item=>item.manifest.id==='sm.'+key);
|
|
69
31
|
if(!block)throw Error('The '+key+' production Block is unavailable.');
|
|
70
|
-
return {id:key,blockId:block.manifest.id,version:block.manifest.version,label:block.manifest.name
|
|
32
|
+
return {id:key,blockId:block.manifest.id,version:block.manifest.version,label:block.manifest.name};
|
|
71
33
|
});
|
|
72
34
|
return {schemaVersion:1,id,version:'0.1.0',name:selected.name,description:selected.description,license:'MIT',startingChat:'native',stages,
|
|
73
|
-
...(selected.id!=='empty'?{defaults:{aspectRatio:
|
|
74
|
-
connections:
|
|
35
|
+
...(selected.id!=='empty'?{defaults:{aspectRatio:'16:9'},onboarding:[{id:'audience',label:'Who is this for?',type:'text'},{id:'tone',label:'What tone fits?',type:'select',options:['Clear and direct','Playful','Cinematic','Documentary']}]}:{}),
|
|
36
|
+
connections:[],
|
|
75
37
|
summary:{text:selected.description,origin:'llm',generation:'build-time',generator:'Codex',status:'ready'}};
|
|
76
38
|
}
|
|
77
39
|
|
|
78
|
-
// Resolve any starter
|
|
40
|
+
// Resolve any starter.
|
|
79
41
|
export async function loadProjectTypeStarter(starter,id){
|
|
80
|
-
if(MOTION_STARTER_IDS.includes(starter))return newProjectTypeFromStarter(starter,id,(await import('./motion-starters.js')).MOTION_STARTERS);
|
|
81
|
-
if(FIXTURE_STARTER_IDS.includes(starter))return newProjectTypeFromStarter(starter,id,(await import('./fixture-starters.js')).FIXTURE_STARTERS);
|
|
82
42
|
return newProjectTypeFromStarter(starter,id);
|
|
83
43
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {assert} from '../block-sdk/contracts.js';
|
|
2
|
-
import {isScriptWriterHandoff} from './script-writer.js';
|
|
3
2
|
import {packageForStage} from './stage-package.js';
|
|
4
3
|
|
|
5
4
|
const SDK_SCRIPT_RUNTIMES = new Set(['recipe', 'javascript', 'capability']);
|
|
@@ -14,14 +13,13 @@ export function resolveScriptHandoff(from, to, edge, pinnedManifest) {
|
|
|
14
13
|
to.blockId !== 'sm.voiceover' || !VOICEOVER_VERSIONS.has(to.version) ||
|
|
15
14
|
edge.to.port !== 'input' || Object.hasOwn(edge, 'adapter')) return null;
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
if (!builtin && (pinnedManifest?.id !== from.blockId || pinnedManifest.version !== from.version ||
|
|
16
|
+
if ((pinnedManifest?.id !== from.blockId || pinnedManifest.version !== from.version ||
|
|
19
17
|
!SDK_SCRIPT_RUNTIMES.has(pinnedManifest.runtime) ||
|
|
20
18
|
!Object.hasOwn(pinnedManifest.outputs || {}, edge.from.port) ||
|
|
21
19
|
pinnedManifest.outputs[edge.from.port]?.type !== 'script')) return null;
|
|
22
20
|
|
|
23
21
|
return {
|
|
24
|
-
kind:
|
|
22
|
+
kind: 'sdk',
|
|
25
23
|
sourceStageId: from.id, sourcePort: edge.from.port,
|
|
26
24
|
targetStageId: to.id, targetPort: edge.to.port,
|
|
27
25
|
blockId: from.blockId, version: from.version,
|
|
@@ -80,6 +80,8 @@ export async function compileTypeWorkspace(type,{experimentalBrowser,experimenta
|
|
|
80
80
|
});
|
|
81
81
|
return { definition, digest: await digest(definition), canvas: { nodes, edges }, activeStage: definition.stages.find(stage=>stage.enabled !== false)?.id || null };
|
|
82
82
|
}
|
|
83
|
+
// A test fixture many tests assert on (its stage ids and order), not a model Type: its Export video Step beside the
|
|
84
|
+
// Editor breaks the builder contract ("One job per Step"). The create-type scaffold (TYPE_SCAFFOLD) is the model.
|
|
83
85
|
export const RECIPE_TYPE_EXAMPLE = {
|
|
84
86
|
schemaVersion: 1, id: 'example.text-workflow', version: '1.0.0', name: 'Text workflow',
|
|
85
87
|
description: 'Clean text through two connected blocks, then use the shared editor to assemble media.',
|
|
@@ -10,7 +10,9 @@ export const APPEARANCE_TOKENS = Object.freeze([
|
|
|
10
10
|
'--signal','--signal-dim','--signal-glow','--accent','--accent-hover','--accent-dim','--accent-glow','--accent-contrast',
|
|
11
11
|
'--bg-primary','--bg-secondary','--bg-tertiary','--bg-elevated','--bg-hover','--border','--border-light',
|
|
12
12
|
'--text-primary','--text-secondary','--text-muted','--success','--warning','--danger','--shadow','--shadow-lg','--shadow-1','--shadow-2',
|
|
13
|
-
'--editor-standout-bg','--editor-standout-fg'
|
|
13
|
+
'--editor-standout-bg','--editor-standout-fg',
|
|
14
|
+
// The Block kit's see-through page background, for words over a photo (block-kit.css, November 3, 2026).
|
|
15
|
+
'--scrim'
|
|
14
16
|
]);
|
|
15
17
|
const tokens = new Set(APPEARANCE_TOKENS);
|
|
16
18
|
const colorProperty = /^(?:color|background(?:-color|-image)?|(?:border|outline)(?:-(?:top|right|bottom|left|inline|block|inline-start|inline-end|block-start|block-end))?(?:-color)?|(?:box|text)-shadow|fill|stroke|caret-color|accent-color|text-decoration(?:-color)?|column-rule(?:-color)?|-webkit-text-fill-color)$/;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// extension an Editor tool (editor-extension)
|
|
8
8
|
// glue the parts any project may use between Steps (When, Only new items, AI write, Import media)
|
|
9
9
|
export const BLOCK_TIERS=Object.freeze(['app','job','ingredient','extension','glue']);
|
|
10
|
-
const GLUE=new Set(['sm.when','sm.
|
|
10
|
+
const GLUE=new Set(['sm.when','sm.connect-app','sm.ai-write','sm.ai-decide','sm.write-summary','sm.import-media']);
|
|
11
11
|
const hostedOperations=manifest=>(manifest?.permissions?.capabilities||[]).filter(name=>name!=='connection.execute'&&name!=='comfyui.execute');
|
|
12
12
|
|
|
13
13
|
/** The tier of a Block, from its manifest. */
|
|
@@ -47,9 +47,11 @@ const SECTIONS=Object.freeze([
|
|
|
47
47
|
// on module Blocks and refused a job that needs one).
|
|
48
48
|
rule('runtime-choice','block',null,
|
|
49
49
|
'Pick the runtime from the job. No hosted AI: a recipe or isolated `javascript`, both able to run unattended. One text, script or brief prompt returning text, script or JSON: a `capability` Block. Hosted operations on other values (a transcript, media, structured data), several hosted operations in a row, or a result shaped in code: one `module` Block, `module/main.js` calling `stillmade.hosted.run(operation, request)` for each operation in `permissions.capabilities` (MODULES.md), never one Block per operation; it runs in the person\'s browser while StillMade is open. Build a new Block in a folder with `stillmade-block create`, `test`, `ready` and `pack`, and import the file `pack` writes (a module Block writes `<id>-<version>.stillmade-module.json`) through Create, then Import Block. A `.stillmade-block` archive is only for adapting a GitHub repository.'),
|
|
50
|
+
// November 3, 2026 (owner, "Yes, one page"): a guided job is one Block on one page, its cheap check before the
|
|
51
|
+
// expensive run on that page too; a workspace (CAD, the Editor) stays its own Step, and using what it made is the next.
|
|
50
52
|
rule('block-scope','both',
|
|
51
|
-
'One Step per job:
|
|
52
|
-
'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
|
|
53
|
+
'One Step per job: a guided job is one Block whose page holds the inputs, a cheap check and the result. New Steps only for a workspace (CAD, Editor; using its work is the next Step), a one-time setup, a later render, a hand-off, handling each new item, or leaving StillMade.',
|
|
54
|
+
'A Step is one job: the work between two moments where a person or the outside world must see the result. A guided job, one the person fills in and then gets a result from, is one Step with one Block whose own screen holds everything: the inputs and settings, then a cheap check before the expensive run (the script and a voice sample before the video), then the result. On a wide screen the inputs sit on the left and the result\'s place on the right; on a phone they stack, the result\'s place under the button. A cheap check or a review the person does on that screen never starts a new Step. Start a new Step only at one of these points; everything else is a part of the neighboring Step.\n\n1. **A person works with the result**: in a workspace they spend a while in (the Editor, a storyboard, a Canvas, a CAD or drawing tool), or someone else approves or takes it over (a hand-off). Turning what a workspace made into something else is the next Step: a CAD tool is its own Step, and the Block that takes pictures of its model and makes a video is the next one. So Bible, Canvas and Editor are separate Steps after Chat.\n2. **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.\n3. **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.\n4. **Setup or a wait**: a one-time setup that is not part of each job (a brand kit, capturing a voice or a likeness), or a render that takes minutes and is delivered later.\n\nIn 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 Type whose own Block renders the finished video (`timeline.render`) needs no Editor Step; the Editor (1.0, captions and export built in) is a Step only when the person edits the cut by hand, and Export video is never a Step beside it.\n\nA part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of these 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.\n\nBuild one Block for a job one Block can do: a module Block calls every hosted operation the job needs (it transcribes a sermon, then finds the verses said in it), a JavaScript Block does several local transforms, a workspace owns its whole document. A Block being built never takes an ingredient\'s result only so the ingredient can be its own part or Step (Transcribe in front of a verse finder): it calls that operation itself. Parts in one Step (`groups` in the Project Type) are for jobs made of Blocks that already exist and app tasks, each keeping its own permission, approval and credit rules. Never invent a combined runtime.\n\nExamples: "a listing video from my photos and the home\'s facts" is one Step: one Block whose page takes the photos and facts, shows the script and a voice sample to check, then makes the video. "every new email with an image becomes a generated image" is two Steps, "Gmail · New emails with images" (When, fetch with an image filter, only new) and "Image from email" (AI write, Generate image); listing When, Fetch, Only new, AI decide, Continue only if, AI write and Generate image as seven Steps splits one job into its parts. "A new Meta lead goes to HubSpot" is three Steps: "Meta Ads · New leads", "Qualify lead" and "HubSpot · Add contact".'),
|
|
53
55
|
rule('capability-contract','block',
|
|
54
56
|
'Define the contract first: typed inputs (optional with safe defaults where possible), outputs, state, permissions, UI, failures and cancellation. Keep the interface small; keep the job whole.',
|
|
55
57
|
'Translate the request into an explicit capability contract before implementation: what the Block does; each input with its type, meaning and whether it is required (prefer optional settings with safe defaults over extra required questions); each output with its type and meaning; persistent state; project context it reads; permissions; UI; long-running behavior; expected failures; cancellation; determinism; and whether it needs hosted models, media, desktop tools or shared editing. Keep the public interface as small and general as possible (few required inputs, one primary output) and keep implementation details out of it; small means a small interface, never a fragment of a job. A Block that generates images, video or speech declares only its prompt and output: every Step that runs it gets the host’s generation settings (model, quality, length and resolution, voice, speed, how many), saved with the Step, changed on its page or by Chat, and shown with their cost before each run.'),
|
|
@@ -71,7 +73,7 @@ const SECTIONS=Object.freeze([
|
|
|
71
73
|
// November 2, 2026 (owner): a sermon Block's video input read "Video"; it should read "Drop your sermon video here".
|
|
72
74
|
rule('input-words','block',
|
|
73
75
|
'Name each input in the job\'s words (`sermonVideo`, not `video`): StillMade words its drop area from the name.',
|
|
74
|
-
'Name each input for what the person brings, in the job\'s words: `sermonVideo` for a sermon clip Block, `listingPhotos` for a listing, `lessonSlides` for a lesson podcast. StillMade titles the input from that name ("Sermon video") and words its drop area from it ("Drop your sermon video here", the formats, "Choose a video"), in StillMade and the SDK preview, so never draw your own upload box
|
|
76
|
+
'Name each input for what the person brings, in the job\'s words: `sermonVideo` for a sermon clip Block, `listingPhotos` for a listing, `lessonSlides` for a lesson podcast. StillMade titles the input from that name ("Sermon video") and words its drop area from it ("Drop your sermon video here", the formats, "Choose a video"), in StillMade and the SDK preview, so never draw your own upload box or `<input type="file">`: a Block\'s screen shows the files as tiles and adds more with `<button class="sm-add" data-stillmade-add="listingPhotos" aria-label="Add photos">`, which opens StillMade\'s picker and upload, and a list\'s tiles drop or reorder a file with `data-stillmade-remove="listingPhotos" data-index="2"` and `data-stillmade-move="listingPhotos" data-index="2" data-to="1"` buttons; the files arrive through `onInput`. Keep a bare type name (`video`, `file`) only when any file of that kind does the job. The `description` says what makes a good one.'),
|
|
75
77
|
// November 2, 2026 (owner): every screen speaks the job, as a product made only for it would, and stays quiet. The same
|
|
76
78
|
// day (owner, of four tiles each saying "Only on the device it was added from"): "if it's a notification make it a
|
|
77
79
|
// notification": state is one StillMade notification or nothing, never a sentence on the page or one per item.
|
|
@@ -84,17 +86,17 @@ const SECTIONS=Object.freeze([
|
|
|
84
86
|
// pictures its full-screen button; a video without controls stays the Block's own. Also that day (owner: "there is no
|
|
85
87
|
// separation and I cannot tell what is going on"): a pick is the kit's ink fill, and media that cannot show is a quiet tile.
|
|
86
88
|
rule('ui','block',
|
|
87
|
-
'Prefer `manifest.ui`; custom views use the Block kit (`sm-*`) unless a look is named. Pictures over words: preview each option before choosing; few words, fold the rest; buttons carry `data-stillmade-action`; the start button names the action (never "Run" or "Make it") and shows no price (StillMade adds it). A job fits one screen unscrolled and a 320px phone; an app (editor, configurator, CAD tool) works like that app: full canvas, panels, tools, undo, never a form. An app puts `data-stillmade-layout="own"` on its root: StillMade never centers or zooms it. `<video controls>` gets StillMade\'s player; leave out `controls` when the Block drives playback. A pick keeps the kit\'s ink fill; media that cannot show is a wordless `sm-media-off` tile.',
|
|
88
|
-
'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`, the picked one in the kit\'s ink fill (never restyled to an outline, a tint or the page\'s background) so it reads at a glance, one solid `sm-primary` naming the action in the job\'s words ("Plan the tour", "Generate voiceover"; never "Run" or "Make it") 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. StillMade centers a standard view narrower than its Step and zooms a view to fill its space; an app that lays itself out (full-size canvas or WebGL, its own scroll, pan or zoom, its own Ctrl/Cmd +/- keys) puts `data-stillmade-layout="own"` on its outermost element, and StillMade then never centers, zooms or takes those keys. StillMade draws one player for every `<video controls>` (play, timeline, time, sound and full screen, shown while the pointer is on the video, with keys and touch) and a full-screen button on result pictures in `sm-stage` or `sm-gallery` and on any element marked `data-stillmade-expand` (the same value makes one set of pictures, `off` removes it); `data-stillmade-player="native"` keeps the browser\'s controls, and a `<video>` without `controls` is the Block\'s own and is never touched. A picture or clip that cannot show here (kept on another device, or it did not open) keeps its place as a quiet `sm-media-off` tile in its own shape with the `image-off` glyph and the reason in its label, never words in its place. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action
|
|
89
|
+
'Prefer `manifest.ui`; custom views use the Block kit (`sm-*`) unless a look is named. Pictures over words: preview each option before choosing; few words, fold the rest; buttons carry `data-stillmade-action`; the start button names the action (never "Run" or "Make it") and shows no price (StillMade adds it). A job fits one screen unscrolled and a 320px phone; an app (editor, configurator, CAD tool) works like that app: full canvas, panels, tools, undo, never a form. An app puts `data-stillmade-layout="own"` on its root: StillMade never centers or zooms it. `<video controls>` gets StillMade\'s player; leave out `controls` when the Block drives playback. A pick keeps the kit\'s ink fill; media that cannot show is a wordless `sm-media-off` tile. Every part shows at once: the result\'s place is drawn before the first run in its own shape and size, so nothing pops in; on wide screens inputs sit left and the result right; on phones they stack, the result under the button.',
|
|
90
|
+
'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`, drawn before the first run in the shape and size of what is coming (`data-ratio="9:16"` for a vertical clip, lines for text, a player bar for sound) so every part of the screen is in place at once and the result fills a place the person already sees instead of popping in somewhere unexpected; StillMade draws it the same way on the Steps it draws; on a wide screen what goes in sits on the left and the result on the right, side by side, and they stack only on narrow screens and phones, the result\'s place under the button, picks as pictures in `sm-choices`, the picked one in the kit\'s ink fill (never restyled to an outline, a tint or the page\'s background) so it reads at a glance, one solid `sm-primary` naming the action in the job\'s words ("Plan the tour", "Generate voiceover"; never "Run" or "Make it") 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. StillMade centers a standard view narrower than its Step and zooms a view to fill its space; an app that lays itself out (full-size canvas or WebGL, its own scroll, pan or zoom, its own Ctrl/Cmd +/- keys) puts `data-stillmade-layout="own"` on its outermost element, and StillMade then never centers, zooms or takes those keys. StillMade draws one player for every `<video controls>` (play, timeline, time, sound and full screen, shown while the pointer is on the video, with keys and touch) and a full-screen button on result pictures in `sm-stage` or `sm-gallery` and on any element marked `data-stillmade-expand` (the same value makes one set of pictures, `off` removes it); `data-stillmade-player="native"` keeps the browser\'s controls, and a `<video>` without `controls` is the Block\'s own and is never touched. A picture or clip that cannot show here (kept on another device, or it did not open) keeps its place as a quiet `sm-media-off` tile in its own shape with the `image-off` glyph and the reason in its label, never words in its place. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action` (`run` on the one that runs the Block, which the device checks press once; a page that checks first keeps `run` on that button for both presses; what it does on the others, `"play sample"`) 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 (the inputs, the button in normal flow, never pinned, then the result\'s place under it), 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.'),
|
|
89
91
|
rule('multiplayer','block',
|
|
90
92
|
'Custom views share edits: `StillMade.onShared`, publish every editable field (`data-stillmade-share`, `bindShared`, `updateShared`), respect `canEdit`, never re-run on remote updates.',
|
|
91
93
|
'Projects are multiplayer. A custom view subscribes through `StillMade.onShared(callback)` and publishes every editable field: give static controls a unique `id` and `data-stillmade-share="field"` (the host owns their synchronization), use `StillMade.bindShared(field, elementId)` for controls created dynamically, and `StillMade.updateShared(values, before, options)` for structured values; private variables and DOM values are not shared. Mark a truly device-only control (playback, hover, device selection) with `data-stillmade-local`. Observe shared outputs as well as state, honor `canEdit` in every mutating callback (read-only participants cannot write), never run, click or call a paid integration in response to a remote update, and preserve conflicting drafts. Shared background work uses `sharedWork` with the `work.shared` permission.'),
|
|
92
94
|
rule('external-services','block',
|
|
93
95
|
'External services only via hosted operations or `connection.execute`; the host owns providers, keys, BYOK, credits and consent.',
|
|
94
|
-
'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs), `stillmade.net.connect` for a live WebSocket to one of them, and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as
|
|
96
|
+
'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs), `stillmade.net.connect` for a live WebSocket to one of them, and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as outcomes the Block reports, not crashes: a call the result needs throws and fails the run (see Useful failures); an optional part\'s failure is caught, given its fallback and named in an optional `notes` text output. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user\'s StillMade credits at StillMade\'s prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly (threaded: `"threads": true` in module.json) or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade\'s theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.'),
|
|
95
97
|
rule('dependencies','block',
|
|
96
|
-
'No npm or system dependencies; inline small licensed code and record its license and notice in the manifest.',
|
|
97
|
-
'A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user\'s machine has it.'),
|
|
98
|
+
'No npm or system dependencies; inline small licensed code and record its license and notice in the manifest; the person is the copyright holder.',
|
|
99
|
+
'A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. The copyright holder in the Block\'s own `provenance.notice` is the person it is made for: replace the placeholder `create` writes with their name, never StillMade, the SDK or yourself. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user\'s machine has it.'),
|
|
98
100
|
rule('security','both',
|
|
99
101
|
'Treat inputs, files and responses as untrusted; request minimum permissions; never leak secrets in outputs, state, errors or UI.',
|
|
100
102
|
'Treat Block code, external data, files, repositories, model output and network responses as untrusted. Request the minimum permissions and capabilities; `stillmade-block permissions --fix` declares exactly what the source uses. Do not weaken the sandbox (QuickJS has no fetch, DOM, timers, modules or Node APIs). Validate and bound every value crossing a boundary. Never expose secrets through logs, outputs, project context, UI, error messages or serialized state.'),
|
|
@@ -187,7 +187,7 @@ export function validateCapability(capability,manifest){
|
|
|
187
187
|
assert(frames.length<2||capability.firstFrame.$input!==capability.lastFrame.$input,'CAPABILITY_BINDING','Use different image inputs for the first and last frame');
|
|
188
188
|
assert(!own(capability,'lastFrame')||own(capability,'firstFrame'),'CAPABILITY_BINDING','A last frame needs a first frame');
|
|
189
189
|
assert(Object.values(manifest.inputs).filter(port=>port.type==='image').length===frames.length,'CAPABILITY_BINDING','Bind every image input as the first or last frame');
|
|
190
|
-
if(own(capability,'settings')){const settings=videoSettings(capability.settings,{strict:true,firstFrame:own(capability,'firstFrame')});const entry=VIDEO_GENERATION_MODELS[settings.model];assert(!own(capability,'lastFrame')||entry.lastFrame,'CAPABILITY_SETTINGS',`${entry.label} does not accept a last frame`);}
|
|
190
|
+
if(own(capability,'settings')){const settings=videoSettings(capability.settings,{strict:true,firstFrame:own(capability,'firstFrame'),lastFrame:own(capability,'lastFrame')});const entry=VIDEO_GENERATION_MODELS[settings.model];assert(!own(capability,'lastFrame')||entry.lastFrame,'CAPABILITY_SETTINGS',`${entry.label} does not accept a last frame`);}
|
|
191
191
|
generationText(capability);
|
|
192
192
|
assert(typeof capability.output==='string'&&own(manifest.outputs,capability.output),'CAPABILITY_BINDING','Use the declared video output port');return true;
|
|
193
193
|
}
|
|
@@ -235,9 +235,11 @@ export function validateManifest(manifest, { builtIn = false } = {}) {
|
|
|
235
235
|
check(plain(permissions), 'permissions', 'Declare permissions explicitly');
|
|
236
236
|
for (const [key, scopes] of Object.entries(plain(permissions) ? permissions : {})) {
|
|
237
237
|
check(['project', 'network', 'connections', 'filesystem', 'secrets', 'capabilities', 'actions', 'desktop'].includes(key), `permissions.${key}`, 'Unknown permission family');
|
|
238
|
-
// Network entries may be {origin, auth} objects; every other family is a string list.
|
|
238
|
+
// Network entries may be {origin, auth} objects; every other family is a string list. A module Block's empty
|
|
239
|
+
// capabilities list is the same as none (November 3, 2026: a module that only calls actions.run was refused for
|
|
240
|
+
// `capabilities: []` with a message about capability and ComfyUI Blocks).
|
|
239
241
|
check(Array.isArray(scopes) && scopes.every(s => typeof s === 'string' || key === 'network' && plain(s)), `permissions.${key}`, 'Expected string array');
|
|
240
|
-
if (Array.isArray(scopes)) check(key === 'desktop' ? scopes.length<=DESKTOP_PERMISSIONS.length && new Set(scopes).size===scopes.length && scopes.every(s=>DESKTOP_PERMISSIONS.includes(s)) : key === 'project' ? scopes.every(s => SCOPES.includes(s)) : key === 'actions' ? ['javascript','module'].includes(manifest.runtime)&&scopes.length<=PORTABLE_ACTIONS.length&&new Set(scopes).size===scopes.length&&scopes.every(s=>PORTABLE_ACTIONS.includes(s)) : key === 'capabilities' ? (manifest.runtime === 'module' ? scopes.length
|
|
242
|
+
if (Array.isArray(scopes)) check(key === 'desktop' ? scopes.length<=DESKTOP_PERMISSIONS.length && new Set(scopes).size===scopes.length && scopes.every(s=>DESKTOP_PERMISSIONS.includes(s)) : key === 'project' ? scopes.every(s => SCOPES.includes(s)) : key === 'actions' ? ['javascript','module'].includes(manifest.runtime)&&scopes.length<=PORTABLE_ACTIONS.length&&new Set(scopes).size===scopes.length&&scopes.every(s=>PORTABLE_ACTIONS.includes(s)) : key === 'capabilities' ? (manifest.runtime === 'module' ? scopes.length <= CAPABILITY_RUN_OPERATIONS.length && new Set(scopes).size === scopes.length && scopes.every(s => CAPABILITY_RUN_OPERATIONS.includes(s)) : scopes.length === 1 && (manifest.runtime === 'comfyui' && scopes[0] === 'comfyui.execute' || manifest.runtime === 'capability' && HOSTED_OPERATION_NAMES.includes(scopes[0]))) : key === 'network' && manifest.runtime === 'module' ? networkValid(scopes) : key === 'connections' ? manifest.runtime === 'module' && connectionsValid(scopes) : scopes.length === 0, `permissions.${key}`, key === 'connections' ? 'Unsupported permission: only module Blocks declare connections, up to 8 published adapters by app ID such as app:openapi:notion' : key === 'network' ? (manifest.runtime === 'module' ? `Declare network origins as https origins, optionally with {origin, auth}: ${networkProblem(scopes)}` : 'Unsupported permission: only module Blocks declare network origins; other runtimes reach services through hosted operations and connections') : key === 'capabilities' ? (['module','capability','comfyui'].includes(manifest.runtime) ? 'Declare each hosted operation once, from the supported list (a capability or ComfyUI Block declares exactly one)' : 'Unsupported permission: only capability, ComfyUI and module Blocks declare hosted operations. A javascript or recipe Block declares none: remove permissions.capabilities, or make the AI work its own capability Block') : 'Unsupported permission; Blocks have no ambient filesystem or secrets');
|
|
241
243
|
}
|
|
242
244
|
if(Object.values(plain(manifest.outputs)?manifest.outputs:{}).some(port=>['workspace-edit','workspace-edit[]'].includes(port?.type))) {
|
|
243
245
|
check(WORKSPACE_EDIT_FIELDS.some(field=>canProposeWorkspace(manifest,field)), 'permissions.project', 'Workspace edits require matching context.FIELD.read and workspace.FIELD.propose permissions');
|
|
@@ -257,6 +259,8 @@ export function validateManifest(manifest, { builtIn = false } = {}) {
|
|
|
257
259
|
continue;
|
|
258
260
|
}
|
|
259
261
|
check(plain(c) && UI_INPUT_CONTROLS.includes(c.control) && Object.hasOwn(manifest.inputs || {}, c.port), 'ui', 'Control must bind to a declared input');
|
|
262
|
+
// A text input of several lines (one fact per line, a list) declares it, so every host draws a box that keeps its line breaks.
|
|
263
|
+
if (plain(c) && c.multiline !== undefined) check(c.multiline === true && c.control === 'text' && manifest.inputs?.[c.port]?.type === 'text', 'ui', 'multiline: true goes on a text control bound to a text input');
|
|
260
264
|
if (plain(c) && Object.hasOwn(UI_CONTROL_TYPES, c.control)) check(UI_CONTROL_TYPES[c.control].includes(manifest.inputs?.[c.port]?.type), 'ui', `The ${c.control} control binds a ${UI_CONTROL_TYPES[c.control].join(', ')} input`);
|
|
261
265
|
if (plain(c) && ['select','multiselect'].includes(c.control)) {
|
|
262
266
|
check(manifest.inputs?.[c.port]?.type === (c.control === 'select' ? 'text' : 'text[]'), 'ui', 'Select binds text; multiselect binds text[]');
|
|
@@ -4,32 +4,6 @@
|
|
|
4
4
|
export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
5
5
|
"sm.ai-decide@1.0.0",
|
|
6
6
|
"sm.ai-write@1.0.0",
|
|
7
|
-
"sm.animated-scene-assistant@1.0.0",
|
|
8
|
-
"sm.animated-scene@2.0.0",
|
|
9
|
-
"sm.animated-scene@2.1.0",
|
|
10
|
-
"sm.animated-scene@2.2.0",
|
|
11
|
-
"sm.animated-scene@2.3.0",
|
|
12
|
-
"sm.animated-scene@2.3.1",
|
|
13
|
-
"sm.animated-scene@2.3.2",
|
|
14
|
-
"sm.animated-scene@2.3.3",
|
|
15
|
-
"sm.animated-scene@2.3.4",
|
|
16
|
-
"sm.animated-scene@2.3.5",
|
|
17
|
-
"sm.animated-scene@2.3.6",
|
|
18
|
-
"sm.animated-scene@2.3.7",
|
|
19
|
-
"sm.animated-scene@2.3.8",
|
|
20
|
-
"sm.animated-scene@2.3.9",
|
|
21
|
-
"sm.blueprint-assistant@1.0.0",
|
|
22
|
-
"sm.blueprint@2.0.0",
|
|
23
|
-
"sm.blueprint@2.1.0",
|
|
24
|
-
"sm.blueprint@2.1.1",
|
|
25
|
-
"sm.blueprint@2.1.2",
|
|
26
|
-
"sm.blueprint@2.1.3",
|
|
27
|
-
"sm.blueprint@2.1.4",
|
|
28
|
-
"sm.blueprint@2.1.5",
|
|
29
|
-
"sm.blueprint@2.1.6",
|
|
30
|
-
"sm.blueprint@2.1.7",
|
|
31
|
-
"sm.blueprint@2.1.8",
|
|
32
|
-
"sm.blueprint@2.1.9",
|
|
33
7
|
"sm.board-editor@2.0.0",
|
|
34
8
|
"sm.board-editor@2.1.0",
|
|
35
9
|
"sm.board-editor@2.2.0",
|
|
@@ -47,19 +21,11 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
47
21
|
"sm.captions@2.0.0",
|
|
48
22
|
"sm.character@2.0.0",
|
|
49
23
|
"sm.character@2.0.1",
|
|
50
|
-
"sm.chat@2.0.0",
|
|
51
|
-
"sm.chat@2.0.1",
|
|
52
|
-
"sm.chat@2.0.2",
|
|
53
|
-
"sm.chat@2.0.3",
|
|
54
24
|
"sm.compare@2.0.0",
|
|
55
25
|
"sm.compare@2.0.1",
|
|
56
26
|
"sm.compare@2.0.2",
|
|
57
27
|
"sm.connect-app@1.0.0",
|
|
58
28
|
"sm.contact-fields@1.0.0",
|
|
59
|
-
"sm.continue-if@1.0.0",
|
|
60
|
-
"sm.describe-media@2.0.0",
|
|
61
|
-
"sm.describe-media@2.0.1",
|
|
62
|
-
"sm.describe-media@2.1.0",
|
|
63
29
|
"sm.edit-assistant@2.0.0",
|
|
64
30
|
"sm.edit-assistant@2.0.1",
|
|
65
31
|
"sm.edit-assistant@2.0.2",
|
|
@@ -92,8 +58,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
92
58
|
"sm.grade@2.0.0",
|
|
93
59
|
"sm.grade@2.0.1",
|
|
94
60
|
"sm.grade@2.0.2",
|
|
95
|
-
"sm.image-editor@2.0.0",
|
|
96
|
-
"sm.image-editor@2.0.1",
|
|
97
61
|
"sm.import-media@2.0.0",
|
|
98
62
|
"sm.import-media@2.0.1",
|
|
99
63
|
"sm.import-media@2.0.2",
|
|
@@ -101,7 +65,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
101
65
|
"sm.location@2.0.1",
|
|
102
66
|
"sm.meta-creative@1.0.0",
|
|
103
67
|
"sm.motion-graphics@2.0.0",
|
|
104
|
-
"sm.new-items@1.0.0",
|
|
105
68
|
"sm.panel-strip@2.0.0",
|
|
106
69
|
"sm.panel-strip@2.1.0",
|
|
107
70
|
"sm.panel-strip@2.2.0",
|
|
@@ -118,7 +81,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
118
81
|
"sm.panel-strip@2.4.2",
|
|
119
82
|
"sm.photo-notes@1.0.0",
|
|
120
83
|
"sm.photo-notes@1.1.0",
|
|
121
|
-
"sm.pick-photos@1.0.0",
|
|
122
84
|
"sm.pipeline@2.0.0",
|
|
123
85
|
"sm.pipeline@2.1.0",
|
|
124
86
|
"sm.pipeline@2.2.0",
|
|
@@ -128,38 +90,12 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
128
90
|
"sm.pipeline@2.2.4",
|
|
129
91
|
"sm.pipeline@2.2.5",
|
|
130
92
|
"sm.pipeline@2.2.6",
|
|
131
|
-
"sm.quality-audit@1.0.0",
|
|
132
|
-
"sm.quality-audit@1.0.1",
|
|
133
|
-
"sm.quality-audit@1.0.2",
|
|
134
|
-
"sm.research@2.0.0",
|
|
135
|
-
"sm.research@2.0.1",
|
|
136
|
-
"sm.research@2.0.2",
|
|
137
93
|
"sm.rough-cut@2.0.0",
|
|
138
94
|
"sm.rough-cut@2.0.1",
|
|
139
95
|
"sm.rough-cut@2.0.2",
|
|
140
96
|
"sm.score@2.0.0",
|
|
141
|
-
"sm.script@2.0.0",
|
|
142
|
-
"sm.script@2.1.0",
|
|
143
|
-
"sm.script@2.1.1",
|
|
144
|
-
"sm.script@2.1.2",
|
|
145
|
-
"sm.script@2.1.3",
|
|
146
|
-
"sm.script@2.1.4",
|
|
147
|
-
"sm.script@2.1.5",
|
|
148
|
-
"sm.shot-plan-assistant@1.0.0",
|
|
149
|
-
"sm.shot-plan@2.0.0",
|
|
150
|
-
"sm.shot-plan@2.1.0",
|
|
151
|
-
"sm.shot-plan@2.1.1",
|
|
152
|
-
"sm.shot-plan@2.1.2",
|
|
153
|
-
"sm.shot-plan@2.1.3",
|
|
154
|
-
"sm.shot-plan@2.2.0",
|
|
155
|
-
"sm.shot-plan@2.2.1",
|
|
156
|
-
"sm.stock-search@2.0.0",
|
|
157
97
|
"sm.style@2.0.0",
|
|
158
98
|
"sm.style@2.0.1",
|
|
159
|
-
"sm.thumbnail@2.0.0",
|
|
160
|
-
"sm.thumbnail@2.0.1",
|
|
161
|
-
"sm.transcribe@2.0.0",
|
|
162
|
-
"sm.transcribe@2.0.1",
|
|
163
99
|
"sm.transition@2.0.0",
|
|
164
100
|
"sm.transition@2.0.1",
|
|
165
101
|
"sm.transition@2.0.2",
|
|
@@ -179,26 +115,7 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
|
|
|
179
115
|
"sm.voiceover@2.1.6",
|
|
180
116
|
"sm.when@1.0.0",
|
|
181
117
|
"sm.when@1.1.0",
|
|
182
|
-
"sm.whiteboard-assistant@1.0.0",
|
|
183
118
|
"sm.write-summary@1.0.0",
|
|
184
|
-
"
|
|
185
|
-
"stillmade.narration@1.0.0",
|
|
186
|
-
"stillmade.narration@1.0.1",
|
|
187
|
-
"stillmade.occasion-story@1.0.0",
|
|
188
|
-
"stillmade.occasion-story@1.0.1",
|
|
189
|
-
"stillmade.occasion-story@1.0.2",
|
|
190
|
-
"stillmade.photo-reel-composer@1.0.0",
|
|
191
|
-
"stillmade.photo-reel-composer@1.0.1",
|
|
192
|
-
"stillmade.photo-reel-composer@1.0.2",
|
|
193
|
-
"stillmade.product-ad-variants@1.0.0",
|
|
194
|
-
"stillmade.release-visuals@1.0.0",
|
|
195
|
-
"stillmade.release-visuals@1.0.1",
|
|
196
|
-
"stillmade.release-visuals@1.0.2",
|
|
197
|
-
"stillmade.screen-recorder@1.0.0",
|
|
198
|
-
"stillmade.screen-recorder@1.0.1",
|
|
199
|
-
"stillmade.short-form-finish@1.0.0",
|
|
200
|
-
"stillmade.short-form-finish@1.0.1",
|
|
201
|
-
"stillmade.short-form-finish@1.0.2",
|
|
202
|
-
"stillmade.short-form-finish@1.0.3"
|
|
119
|
+
"stillmade.generate@1.0.0"
|
|
203
120
|
]));
|
|
204
121
|
export const isFirstPartySdkIdentity=manifest=>FIRST_PARTY_SDK_IDENTITIES.has(`${manifest?.id}@${manifest?.version}`);
|