stillmade-sdk 0.1.41 → 0.1.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.stillmade/sdk-baseline.json +19 -21
- package/AGENTS.md +17 -12
- package/FRAME_VIEWS.md +113 -0
- package/INTERFACE_DESIGN.md +67 -22
- package/MOBILE_UI.md +4 -2
- package/MODULES.md +152 -6
- package/PASTE_TO_LLM.md +42 -32
- package/README.md +3 -2
- package/SDK.md +1028 -563
- package/UI_AESTHETIC.md +30 -3
- 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 +8 -1
- package/examples/clean-text.stillmade.json +4 -0
- package/examples/custom-interface.stillmade.json +4 -0
- package/examples/describe-image.stillmade.json +4 -0
- package/examples/foley-cue.stillmade.json +4 -0
- package/examples/generate-image.stillmade.json +4 -0
- package/examples/generate-video.stillmade.json +10 -2
- package/examples/module-word-stats/stillmade.block.json +4 -0
- package/examples/narrate-script.stillmade.json +4 -0
- package/examples/numbered-takes.stillmade.json +4 -0
- package/examples/research-topic.stillmade.json +9 -1
- package/examples/score-a-scene.stillmade.json +4 -0
- package/examples/script-draft.stillmade.json +4 -0
- package/examples/summarize-page.stillmade.json +4 -0
- package/examples/text-workflow.stillmade.json +7 -32
- package/examples/thicken-lines.stillmade.json +4 -0
- package/examples/timeline-polish.stillmade.json +15 -3
- package/examples/transcribe-audio.stillmade.json +15 -3
- package/examples/word-count.stillmade.json +4 -0
- package/package.json +1 -1
- package/packages/block-cli/cli.js +13 -6
- package/packages/block-cli/dev-host/assets/host.js +81 -38
- package/packages/block-cli/dev-host/assets/index.css +1 -1
- package/packages/block-cli/format.js +1 -1
- package/packages/block-cli/ready-gate.js +4 -0
- package/packages/block-cli/templates.js +37 -19
- package/packages/block-cli/type-packages.js +119 -0
- package/packages/block-platform/block-readiness.js +8 -1
- 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/audio-generation-example.js +2 -2
- package/packages/block-sdk/block-tier.js +1 -1
- package/packages/block-sdk/builder-contract.js +13 -11
- package/packages/block-sdk/capabilities.js +1 -1
- package/packages/block-sdk/capability-example.js +1 -1
- package/packages/block-sdk/contracts.js +6 -2
- package/packages/block-sdk/examples.js +3 -3
- 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/image-description-example.js +1 -1
- package/packages/block-sdk/image-generation-example.js +1 -1
- package/packages/block-sdk/index.d.ts +2 -1
- package/packages/block-sdk/media-analysis-example.js +2 -2
- package/packages/block-sdk/mock-host.js +7 -1
- package/packages/block-sdk/module-example.js +1 -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/speech-example.js +1 -1
- package/packages/block-sdk/state-example.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/trigger-examples.js +2 -2
- 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 +44 -15
- package/packages/block-sdk/web-example.js +2 -2
- package/packages/design-system/block-kit.css +53 -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
package/UI_AESTHETIC.md
CHANGED
|
@@ -209,6 +209,23 @@ before/after examples from an Image Panels project (Voiceover 1,210 px to about
|
|
|
209
209
|
|
|
210
210
|
- Label and control on one row. No label stacked above a short control, no card
|
|
211
211
|
around a single control, no help line under every field.
|
|
212
|
+
- Sections stand apart (November 3, 2026, owner: "make sure it's more
|
|
213
|
+
separated"). What goes in (the files), the settings and the result are separate
|
|
214
|
+
parts: the settings sit together on one card, the same surface as the files
|
|
215
|
+
card, with a hairline between rows. StillMade's Step page does this for every
|
|
216
|
+
Block; a Block's own screen gets it from the kit's `sm-fields` / `sm-settings`
|
|
217
|
+
group. Never a card inside a card.
|
|
218
|
+
- Every part shows at once (November 3, 2026, owner: "show all elements at once,
|
|
219
|
+
like where something will appear ... it's gonna look sloppy if it just pops in a
|
|
220
|
+
random place"). Before the first run the result's place is drawn in the shape
|
|
221
|
+
and size of what is coming: a frame at the video's or picture's own ratio, lines
|
|
222
|
+
for text, a player bar for sound (`ResultPlace.jsx`). The result then fills it;
|
|
223
|
+
nothing appears in a place the person did not expect, and nothing below jumps.
|
|
224
|
+
- Side by side on wide screens (November 3, 2026, owner: "you're allowed to have
|
|
225
|
+
it side to side, which looks good; collapse to vertical when the screen gets
|
|
226
|
+
smaller"). What goes in sits on the left, the result's place on the right; under
|
|
227
|
+
860px they stack, the result under the button. A Step with a few inputs puts its
|
|
228
|
+
heading over the inputs, not across the page.
|
|
212
229
|
- Status is a dot or a few words ("No narration yet"), never a paragraph; the
|
|
213
230
|
reason sits behind an info toggle.
|
|
214
231
|
- Files are tiles: a saved file is a thumbnail or chip, adding one is a + tile.
|
|
@@ -224,8 +241,17 @@ before/after examples from an Image Panels project (Voiceover 1,210 px to about
|
|
|
224
241
|
left at 300–380 px and the result fills the right in its real shape; extra
|
|
225
242
|
width goes to the result, never to wider fields. Before there is a result,
|
|
226
243
|
the right side previews what will be made from the inputs. Under 860 px it is
|
|
227
|
-
one elastic column
|
|
228
|
-
|
|
244
|
+
one elastic column in the page's order: the inputs, the action in normal flow
|
|
245
|
+
(nothing sticky), then the result's place under it (November 3, 2026: this
|
|
246
|
+
replaces "result first, action pinned"). Mockup:
|
|
247
|
+
`docs/ui-review/wide-step-layout.html`.
|
|
248
|
+
- One page per job (November 3, 2026, owner: "Yes, one page"). A job the person
|
|
249
|
+
fills in and then gets a result from is one Step on one page: inputs and
|
|
250
|
+
settings, a cheap check before the expensive run (the script and a voice
|
|
251
|
+
sample before the video), then the result. The check never becomes a Step of
|
|
252
|
+
its own. A workspace the person works in for a while (the Editor, a CAD tool, a
|
|
253
|
+
canvas) is its own Step, and turning what it made into something else is the
|
|
254
|
+
next one.
|
|
229
255
|
- Results are shown big (November 1, 2026). A picture, clip or sound a Block
|
|
230
256
|
makes is the biggest thing on its page: one result fills the result area in
|
|
231
257
|
its own shape, as large as fits without scrolling (a vertical clip is sized by
|
|
@@ -320,7 +346,8 @@ asks for something else, that wins. Mockups: `docs/ui-review/job-words-journeys.
|
|
|
320
346
|
only a paragraph that has to be split across fields goes to a quick, cheap model,
|
|
321
347
|
free to the person.
|
|
322
348
|
- Days and times are picked from a calendar and a list of times, never typed.
|
|
323
|
-
- History is one icon in the
|
|
349
|
+
- History is one icon in the Project map's header, shown on every saved project whether or not anything is recorded
|
|
350
|
+
yet (November 3, 2026). A Step has no row of tool icons of its own.
|
|
324
351
|
- Chat can do everything these screens do: pick a reference, fill fields, pick a
|
|
325
352
|
time, post, and bring back an earlier version.
|
|
326
353
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from './versions/1.0.0/package.js';
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
// Generated from the owned versioned source by build-package.mjs.
|
|
2
|
+
export default {
|
|
3
|
+
"manifest": {
|
|
4
|
+
"schemaVersion": 1,
|
|
5
|
+
"sdkVersion": "0.1.0",
|
|
6
|
+
"id": "stillmade.generate",
|
|
7
|
+
"version": "1.0.0",
|
|
8
|
+
"name": "Generate",
|
|
9
|
+
"description": "Generate images and videos with any model StillMade runs: pictures from a prompt or your own photos, clips from a start and end frame. Pick the model, shape, size and length, and make up to four at once.",
|
|
10
|
+
"kind": "task",
|
|
11
|
+
"runtime": "module",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"inputs": {
|
|
14
|
+
"prompt": {
|
|
15
|
+
"type": "text",
|
|
16
|
+
"primary": true,
|
|
17
|
+
"required": true,
|
|
18
|
+
"semantic": "generation_prompt",
|
|
19
|
+
"accepts": [
|
|
20
|
+
"written_message",
|
|
21
|
+
"summary_report",
|
|
22
|
+
"narration_script"
|
|
23
|
+
],
|
|
24
|
+
"sources": [
|
|
25
|
+
"upstream",
|
|
26
|
+
"user"
|
|
27
|
+
],
|
|
28
|
+
"description": "What to make: the picture, or the shot and how it moves."
|
|
29
|
+
},
|
|
30
|
+
"mode": {
|
|
31
|
+
"type": "text",
|
|
32
|
+
"required": false,
|
|
33
|
+
"semantic": "generation_mode",
|
|
34
|
+
"description": "Image or video."
|
|
35
|
+
},
|
|
36
|
+
"model": {
|
|
37
|
+
"type": "text",
|
|
38
|
+
"required": false,
|
|
39
|
+
"semantic": "generation_model",
|
|
40
|
+
"description": "The model that makes it. Each one takes different shapes, sizes and lengths."
|
|
41
|
+
},
|
|
42
|
+
"aspectRatio": {
|
|
43
|
+
"type": "text",
|
|
44
|
+
"required": false,
|
|
45
|
+
"semantic": "aspect_ratio",
|
|
46
|
+
"description": "The shape, such as 16:9 for wide or 9:16 for a phone screen."
|
|
47
|
+
},
|
|
48
|
+
"size": {
|
|
49
|
+
"type": "text",
|
|
50
|
+
"required": false,
|
|
51
|
+
"semantic": "render_resolution",
|
|
52
|
+
"description": "How large it is, such as 2K for a picture or 1080p for a video."
|
|
53
|
+
},
|
|
54
|
+
"quality": {
|
|
55
|
+
"type": "text",
|
|
56
|
+
"required": false,
|
|
57
|
+
"semantic": "render_quality",
|
|
58
|
+
"description": "Low, medium or high, for the models that offer it. Higher costs more."
|
|
59
|
+
},
|
|
60
|
+
"duration": {
|
|
61
|
+
"type": "integer",
|
|
62
|
+
"required": false,
|
|
63
|
+
"semantic": "duration_seconds",
|
|
64
|
+
"description": "How long the video is, in seconds."
|
|
65
|
+
},
|
|
66
|
+
"sound": {
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"required": false,
|
|
69
|
+
"semantic": "generate_audio",
|
|
70
|
+
"description": "Whether the video comes with its own sound, for the models that make it."
|
|
71
|
+
},
|
|
72
|
+
"count": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"required": false,
|
|
75
|
+
"semantic": "generation_count",
|
|
76
|
+
"description": "How many to make at once, from 1 to 4. Each one is priced."
|
|
77
|
+
},
|
|
78
|
+
"avoid": {
|
|
79
|
+
"type": "text",
|
|
80
|
+
"required": false,
|
|
81
|
+
"semantic": "negative_prompt",
|
|
82
|
+
"description": "What to leave out of it."
|
|
83
|
+
},
|
|
84
|
+
"seed": {
|
|
85
|
+
"type": "integer",
|
|
86
|
+
"required": false,
|
|
87
|
+
"semantic": "generation_seed",
|
|
88
|
+
"description": "A number that makes the same choices again. Leave it empty for a new result each time."
|
|
89
|
+
},
|
|
90
|
+
"references": {
|
|
91
|
+
"type": "image[]",
|
|
92
|
+
"required": false,
|
|
93
|
+
"semantic": "reference_image",
|
|
94
|
+
"sources": [
|
|
95
|
+
"upstream",
|
|
96
|
+
"user"
|
|
97
|
+
],
|
|
98
|
+
"description": "Your own photos the picture keeps, such as a product, a person or a place."
|
|
99
|
+
},
|
|
100
|
+
"startFrame": {
|
|
101
|
+
"type": "image",
|
|
102
|
+
"required": false,
|
|
103
|
+
"semantic": "first_frame",
|
|
104
|
+
"sources": [
|
|
105
|
+
"upstream",
|
|
106
|
+
"user"
|
|
107
|
+
],
|
|
108
|
+
"description": "The picture the video starts on."
|
|
109
|
+
},
|
|
110
|
+
"endFrame": {
|
|
111
|
+
"type": "image",
|
|
112
|
+
"required": false,
|
|
113
|
+
"semantic": "last_frame",
|
|
114
|
+
"sources": [
|
|
115
|
+
"upstream",
|
|
116
|
+
"user"
|
|
117
|
+
],
|
|
118
|
+
"description": "The picture the video ends on. It needs a start frame."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"outputs": {
|
|
122
|
+
"media": {
|
|
123
|
+
"type": "asset[]",
|
|
124
|
+
"primary": true,
|
|
125
|
+
"semantic": "generated_media",
|
|
126
|
+
"description": "The pictures or clips that were made, in order."
|
|
127
|
+
},
|
|
128
|
+
"notMade": {
|
|
129
|
+
"type": "text",
|
|
130
|
+
"required": false,
|
|
131
|
+
"semantic": "generation_failures",
|
|
132
|
+
"description": "What could not be made, and why. Nothing is charged for those."
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"permissions": {
|
|
136
|
+
"project": [],
|
|
137
|
+
"actions": [
|
|
138
|
+
"media.generate"
|
|
139
|
+
],
|
|
140
|
+
"network": [],
|
|
141
|
+
"filesystem": [],
|
|
142
|
+
"secrets": []
|
|
143
|
+
},
|
|
144
|
+
"features": [
|
|
145
|
+
{
|
|
146
|
+
"id": "generate-image",
|
|
147
|
+
"label": "Generate pictures",
|
|
148
|
+
"does": "Makes up to four pictures with the image model you pick, in its shapes and sizes, keeping up to eight of your own photos.",
|
|
149
|
+
"asks": [
|
|
150
|
+
"make a picture of my product on a beach",
|
|
151
|
+
"generate four images of a cozy cafe",
|
|
152
|
+
"make a 9:16 poster with my logo"
|
|
153
|
+
],
|
|
154
|
+
"inputs": [
|
|
155
|
+
"prompt",
|
|
156
|
+
"model",
|
|
157
|
+
"aspectRatio",
|
|
158
|
+
"size",
|
|
159
|
+
"quality",
|
|
160
|
+
"count",
|
|
161
|
+
"references",
|
|
162
|
+
"avoid",
|
|
163
|
+
"seed"
|
|
164
|
+
],
|
|
165
|
+
"outputs": [
|
|
166
|
+
"media"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "generate-video",
|
|
171
|
+
"label": "Generate video clips",
|
|
172
|
+
"does": "Makes up to four clips with the video model you pick, at its lengths, sizes and shapes, from a start and end frame you add, with sound where offered.",
|
|
173
|
+
"asks": [
|
|
174
|
+
"make a 5 second clip of waves at sunset",
|
|
175
|
+
"animate this photo",
|
|
176
|
+
"make a vertical video from this first frame"
|
|
177
|
+
],
|
|
178
|
+
"inputs": [
|
|
179
|
+
"prompt",
|
|
180
|
+
"model",
|
|
181
|
+
"aspectRatio",
|
|
182
|
+
"size",
|
|
183
|
+
"duration",
|
|
184
|
+
"sound",
|
|
185
|
+
"count",
|
|
186
|
+
"startFrame",
|
|
187
|
+
"endFrame",
|
|
188
|
+
"avoid",
|
|
189
|
+
"seed"
|
|
190
|
+
],
|
|
191
|
+
"outputs": [
|
|
192
|
+
"media"
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
"platforms": {
|
|
197
|
+
"phone": {
|
|
198
|
+
"supported": true
|
|
199
|
+
},
|
|
200
|
+
"browser": {
|
|
201
|
+
"supported": true
|
|
202
|
+
},
|
|
203
|
+
"desktop": {
|
|
204
|
+
"supported": true
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"ui": [
|
|
208
|
+
{
|
|
209
|
+
"port": "prompt",
|
|
210
|
+
"control": "text",
|
|
211
|
+
"multiline": true
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"port": "mode",
|
|
215
|
+
"control": "select",
|
|
216
|
+
"options": [
|
|
217
|
+
"image",
|
|
218
|
+
"video"
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"port": "model",
|
|
223
|
+
"control": "select",
|
|
224
|
+
"options": [
|
|
225
|
+
"nano-banana-2",
|
|
226
|
+
"nano-banana-2-lite",
|
|
227
|
+
"nano-banana-pro",
|
|
228
|
+
"gpt-image-2",
|
|
229
|
+
"gpt-image-1.5",
|
|
230
|
+
"kling-image-v3",
|
|
231
|
+
"seedream-v4.5",
|
|
232
|
+
"seedance-2.0",
|
|
233
|
+
"seedance-2.0-fast",
|
|
234
|
+
"seedance-2.0-mini",
|
|
235
|
+
"kling-v3-turbo",
|
|
236
|
+
"wan-2.6-flash",
|
|
237
|
+
"veo-3.1-lite",
|
|
238
|
+
"vidu-q3-turbo",
|
|
239
|
+
"wan-2.7"
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"port": "aspectRatio",
|
|
244
|
+
"control": "select",
|
|
245
|
+
"options": [
|
|
246
|
+
"auto",
|
|
247
|
+
"1:1",
|
|
248
|
+
"4:5",
|
|
249
|
+
"3:4",
|
|
250
|
+
"2:3",
|
|
251
|
+
"9:16",
|
|
252
|
+
"16:9",
|
|
253
|
+
"3:2",
|
|
254
|
+
"4:3",
|
|
255
|
+
"21:9"
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"port": "size",
|
|
260
|
+
"control": "select",
|
|
261
|
+
"options": [
|
|
262
|
+
"480p",
|
|
263
|
+
"720p",
|
|
264
|
+
"1080p",
|
|
265
|
+
"1K",
|
|
266
|
+
"2K",
|
|
267
|
+
"4K",
|
|
268
|
+
"4k"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"port": "quality",
|
|
273
|
+
"control": "select",
|
|
274
|
+
"options": [
|
|
275
|
+
"low",
|
|
276
|
+
"medium",
|
|
277
|
+
"high"
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"port": "duration",
|
|
282
|
+
"control": "number"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"port": "sound",
|
|
286
|
+
"control": "checkbox"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"port": "count",
|
|
290
|
+
"control": "number"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"port": "avoid",
|
|
294
|
+
"control": "text"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"port": "seed",
|
|
298
|
+
"control": "number"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"port": "references",
|
|
302
|
+
"control": "asset"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"port": "startFrame",
|
|
306
|
+
"control": "asset"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"port": "endFrame",
|
|
310
|
+
"control": "asset"
|
|
311
|
+
}
|
|
312
|
+
],
|
|
313
|
+
"provenance": {
|
|
314
|
+
"notice": "MIT License\n\nCopyright (c) <year> <copyright holders>\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"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
"module": {
|
|
318
|
+
"schemaVersion": 1,
|
|
319
|
+
"entry": "main.js",
|
|
320
|
+
"files": {
|
|
321
|
+
"main.js": {
|
|
322
|
+
"sha256": "dc29df496b0440bc90f341ebc795ffe7fd45e85d6f14fed287681d2d4b040b27",
|
|
323
|
+
"bytes": 2898,
|
|
324
|
+
"mediaType": "text/javascript"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"moduleSource": {
|
|
329
|
+
"main.js": "// Generate: every image and video model StillMade runs, in one Block. The view sets the choices; this run turns\n// them into one media.generate batch. StillMade prices the whole batch, the person approves it once, and the\n// pictures or clips come back here in order.\nconst clean = value => typeof value === 'string' && value && value !== 'default' ? value : undefined;\n\nexport function generationItems(input) {\n const mode = input.mode === 'video' ? 'video' : 'image';\n const prompt = String(input.prompt || '').trim();\n if (!prompt) throw new Error(mode === 'video' ? 'Describe the shot first.' : 'Describe the picture first.');\n if (mode === 'video' && input.endFrame && !input.startFrame) throw new Error('Add a start frame before an end frame.');\n const count = Math.max(1, Math.min(4, Math.floor(Number(input.count) || 1)));\n const settings = {};\n if (clean(input.model)) settings.model = input.model;\n if (clean(input.aspectRatio)) settings.aspectRatio = input.aspectRatio;\n if (clean(input.size)) settings.resolution = input.size;\n if (mode === 'image' && clean(input.quality)) settings.quality = input.quality;\n if (mode === 'video') {\n const seconds = Math.floor(Number(input.duration));\n if (Number.isFinite(seconds) && seconds > 0) settings.duration = seconds;\n if (typeof input.sound === 'boolean') settings.generateAudio = input.sound;\n }\n const references = Array.isArray(input.references) ? input.references.filter(Boolean).slice(0, 8) : [];\n const avoid = String(input.avoid || '').trim();\n const seed = Number.isInteger(input.seed) && input.seed >= 0 ? input.seed : undefined;\n const items = [];\n for (let index = 0; index < count; index++) {\n const item = { key: `${mode}-${index + 1}`, operation: mode, prompt };\n if (Object.keys(settings).length) item.settings = settings;\n if (mode === 'image' && references.length) item.references = references;\n if (mode === 'video' && input.startFrame) item.firstFrame = input.startFrame;\n if (mode === 'video' && input.startFrame && input.endFrame) item.lastFrame = input.endFrame;\n if (avoid) item.negativePrompt = avoid;\n if (seed !== undefined) item.seed = Math.min(2147483647, seed + index);\n items.push(item);\n }\n return items;\n}\n\nexport default async function run(input, stillmade) {\n const items = generationItems(input);\n stillmade.progress(0.05, items.length > 1 ? `Making ${items.length}` : 'Making it');\n const { value, failures } = await stillmade.actions.run({\n schemaVersion: 1, kind: 'stillmade.media-action', operation: 'media.generate', resultPort: null, request: { items },\n });\n const media = value.map(({ key, ...made }) => made);\n const outputs = { media };\n if (failures && failures.length) outputs.notMade = `${failures.length} of ${items.length} were not made: ${failures.map(failure => failure.message).join(' ')}`.slice(0, 2000);\n return outputs;\n}\n"
|
|
330
|
+
},
|
|
331
|
+
"view": {
|
|
332
|
+
"runtime": "frame",
|
|
333
|
+
"html": "<main class=\"sm-block gen\" id=\"root\" data-mode=\"image\">\n <div class=\"gen-layout\">\n <section class=\"gen-controls\" aria-label=\"What to make\">\n <div class=\"sm-fields\">\n <fieldset class=\"sm-choices gen-mode\" data-segmented id=\"mode\" data-stillmade-share=\"mode\">\n <legend class=\"sm-sr\">Make</legend>\n <label><input type=\"radio\" name=\"mode\" value=\"image\" checked><span class=\"sm-glyph\" data-glyph=\"image\" aria-hidden=\"true\"></span><span>Image</span></label>\n <label><input type=\"radio\" name=\"mode\" value=\"video\"><span class=\"sm-glyph\" data-glyph=\"video\" aria-hidden=\"true\"></span><span>Video</span></label>\n </fieldset>\n\n <div class=\"sm-field gen-prompt\">\n <label class=\"sm-sr\" for=\"prompt\">Describe it</label>\n <textarea id=\"prompt\" rows=\"4\" maxlength=\"4000\" placeholder=\"Describe the picture\" data-stillmade-share=\"prompt\"></textarea>\n </div>\n </div>\n\n <div class=\"sm-field gen-only-image\" id=\"references-row\">\n <span class=\"sm-label\">Your photos <small class=\"gen-muted\" id=\"references-limit\"></small></span>\n <ul class=\"sm-gallery\" data-size=\"small\" id=\"references\" aria-label=\"Your photos\">\n <li><button type=\"button\" class=\"sm-add\" data-stillmade-add=\"references\" aria-label=\"Add photos\" title=\"Add photos\"></button></li>\n </ul>\n </div>\n\n <div class=\"sm-field gen-only-video gen-frames\" id=\"frames-row\">\n <span class=\"sm-label\">Frames</span>\n <ul class=\"sm-gallery\" data-size=\"small\" aria-label=\"Start and end frames\">\n <li id=\"start-frame\"><button type=\"button\" class=\"sm-add\" data-stillmade-add=\"startFrame\" aria-label=\"Add a start frame\" title=\"Add a start frame\"></button><small>Start</small></li>\n <li id=\"end-frame\"><button type=\"button\" class=\"sm-add\" data-stillmade-add=\"endFrame\" aria-label=\"Add an end frame\" title=\"Add an end frame\"></button><small>End</small></li>\n </ul>\n </div>\n\n <div class=\"sm-settings\">\n <fieldset class=\"sm-choices gen-row gen-chips\" id=\"shape\" data-stillmade-share=\"shape\">\n <legend>Shape</legend>\n <label><input type=\"radio\" name=\"shape\" value=\"auto\"><span>Auto</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"1:1\" checked><span class=\"sm-shape\" style=\"--r:1/1\" aria-hidden=\"true\"></span><span>1:1</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"4:5\"><span class=\"sm-shape\" style=\"--r:4/5\" aria-hidden=\"true\"></span><span>4:5</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"3:4\"><span class=\"sm-shape\" style=\"--r:3/4\" aria-hidden=\"true\"></span><span>3:4</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"2:3\"><span class=\"sm-shape\" style=\"--r:2/3\" aria-hidden=\"true\"></span><span>2:3</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"9:16\"><span class=\"sm-shape\" style=\"--r:9/16\" aria-hidden=\"true\"></span><span>9:16</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"16:9\"><span class=\"sm-shape\" style=\"--r:16/9\" aria-hidden=\"true\"></span><span>16:9</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"3:2\"><span class=\"sm-shape\" style=\"--r:3/2\" aria-hidden=\"true\"></span><span>3:2</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"4:3\"><span class=\"sm-shape\" style=\"--r:4/3\" aria-hidden=\"true\"></span><span>4:3</span></label>\n <label><input type=\"radio\" name=\"shape\" value=\"21:9\"><span class=\"sm-shape\" style=\"--r:21/9\" aria-hidden=\"true\"></span><span>21:9</span></label>\n </fieldset>\n <fieldset class=\"sm-choices gen-row\" data-segmented id=\"size\" data-stillmade-share=\"size\">\n <legend>Size</legend>\n <label><input type=\"radio\" name=\"size\" value=\"480p\" checked><span>480p</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"720p\"><span>720p</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"1080p\"><span>1080p</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"1K\"><span>1K</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"2K\"><span>2K</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"4K\"><span>4K</span></label>\n <label><input type=\"radio\" name=\"size\" value=\"4k\"><span>4K</span></label>\n </fieldset>\n <fieldset class=\"sm-choices gen-row gen-only-image\" data-segmented id=\"quality\" data-stillmade-share=\"quality\">\n <legend>Quality</legend>\n <label><input type=\"radio\" name=\"quality\" value=\"low\"><span>Low</span></label>\n <label><input type=\"radio\" name=\"quality\" value=\"medium\" checked><span>Medium</span></label>\n <label><input type=\"radio\" name=\"quality\" value=\"high\"><span>High</span></label>\n </fieldset>\n <fieldset class=\"sm-choices gen-row gen-chips gen-only-video\" id=\"length\" data-stillmade-share=\"length\">\n <legend>Length</legend>\n <label><input type=\"radio\" name=\"length\" value=\"1\"><span>1s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"2\"><span>2s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"3\"><span>3s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"4\"><span>4s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"5\" checked><span>5s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"6\"><span>6s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"7\"><span>7s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"8\"><span>8s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"9\"><span>9s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"10\"><span>10s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"11\"><span>11s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"12\"><span>12s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"13\"><span>13s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"14\"><span>14s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"15\"><span>15s</span></label>\n <label><input type=\"radio\" name=\"length\" value=\"16\"><span>16s</span></label>\n </fieldset>\n <fieldset class=\"sm-choices gen-row\" data-segmented id=\"count\" data-stillmade-share=\"count\">\n <legend>How many</legend>\n <label><input type=\"radio\" name=\"count\" value=\"1\" checked><span>1</span></label>\n <label><input type=\"radio\" name=\"count\" value=\"2\"><span>2</span></label>\n <label><input type=\"radio\" name=\"count\" value=\"3\"><span>3</span></label>\n <label><input type=\"radio\" name=\"count\" value=\"4\"><span>4</span></label>\n </fieldset>\n <label class=\"sm-field gen-only-video gen-sound\" id=\"sound-row\"><span>Sound</span><input type=\"checkbox\" id=\"sound\" checked data-stillmade-share=\"sound\"></label>\n </div>\n\n <details class=\"sm-more\" id=\"more\">\n <summary>More</summary>\n <div class=\"sm-fields\">\n <div class=\"sm-field\"><label for=\"avoid\">Leave out</label><input id=\"avoid\" type=\"text\" maxlength=\"2000\" placeholder=\"Blur, text, extra fingers\" data-stillmade-share=\"avoid\"></div>\n <div class=\"sm-field\" id=\"seed-row\"><label for=\"seed\">Seed</label><input id=\"seed\" type=\"number\" min=\"0\" max=\"2147483647\" step=\"1\" inputmode=\"numeric\" placeholder=\"Random\" data-stillmade-share=\"seed\"></div>\n </div>\n </details>\n\n <div class=\"sm-actions gen-actions\">\n <details class=\"gen-model\" id=\"model-picker\">\n <summary class=\"gen-chip\" data-stillmade-action=\"choose-model\"><span id=\"model-name\">Nano Banana 2</span><span class=\"sm-glyph\" data-glyph=\"chevron-down\" aria-hidden=\"true\"></span></summary>\n <div class=\"gen-models\">\n <fieldset class=\"sm-choices gen-only-image\" data-size=\"wide\" id=\"imageModel\" data-stillmade-share=\"imageModel\">\n <legend>Image model</legend>\n <label><input type=\"radio\" name=\"imageModel\" value=\"nano-banana-2\" checked><strong>Nano Banana 2</strong><span class=\"gen-tags\"><span>up to 4K</span><span>keeps 8 photos</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"nano-banana-2-lite\"><strong>Nano Banana 2 Lite</strong><span class=\"gen-tags\"><span>up to 4K</span><span>keeps 8 photos</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"nano-banana-pro\"><strong>Nano Banana Pro</strong><span class=\"gen-tags\"><span>up to 4K</span><span>keeps 8 photos</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"gpt-image-2\"><strong>GPT Image 2</strong><span class=\"gen-tags\"><span>up to 4K</span><span>keeps 8 photos</span><span>quality levels</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"gpt-image-1.5\"><strong>GPT-Image 1.5</strong><span class=\"gen-tags\"><span>quality levels</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"kling-image-v3\"><strong>Kling Image V3</strong><span class=\"gen-tags\"><span>up to 2K</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"imageModel\" value=\"seedream-v4.5\"><strong>Seedream 4.5</strong><span class=\"gen-tags\"><span>up to 4K</span></span><span class=\"gen-why\"></span></label>\n </fieldset>\n <fieldset class=\"sm-choices gen-only-video\" data-size=\"wide\" id=\"videoModel\" data-stillmade-share=\"videoModel\">\n <legend>Video model</legend>\n <label><input type=\"radio\" name=\"videoModel\" value=\"seedance-2.0\"><strong>Seedance 2.0</strong><span class=\"gen-tags\"><span>up to 15s</span><span>up to 4K</span><span>sound</span><span>end frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"seedance-2.0-fast\"><strong>Seedance 2.0 Fast</strong><span class=\"gen-tags\"><span>up to 15s</span><span>up to 720p</span><span>sound</span><span>end frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"seedance-2.0-mini\"><strong>Seedance 2.0 Mini</strong><span class=\"gen-tags\"><span>up to 15s</span><span>up to 720p</span><span>sound</span><span>end frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"kling-v3-turbo\"><strong>Kling 3.0 Turbo</strong><span class=\"gen-tags\"><span>up to 10s</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"wan-2.6-flash\"><strong>Wan 2.6 Flash</strong><span class=\"gen-tags\"><span>up to 5s</span><span>starts from a frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"veo-3.1-lite\"><strong>Veo 3.1 Lite</strong><span class=\"gen-tags\"><span>up to 8s</span><span>up to 1080p</span><span>sound</span><span>end frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"vidu-q3-turbo\" checked><strong>Vidu Q3 Turbo</strong><span class=\"gen-tags\"><span>up to 16s</span><span>sound</span><span>end frame</span></span><span class=\"gen-why\"></span></label>\n <label><input type=\"radio\" name=\"videoModel\" value=\"wan-2.7\"><strong>Wan 2.7</strong><span class=\"gen-tags\"><span>up to 15s</span><span>up to 1080p</span><span>end frame</span><span>starts from a frame</span></span><span class=\"gen-why\"></span></label>\n </fieldset>\n </div>\n </details>\n <button type=\"button\" class=\"sm-primary\" id=\"generate\" data-stillmade-action=\"generate\">Generate image</button>\n </div>\n <p class=\"sm-status\" id=\"status\" role=\"status\"></p>\n </section>\n\n <section class=\"gen-results\" aria-label=\"What was made\" aria-live=\"polite\">\n <div class=\"sm-stage gen-place\" id=\"place\">\n <span class=\"sm-shape\" id=\"place-shape\" style=\"--r:1/1\"></span>\n <p class=\"sm-empty\" id=\"place-text\">Your picture appears here</p>\n </div>\n <ul class=\"sm-gallery gen-gallery\" id=\"gallery\" aria-label=\"Made in this Step\" hidden></ul>\n </section>\n </div>\n</main>\n",
|
|
334
|
+
"css": "/* The Block kit (injected by the host) carries fields, choices, galleries and buttons; this file only lays the\n page out (choices on the left, what was made on the right) and draws the model picker. */\n.gen [hidden], .gen [data-off=\"true\"] { display: none !important; }\n.gen[data-mode=\"image\"] .gen-only-video, .gen[data-mode=\"video\"] .gen-only-image { display: none !important; }\n.gen-layout { display: grid; gap: var(--s-4, 16px); align-items: start; }\n@container (min-width: 860px) { .gen-layout { grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); } }\n@media (min-width: 860px) { .gen-layout { grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); } }\n.gen-controls { display: grid; gap: var(--s-3, 12px); min-width: 0; }\n.gen-mode { --sm-tile: 120px; }\n.gen-prompt textarea { width: 100%; box-sizing: border-box; resize: vertical; min-height: 96px; }\n.gen-muted { color: var(--fg-muted); font-weight: 400; }\n.gen .sm-settings { grid-template-columns: minmax(0, 1fr); }\n/* Shape and length have many options: small chips that wrap, the pick in ink as in every choice. */\n.gen-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.gen-chips > legend { width: 100%; }\n.gen-chips > label { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 4px 10px; border-radius: 999px; font-size: var(--fs-body-s, .875rem); white-space: nowrap; }\n.gen-row .sm-shape { width: 14px; }\n.gen-sound { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2, 8px); }\n.gen-frames li { display: grid; justify-items: center; gap: 4px; }\n.gen-frames small { color: var(--fg-muted); }\n.gen-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2, 8px); }\n.gen-actions .sm-primary { flex: 1 1 auto; }\n.gen-model { position: relative; }\n.gen-chip { display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 6px 12px; border: 1px solid var(--border-strong); border-radius: 999px; background: var(--bg-elev-1); color: var(--fg); cursor: pointer; list-style: none; white-space: nowrap; }\n.gen-chip::-webkit-details-marker { display: none; }\n.gen-chip:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }\n.gen-models { position: absolute; z-index: 2; bottom: calc(100% + 6px); left: 0; width: min(360px, 90vw); max-height: 360px; overflow: auto; padding: var(--s-3, 12px); border: 1px solid var(--border-strong); border-radius: var(--r-3, 8px); background: var(--bg-elev-1); box-shadow: 0 8px 24px rgba(0, 0, 0, .12); }\n.gen-models .sm-choices > label { justify-items: start; text-align: left; }\n.gen-models strong { font-weight: 500; }\n.gen-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.gen-tags span { font-size: var(--fs-body-xs, .75rem); color: var(--fg-muted); border: 1px solid var(--border-tok); border-radius: 999px; padding: 1px 6px; }\n.gen-models label:has(input:disabled) { opacity: .5; }\n.gen-why { font-size: var(--fs-body-xs, .75rem); color: var(--fg-muted); }\n.gen-results { display: grid; gap: var(--s-3, 12px); min-width: 0; }\n.gen-place { display: grid; place-items: center; min-height: 240px; }\n.gen-place .sm-shape { width: min(100%, 420px); }\n.gen-place > .sm-empty { position: absolute; }\n.gen-place { position: relative; }\n.gen-gallery { --sm-tile: 220px; }\n.gen-gallery li { display: grid; gap: 4px; }\n.gen-gallery img, .gen-gallery video { width: 100%; border-radius: var(--r-2, 4px); background: var(--bg-inset); }\n.gen-gallery small { color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n",
|
|
335
|
+
"javascript": "(()=>{\n\"use strict\";\nconst CATALOG={\"image\":{\"nano-banana-2\":{\"label\":\"Nano Banana 2\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"3:2\",\"4:3\",\"1:1\",\"4:5\",\"3:4\",\"2:3\",\"9:16\"],\"resolutions\":[\"1K\",\"2K\",\"4K\"],\"qualities\":[],\"references\":8,\"seed\":true},\"nano-banana-2-lite\":{\"label\":\"Nano Banana 2 Lite\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"3:2\",\"4:3\",\"1:1\",\"4:5\",\"3:4\",\"2:3\",\"9:16\"],\"resolutions\":[\"1K\",\"2K\",\"4K\"],\"qualities\":[],\"references\":8,\"seed\":true},\"nano-banana-pro\":{\"label\":\"Nano Banana Pro\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"3:2\",\"4:3\",\"1:1\",\"4:5\",\"3:4\",\"2:3\",\"9:16\"],\"resolutions\":[\"1K\",\"2K\",\"4K\"],\"qualities\":[],\"references\":8,\"seed\":true},\"gpt-image-2\":{\"label\":\"GPT Image 2\",\"aspectRatios\":[\"auto\",\"16:9\",\"9:16\",\"1:1\",\"4:3\",\"3:4\"],\"resolutions\":[\"1K\",\"2K\",\"4K\"],\"qualities\":[\"low\",\"medium\",\"high\"],\"references\":8,\"seed\":false},\"gpt-image-1.5\":{\"label\":\"GPT-Image 1.5\",\"aspectRatios\":[\"16:9\",\"9:16\",\"1:1\"],\"resolutions\":[],\"qualities\":[\"low\",\"medium\",\"high\"],\"references\":0,\"seed\":false},\"kling-image-v3\":{\"label\":\"Kling Image V3\",\"aspectRatios\":[\"21:9\",\"16:9\",\"9:16\",\"1:1\",\"4:3\",\"3:4\",\"3:2\",\"2:3\"],\"resolutions\":[\"1K\",\"2K\"],\"qualities\":[],\"references\":0,\"seed\":false},\"seedream-v4.5\":{\"label\":\"Seedream 4.5\",\"aspectRatios\":[\"16:9\",\"9:16\",\"1:1\",\"4:3\",\"3:4\"],\"resolutions\":[\"2K\",\"4K\"],\"qualities\":[],\"references\":0,\"seed\":true}},\"video\":{\"seedance-2.0\":{\"label\":\"Seedance 2.0\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"4:3\",\"1:1\",\"3:4\",\"9:16\"],\"resolutions\":[\"480p\",\"720p\",\"1080p\",\"4k\"],\"durations\":[4,5,6,7,8,9,10,11,12,13,14,15],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":false,\"seed\":true,\"nativeAudio\":true},\"seedance-2.0-fast\":{\"label\":\"Seedance 2.0 Fast\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"4:3\",\"1:1\",\"3:4\",\"9:16\"],\"resolutions\":[\"480p\",\"720p\"],\"durations\":[4,5,6,7,8,9,10,11,12,13,14,15],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":false,\"seed\":true,\"nativeAudio\":true},\"seedance-2.0-mini\":{\"label\":\"Seedance 2.0 Mini\",\"aspectRatios\":[\"auto\",\"21:9\",\"16:9\",\"4:3\",\"1:1\",\"3:4\",\"9:16\"],\"resolutions\":[\"480p\",\"720p\"],\"durations\":[4,5,6,7,8,9,10,11,12,13,14,15],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":false,\"seed\":true,\"nativeAudio\":true},\"kling-v3-turbo\":{\"label\":\"Kling 3.0 Turbo\",\"aspectRatios\":[\"16:9\",\"9:16\",\"1:1\"],\"resolutions\":[],\"durations\":[5,10],\"firstFrame\":true,\"lastFrame\":false,\"requiresFirstFrame\":false,\"seed\":false,\"nativeAudio\":false},\"wan-2.6-flash\":{\"label\":\"Wan 2.6 Flash\",\"aspectRatios\":[\"16:9\",\"9:16\",\"1:1\"],\"resolutions\":[],\"durations\":[3,4,5],\"firstFrame\":true,\"lastFrame\":false,\"requiresFirstFrame\":true,\"seed\":false,\"nativeAudio\":false},\"veo-3.1-lite\":{\"label\":\"Veo 3.1 Lite\",\"aspectRatios\":[\"16:9\",\"9:16\"],\"resolutions\":[\"720p\",\"1080p\"],\"durations\":[4,6,8],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":false,\"seed\":false,\"nativeAudio\":true},\"vidu-q3-turbo\":{\"label\":\"Vidu Q3 Turbo\",\"aspectRatios\":[],\"resolutions\":[],\"durations\":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":false,\"seed\":false,\"nativeAudio\":true},\"wan-2.7\":{\"label\":\"Wan 2.7\",\"aspectRatios\":[],\"resolutions\":[\"720p\",\"1080p\"],\"durations\":[2,3,4,5,6,7,8,9,10,11,12,13,14,15],\"firstFrame\":true,\"lastFrame\":true,\"requiresFirstFrame\":true,\"seed\":true,\"nativeAudio\":false}}};\n// Generate's one page. CATALOG (inlined by build-package.mjs from the SDK's generation catalog) says what each\n// model accepts, so the page only offers what the chosen model can make: its shapes, sizes, lengths, sound, your\n// photos and frames. Every choice is a shared field (data-stillmade-share), so people editing the Step together see\n// the same page. The button runs the Block once; StillMade shows the price and asks before anything is made.\nconst $ = id => document.getElementById(id);\nconst root = $('root');\nconst state = { input: {}, busy: false, history: [], shownKeys: '' };\n\nconst checked = name => { const el = document.querySelector(`input[name=\"${name}\"]:checked`); return el ? el.value : undefined; };\nconst visibleChecked = name => { const el = document.querySelector(`input[name=\"${name}\"]:checked`); return el && !el.disabled && !el.closest('label').hidden ? el.value : undefined; };\nfunction pick(name, value) {\n const el = document.querySelector(`input[name=\"${name}\"][value=\"${value}\"]`);\n if (!el || el.checked) return;\n el.checked = true;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n}\nconst mode = () => checked('mode') === 'video' ? 'video' : 'image';\nfunction model() {\n const kind = mode(), list = CATALOG[kind], id = checked(kind === 'video' ? 'videoModel' : 'imageModel');\n return list[id] ? [id, list[id]] : Object.entries(list)[0];\n}\nconst message = error => (error && error.message) || 'That did not work. Try again.';\nfunction status(text, tone) { const el = $('status'); el.textContent = text || ''; if (tone) el.dataset.tone = tone; else delete el.dataset.tone; }\n\n// One axis (shape, size, length): show the values the model accepts, keep the pick when it still fits,\n// otherwise move to the model's first value. No values hides the row.\nfunction axis(name, allowed, fallback) {\n const group = $(name), options = [...group.querySelectorAll('input')];\n for (const option of options) option.closest('label').hidden = !allowed.includes(option.value);\n group.dataset.off = String(!allowed.length);\n if (!allowed.length) return;\n const current = checked(name);\n if (!allowed.includes(current)) pick(name, allowed.includes(fallback) ? fallback : allowed[0]);\n}\n\nfunction refresh() {\n const kind = mode(), [id, entry] = model();\n root.dataset.mode = kind;\n $('prompt').placeholder = kind === 'video' ? 'Describe the shot: who or what, how it moves, the camera' : 'Describe the picture';\n $('model-name').textContent = entry.label;\n axis('shape', entry.aspectRatios, kind === 'video' ? '16:9' : '1:1');\n axis('size', entry.resolutions, entry.resolutions[0]);\n if (kind === 'image') {\n $('quality').dataset.off = String(!entry.qualities.length);\n const references = state.input.references || [];\n $('references-row').dataset.off = String(!entry.references && !references.length);\n $('references-limit').textContent = entry.references ? `up to ${entry.references}` : 'this model takes none';\n } else {\n axis('length', entry.durations.map(String), '5');\n $('sound-row').dataset.off = String(!entry.nativeAudio);\n $('end-frame').dataset.off = String(!entry.lastFrame);\n }\n $('seed-row').dataset.off = String(!entry.seed);\n // Models that cannot use what is already here are not offered: photos need a model that keeps them,\n // a video model that needs a start frame waits for one, an end frame needs a model that takes it.\n const refs = (state.input.references || []).length, start = !!state.input.startFrame, end = !!state.input.endFrame;\n for (const option of document.querySelectorAll('input[name=\"imageModel\"]')) {\n const item = CATALOG.image[option.value];\n option.disabled = refs > 0 && !item.references;\n option.closest('label').querySelector('.gen-why').textContent = option.disabled ? 'Does not keep your photos' : '';\n }\n for (const option of document.querySelectorAll('input[name=\"videoModel\"]')) {\n const item = CATALOG.video[option.value];\n const reason = item.requiresFirstFrame && !start ? 'Needs a start frame' : end && !item.lastFrame ? 'Takes no end frame' : '';\n option.disabled = !!reason;\n option.closest('label').querySelector('.gen-why').textContent = reason;\n }\n const count = Number(checked('count')) || 1, noun = kind === 'video' ? 'video' : 'image';\n $('generate').textContent = count > 1 ? `Generate ${count} ${noun}s` : `Generate ${noun}`;\n const shape = visibleChecked('shape');\n $('place-shape').style.setProperty('--r', shape && shape !== 'auto' ? shape.replace(':', '/') : kind === 'video' ? '16/9' : '1/1');\n $('place-text').textContent = kind === 'video' ? 'Your video appears here' : 'Your picture appears here';\n}\n\ndocument.addEventListener('change', event => { if (event.target.closest('fieldset, #sound')) refresh(); });\n$('model-picker').addEventListener('change', () => { $('model-picker').open = false; });\n\nasync function preview(value) {\n const shown = value.kind === 'video' ? await StillMade.previewMedia(value) : await StillMade.previewImage(value);\n return typeof shown === 'string' ? shown : shown && shown.url;\n}\n\nasync function tile(value, { label, list, index }) {\n const li = document.createElement('li');\n try {\n const url = await preview(value);\n const img = document.createElement('img'); img.src = url; img.alt = label; li.append(img);\n } catch { li.append(Object.assign(document.createElement('span'), { className: 'sm-empty', textContent: label })); }\n if (list) {\n const tools = document.createElement('span'); tools.className = 'sm-tile-tools';\n const remove = document.createElement('button');\n remove.type = 'button'; remove.dataset.stillmadeRemove = list; remove.dataset.index = String(index);\n remove.setAttribute('aria-label', 'Remove'); remove.title = 'Remove';\n const glyph = document.createElement('span'); glyph.className = 'sm-glyph'; glyph.dataset.glyph = 'close'; glyph.setAttribute('aria-hidden', 'true');\n remove.append(glyph);\n tools.append(remove); li.append(tools);\n }\n return li;\n}\n\nasync function drawInputs(input) {\n const list = $('references'), add = list.lastElementChild;\n for (const li of [...list.children]) if (li !== add) li.remove();\n const photos = Array.isArray(input.references) ? input.references : [];\n for (let index = 0; index < photos.length; index++) list.insertBefore(await tile(photos[index], { label: `Photo ${index + 1}`, list: 'references', index }), add);\n for (const [id, name, label] of [['start-frame', 'startFrame', 'Start'], ['end-frame', 'endFrame', 'End']]) {\n const slot = $(id), value = input[name];\n for (const child of [...slot.querySelectorAll('img, .sm-empty')]) child.remove();\n if (value) { const shown = await tile(value, { label }); const img = shown.querySelector('img, .sm-empty'); if (img) slot.prepend(img); }\n }\n}\n\nStillMade.onInput(async input => {\n state.input = input || {};\n if (typeof state.input.prompt === 'string' && state.input.prompt && !$('prompt').value) $('prompt').value = state.input.prompt;\n // Choices that arrive as inputs (from Chat or an earlier Step) set the page; the person can change them.\n const given = state.input, kind = given.mode === 'video' ? 'video' : given.mode === 'image' ? 'image' : undefined;\n if (kind) pick('mode', kind);\n if (given.model && CATALOG.image[given.model]) { pick('mode', 'image'); pick('imageModel', given.model); }\n if (given.model && CATALOG.video[given.model]) { pick('mode', 'video'); pick('videoModel', given.model); }\n refresh();\n if (given.aspectRatio) pick('shape', given.aspectRatio);\n if (given.size) pick('size', given.size);\n if (given.quality) pick('quality', given.quality);\n if (Number.isInteger(given.duration)) pick('length', String(given.duration));\n if (Number.isInteger(given.count)) pick('count', String(Math.max(1, Math.min(4, given.count))));\n if (typeof given.sound === 'boolean') $('sound').checked = given.sound;\n if (typeof given.avoid === 'string' && given.avoid && !$('avoid').value) $('avoid').value = given.avoid;\n if (Number.isInteger(given.seed) && !$('seed').value) $('seed').value = String(given.seed);\n try { await drawInputs(state.input); } catch (error) { status(message(error), 'error'); }\n refresh();\n});\n\n// What was made: newest first, one tile each. Earlier batches stay in the shared history (the last 24).\nasync function drawResults() {\n const items = state.history, gallery = $('gallery'), keys = items.map(item => item.assetId + ':' + item.versionId).join('|');\n if (keys === state.shownKeys) return;\n state.shownKeys = keys;\n gallery.replaceChildren();\n for (const item of items) {\n const li = document.createElement('li');\n try {\n const url = await preview(item);\n if (item.kind === 'video') {\n const video = document.createElement('video'); video.src = url; video.controls = true; video.playsInline = true; video.preload = 'metadata';\n li.append(video);\n } else {\n const img = document.createElement('img'); img.src = url; img.alt = item.prompt || 'Generated picture'; img.dataset.stillmadeExpand = 'made';\n li.append(img);\n }\n } catch { li.append(Object.assign(document.createElement('span'), { className: 'sm-empty', textContent: 'Could not show this one' })); }\n li.append(Object.assign(document.createElement('small'), { textContent: [item.model, item.prompt].filter(Boolean).join(' · '), title: item.prompt || '' }));\n gallery.append(li);\n }\n gallery.hidden = !items.length;\n $('place').hidden = !!items.length;\n}\n\nfunction remember(made, details) {\n const fresh = made.filter(item => item && item.url && ['image', 'video'].includes(item.kind))\n .map(item => ({ kind: item.kind, assetId: item.assetId, versionId: item.versionId, url: item.url, ...(item.duration ? { duration: item.duration } : {}), ...details }));\n if (!fresh.length) return;\n const seen = new Set(fresh.map(item => item.assetId + ':' + item.versionId));\n state.history = [...fresh, ...state.history.filter(item => !seen.has(item.assetId + ':' + item.versionId))].slice(0, 24);\n StillMade.updateShared({ history: state.history }).catch(() => {});\n drawResults();\n}\n\nStillMade.onShared(shared => {\n if (shared && shared.state && Array.isArray(shared.state.history)) state.history = shared.state.history;\n const made = shared && shared.outputs && Array.isArray(shared.outputs.media) ? shared.outputs.media : [];\n if (made.length) remember(made, {});\n else drawResults();\n});\n\nfunction busy(on) {\n state.busy = on;\n $('generate').disabled = on;\n root.toggleAttribute('aria-busy', on);\n}\n\nasync function generate() {\n if (state.busy) return;\n const kind = mode(), [id, entry] = model(), prompt = $('prompt').value.trim();\n if (!prompt) { status(kind === 'video' ? 'Describe the shot first.' : 'Describe the picture first.', 'error'); $('prompt').focus(); return; }\n if (kind === 'video' && entry.requiresFirstFrame && !state.input.startFrame) { status(`${entry.label} needs a start frame.`, 'error'); return; }\n const count = Number(checked('count')) || 1;\n const values = { prompt, mode: kind, model: id, count };\n const shape = visibleChecked('shape'), size = visibleChecked('size');\n if (shape) values.aspectRatio = shape;\n if (size) values.size = size;\n if (kind === 'image') {\n if (entry.qualities.length) values.quality = checked('quality');\n if (entry.references && Array.isArray(state.input.references) && state.input.references.length) values.references = state.input.references.slice(0, entry.references);\n } else {\n const length = Number(visibleChecked('length'));\n if (length) values.duration = length;\n if (entry.nativeAudio) values.sound = $('sound').checked;\n if (state.input.startFrame) values.startFrame = state.input.startFrame;\n if (state.input.startFrame && state.input.endFrame && entry.lastFrame) values.endFrame = state.input.endFrame;\n }\n const avoid = $('avoid').value.trim();\n if (avoid) values.avoid = avoid;\n const seed = Number($('seed').value);\n if (entry.seed && $('seed').value !== '' && Number.isInteger(seed) && seed >= 0) values.seed = seed;\n busy(true);\n status(count > 1 ? `Generating ${count} ${kind}s…` : `Generating ${kind === 'video' ? 'the video' : 'the picture'}…`, 'loading');\n try {\n const outputs = await StillMade.run(values);\n const made = outputs && Array.isArray(outputs.media) ? outputs.media : [];\n remember(made, { prompt, model: entry.label });\n status(made.length ? `${made.length} made` : '', made.length ? 'success' : undefined);\n } catch (error) { status(message(error), 'error'); }\n finally { busy(false); }\n}\n$('generate').addEventListener('click', generate);\nrefresh();\n\n})();\n"
|
|
336
|
+
},
|
|
337
|
+
"tests": [
|
|
338
|
+
{
|
|
339
|
+
"name": "Makes two pictures that keep your photo",
|
|
340
|
+
"input": {
|
|
341
|
+
"prompt": "The bottle on a wet rock at dawn",
|
|
342
|
+
"mode": "image",
|
|
343
|
+
"model": "nano-banana-pro",
|
|
344
|
+
"aspectRatio": "4:5",
|
|
345
|
+
"size": "2K",
|
|
346
|
+
"count": 2,
|
|
347
|
+
"references": [
|
|
348
|
+
{
|
|
349
|
+
"kind": "image",
|
|
350
|
+
"assetId": "photo-asset",
|
|
351
|
+
"versionId": "photo-v1",
|
|
352
|
+
"url": "https://media.example/photo.png",
|
|
353
|
+
"mimeType": "image/png",
|
|
354
|
+
"width": 1024,
|
|
355
|
+
"height": 1024,
|
|
356
|
+
"bytes": 200000
|
|
357
|
+
}
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
"mocks": {
|
|
361
|
+
"actions": {
|
|
362
|
+
"media.generate": {
|
|
363
|
+
"value": [
|
|
364
|
+
{
|
|
365
|
+
"key": "image-1",
|
|
366
|
+
"kind": "image",
|
|
367
|
+
"assetId": "made-image-1",
|
|
368
|
+
"versionId": "made-image-1-v1",
|
|
369
|
+
"url": "https://media.example/made-image-1.png"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"key": "image-2",
|
|
373
|
+
"kind": "image",
|
|
374
|
+
"assetId": "made-image-2",
|
|
375
|
+
"versionId": "made-image-2-v1",
|
|
376
|
+
"url": "https://media.example/made-image-2.png"
|
|
377
|
+
}
|
|
378
|
+
],
|
|
379
|
+
"failures": []
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
"expected": {
|
|
384
|
+
"media": [
|
|
385
|
+
{
|
|
386
|
+
"kind": "image",
|
|
387
|
+
"assetId": "made-image-1",
|
|
388
|
+
"versionId": "made-image-1-v1",
|
|
389
|
+
"url": "https://media.example/made-image-1.png"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"kind": "image",
|
|
393
|
+
"assetId": "made-image-2",
|
|
394
|
+
"versionId": "made-image-2-v1",
|
|
395
|
+
"url": "https://media.example/made-image-2.png"
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"name": "Makes a vertical clip from a start and end frame with its own sound",
|
|
402
|
+
"input": {
|
|
403
|
+
"prompt": "Slow push in as the door opens",
|
|
404
|
+
"mode": "video",
|
|
405
|
+
"model": "seedance-2.0",
|
|
406
|
+
"aspectRatio": "9:16",
|
|
407
|
+
"size": "720p",
|
|
408
|
+
"duration": 6,
|
|
409
|
+
"sound": true,
|
|
410
|
+
"startFrame": {
|
|
411
|
+
"kind": "image",
|
|
412
|
+
"assetId": "photo-asset",
|
|
413
|
+
"versionId": "photo-v1",
|
|
414
|
+
"url": "https://media.example/photo.png",
|
|
415
|
+
"mimeType": "image/png",
|
|
416
|
+
"width": 1024,
|
|
417
|
+
"height": 1024,
|
|
418
|
+
"bytes": 200000
|
|
419
|
+
},
|
|
420
|
+
"endFrame": {
|
|
421
|
+
"kind": "image",
|
|
422
|
+
"assetId": "frame-asset",
|
|
423
|
+
"versionId": "frame-v1",
|
|
424
|
+
"url": "https://media.example/frame.png",
|
|
425
|
+
"mimeType": "image/png",
|
|
426
|
+
"width": 1024,
|
|
427
|
+
"height": 1024,
|
|
428
|
+
"bytes": 200000
|
|
429
|
+
},
|
|
430
|
+
"avoid": "blur",
|
|
431
|
+
"seed": 7
|
|
432
|
+
},
|
|
433
|
+
"mocks": {
|
|
434
|
+
"actions": {
|
|
435
|
+
"media.generate": {
|
|
436
|
+
"value": [
|
|
437
|
+
{
|
|
438
|
+
"key": "video-1",
|
|
439
|
+
"kind": "video",
|
|
440
|
+
"assetId": "made-video-1",
|
|
441
|
+
"versionId": "made-video-1-v1",
|
|
442
|
+
"url": "https://media.example/made-video-1.mp4",
|
|
443
|
+
"duration": 6
|
|
444
|
+
}
|
|
445
|
+
],
|
|
446
|
+
"failures": []
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
"expected": {
|
|
451
|
+
"media": [
|
|
452
|
+
{
|
|
453
|
+
"kind": "video",
|
|
454
|
+
"assetId": "made-video-1",
|
|
455
|
+
"versionId": "made-video-1-v1",
|
|
456
|
+
"url": "https://media.example/made-video-1.mp4",
|
|
457
|
+
"duration": 6
|
|
458
|
+
}
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
]
|
|
463
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Add speech to the Editor
|
|
2
|
+
|
|
3
|
+
Use **Audio from Blocks** to place an accepted speech recording on the Editor's
|
|
4
|
+
audio lane. This action is available in the desktop and mobile Editor. It uses
|
|
5
|
+
the recording already generated by a project Step; adding it does not generate
|
|
6
|
+
speech again.
|
|
7
|
+
|
|
8
|
+
1. Run the speech Block in a saved project. Review its text, voice, speed, and
|
|
9
|
+
cost, confirm generation, listen, and choose **Use this audio**.
|
|
10
|
+
2. In the Editor, open **Media** and find **Audio from Blocks**. This section
|
|
11
|
+
appears in the desktop Media Bin and the mobile Media sheet. Listen to the
|
|
12
|
+
accepted result you want to use.
|
|
13
|
+
3. Move the playhead to the desired start time and choose **Add at playhead**.
|
|
14
|
+
The Editor adds an audio item with the recording's measured duration.
|
|
15
|
+
|
|
16
|
+
Adding an item requires project edit access. The list offers current, accepted
|
|
17
|
+
project speech results. Import-review fixtures, sample previews, unfinished
|
|
18
|
+
runs, and outdated results are not sources for new items. If the Block's input
|
|
19
|
+
or result changes, complete and accept its current project run before adding
|
|
20
|
+
that result.
|
|
21
|
+
|
|
22
|
+
An inserted item retains the recording you chose. Rerunning or removing its
|
|
23
|
+
source Block does not remove the item or replace its audio. To use a later
|
|
24
|
+
recording, accept that result and add it explicitly; existing timeline items
|
|
25
|
+
remain available for normal editing.
|
|
26
|
+
|
|
27
|
+
This action adds Editor audio. It leaves the native Voiceover master recording,
|
|
28
|
+
takes, transcript, word timings, and shared narration unchanged. Voiceover's
|
|
29
|
+
primary typed input remains `script`. The `audio.speech` package contract is
|
|
30
|
+
unchanged: declare one `audio` output, and let the host handle playback and
|
|
31
|
+
explicit insertion. Package code and custom interfaces do not receive a new
|
|
32
|
+
timeline-writing API.
|
|
33
|
+
|
|
34
|
+
The [speech guide](hosted-speech.md) describes source validation and the
|
|
35
|
+
required provider review. Playback and insertion checks do not establish that
|
|
36
|
+
a live provider speech test has passed.
|