stillmade-sdk 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.stillmade/sdk-baseline.json +1 -1
- package/AGENTS.md +8 -2
- package/DESKTOP_CAPABILITIES.md +1 -1
- package/INTERFACE_DESIGN.md +167 -83
- package/MODULES.md +1 -1
- package/NATIVE_MCP.md +1 -1
- package/PASTE_TO_LLM.md +151 -12
- package/README.md +5 -2
- package/SDK.md +510 -115
- package/UI_AESTHETIC.md +64 -0
- package/blocks/native/sm.export-video/versions/2.1.0/package.js +580 -0
- package/blocks/native/sm.panel-strip/versions/2.4.0/package.js +1176 -0
- package/blocks/native/sm.panel-strip/versions/2.4.1/package.js +1316 -0
- package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +800 -0
- package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +920 -0
- package/examples/README.md +10 -0
- package/examples/custom-interface.stillmade.json +3 -3
- package/package.json +7 -1
- package/packages/block-cli/dev-host/assets/host.js +219 -26
- 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 +53 -22
- package/packages/block-platform/block-readiness.js +5 -0
- package/packages/block-platform/brief-ports.js +29 -0
- package/packages/block-platform/catalog.js +32 -9
- package/packages/block-platform/current-semantic-source.js +7 -3
- package/packages/block-platform/flow-inspection.js +14 -4
- package/packages/block-platform/plain-descriptions.js +1 -1
- package/packages/block-platform/project-context.js +7 -0
- package/packages/block-platform/project-input-samples.js +3 -1
- package/packages/block-platform/project-inputs.js +11 -2
- package/packages/block-platform/project-start-input.js +46 -0
- package/packages/block-platform/segment-runner.js +4 -2
- package/packages/block-platform/step-groups.js +4 -0
- package/packages/block-platform/steps.js +55 -4
- package/packages/block-platform/type-preview.js +19 -3
- package/packages/block-platform/workspace-input-readset.js +6 -1
- package/packages/block-sdk/builder-contract.js +11 -5
- package/packages/block-sdk/collaboration-admission.js +4 -2
- package/packages/block-sdk/connection-adapters.js +35 -2
- package/packages/block-sdk/contracts.js +5 -2
- package/packages/block-sdk/examples.js +3 -3
- package/packages/block-sdk/first-party-identities.js +190 -0
- package/packages/block-sdk/first-party-packages.js +6 -1
- package/packages/block-sdk/index.d.ts +2 -0
- package/packages/block-sdk/index.js +1 -1
- package/packages/block-sdk/javascript.js +5 -1
- package/packages/block-sdk/platform-checks.js +13 -2
- package/packages/block-sdk/shared-agent-controls.js +5 -4
- package/packages/block-sdk/shared-controls.js +13 -9
- package/packages/block-sdk/view-design.js +104 -27
- package/packages/block-sdk/view.js +4 -2
- package/packages/design-system/block-kit.css +184 -0
- package/packages/design-system/block-kit.html +187 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
@font-face{font-family:Hanken Grotesk;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Variable.ttf)format("truetype")}@font-face{font-family:Hanken Grotesk;font-style:italic;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Italic-Variable.ttf)format("truetype")}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/JetBrainsMono-Variable.ttf)format("truetype")}@font-face{font-family:JetBrains Mono;font-style:italic;font-weight:100 900;font-display:swap;src:url(/fonts/JetBrainsMono-Italic-Variable.ttf)format("truetype")}:root{--cream-50:#fdfaf1;--cream-100:#f7f0db;--cream-200:#ece3c9;--cream-300:#dcd2b5;--cream-600:#4e483a;--ink-50:#f2f0ec;--ink-100:#c9c6be;--ink-300:#4a4843;--ink-400:#26241f;--ink-500:#16140f;--ink-600:#0a0a0a;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--font-display:"Balgin", "Hanken Grotesk", ui-sans-serif, sans-serif;--font-body:"Hanken Grotesk", ui-sans-serif, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, monospace;--font:var(--font-body);--fs-display-xl:clamp(72px, 14vw, 220px);--fs-display-l:clamp(56px, 9vw, 144px);--fs-display-m:clamp(40px, 6vw, 96px);--fs-h4:1.25rem;--fs-body:1rem;--fs-body-s:.875rem;--fs-caption:.8125rem;--fs-micro:.75rem;--tr-display:-.02em;--tr-body:-.005em;--tr-micro:.08em;--lh-display:.92;--lh-tight:1.05;--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;--s-7:48px;--s-8:64px;--s-9:96px;--s-10:128px;--s-11:192px;--page-gutter:clamp(20px, 3vw, 48px);--page-gutter-compact:clamp(16px, 5vw, 24px);--page-section-gap:clamp(32px, 5vw, 64px);--content-readable:720px;--content-wide:1440px;--control-height:40px;--touch-target:44px;--mobile-dock-height:66px;--z-nav:30;--hover:var(--hover-wash);--r-0:0;--r-1:2px;--r-2:4px;--r-3:8px;--r-pill:999px;--radius-sm:var(--r-2);--radius:var(--r-3);--radius-lg:var(--r-3);--bw-hair:.5px;--bw-1:1px;--bw-2:2px;--ease-out:cubic-bezier(.22, 1, .36, 1);--ease-in-out:cubic-bezier(.65, 0, .35, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--dur-instant:80ms;--dur-fast:.16s;--dur-base:.24s;--dur-slow:.48s;--dur-reveal:.72s}:root,[data-mode=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#101010;--bg-elev-1:#232221;--bg-elev-2:#33312f;--bg-inset:#171716;--fg:#f3eee5;--fg-muted:#bbb2a7;--fg-subtle:#f3eee594;--fg-on-accent:#191716;--border-tok:#58534d;--border-strong:#8b7e6f;--divider:#393735;--signal:#d0ae77;--signal-dim:#d0ae7726;--signal-glow:#d0ae774d;--hover-wash:#f7f0db0f;--press-wash:#f7f0db1f;--selection-bg:var(--cream-100);--selection-fg:var(--ink-600);--shadow-1:0 1px 0 #f7f0db0a inset, 0 4px 12px #00000080;--shadow-2:0 1px 0 #f7f0db0f inset, 0 16px 48px #0009;--scrim-top:linear-gradient(to bottom, #0a0a0ad9, #0a0a0a00);--scrim-bottom:linear-gradient(to top, #0a0a0ad9, #0a0a0a00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#403d3a;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--cream-50);--accent-dim:var(--hover-wash);--accent-glow:#f7f0db38;--accent-contrast:var(--bg);--editor-standout-bg:var(--fg);--editor-standout-fg:var(--bg);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--bg:var(--cream-100);--bg-elev-1:var(--cream-50);--bg-elev-2:var(--cream-200);--bg-inset:var(--cream-200);--fg:var(--ink-600);--fg-muted:var(--cream-600);--fg-subtle:#0a0a0a61;--fg-on-accent:var(--cream-100);--border-tok:#0a0a0a1a;--border-strong:#0a0a0a38;--divider:#0a0a0a0f;--hover-wash:#0a0a0a0d;--press-wash:#0a0a0a1a;--selection-bg:var(--ink-600);--selection-fg:var(--cream-100);--shadow-1:0 1px 0 #ffffff80 inset, 0 4px 12px #0a0a0a0f;--shadow-2:0 1px 0 #fff9 inset, 0 16px 48px #0a0a0a1a;--scrim-top:linear-gradient(to bottom, #f7f0dbe6, #f7f0db00);--scrim-bottom:linear-gradient(to top, #f7f0dbe6, #f7f0db00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-2);--bg-hover:var(--cream-300);--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--ink-500);--accent-dim:var(--hover-wash);--accent-glow:#0a0a0a38;--accent-contrast:var(--bg);--editor-standout-bg:var(--ink-300);--editor-standout-fg:var(--cream-100);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=white]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#e9e8e6;--bg-elev-1:#fff;--bg-elev-2:#ebe9e6;--bg-inset:#f7f6f4;--fg:#1a1917;--fg-muted:#615c56;--fg-subtle:#77716b;--fg-on-accent:#fff;--border-tok:#bcb8b2;--border-strong:#978b7d;--divider:#cdc8c2;--hover-wash:#00000014;--press-wash:#00000021;--selection-bg:#1a1917;--selection-fg:#fff;--signal:#8e6b46;--signal-dim:#8e6b461a;--signal-glow:#8e6b4638;--shadow-1:0 1px 3px #0000000a;--shadow-2:0 12px 40px #0000001a;--scrim-top:linear-gradient(to bottom,#e9e8e6e6,#e9e8e600);--scrim-bottom:linear-gradient(to top,#e9e8e6e6,#e9e8e600);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#e0ddd8;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:#33302c;--accent-dim:var(--hover-wash);--accent-glow:#0000001f;--accent-contrast:var(--bg);--editor-standout-bg:#24211d;--editor-standout-fg:#fff;--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}button[data-icon-action=true]{cursor:pointer;flex-shrink:0;line-height:1;min-width:36px!important;min-height:36px!important;box-shadow:none!important;color:var(--text-secondary,var(--fg))!important;background:0 0!important;border:0!important;border-radius:0!important;justify-content:center!important;align-items:center!important;padding:8px!important;display:inline-flex!important}button[data-icon-action=true]:hover:not(:disabled){color:var(--text-primary,var(--fg))!important}button[data-icon-action=true]:focus-visible{outline-offset:2px;outline:2px solid var(--signal)!important}button[data-icon-action=true]:disabled{cursor:not-allowed;opacity:.4!important}button[data-icon-action=true][aria-pressed=true]{text-underline-offset:5px;text-decoration:underline;color:var(--text-primary,var(--fg))!important}@media (pointer:coarse){button[data-icon-action=true]{min-width:44px!important;min-height:44px!important}}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root[data-anim-theme="1"]:not([data-mode=white]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#0a0d13;--bg-elev-1:#0f141c;--bg-elev-2:#1a2230;--bg-inset:#0f141c;--fg:#d8dee8;--fg-muted:#8b98ac;--fg-subtle:#64748b;--fg-on-accent:#0a0d13;--border-tok:#232d3d;--border-strong:#303d50;--divider:#1a2230;--hover-wash:#8b98ac14;--press-wash:#8b98ac29;--selection-bg:#303d50;--selection-fg:#d8dee8;--bg-primary:#0a0d13;--bg-secondary:#0f141c;--bg-tertiary:#1a2230;--bg-elevated:#1a2230;--bg-hover:#232d3d;--border:#232d3d;--border-light:#303d50;--accent:#8b98ac;--accent-hover:#b6c0ce;--accent-dim:#8b98ac1f;--accent-glow:#8b98ac40;--accent-contrast:#0a0d13;--editor-standout-bg:#d8dee8;--editor-standout-fg:#0a0d13;--text-primary:#d8dee8;--text-secondary:#8b98ac;--text-muted:#64748b;--success:#8b98ac;--warning:#b6c0ce;--danger:#b6c0ce}html,body{background:var(--bg);width:100%;height:100%;color:var(--fg);font-family:var(--font-body);letter-spacing:var(--tr-body);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:14px;line-height:1.6;overflow:hidden}#root{width:100%;height:100%;overflow:hidden}.sm-app-shell{height:100dvh}@keyframes sm-navmenu-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}.mobile-nav-menu{animation:.2s cubic-bezier(.22,1,.36,1) sm-navmenu-in}::selection{background:var(--selection-bg);color:var(--selection-fg)}.display,h1,h2{font-family:var(--font-display);font-style:italic;font-weight:200;line-height:var(--lh-tight);letter-spacing:var(--tr-display);margin:0}.display{line-height:var(--lh-display)}h3,h4{font-family:var(--font-body);font-weight:500;line-height:var(--lh-tight);letter-spacing:-.01em;margin:0}.eyebrow{text-transform:uppercase;letter-spacing:var(--tr-micro);color:var(--fg-muted);font-size:11px;font-weight:500}code,pre,.mono{font-family:var(--font-mono);font-size:.92em}button{font-family:var(--font-body);cursor:pointer;letter-spacing:var(--tr-body);border:none;outline:none}textarea,input,select{font-family:var(--font-body);outline:none}select{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2)}select option,select optgroup{background-color:var(--bg-elevated);color:var(--text-primary)}select option:checked{font-weight:600}.tsel-trigger{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2);font-family:var(--font-body);font-size:inherit;cursor:pointer;text-align:left;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;display:inline-flex}.tsel-trigger:hover{border-color:var(--border-strong)}.tsel-trigger:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.tsel-trigger:disabled{opacity:.5;cursor:default}.tsel-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.tsel-caret{opacity:.7;flex:none;font-size:10px}.tsel-pop{z-index:100100;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--r-2);max-height:260px;padding:4px;overflow-y:auto;box-shadow:0 8px 28px #00000047}.tsel-opt{color:var(--text-primary);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:6px 8px;font-size:13px;display:flex}.tsel-opt[data-active=true]{background:var(--bg-elev-1)}.tsel-opt[aria-selected=true]{font-weight:600}.tsel-opt[aria-disabled=true]{color:var(--text-muted);cursor:default}.tsel-opt[aria-disabled=true][data-active=true]{background:0 0}.tsel-check{opacity:.8;flex:none;width:12px;font-size:11px}.tsel-opt-label{text-overflow:ellipsis;flex:auto;overflow:hidden}.tsel-note{cursor:help;width:16px;height:16px;color:var(--text-muted);border:1px solid var(--border);border-radius:999px;flex:none;justify-content:center;align-items:center;font-size:11px;display:inline-flex;position:relative}.tsel-note:hover{color:var(--text-primary);border-color:var(--border-strong)}.tsel-tip{z-index:100110;background:var(--text-primary);color:var(--bg-elevated);box-sizing:border-box;white-space:normal;pointer-events:none;border-radius:5px;max-width:min(260px,100vw - 16px);padding:4px 8px;font-size:11px;font-weight:600;box-shadow:0 2px 10px #0000004d}@media (width<=767.98px),(height<=480px) and (pointer:coarse){.tsel-opt{box-sizing:border-box;min-height:44px;font-size:14px}.tsel-note{flex:0 0 44px;width:44px;height:44px;font-size:14px}}.video-editor input,.video-editor select,.video-editor textarea{background:var(--bg-inset);width:100%;min-width:0;color:var(--fg);border:var(--bw-1) solid var(--border-strong);border-radius:var(--r-2);font-family:var(--font-body);accent-color:var(--signal);padding:7px 8px;font-size:12px}.video-editor,.mobile-editor-root{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.video-editor input,.video-editor select,.video-editor textarea,.video-editor [contenteditable=true],.mobile-editor-root input,.mobile-editor-root select,.mobile-editor-root textarea,.mobile-editor-root [contenteditable=true]{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}.video-editor input[type=range]{background:0 0;border:0;padding:0}.video-editor input[type=checkbox]{width:auto;min-width:auto}.video-editor select:focus,.video-editor input:focus,.video-editor textarea:focus{border-color:var(--signal);box-shadow:0 0 0 1px var(--signal-dim)}:focus-visible{outline:2px solid var(--signal);outline-offset:2px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--r-pill)}::-webkit-scrollbar-thumb:hover{background:var(--fg-subtle)}.sm-hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.sm-hide-scrollbar::-webkit-scrollbar{width:0;height:0;display:none}.btn{align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-4);border-radius:var(--r-pill);letter-spacing:var(--tr-body);transition:transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);font-size:13px;font-weight:500;display:inline-flex}.btn:active{transform:scale(.98)}.btn-primary{background:var(--fg);color:var(--bg)}.btn-primary:hover{background:var(--fg);opacity:.86}.btn-primary:disabled{opacity:.4;cursor:not-allowed}.btn-secondary{color:var(--fg);border:var(--bw-1) solid var(--border-strong);background:0 0}.btn-secondary:hover{background:var(--hover-wash);border-color:var(--fg)}.btn-ghost{color:var(--fg-muted);background:0 0;padding:6px 10px}.btn-ghost:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon{color:var(--fg-muted);border-radius:var(--r-2);transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);background:0 0;justify-content:center;align-items:center;padding:5px;display:inline-flex}.btn-icon:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon:active{transform:scale(.98)}.btn-icon:disabled{opacity:.4;cursor:not-allowed}.typing-indicator{align-items:center;gap:4px;padding:4px 0;display:flex}.typing-indicator span{border-radius:var(--r-pill);background:var(--fg-subtle);width:6px;height:6px;animation:1.2s infinite bounce}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes bounce{0%,60%,to{opacity:.4;transform:translateY(0)}30%{opacity:1;transform:translateY(-5px)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}@keyframes toastIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}button:focus-visible,[role=button]:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}@keyframes skeletonShimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#ffffff08 0%,#ffffff1a 50%,#ffffff08 100%) 0 0/200% 100%;border-radius:6px;animation:1.4s ease-in-out infinite skeletonShimmer}.node-state-empty{border-style:dashed!important}.node-state-generating{box-shadow:0 0 0 2px var(--accent);position:relative}.node-state-generating:after{content:"";border-radius:inherit;pointer-events:none;will-change:opacity;animation:1.6s ease-in-out infinite nodePulse;position:absolute;inset:-2px;box-shadow:0 0 18px #f7c8328c}@keyframes nodePulse{0%,to{opacity:.36}50%{opacity:1}}.node-state-failed{border-top:4px solid #d04848!important}.node-state-failed:before{content:"⚠ ";color:#d04848;z-index:2;pointer-events:none;font-size:12px;font-weight:800;position:absolute;top:6px;left:8px}.react-flow__node{cursor:default}.react-flow__node.dragging,.react-flow__pane.dragging .react-flow__viewport{will-change:transform}.react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){box-shadow:1px 1px 0 var(--node-side,#d8cfb3), 2px 2px 0 var(--node-side,#d8cfb3), 3px 3px 0 var(--node-side,#d8cfb3), 4px 4px 0 var(--node-side,#d8cfb3), 5px 5px 0 var(--node-side,#d8cfb3), 6px 6px 0 var(--node-side,#d8cfb3), 7px 7px 0 var(--node-edge,#0a0a0a2e), 14px 16px 18px -4px #0a0a0a2e;border-radius:10px}[data-mode=dark] .react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){--node-side:#1f1d18;--node-edge:#f7f0db2e}.react-flow__controls{grid-template-columns:repeat(2,1fr)!important;display:grid!important}.react-flow__controls button{background:var(--bg-elev-2)!important;border-color:var(--border-tok)!important;color:var(--fg-muted)!important;fill:var(--fg-muted)!important}.react-flow__controls button:hover{background:var(--ink-300)!important}.react-flow__edge-path{stroke:var(--border-strong)}.react-flow.rf-edges-panning .react-flow__edges{display:none}.react-flow__node.node-controls-locked input,.react-flow__node.node-controls-locked textarea,.react-flow__node.node-controls-locked select,.react-flow__node.node-controls-locked button,.react-flow__node.node-controls-locked [role=button]{pointer-events:none!important}.react-flow__node.node-controls-locked{cursor:grab}.react-flow__node.node-controls-locked:active{cursor:grabbing}.react-flow__node.scene-add-available-node{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.scene-add-available-node>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.scene-add-available-node:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}.react-flow__node.node-replace-available{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.node-replace-available>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.node-replace-available:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}select option{background:var(--bg-elev-2);color:var(--fg)}.spinner{border:2px solid var(--border-tok);border-top-color:var(--fg);border-radius:var(--r-pill);width:16px;height:16px;animation:.7s linear infinite spin}.media-upload-preview:hover .media-upload-overlay{opacity:1!important}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:1ms!important;animation-duration:1ms!important}}.assets-card .assets-card-overlay{opacity:0;transition:opacity .15s}.assets-card:hover .assets-card-overlay{opacity:1}.yt-video-row{transition:background .12s,box-shadow .12s,color .12s}.yt-video-row:hover{box-shadow:inset 2px 0 0 var(--accent), inset 0 0 14px var(--accent-glow);color:var(--text-primary);background:var(--hover-wash)!important}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble:hover .chat-bubble-copy,.chat-bubble:focus-within .chat-bubble-copy,.chat-bubble-copy:hover,.chat-bubble-copy:focus{opacity:1!important}.chat-bubble-copy:hover{background:var(--bg-elevated-2,var(--bg-elevated));color:var(--text-primary)!important}.chat-bubble-copy:active{transform:scale(.94)}.sm-brand .sm-brandmark{transform-origin:50%;transition:transform .76s cubic-bezier(.2,.8,.2,1),filter .28s}.sm-brand:hover .sm-brandmark{filter:drop-shadow(0 0 7px color-mix(in srgb, currentColor 45%, transparent));will-change:transform, filter;transform:perspective(340px)rotateY(360deg)scale(1.06)}.sm-brand .sm-word{transition:transform .3s cubic-bezier(.2,.8,.2,1);transform:translate(-4px)}.sm-brand:hover .sm-word{will-change:transform;transform:translate(-4px,-1px)}@media (prefers-reduced-motion:reduce){.sm-brand .sm-brandmark{transition:transform .24s,filter .24s}.sm-brand:hover .sm-brandmark{transform:scale(1.06)}.sm-brand:hover .sm-word{transform:translate(-4px)}}body.sm-mobile-ui input,body.sm-mobile-ui textarea,body.sm-mobile-ui select{font-size:16px!important}body.sm-mobile-ui button,body.sm-mobile-ui select,body.sm-mobile-ui [role=button]{min-height:44px}body.sm-mobile-ui select:not([multiple]):not([size]){height:max(44px,2.75em)}body.sm-mobile-ui button.btn-icon,body.sm-mobile-ui button[data-compact],body.sm-mobile-ui .no-min-tap{min-height:0}body.sm-mobile-ui .yt-video-row{min-width:560px}html,body{background:var(--bg);color:var(--fg);margin:0}.dh-page{min-height:100dvh;font-family:var(--font-body);flex-direction:column;display:flex}.dh-header{justify-content:space-between;align-items:flex-end;gap:var(--s-4,16px);padding:var(--s-5,20px) var(--s-6,24px) var(--s-4,16px);border-bottom:var(--bw-1,1px) solid var(--border-tok);flex-wrap:wrap;display:flex}.dh-title{gap:var(--s-1,4px);min-width:0;display:grid}.dh-wordmark{letter-spacing:.08em;text-transform:uppercase;color:var(--fg-muted);font-size:12px}.dh-title h1{font-family:var(--font-display);letter-spacing:var(--tr-display);overflow-wrap:anywhere;margin:0;font-size:30px;font-style:italic;font-weight:200;line-height:1.1}.dh-meta{font-family:var(--font-mono);color:var(--fg-muted);overflow-wrap:anywhere;font-size:12px}.dh-controls{align-items:flex-end;gap:var(--s-3,12px);flex-wrap:wrap;display:flex}.dh-controls label{gap:var(--s-1,4px);color:var(--fg-muted);font-size:12px;display:grid}.dh-controls select{min-height:36px;padding:0 var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font:inherit;font-size:13px}.dh-controls select:focus-visible,.dh-input textarea:focus-visible,.dh-page .btn:focus-visible{outline:2px solid var(--signal);outline-offset:2px}.dh-problem{margin:var(--s-4,16px) var(--s-6,24px) 0;padding:var(--s-3,12px) var(--s-4,16px);border:var(--bw-1,1px) solid var(--border-strong);border-radius:var(--r-2);background:var(--bg-elev-1);white-space:pre-wrap;overflow-wrap:anywhere}.dh-main{gap:var(--s-6,24px);padding:var(--s-6,24px);flex:1;grid-template-columns:minmax(0,1fr) minmax(280px,380px);display:grid}.dh-stage{min-width:0}.dh-frame{min-width:0;margin:0 auto}.dh-noview{gap:var(--s-4,16px);padding:var(--s-6,24px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-3);justify-items:start;display:grid}.dh-noview p{color:var(--fg-muted);margin:0}.dh-panel{gap:var(--s-5,20px);flex-direction:column;min-width:0;display:flex}.dh-section-head{justify-content:space-between;align-items:center;gap:var(--s-2,8px);margin-bottom:var(--s-2,8px);display:flex}.dh-section-head h2{letter-spacing:var(--tr-body);margin:0;font-size:13px;font-weight:600}.dh-input textarea{box-sizing:border-box;width:100%;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font-family:var(--font-mono);resize:vertical;font-size:12px;line-height:1.5}.dh-input p,.dh-muted{margin:var(--s-2,8px) 0 0;color:var(--fg-muted);font-size:13px}.dh-json{max-height:240px;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.5;overflow:auto}.dh-console{flex-direction:column;min-height:0;display:flex}.dh-console ol{border-top:var(--bw-1,1px) solid var(--divider);max-height:360px;margin:0;padding:0;list-style:none;overflow:auto}.dh-entry{gap:var(--s-2,8px);padding:var(--s-2,8px) 0;border-bottom:var(--bw-1,1px) solid var(--divider);grid-template-columns:auto auto minmax(0,1fr);align-items:baseline;font-size:12px;display:grid}.dh-entry time{color:var(--fg-muted);font-variant-numeric:tabular-nums}.dh-kind{color:var(--fg-muted);text-transform:uppercase;letter-spacing:.06em;font-size:10px}.dh-entry pre{font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.dh-error .dh-kind,.dh-error pre{color:var(--fg);font-weight:600}.dh-stand-in .dh-kind{color:var(--signal)}@media (width<=900px){.dh-main{padding:var(--s-4,16px);grid-template-columns:minmax(0,1fr)}.dh-header{padding:var(--s-4,16px)}}
|
|
1
|
+
@font-face{font-family:Hanken Grotesk;font-style:normal;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Variable.ttf)format("truetype")}@font-face{font-family:Hanken Grotesk;font-style:italic;font-weight:100 900;font-display:swap;src:url(/fonts/HankenGrotesk-Italic-Variable.ttf)format("truetype")}:root{--cream-50:#fdfaf1;--cream-100:#f7f0db;--cream-200:#ece3c9;--cream-300:#dcd2b5;--cream-600:#4e483a;--ink-50:#f2f0ec;--ink-100:#c9c6be;--ink-300:#4a4843;--ink-400:#26241f;--ink-500:#16140f;--ink-600:#0a0a0a;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--font-display:"Balgin", "Hanken Grotesk", ui-sans-serif, sans-serif;--font-body:"Hanken Grotesk", ui-sans-serif, sans-serif;--font-mono:ui-monospace, "SF Mono", Menlo, Consolas, monospace;--font:var(--font-body);--fs-display-xl:clamp(72px, 14vw, 220px);--fs-display-l:clamp(56px, 9vw, 144px);--fs-display-m:clamp(40px, 6vw, 96px);--fs-h4:1.25rem;--fs-body:1rem;--fs-body-s:.875rem;--fs-caption:.8125rem;--fs-micro:.75rem;--tr-display:-.02em;--tr-body:-.005em;--tr-micro:.08em;--lh-display:.92;--lh-tight:1.05;--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-5:24px;--s-6:32px;--s-7:48px;--s-8:64px;--s-9:96px;--s-10:128px;--s-11:192px;--page-gutter:clamp(20px, 3vw, 48px);--page-gutter-compact:clamp(16px, 5vw, 24px);--page-section-gap:clamp(32px, 5vw, 64px);--content-readable:720px;--content-wide:1440px;--control-height:40px;--touch-target:44px;--mobile-dock-height:66px;--z-nav:30;--hover:var(--hover-wash);--r-0:0;--r-1:2px;--r-2:4px;--r-3:8px;--r-pill:999px;--radius-sm:var(--r-2);--radius:var(--r-3);--radius-lg:var(--r-3);--bw-hair:.5px;--bw-1:1px;--bw-2:2px;--ease-out:cubic-bezier(.22, 1, .36, 1);--ease-in-out:cubic-bezier(.65, 0, .35, 1);--ease-spring:cubic-bezier(.34, 1.56, .64, 1);--dur-instant:80ms;--dur-fast:.16s;--dur-base:.24s;--dur-slow:.48s;--dur-reveal:.72s}:root,[data-mode=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#101010;--bg-elev-1:#232221;--bg-elev-2:#33312f;--bg-inset:#171716;--fg:#f3eee5;--fg-muted:#bbb2a7;--fg-subtle:#f3eee594;--fg-on-accent:#191716;--border-tok:#58534d;--border-strong:#8b7e6f;--divider:#393735;--signal:#d0ae77;--signal-dim:#d0ae7726;--signal-glow:#d0ae774d;--hover-wash:#f7f0db0f;--press-wash:#f7f0db1f;--selection-bg:var(--cream-100);--selection-fg:var(--ink-600);--shadow-1:0 1px 0 #f7f0db0a inset, 0 4px 12px #00000080;--shadow-2:0 1px 0 #f7f0db0f inset, 0 16px 48px #0009;--scrim-top:linear-gradient(to bottom, #0a0a0ad9, #0a0a0a00);--scrim-bottom:linear-gradient(to top, #0a0a0ad9, #0a0a0a00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#403d3a;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--cream-50);--accent-dim:var(--hover-wash);--accent-glow:#f7f0db38;--accent-contrast:var(--bg);--editor-standout-bg:var(--fg);--editor-standout-fg:var(--bg);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#e0b24f;--attention-error:#e26a5f;--attention-ok:#6bc38e;--attention-info:#7da7e8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--signal:#e8b23a;--signal-dim:#e8b23a26;--signal-glow:#e8b23a4d;--bg:var(--cream-100);--bg-elev-1:var(--cream-50);--bg-elev-2:var(--cream-200);--bg-inset:var(--cream-200);--fg:var(--ink-600);--fg-muted:var(--cream-600);--fg-subtle:#0a0a0a61;--fg-on-accent:var(--cream-100);--border-tok:#0a0a0a1a;--border-strong:#0a0a0a38;--divider:#0a0a0a0f;--hover-wash:#0a0a0a0d;--press-wash:#0a0a0a1a;--selection-bg:var(--ink-600);--selection-fg:var(--cream-100);--shadow-1:0 1px 0 #ffffff80 inset, 0 4px 12px #0a0a0a0f;--shadow-2:0 1px 0 #fff9 inset, 0 16px 48px #0a0a0a1a;--scrim-top:linear-gradient(to bottom, #f7f0dbe6, #f7f0db00);--scrim-bottom:linear-gradient(to top, #f7f0dbe6, #f7f0db00);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-2);--bg-hover:var(--cream-300);--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:var(--ink-500);--accent-dim:var(--hover-wash);--accent-glow:#0a0a0a38;--accent-contrast:var(--bg);--editor-standout-bg:var(--ink-300);--editor-standout-fg:var(--cream-100);--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}[data-mode=white]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--bg:#e9e8e6;--bg-elev-1:#fff;--bg-elev-2:#ebe9e6;--bg-inset:#f7f6f4;--fg:#1a1917;--fg-muted:#615c56;--fg-subtle:#77716b;--fg-on-accent:#fff;--border-tok:#bcb8b2;--border-strong:#978b7d;--divider:#cdc8c2;--hover-wash:#00000014;--press-wash:#00000021;--selection-bg:#1a1917;--selection-fg:#fff;--signal:#8e6b46;--signal-dim:#8e6b461a;--signal-glow:#8e6b4638;--shadow-1:0 1px 3px #0000000a;--shadow-2:0 12px 40px #0000001a;--scrim-top:linear-gradient(to bottom,#e9e8e6e6,#e9e8e600);--scrim-bottom:linear-gradient(to top,#e9e8e6e6,#e9e8e600);--bg-primary:var(--bg);--bg-secondary:var(--bg-elev-1);--bg-tertiary:var(--bg-elev-2);--bg-elevated:var(--bg-elev-1);--bg-hover:#e0ddd8;--border:var(--border-tok);--border-light:var(--border-strong);--accent:var(--fg);--accent-hover:#33302c;--accent-dim:var(--hover-wash);--accent-glow:#0000001f;--accent-contrast:var(--bg);--editor-standout-bg:#24211d;--editor-standout-fg:#fff;--text-primary:var(--fg);--text-secondary:var(--fg-muted);--text-muted:var(--fg-subtle);--success:var(--fg);--warning:var(--signal);--danger:var(--signal);--attention-warning:#b98200;--attention-error:#c2453d;--attention-ok:#2e7d4f;--attention-info:#2f67b8;--shadow:var(--shadow-1);--shadow-lg:var(--shadow-2)}button[data-icon-action=true]{cursor:pointer;flex-shrink:0;line-height:1;min-width:36px!important;min-height:36px!important;box-shadow:none!important;color:var(--text-secondary,var(--fg))!important;background:0 0!important;border:0!important;border-radius:0!important;justify-content:center!important;align-items:center!important;padding:8px!important;display:inline-flex!important}button[data-icon-action=true]:hover:not(:disabled){color:var(--text-primary,var(--fg))!important}button[data-icon-action=true]:focus-visible{outline-offset:2px;outline:2px solid var(--signal)!important}button[data-icon-action=true]:disabled{cursor:not-allowed;opacity:.4!important}button[data-icon-action=true][aria-pressed=true]{text-underline-offset:5px;text-decoration:underline;color:var(--text-primary,var(--fg))!important}@media (pointer:coarse){button[data-icon-action=true]{min-width:44px!important;min-height:44px!important}}*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root[data-anim-theme="1"]:not([data-mode=white]){--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--bg:#0a0d13;--bg-elev-1:#0f141c;--bg-elev-2:#1a2230;--bg-inset:#0f141c;--fg:#d8dee8;--fg-muted:#8b98ac;--fg-subtle:#64748b;--fg-on-accent:#0a0d13;--border-tok:#232d3d;--border-strong:#303d50;--divider:#1a2230;--hover-wash:#8b98ac14;--press-wash:#8b98ac29;--selection-bg:#303d50;--selection-fg:#d8dee8;--bg-primary:#0a0d13;--bg-secondary:#0f141c;--bg-tertiary:#1a2230;--bg-elevated:#1a2230;--bg-hover:#232d3d;--border:#232d3d;--border-light:#303d50;--accent:#8b98ac;--accent-hover:#b6c0ce;--accent-dim:#8b98ac1f;--accent-glow:#8b98ac40;--accent-contrast:#0a0d13;--editor-standout-bg:#d8dee8;--editor-standout-fg:#0a0d13;--text-primary:#d8dee8;--text-secondary:#8b98ac;--text-muted:#64748b;--success:#8b98ac;--warning:#b6c0ce;--danger:#b6c0ce}html,body{background:var(--bg);width:100%;height:100%;color:var(--fg);font-family:var(--font-body);letter-spacing:var(--tr-body);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:14px;line-height:1.6;overflow:hidden}#root{width:100%;height:100%;overflow:hidden}.sm-app-shell{height:100dvh}@keyframes sm-navmenu-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}.mobile-nav-menu{animation:.2s cubic-bezier(.22,1,.36,1) sm-navmenu-in}::selection{background:var(--selection-bg);color:var(--selection-fg)}.display,h1,h2{font-family:var(--font-display);font-style:italic;font-weight:200;line-height:var(--lh-tight);letter-spacing:var(--tr-display);margin:0}.display{line-height:var(--lh-display)}h3,h4{font-family:var(--font-body);font-weight:500;line-height:var(--lh-tight);letter-spacing:-.01em;margin:0}.eyebrow{color:var(--fg-muted);font-size:11px;font-weight:500}code,pre,.mono{font-family:var(--font-mono);font-size:.92em}.listing-identity code{font-family:inherit;font-size:inherit}button{font-family:var(--font-body);cursor:pointer;letter-spacing:var(--tr-body);border:none;outline:none}textarea,input,select{font-family:var(--font-body);outline:none}select{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2)}select option,select optgroup{background-color:var(--bg-elevated);color:var(--text-primary)}select option:checked{font-weight:600}.tsel-trigger{background-color:var(--bg-elev-1);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--r-2);font-family:var(--font-body);font-size:inherit;cursor:pointer;text-align:left;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;display:inline-flex}.tsel-trigger:hover{border-color:var(--border-strong)}.tsel-trigger:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.tsel-trigger:disabled{opacity:.5;cursor:default}.tsel-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.tsel-caret{opacity:.7;flex:none;font-size:10px}.tsel-pop{z-index:100100;background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--r-2);max-height:260px;padding:4px;overflow-y:auto;box-shadow:0 8px 28px #00000047}.tsel-opt{color:var(--text-primary);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:6px 8px;font-size:13px;display:flex}.tsel-opt[data-active=true]{background:var(--bg-elev-1)}.tsel-opt[aria-selected=true]{font-weight:600}.tsel-opt[aria-disabled=true]{color:var(--text-muted);cursor:default}.tsel-opt[aria-disabled=true][data-active=true]{background:0 0}.tsel-check{opacity:.8;flex:none;width:12px;font-size:11px}.tsel-opt-label{text-overflow:ellipsis;flex:auto;overflow:hidden}.tsel-note{cursor:help;width:16px;height:16px;color:var(--text-muted);border:1px solid var(--border);border-radius:999px;flex:none;justify-content:center;align-items:center;font-size:11px;display:inline-flex;position:relative}.tsel-note:hover{color:var(--text-primary);border-color:var(--border-strong)}.tsel-tip{z-index:100110;background:var(--text-primary);color:var(--bg-elevated);box-sizing:border-box;white-space:normal;pointer-events:none;border-radius:5px;max-width:min(260px,100vw - 16px);padding:4px 8px;font-size:11px;font-weight:600;box-shadow:0 2px 10px #0000004d}@media (width<=767.98px),(height<=480px) and (pointer:coarse){.tsel-opt{box-sizing:border-box;min-height:44px;font-size:14px}.tsel-note{flex:0 0 44px;width:44px;height:44px;font-size:14px}}.video-editor input,.video-editor select,.video-editor textarea{background:var(--bg-inset);width:100%;min-width:0;color:var(--fg);border:var(--bw-1) solid var(--border-strong);border-radius:var(--r-2);font-family:var(--font-body);accent-color:var(--signal);padding:7px 8px;font-size:12px}.video-editor,.mobile-editor-root{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}.video-editor input,.video-editor select,.video-editor textarea,.video-editor [contenteditable=true],.mobile-editor-root input,.mobile-editor-root select,.mobile-editor-root textarea,.mobile-editor-root [contenteditable=true]{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}.video-editor input[type=range]{background:0 0;border:0;padding:0}.video-editor input[type=checkbox]{width:auto;min-width:auto}.video-editor select:focus,.video-editor input:focus,.video-editor textarea:focus{border-color:var(--signal);box-shadow:0 0 0 1px var(--signal-dim)}:focus-visible{outline:2px solid var(--signal);outline-offset:2px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--r-pill)}::-webkit-scrollbar-thumb:hover{background:var(--fg-subtle)}.sm-hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.sm-hide-scrollbar::-webkit-scrollbar{width:0;height:0;display:none}.btn{align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-4);border-radius:var(--r-pill);letter-spacing:var(--tr-body);transition:transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);font-size:13px;font-weight:500;display:inline-flex}.btn:active{transform:scale(.98)}.btn-primary{background:var(--fg);color:var(--bg)}.btn-primary:hover{background:var(--fg);opacity:.86}.btn-primary:disabled{opacity:.4;cursor:not-allowed}.btn-secondary{color:var(--fg);border:var(--bw-1) solid var(--border-strong);background:0 0}.btn-secondary:hover{background:var(--hover-wash);border-color:var(--fg)}.btn-ghost{color:var(--fg-muted);background:0 0;padding:6px 10px}.btn-ghost:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon{color:var(--fg-muted);border-radius:var(--r-2);transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);background:0 0;justify-content:center;align-items:center;padding:5px;display:inline-flex}.btn-icon:hover{background:var(--hover-wash);color:var(--fg)}.btn-icon:active{transform:scale(.98)}.btn-icon:disabled{opacity:.4;cursor:not-allowed}.typing-indicator{align-items:center;gap:4px;padding:4px 0;display:flex}.typing-indicator span{border-radius:var(--r-pill);background:var(--fg-subtle);width:6px;height:6px;animation:1.2s infinite bounce}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes bounce{0%,60%,to{opacity:.4;transform:translateY(0)}30%{opacity:1;transform:translateY(-5px)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{0%,to{opacity:.4;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}@keyframes toastIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}button:focus-visible,[role=button]:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=button]):not([type=submit]):not([type=reset]):is(:focus,:focus-visible),textarea:is(:focus,:focus-visible),select:is(:focus,:focus-visible),[contenteditable]:not([contenteditable=false]):is(:focus,:focus-visible){outline:none!important}@keyframes skeletonShimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#ffffff08 0%,#ffffff1a 50%,#ffffff08 100%) 0 0/200% 100%;border-radius:6px;animation:1.4s ease-in-out infinite skeletonShimmer}.node-state-empty{border-style:dashed!important}.node-state-generating{box-shadow:0 0 0 2px var(--accent);position:relative}.node-state-generating:after{content:"";border-radius:inherit;pointer-events:none;will-change:opacity;animation:1.6s ease-in-out infinite nodePulse;position:absolute;inset:-2px;box-shadow:0 0 18px #f7c8328c}@keyframes nodePulse{0%,to{opacity:.36}50%{opacity:1}}.node-state-warning{position:relative;border-color:var(--attention-warning)!important}.node-state-warning:after{content:"";border-radius:inherit;pointer-events:none;box-shadow:0 0 0 1px var(--attention-warning), 0 0 14px color-mix(in srgb, var(--attention-warning) 40%, transparent);position:absolute;inset:-2px}.node-state-failed{border-top:4px solid #d04848!important}.node-state-failed:before{content:"⚠ ";color:#d04848;z-index:2;pointer-events:none;font-size:12px;font-weight:800;position:absolute;top:6px;left:8px}.react-flow__node{cursor:default}.react-flow__node.dragging,.react-flow__pane.dragging .react-flow__viewport{will-change:transform}.react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){box-shadow:1px 1px 0 var(--node-side,#d8cfb3), 2px 2px 0 var(--node-side,#d8cfb3), 3px 3px 0 var(--node-side,#d8cfb3), 4px 4px 0 var(--node-side,#d8cfb3), 5px 5px 0 var(--node-side,#d8cfb3), 6px 6px 0 var(--node-side,#d8cfb3), 7px 7px 0 var(--node-edge,#0a0a0a2e), 14px 16px 18px -4px #0a0a0a2e;border-radius:10px}[data-mode=dark] .react-flow__node:not(.react-flow__node-sceneGroupNode):not(.react-flow__node-shotLabelNode):not(.react-flow__node-stickyNoteNode):not(.react-flow__node-transitionNode){--node-side:#1f1d18;--node-edge:#f7f0db2e}.react-flow__controls{grid-template-columns:repeat(2,1fr)!important;display:grid!important}.react-flow__controls button{background:var(--bg-elev-2)!important;border-color:var(--border-tok)!important;color:var(--fg-muted)!important;fill:var(--fg-muted)!important}.react-flow__controls button:hover{background:var(--ink-300)!important}.react-flow__edge-path{stroke:var(--border-strong)}.react-flow.rf-edges-panning .react-flow__edges{display:none}.react-flow__node.node-controls-locked input,.react-flow__node.node-controls-locked textarea,.react-flow__node.node-controls-locked select,.react-flow__node.node-controls-locked button,.react-flow__node.node-controls-locked [role=button]{pointer-events:none!important}.react-flow__node.node-controls-locked{cursor:grab}.react-flow__node.node-controls-locked:active{cursor:grabbing}.react-flow__node.scene-add-available-node{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.scene-add-available-node>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.scene-add-available-node:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}.react-flow__node.node-replace-available{filter:drop-shadow(0 0 10px #f7f0db38)}.react-flow__node.node-replace-available>div{outline:1px solid var(--border-strong);outline-offset:4px;border-radius:var(--r-3)}.react-flow__node.node-replace-available:after{content:"";border:1px dashed var(--border-strong);border-radius:var(--r-3);pointer-events:none;position:absolute;inset:-8px}select option{background:var(--bg-elev-2);color:var(--fg)}.spinner{border:2px solid var(--border-tok);border-top-color:var(--fg);border-radius:var(--r-pill);width:16px;height:16px;animation:.7s linear infinite spin}.media-upload-preview:hover .media-upload-overlay{opacity:1!important}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:1ms!important;animation-duration:1ms!important}}.assets-card .assets-card-overlay{opacity:0;transition:opacity .15s}.assets-card:hover .assets-card-overlay{opacity:1}.yt-video-row{transition:background .12s,box-shadow .12s,color .12s}.yt-video-row:hover{box-shadow:inset 2px 0 0 var(--accent), inset 0 0 14px var(--accent-glow);color:var(--text-primary);background:var(--hover-wash)!important}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble ::selection{background:var(--signal);color:#0a0a0a}.chat-bubble:hover .chat-bubble-copy,.chat-bubble:focus-within .chat-bubble-copy,.chat-bubble-copy:hover,.chat-bubble-copy:focus{opacity:1!important}.chat-bubble-copy:hover{background:var(--bg-elevated-2,var(--bg-elevated));color:var(--text-primary)!important}.chat-bubble-copy:active{transform:scale(.94)}.sm-brand .sm-brandmark{transform-origin:50%;transition:transform .76s cubic-bezier(.2,.8,.2,1),filter .28s}.sm-brand:hover .sm-brandmark{filter:drop-shadow(0 0 7px color-mix(in srgb, currentColor 45%, transparent));will-change:transform, filter;transform:perspective(340px)rotateY(360deg)scale(1.06)}.sm-brand .sm-word{transition:transform .3s cubic-bezier(.2,.8,.2,1);transform:translate(-4px)}.sm-brand:hover .sm-word{will-change:transform;transform:translate(-4px,-1px)}@media (prefers-reduced-motion:reduce){.sm-brand .sm-brandmark{transition:transform .24s,filter .24s}.sm-brand:hover .sm-brandmark{transform:scale(1.06)}.sm-brand:hover .sm-word{transform:translate(-4px)}}body.sm-mobile-ui input,body.sm-mobile-ui textarea,body.sm-mobile-ui select{font-size:16px!important}body.sm-mobile-ui button,body.sm-mobile-ui select,body.sm-mobile-ui [role=button]{min-height:44px}body.sm-mobile-ui select:not([multiple]):not([size]){height:max(44px,2.75em)}body.sm-mobile-ui button.btn-icon,body.sm-mobile-ui button[data-compact],body.sm-mobile-ui .no-min-tap{min-height:0}body.sm-mobile-ui .yt-video-row{min-width:560px}:where(html:has(.sm-block)){container-type:inline-size}:where(.sm-block){gap:var(--s-5,24px);min-width:0;display:grid}:where(.sm-block,.sm-block *){box-sizing:border-box}:where(.sm-block)>:where(*){min-width:0}@container (width>=720px){:where(.sm-block[data-layout=split]){grid-template-columns:minmax(0,1.6fr) minmax(260px,1fr);align-items:start}:where(.sm-block[data-layout=split])>:where(.sm-stage){grid-row:span 6}}:where(.sm-row){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-stage){min-height:220px;padding:var(--s-3,12px);border-radius:var(--r-3,8px);background:var(--bg-inset);place-items:center;display:grid;position:relative;overflow:hidden}:where(.sm-stage[data-ratio="16:9"]){aspect-ratio:16/9;min-height:0}:where(.sm-stage[data-ratio="9:16"]){aspect-ratio:9/16;justify-self:center;width:auto;height:min(70vh,640px);min-height:0;max-height:70vh}:where(.sm-stage[data-ratio="1:1"]){aspect-ratio:1;min-height:0}:where(.sm-stage)>:where(img,video,canvas){object-fit:contain;border-radius:var(--r-2,4px);max-width:100%;max-height:100%;display:block}:where(.sm-stage[data-fill])>:where(img,video,canvas){object-fit:cover;width:100%;height:100%}:where(.sm-empty){justify-items:center;gap:var(--s-3,12px);padding:var(--s-5,24px) var(--s-4,16px);text-align:center;color:var(--fg-muted);display:grid}:where(.sm-empty)>:where(.sm-glyph){--sm-glyph-size:40px;color:var(--fg-subtle)}:where(.sm-empty)>:where(p){max-width:24ch;font-size:var(--fs-body,1rem);margin:0}:where(.sm-drop){outline:1px dashed var(--border-tok);outline-offset:-8px}:where(.sm-drop[data-over]){outline-color:var(--fg);background:var(--hover-wash)}:where(.sm-actions){align-items:center;gap:var(--s-2,8px);flex-wrap:wrap;display:flex}:where(.sm-actions)>:where(.sm-status){margin-inline-start:var(--s-2,8px)}:where(.sm-primary,.sm-quiet){justify-content:center;align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);border-radius:var(--r-pill,999px);font:inherit;cursor:pointer;text-align:center;overflow-wrap:anywhere;max-width:100%;padding:10px 20px;font-weight:500;display:inline-flex}:where(.sm-primary){border:1px solid var(--fg);background:var(--fg);color:var(--bg)}:where(.sm-quiet){border:1px solid var(--border-tok);color:var(--fg);background:0 0}:where(.sm-quiet):hover{background:var(--hover-wash)}:where(.sm-icon){min-width:var(--touch-target,44px);min-height:var(--touch-target,44px);border-radius:var(--r-2,4px);color:var(--fg-muted);cursor:pointer;background:0 0;border:0;place-items:center;padding:0;display:inline-grid}:where(.sm-icon):hover{color:var(--fg);background:var(--hover-wash)}:where(.sm-icon[aria-pressed=true]){color:var(--fg);background:var(--press-wash)}:where(.sm-primary,.sm-quiet,.sm-icon):disabled{opacity:.45;cursor:default}:where(.sm-primary,.sm-quiet,.sm-icon):focus-visible{outline:2px solid var(--signal);outline-offset:3px}@container (width<=420px){:where(.sm-actions)>:where(.sm-primary){flex:100%}}:where(.sm-field){gap:6px;min-width:0;display:grid}:where(.sm-field)>:where(label,.sm-label){align-items:center;gap:var(--s-1,4px);font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);min-height:24px;display:flex}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select,textarea){width:100%;min-height:var(--touch-target,44px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg);font:inherit;padding:10px 12px}:where(.sm-field) :where(textarea){resize:vertical;min-height:96px}:where(.sm-fields){gap:var(--s-4,16px);grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));display:grid}@container (width>=520px){:where(.sm-field:not(:has(textarea,.sm-choices))){align-items:center;gap:var(--s-2,8px);grid-template-columns:minmax(5rem,9rem) minmax(0,1fr)}:where(.sm-field) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]),select){min-height:36px;padding:6px 10px}:where(.sm-fields){gap:var(--s-2,8px) var(--s-4,16px)}}:where(.sm-choices){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(var(--sm-tile,96px),1fr));border:0;min-width:0;margin:0;padding:0;display:grid}:where(.sm-choices[data-size=wide]){--sm-tile:160px}:where(.sm-choices)>:where(legend){font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);margin-bottom:6px;padding:0}:where(.sm-choices)>:where(label){padding:var(--s-2,8px);border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-1);color:var(--fg-muted);font-size:var(--fs-caption,.8125rem);text-align:center;cursor:pointer;align-content:start;justify-items:center;gap:6px;display:grid;position:relative}:where(.sm-choices)>:where(label):hover{border-color:var(--border-strong);color:var(--fg)}:where(.sm-choices) :where(input[type=radio],input[type=checkbox]){opacity:0;cursor:pointer;width:100%;height:100%;margin:0;position:absolute;inset:0}:where(.sm-choices)>:where(label:has(input:checked)){border-color:var(--fg);color:var(--fg);box-shadow:inset 0 0 0 1px var(--fg)}:where(.sm-choices)>:where(label:has(input:focus-visible)){outline:2px solid var(--signal);outline-offset:2px}:where(.sm-choices) :where(img,video,canvas,.sm-swatch){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;border-radius:var(--r-2,4px);background:var(--bg-inset);display:block}:where(.sm-choices) :where(.sm-glyph){--sm-glyph-size:28px;margin:10px 0 4px}:where(.sm-choices[data-ratio="16:9"]){--sm-tile-ratio:16/9}:where(.sm-choices[data-ratio="9:16"]){--sm-tile-ratio:9/16}:where(.sm-choices[data-compact])>:where(label){min-height:var(--touch-target,44px);grid-auto-flow:column;place-content:center;align-items:center;padding:6px 12px}:where(.sm-choices[data-compact]) :where(.sm-glyph){--sm-glyph-size:18px;margin:0}:where(.sm-choices) :where(audio){z-index:1;width:100%;height:32px;position:relative}:where(.sm-shape){box-sizing:border-box;width:calc(var(--sm-shape-size,32px) * min(1,var(--r,1)));aspect-ratio:var(--r,1);border:2px solid;border-radius:3px;max-width:100%;margin:4px auto;display:block}:where(.sm-choices)>:where(label:has(.sm-shape)){grid-template-rows:1fr auto;align-content:stretch}:where(.sm-choices) :where(.sm-shape){--sm-shape-size:36px;align-self:center;margin:6px auto 2px}:where(.sm-choices[data-compact]) :where(.sm-shape){--sm-shape-size:20px;margin:0}:where(.sm-gallery){gap:var(--s-2,8px);grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--sm-tile,140px)),1fr));margin:0;padding:0;list-style:none;display:grid}:where(.sm-gallery)>:where(*){border-radius:var(--r-3,8px);background:var(--bg-inset);margin:0;position:relative;overflow:hidden}:where(.sm-gallery) :where(img,video,canvas){width:100%;aspect-ratio:var(--sm-tile-ratio,1);object-fit:cover;display:block}:where(.sm-gallery) :where(figcaption){font-size:var(--fs-caption,.8125rem);color:var(--fg-muted);padding:6px 8px}:where(.sm-gallery) :where([aria-selected=true],.is-selected){box-shadow:inset 0 0 0 2px var(--fg)}:where(.sm-status){align-items:center;gap:var(--s-2,8px);min-height:24px;font-size:var(--fs-body-s,.875rem);color:var(--fg-muted);display:inline-flex}:where(.sm-status):empty{display:none}:where(.sm-status[data-state]):before{content:"";background:currentColor;border-radius:50%;flex:none;width:14px;height:14px}:where(.sm-status[data-state=loading]):before{background:0 0;border:2px solid;border-right-color:#0000;animation:.8s linear infinite sm-spin}:where(.sm-status[data-state=success]){color:var(--attention-ok,var(--fg))}:where(.sm-status[data-state=success]):before{background:0 0;border:2px solid;border-width:0 2px 2px 0;border-radius:0;width:6px;height:11px;margin:0 4px 3px;transform:rotate(45deg)}:where(.sm-status[data-state=waiting]){color:var(--attention-warning,var(--signal))}:where(.sm-status[data-state=waiting]):before{width:8px;height:8px;margin:0 3px}:where(.sm-status[data-state=error]){color:var(--attention-error,var(--signal))}:where(.sm-status[data-state=error]):before{clip-path:polygon(50% 0,100% 100%,0 100%);border-radius:0}:where(.sm-progress){border-radius:var(--r-pill,999px);background:var(--bg-elev-2);width:100%;height:4px;accent-color:var(--fg);appearance:none;border:0;overflow:hidden}:where(.sm-progress)::-webkit-progress-bar{background:var(--bg-elev-2)}:where(.sm-progress)::-webkit-progress-value{background:var(--fg)}:where(.sm-progress)::-moz-progress-bar{background:var(--fg)}:where(.sm-more){border-top:1px solid var(--divider)}:where(.sm-more)>:where(summary){align-items:center;gap:var(--s-2,8px);min-height:var(--touch-target,44px);color:var(--fg-muted);font-size:var(--fs-body-s,.875rem);cursor:pointer;padding:0;list-style:none;display:flex}:where(.sm-more)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-more)>:where(summary):after{content:"";width:7px;height:7px;transition:transform var(--dur-fast,.16s) var(--ease-out,ease);border:1.5px solid;border-width:0 1.5px 1.5px 0;margin-inline-start:2px;transform:translateY(-2px)rotate(45deg)}:where(.sm-more[open])>:where(summary):after{transform:translateY(2px)rotate(-135deg)}:where(.sm-more)>:where(summary):hover{color:var(--fg)}:where(.sm-more)>:where(:not(summary)){margin-top:var(--s-2,8px)}:where(.sm-more[open]){padding-bottom:var(--s-3,12px)}:where(.sm-label){position:relative}:where(.sm-hint){vertical-align:middle;display:inline-block}:where(:not(.sm-label))>:where(.sm-hint){position:relative}:where(.sm-hint)>:where(summary){width:var(--touch-target,44px);height:var(--touch-target,44px);min-height:0;color:var(--fg-subtle);cursor:pointer;border:0;place-items:center;margin:-8px;padding:0;list-style:none;display:inline-grid}:where(.sm-hint)>:where(summary)::-webkit-details-marker{display:none}:where(.sm-hint)>:where(summary):before{content:"i";width:15px;height:15px;font:600 10px/1 var(--font-body);border:1.5px solid;border-radius:50%;place-items:center;font-style:normal;display:grid}:where(.sm-hint)>:where(summary):hover,:where(.sm-hint[open])>:where(summary){color:var(--fg)}:where(.sm-hint)>:where(:not(summary)){z-index:5;inset-inline-start:0;border:1px solid var(--border-tok);border-radius:var(--r-3,8px);background:var(--bg-elev-2);width:max-content;max-width:min(280px,100%);color:var(--fg);font-size:var(--fs-body-s,.875rem);box-shadow:var(--shadow-1);margin:4px 0 0;padding:10px 12px;line-height:1.45;position:absolute;top:100%}:where(:not(.sm-label))>:where(.sm-hint[open]){display:block}:where(:not(.sm-label))>:where(.sm-hint)>:where(:not(summary)){width:auto;max-width:60ch;position:static}:where(.sm-glyph){--g:var(--sm-glyph-size,20px);width:var(--g);height:var(--g);color:inherit;vertical-align:middle;flex:none;display:inline-block;position:relative}:where(.sm-glyph):before,:where(.sm-glyph):after{content:"";box-sizing:border-box;position:absolute}:where(.sm-glyph[data-glyph=play]):before{clip-path:polygon(0 0,100% 50%,0 100%);background:currentColor;inset:15% 15% 15% 25%}:where(.sm-glyph[data-glyph=pause]):before{border:solid;border-width:0 calc(var(--g)*.14);inset:18% 25%}:where(.sm-glyph[data-glyph=plus]):before,:where(.sm-glyph[data-glyph=close]):before{background:linear-gradient(currentColor,currentColor) 50%/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 50%/2px 100% no-repeat;inset:15%}:where(.sm-glyph[data-glyph=close]):before{transform:rotate(45deg)}:where(.sm-glyph[data-glyph=check]):before{border:2px solid;border-width:0 2px 2px 0;width:30%;height:58%;top:12%;left:36%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=chevron]):before{border:2px solid;border-width:0 2px 2px 0;width:40%;height:40%;top:22%;left:30%;transform:rotate(45deg)}:where(.sm-glyph[data-glyph=upload]):before{border:2px solid;border-top:0;border-radius:0 0 3px 3px;height:35%;bottom:10%;left:10%;right:10%}:where(.sm-glyph[data-glyph=upload]):after{clip-path:polygon(50% 0,100% 45%,60% 45%,60% 100%,40% 100%,40% 45%,0 45%);background:currentColor;height:62%;top:4%;left:25%;right:25%}:where(.sm-glyph[data-glyph=image]):before{border:2px solid;border-radius:3px;inset:12%}:where(.sm-glyph[data-glyph=image]):after{clip-path:polygon(0 100%,35% 20%,60% 65%,75% 45%,100% 100%);background:currentColor;height:34%;bottom:24%;left:22%;right:22%}:where(.sm-glyph[data-glyph=video]):before{border:2px solid;border-radius:3px;inset:22% 28% 22% 8%}:where(.sm-glyph[data-glyph=video]):after{clip-path:polygon(0 35%,100% 0,100% 100%,0 65%);background:currentColor;width:20%;top:30%;bottom:30%;right:8%}:where(.sm-glyph[data-glyph=audio]):before{background:linear-gradient(currentColor,currentColor) 0 60%/2px 40% no-repeat,linear-gradient(currentColor,currentColor) 33%/2px 80% no-repeat,linear-gradient(currentColor,currentColor) 66%/2px 100% no-repeat,linear-gradient(currentColor,currentColor) 100% 60%/2px 50% no-repeat;inset:15% 12%}:where(.sm-glyph[data-glyph=text]):before{background:linear-gradient(currentColor,currentColor) 0 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0/100% 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/60% 2px no-repeat;inset:20% 12%}:where(.sm-glyph[data-glyph=spark]):before{clip-path:polygon(50% 0,60% 40%,100% 50%,60% 60%,50% 100%,40% 60%,0 50%,40% 40%);background:currentColor;inset:8%}:where(.sm-glyph[data-glyph=info]):before{border:2px solid;border-radius:50%;inset:10%}:where(.sm-glyph[data-glyph=info]):after{background:linear-gradient(currentColor,currentColor) 0 0/2px 2px no-repeat,linear-gradient(currentColor,currentColor) 0 100%/2px 60% no-repeat;width:2px;height:46%;margin-left:-1px;top:28%;left:50%}:where(.sm-glyph[data-glyph=warning]):before{clip-path:polygon(50% 0,100% 100%,0 100%);background:currentColor;inset:8% 4% 10%}:where(.sm-glyph[data-glyph=warning]):after{background:linear-gradient(var(--bg),var(--bg)) 0 0/2px 60% no-repeat,linear-gradient(var(--bg),var(--bg)) 0 100%/2px 2px no-repeat;width:2px;height:38%;margin-left:-1px;top:38%;left:50%}:where(.sm-glyph[data-glyph=spinner]):before{border:2px solid;border-right-color:#0000;border-radius:50%;animation:.8s linear infinite sm-spin;inset:12%}:where([class*=sm-][hidden]){display:none!important}:where(.sm-sr){clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@keyframes sm-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){:where(.sm-status[data-state=loading]):before,:where(.sm-glyph[data-glyph=spinner]):before{opacity:.6;border-right-color:currentColor;animation:none}:where(.sm-more)>:where(summary):after{transition:none}}.sm-quiet{border:1px solid var(--border-tok)}html,body{background:var(--bg);color:var(--fg);margin:0}.dh-page{min-height:100dvh;font-family:var(--font-body);flex-direction:column;display:flex}.dh-header{justify-content:space-between;align-items:flex-end;gap:var(--s-4,16px);padding:var(--s-5,20px) var(--s-6,24px) var(--s-4,16px);border-bottom:var(--bw-1,1px) solid var(--border-tok);flex-wrap:wrap;display:flex}.dh-title{gap:var(--s-1,4px);min-width:0;display:grid}.dh-wordmark{color:var(--fg-muted);font-size:13px}.dh-title h1{font-family:var(--font-display);letter-spacing:var(--tr-display);overflow-wrap:anywhere;margin:0;font-size:30px;font-style:italic;font-weight:200;line-height:1.1}.dh-meta{color:var(--fg-muted);overflow-wrap:anywhere;font-size:12px}.dh-controls{align-items:flex-end;gap:var(--s-3,12px);flex-wrap:wrap;display:flex}.dh-controls label{gap:var(--s-1,4px);color:var(--fg-muted);font-size:12px;display:grid}.dh-controls select{min-height:36px;padding:0 var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font:inherit;font-size:13px}.dh-controls select:focus-visible,.dh-input textarea:focus-visible,.dh-page .btn:focus-visible{outline:2px solid var(--signal);outline-offset:2px}.dh-problem{margin:var(--s-4,16px) var(--s-6,24px) 0;padding:var(--s-3,12px) var(--s-4,16px);border:var(--bw-1,1px) solid var(--border-strong);border-radius:var(--r-2);background:var(--bg-elev-1);white-space:pre-wrap;overflow-wrap:anywhere}.dh-main{gap:var(--s-6,24px);padding:var(--s-6,24px);flex:1;grid-template-columns:minmax(0,1fr) minmax(280px,380px);display:grid}.dh-stage{min-width:0}.dh-frame{min-width:0;margin:0 auto}.dh-noview{gap:var(--s-4,16px);padding:var(--s-6,24px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-3);justify-items:start;display:grid}.dh-noview p{color:var(--fg-muted);margin:0}.dh-panel{gap:var(--s-5,20px);flex-direction:column;min-width:0;display:flex}.dh-section-head{justify-content:space-between;align-items:center;gap:var(--s-2,8px);margin-bottom:var(--s-2,8px);display:flex}.dh-section-head h2{letter-spacing:var(--tr-body);margin:0;font-size:13px;font-weight:600}.dh-input textarea{box-sizing:border-box;width:100%;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);color:var(--fg);font-family:var(--font-mono);resize:vertical;font-size:12px;line-height:1.5}.dh-input p,.dh-muted{margin:var(--s-2,8px) 0 0;color:var(--fg-muted);font-size:13px}.dh-json{max-height:240px;padding:var(--s-3,12px);border:var(--bw-1,1px) solid var(--border-tok);border-radius:var(--r-2);background:var(--bg-elev-1);font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.5;overflow:auto}.dh-console{flex-direction:column;min-height:0;display:flex}.dh-console ol{border-top:var(--bw-1,1px) solid var(--divider);max-height:360px;margin:0;padding:0;list-style:none;overflow:auto}.dh-entry{gap:var(--s-2,8px);padding:var(--s-2,8px) 0;border-bottom:var(--bw-1,1px) solid var(--divider);grid-template-columns:auto auto minmax(0,1fr);align-items:baseline;font-size:12px;display:grid}.dh-entry time{color:var(--fg-muted);font-variant-numeric:tabular-nums}.dh-kind{color:var(--fg-muted);font-size:11px}.dh-entry pre{font-family:var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.dh-error .dh-kind,.dh-error pre{color:var(--fg);font-weight:600}.dh-stand-in .dh-kind{color:var(--signal)}@media (width<=900px){.dh-main{padding:var(--s-4,16px);grid-template-columns:minmax(0,1fr)}.dh-header{padding:var(--s-4,16px)}}
|
|
@@ -13,7 +13,7 @@ Start
|
|
|
13
13
|
Build and check
|
|
14
14
|
validate [folder] Check the manifest, source and interface (--mobile adds the phone profile)
|
|
15
15
|
test [folder] Run tests/fixtures.json
|
|
16
|
-
run [folder] [input.json] Run once and print the outputs (--state file, --save-state file)
|
|
16
|
+
run [folder] [input.json] Run once and print the outputs (--state file, --save-state file); preview is the same command
|
|
17
17
|
dev [folder] Preview the interface with hot reload (--port 4800, --build "npm run build";
|
|
18
18
|
--live: review each saved version in your StillMade account)
|
|
19
19
|
types [folder] Write stillmade-env.d.ts: TypeScript types for the ports and globals
|
|
@@ -103,40 +103,71 @@ export default async function run(input, stillmade) {
|
|
|
103
103
|
`;
|
|
104
104
|
const cardsManifest=(id,name)=>manifest({id,version:'1.0.0',name,description:'Turn lines of text into numbered title cards, with a pinned card shared by everyone in the project.',runtime:'javascript',
|
|
105
105
|
inputs:{lines:{type:'text',primary:true,required:true,semantic:'plain_text',description:'One title per line.'}},
|
|
106
|
-
outputs:{cards:{type:'json',primary:true,semantic:'title_cards',description:'The numbered title cards.',schema:{cards:'object[]'}}},
|
|
106
|
+
outputs:{cards:{type:'json',primary:true,semantic:'title_cards',description:'The numbered title cards.',schema:{cards:'object[]'}}},
|
|
107
|
+
// The required input also gets a generated control: pack --original and import refuse a required input nobody can set.
|
|
108
|
+
ui:[{control:'text',port:'lines'}]});
|
|
107
109
|
const CARDS_TESTS=[{name:'Two titles',input:{lines:'Opening\nThe long road'},expected:{cards:{cards:[{number:1,title:'Opening'},{number:2,title:'The long road'}]}}}];
|
|
108
110
|
|
|
109
|
-
const HTML_VIEW_HTML=`<main class="
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
111
|
+
const HTML_VIEW_HTML=`<main class="sm-block">
|
|
112
|
+
<section class="sm-stage" aria-live="polite">
|
|
113
|
+
<div class="sm-empty" id="empty">
|
|
114
|
+
<span class="sm-glyph" data-glyph="text" aria-hidden="true"></span>
|
|
115
|
+
<p>Cards show here</p>
|
|
116
|
+
</div>
|
|
117
|
+
<ol class="sm-gallery cards" id="list" hidden></ol>
|
|
116
118
|
</section>
|
|
119
|
+
<div class="sm-field">
|
|
120
|
+
<div class="sm-label">
|
|
121
|
+
<label for="lines">Titles</label>
|
|
122
|
+
<details class="sm-hint"><summary aria-label="About titles" title="About titles"></summary><p>One per line, up to 50.</p></details>
|
|
123
|
+
</div>
|
|
124
|
+
<textarea id="lines" rows="4" data-stillmade-input="lines" data-stillmade-share="lines"></textarea>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="sm-actions">
|
|
127
|
+
<button type="button" class="sm-primary" id="make" data-stillmade-action="run">Make cards</button>
|
|
128
|
+
<p class="sm-status" id="status" role="status"></p>
|
|
129
|
+
</div>
|
|
117
130
|
</main>
|
|
118
131
|
`;
|
|
119
|
-
|
|
132
|
+
// The Block kit (injected by the host) styles everything above; this is only
|
|
133
|
+
// what is specific to title cards.
|
|
134
|
+
const HTML_VIEW_CSS=`.cards{--sm-tile:180px;align-self:start;width:100%}
|
|
135
|
+
.cards li{display:grid;gap:4px;padding:12px;background:var(--bg-elev-1)}
|
|
136
|
+
.cards li span{color:var(--fg-muted);font-size:.8125rem}
|
|
137
|
+
`;
|
|
138
|
+
const HTML_VIEW_JS=`// A frame view in plain JavaScript: no build step. Layout and controls come
|
|
139
|
+
// from the Block kit classes (sm-*) that StillMade adds to every view.
|
|
120
140
|
const lines = document.getElementById('lines'), make = document.getElementById('make');
|
|
121
|
-
const
|
|
122
|
-
// loading, error or success on the
|
|
123
|
-
const show = (phase, text) => {
|
|
141
|
+
const empty = document.getElementById('empty'), list = document.getElementById('list'), status = document.getElementById('status');
|
|
142
|
+
// loading, error or success on the status, for people and StillMade's device checks.
|
|
143
|
+
const show = (phase, text) => { status.dataset.state = phase; status.dataset.stillmadeState = phase; status.setAttribute('role', phase === 'error' ? 'alert' : 'status'); status.textContent = text; };
|
|
144
|
+
// Everyone in the project sees the same titles and cards; read-only teammates cannot run.
|
|
145
|
+
let busy = false, canEdit = true;
|
|
146
|
+
const render = (cards) => {
|
|
147
|
+
list.replaceChildren(...cards.map((card) => {
|
|
148
|
+
const item = document.createElement('li');
|
|
149
|
+
const number = document.createElement('span'); number.textContent = String(card.number);
|
|
150
|
+
const title = document.createElement('strong'); title.textContent = card.title;
|
|
151
|
+
item.append(number, title);
|
|
152
|
+
return item;
|
|
153
|
+
}));
|
|
154
|
+
empty.hidden = cards.length > 0; list.hidden = cards.length === 0;
|
|
155
|
+
};
|
|
124
156
|
StillMade.onInput((input) => { lines.value = input.lines || ''; });
|
|
157
|
+
StillMade.onShared((shared) => {
|
|
158
|
+
canEdit = shared.canEdit; make.disabled = busy || !canEdit;
|
|
159
|
+
if (shared.outputs && shared.outputs.cards) render(shared.outputs.cards.cards);
|
|
160
|
+
});
|
|
125
161
|
make.addEventListener('click', async () => {
|
|
126
|
-
|
|
162
|
+
if (busy || !canEdit) return;
|
|
163
|
+
busy = true; make.disabled = true; show('loading', 'Making');
|
|
127
164
|
try {
|
|
128
165
|
const output = await StillMade.run({ lines: lines.value });
|
|
129
|
-
|
|
130
|
-
const item = document.createElement('li');
|
|
131
|
-
const number = document.createElement('span'); number.textContent = String(card.number);
|
|
132
|
-
const title = document.createElement('strong'); title.textContent = card.title;
|
|
133
|
-
item.append(number, title);
|
|
134
|
-
return item;
|
|
135
|
-
}));
|
|
166
|
+
render(output.cards.cards);
|
|
136
167
|
const count = output.cards.cards.length;
|
|
137
168
|
show('success', count + (count === 1 ? ' card' : ' cards'));
|
|
138
169
|
} catch (error) { show('error', error.message); }
|
|
139
|
-
finally { make.disabled =
|
|
170
|
+
finally { busy = false; make.disabled = !canEdit; }
|
|
140
171
|
});
|
|
141
172
|
`;
|
|
142
173
|
|
|
@@ -213,7 +244,7 @@ export const TEMPLATES=Object.freeze({
|
|
|
213
244
|
}},
|
|
214
245
|
'view-html':{description:'An interface in plain HTML, CSS and JavaScript; no build step.',runtime:'javascript',files:async()=>{
|
|
215
246
|
const pkg=cardsManifest('example.title-cards-html','Title cards');
|
|
216
|
-
return {'stillmade.block.json':json(pkg),'src/run.js':CARDS_RUN,'src/view.html':HTML_VIEW_HTML,'src/view.css':
|
|
247
|
+
return {'stillmade.block.json':json(pkg),'src/run.js':CARDS_RUN,'src/view.html':HTML_VIEW_HTML,'src/view.css':HTML_VIEW_CSS,'src/view.js':HTML_VIEW_JS,'src/view.config.json':json({runtime:'frame'}),'tests/fixtures.json':json(CARDS_TESTS),'README.md':readme(pkg.name,'A Block with a frame view in plain web code. Edit src/view.html, src/view.css and src/view.js; `stillmade-block dev .` reloads on save.',OFFLINE)};
|
|
217
248
|
}},
|
|
218
249
|
text:{description:'Hosted text generation (text.generate) on the person\'s credits.',runtime:'capability',files:fromExample(async()=>(await import('../block-sdk/capability-example.js')).textGenerate,'A capability Block: src/capability.json asks StillMade for text.generate. People choose a model and approve the price.',HOSTED)},
|
|
219
250
|
'image-generate':{description:'Hosted image generation with any catalog model.',runtime:'capability',files:fromExample(async()=>(await import('../block-sdk/image-generation-example.js')).imageGenerate,'Hosted image generation.',HOSTED)},
|
|
@@ -15,6 +15,7 @@ import {initialBlockState,validateBlockState} from '../block-sdk/state.js';
|
|
|
15
15
|
import {createUniversalResolver} from '../block-sdk/universal-resolver.js';
|
|
16
16
|
import {requiresDesktopExecution} from '../block-sdk/desktop-execution.js';
|
|
17
17
|
import {mobileActivationStatus} from '../block-sdk/platform-checks.js';
|
|
18
|
+
import {blockKitProblems,BLOCK_KIT_READINESS_RULES} from '../block-sdk/view-design.js';
|
|
18
19
|
import {FIRST_PARTY_SDK_PACKAGES} from '../block-sdk/first-party-packages.js';
|
|
19
20
|
import {previewProjectType} from './type-preview.js';
|
|
20
21
|
import {semanticRegistryForManifests} from '../block-sdk/package-semantics.js';
|
|
@@ -140,6 +141,10 @@ export async function assessBlockReadiness(pkg,{catalog=FIRST_PARTY_SDK_PACKAGES
|
|
|
140
141
|
else if(sharedEditing?.passed===true)verified('DOD-13',sharedEditing.reason);
|
|
141
142
|
else if(sharedEditing?.passed===false)failed('DOD-13',sharedEditing.reason);
|
|
142
143
|
else notVerified('DOD-13',sharedEditing?.reason||'The shared-interface source contract passed; two-participant runtime behavior has not been checked.');
|
|
144
|
+
// A new interface names its action, shows no price and no page heading before device checks
|
|
145
|
+
// count. StillMade's own built-in Blocks (sm.*) are redesigned separately.
|
|
146
|
+
const kit=String(manifest.id).startsWith('sm.')?[]:blockKitProblems(pkg.view).filter(problem=>BLOCK_KIT_READINESS_RULES.includes(problem.rule));
|
|
147
|
+
if(kit.length)failed('DOD-15',kit.map(problem=>problem.message).join(' '));
|
|
143
148
|
const ui=platformChecks?mobileActivationStatus(platformChecks,sourceDigest,manifest):null;
|
|
144
149
|
if(ui?.ready)verified('DOD-15',ui.reason);else notVerified('DOD-15',ui?.reason||'Device UI checks have not run for this exact package.');
|
|
145
150
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// One rule for which of a Block's inputs is its brief (October 16, 2026). A brief is
|
|
2
|
+
// the facts about the thing a project makes (the car, the shop, the offer), so it comes
|
|
3
|
+
// from that project's own Chat step, never from the conversation that built the Project
|
|
4
|
+
// Type or from the Type's saved defaults. Built-in Blocks declare it as a brief input;
|
|
5
|
+
// a custom Block often uses a plain text input named or labelled "brief". Every writer
|
|
6
|
+
// that must leave briefs alone (the planner, Chat's build actions, a new project's start)
|
|
7
|
+
// and the brief UI use this rule, so they agree.
|
|
8
|
+
// A context input reads a project value and is never a brief the person writes.
|
|
9
|
+
|
|
10
|
+
const spaced=value=>String(value??'').replace(/([a-z0-9])([A-Z])/g,'$1 $2').replace(/[_\-.]+/g,' ');
|
|
11
|
+
const BRIEF=/\bbrief\b/i;
|
|
12
|
+
|
|
13
|
+
/** True when an input is a Step's brief: a brief input, or a text input named or labelled "brief". */
|
|
14
|
+
export function isBriefPort(name,port,{label}={}){
|
|
15
|
+
if(!port||typeof port!=='object'||Array.isArray(port)||port.context)return false;
|
|
16
|
+
if(port.type==='brief')return true;
|
|
17
|
+
return port.type==='text'&&[name,label,port.label].some(value=>BRIEF.test(spaced(value)));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The label a Block's own control gives an input, if any (manifest.ui). */
|
|
21
|
+
export function inputLabel(manifest,name){
|
|
22
|
+
const control=Array.isArray(manifest?.ui)?manifest.ui.find(item=>item?.port===name):null;
|
|
23
|
+
return typeof control?.label==='string'?control.label:undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The names of a Block's brief inputs. */
|
|
27
|
+
export function briefInputNames(manifest){
|
|
28
|
+
return Object.entries(manifest?.inputs||{}).filter(([name,port])=>isBriefPort(name,port,{label:inputLabel(manifest,name)})).map(([name])=>name);
|
|
29
|
+
}
|
|
@@ -79,17 +79,25 @@ const screens = [
|
|
|
79
79
|
];
|
|
80
80
|
const HOST_WORKSPACE_BLOCKS = [SCRIPT_WRITER_BLOCK,...screens.map(([key,name,phase,source,description,features])=>{
|
|
81
81
|
const legacy=LEGACY_BLOCKS.find(block=>block.manifest.id===`sm.${key}`);
|
|
82
|
-
|
|
82
|
+
// The Editor's main release is the original one (1.0): the full video editor.
|
|
83
|
+
// Its portable 2.x releases are a separate, simpler view kept for their exact pins.
|
|
84
|
+
// Its released manifest keeps its name (Video editor); it is listed as the Editor, at the same address.
|
|
85
|
+
if(key==='editor')return {...legacy,source,workspace:{phase,features},summary:{...legacy.summary,text:description},adapterStatus:'workspace',listingName:name};
|
|
86
|
+
const version=key==='pipeline'?'1.2.0':'1.1.0';
|
|
83
87
|
return {...legacy,manifest:hostReleaseManifest(legacy.manifest.id,version),source,workspace:{phase,features},summary:{...legacy.summary,text:description},adapterStatus:'workspace'};
|
|
84
88
|
})];
|
|
85
89
|
HOST_WORKSPACE_BLOCKS.splice(HOST_WORKSPACE_BLOCKS.findIndex(block=>block.manifest.id==='sm.pipeline')+1,0,VIDEO_ASSETS_BLOCK);
|
|
86
90
|
// Promote a portable workspace after its accepted production MVP and rendered
|
|
87
91
|
// acceptance are complete; advanced host extensions can remain on old exact pins. Exact portable releases remain usable as normal
|
|
88
92
|
// Steps before promotion; this map only controls the default production route.
|
|
89
|
-
export const CURRENT_PORTABLE_WORKSPACE_VERSIONS=Object.freeze({'sm.chat':'2.0.3','sm.script':'2.1.5','sm.voiceover':'2.1.6','sm.shot-plan':'2.1
|
|
93
|
+
export const CURRENT_PORTABLE_WORKSPACE_VERSIONS=Object.freeze({'sm.chat':'2.0.3','sm.script':'2.1.5','sm.voiceover':'2.1.6','sm.shot-plan':'2.2.1','sm.blueprint':'2.1.9','sm.pipeline':'2.2.5','sm.video-assets':'2.1.4','sm.panel-strip':'2.4.1','sm.board-editor':'2.2.10','sm.animated-scene':'2.3.8','sm.editor':'2.2.11'});
|
|
94
|
+
// Workspaces whose main release stays the host one: the original Editor (1.0) is
|
|
95
|
+
// the full video editor. Its accepted portable release stays discoverable for
|
|
96
|
+
// Steps that hand the Editor a prepared timeline, which only it accepts.
|
|
97
|
+
export const MAIN_HOST_WORKSPACES=new Set(['sm.editor']);
|
|
90
98
|
export const WORKSPACE_BLOCKS=HOST_WORKSPACE_BLOCKS.map(block=>{
|
|
91
99
|
const version=CURRENT_PORTABLE_WORKSPACE_VERSIONS[block.manifest.id];
|
|
92
|
-
if(!version)return block;
|
|
100
|
+
if(!version||MAIN_HOST_WORKSPACES.has(block.manifest.id))return block;
|
|
93
101
|
const portable=FIRST_PARTY_SDK_PACKAGES.find(pkg=>pkg.manifest.id===block.manifest.id&&pkg.manifest.version===version);
|
|
94
102
|
if(!portable)throw Error(`Missing accepted portable workspace ${block.manifest.id}@${version}`);
|
|
95
103
|
return portable;
|
|
@@ -124,10 +132,23 @@ const newer=(left,right)=>{const a=semverParts(left),b=semverParts(right);for(le
|
|
|
124
132
|
const currentPortableById=new Map();
|
|
125
133
|
// An unset app connector is a project placeholder, not something to find or install.
|
|
126
134
|
export const UNDISCOVERABLE_SDK_BLOCKS=new Set(['sm.connect-app']);
|
|
127
|
-
|
|
135
|
+
// Export video is offered as the original Editor opened at its Export (1.0.0), so
|
|
136
|
+
// people can still change the cut before rendering (the owner, Oct 15). The
|
|
137
|
+
// render-only 2.0.0 stays in BUILTIN_BLOCKS for exact project pins and for
|
|
138
|
+
// planning with explicit candidates; a prepared timeline goes to the portable Editor.
|
|
139
|
+
const EXPORT_VIDEO_EDITOR=LEGACY_BLOCKS.find(block=>block.manifest.id==='sm.export-video'&&block.manifest.version==='1.0.0');
|
|
140
|
+
export const DISCOVERED_AS_HOST=new Map([['sm.export-video',EXPORT_VIDEO_EDITOR]]);
|
|
141
|
+
for(const block of SDK_BUILTIN_BLOCKS){if(UNDISCOVERABLE_SDK_BLOCKS.has(block.manifest.id)||DISCOVERED_AS_HOST.has(block.manifest.id))continue;const prior=currentPortableById.get(block.manifest.id);if(!prior||newer(block.manifest.version,prior.manifest.version))currentPortableById.set(block.manifest.id,block);}
|
|
128
142
|
export const DISCOVERABLE_SDK_BUILTIN_BLOCKS = Object.freeze([...currentPortableById.values()]);
|
|
129
|
-
|
|
130
|
-
export const
|
|
143
|
+
const ACCEPTED_PORTABLE_RELEASES=[...MAIN_HOST_WORKSPACES].map(id=>FIRST_PARTY_SDK_PACKAGES.find(pkg=>pkg.manifest.id===id&&pkg.manifest.version===CURRENT_PORTABLE_WORKSPACE_VERSIONS[id])).filter(Boolean);
|
|
144
|
+
export const DISCOVERABLE_BLOCKS = [STARTING_CHAT_BLOCK,...WORKSPACE_BLOCKS,...ACCEPTED_PORTABLE_RELEASES,...DISCOVERED_AS_HOST.values(),...DISCOVERABLE_SDK_BUILTIN_BLOCKS.filter(block=>!ACCEPTED_PORTABLE_RELEASES.includes(block)).filter(block=>!WORKSPACE_BLOCKS.some(current=>current.manifest.id===block.manifest.id&¤t.manifest.version===block.manifest.version))];
|
|
145
|
+
// Planners pin a finished file to a Block's output port, which the Editor alias has
|
|
146
|
+
// none of, so planning keeps the newest render-only release in its place. Chat
|
|
147
|
+
// builds then fold it into the Editor (compact-project.js), so people still get
|
|
148
|
+
// the Editor at its Export.
|
|
149
|
+
const newestRelease=id=>SDK_BUILTIN_BLOCKS.filter(block=>block.manifest.id===id).reduce((best,block)=>!best||newer(block.manifest.version,best.manifest.version)?block:best,null);
|
|
150
|
+
export const PLANNING_BLOCKS = DISCOVERABLE_BLOCKS.map(block=>DISCOVERED_AS_HOST.get(block.manifest.id)===block?newestRelease(block.manifest.id)||block:block);
|
|
151
|
+
export const BUILTIN_BLOCKS = [STARTING_CHAT_BLOCK,...WORKSPACE_BLOCKS,...HISTORICAL_WORKSPACE_BLOCKS,DESCRIBE_MEDIA_HOST_1_1,YOUTUBE_HOST_1_1,...GENERATION_HOST_RELEASES,...SDK_BUILTIN_BLOCKS.filter(block=>!WORKSPACE_BLOCKS.some(current=>current.manifest.id===block.manifest.id&¤t.manifest.version===block.manifest.version)),...LEGACY_BLOCKS.filter(block=>!WORKSPACE_BLOCKS.some(current=>current.manifest.id===block.manifest.id&¤t.manifest.version===block.manifest.version))];
|
|
131
152
|
const flows={guided:['voiceover','shot-plan','blueprint','pipeline','editor'],blank:['pipeline','editor'],imagePanel:['voiceover','panel-strip','editor'],whiteboard:['voiceover','board-editor','editor'],animated:['voiceover','shot-plan','animated-scene','editor']};
|
|
132
153
|
const typeDescriptions={
|
|
133
154
|
guided:'Turn an idea into a video, from the script to the final cut.',
|
|
@@ -139,7 +160,7 @@ const typeDescriptions={
|
|
|
139
160
|
// Freeze the actual pre-collaboration-release catalog fallback. Historical type
|
|
140
161
|
// definitions must not follow future changes to the discoverable Block map.
|
|
141
162
|
const PINNED_TYPE_WORKSPACE_VERSIONS=Object.freeze({'voiceover':'2.1.3','shot-plan':'2.1.2','blueprint':'2.1.7','pipeline':'2.2.2','editor':'2.2.7','panel-strip':'2.3.5','board-editor':'2.2.8','animated-scene':'2.3.6'});
|
|
142
|
-
const currentBuiltinTypes=(version,pipelineVersion,scriptVersion=SCRIPT_WRITER_BLOCK.manifest.version,versions={})=>LEGACY_TYPES.map(type=>({...type,description:typeDescriptions[type.legacyMode],summary:{...type.summary,text:typeDescriptions[type.legacyMode]},version,stages:flows[type.legacyMode].map((key,i)=>{const block=WORKSPACE_BLOCKS.find(block=>block.manifest.id===`sm.${key}`);return {id:`stage-${i+1}`,blockId:block.manifest.id,version:versions[key]||(key==='pipeline'?pipelineVersion:key==='script'?scriptVersion:PINNED_TYPE_WORKSPACE_VERSIONS[key]),label:block.manifest.name};})}));
|
|
163
|
+
const currentBuiltinTypes=(version,pipelineVersion,scriptVersion=SCRIPT_WRITER_BLOCK.manifest.version,versions={})=>LEGACY_TYPES.map(type=>({...type,description:typeDescriptions[type.legacyMode],summary:{...type.summary,text:typeDescriptions[type.legacyMode]},version,stages:flows[type.legacyMode].map((key,i)=>{const block=WORKSPACE_BLOCKS.find(block=>block.manifest.id===`sm.${key}`);return {id:`stage-${i+1}`,blockId:block.manifest.id,version:versions[key]||(key==='pipeline'?pipelineVersion:key==='script'?scriptVersion:PINNED_TYPE_WORKSPACE_VERSIONS[key]),label:key==='editor'?'Editor':block.manifest.name};})}));
|
|
143
164
|
const PRIOR_HOST_WORKSPACE_VERSIONS={'shot-plan':'1.1.0','blueprint':'1.1.0','panel-strip':'2.3.4','board-editor':'1.1.0','animated-scene':'1.1.0'};
|
|
144
165
|
const PREVIOUS_BUILTIN_TYPES=currentBuiltinTypes('1.2.0','1.2.0',SCRIPT_WRITER_BLOCK.manifest.version,PRIOR_HOST_WORKSPACE_VERSIONS);
|
|
145
166
|
const SHOTS_MIGRATED_TYPES=currentBuiltinTypes('1.3.0','1.2.0',SCRIPT_WRITER_BLOCK.manifest.version,{'blueprint':'1.1.0','board-editor':'1.1.0','animated-scene':'1.1.0'});
|
|
@@ -155,8 +176,10 @@ const SHARED_CANVAS_TYPES = currentBuiltinTypes('1.11.0','2.2.3',SCRIPT_WRITER_B
|
|
|
155
176
|
const SHARED_WORKSPACE_TYPES = currentBuiltinTypes('1.12.0','2.2.3',SCRIPT_WRITER_BLOCK.manifest.version,{'blueprint':'2.1.8','panel-strip':'2.3.6','board-editor':'2.2.9','animated-scene':'2.3.7'});
|
|
156
177
|
const PRIOR_CURRENT_TYPES = currentBuiltinTypes('1.13.0','2.2.3',SCRIPT_WRITER_BLOCK.manifest.version,{'blueprint':'2.1.8','panel-strip':'2.3.6','board-editor':'2.2.9','animated-scene':'2.3.7','editor':'2.2.8'});
|
|
157
178
|
const PREVIOUS_PORTABLE_TYPES=currentBuiltinTypes('1.14.0','2.2.4',SCRIPT_WRITER_BLOCK.manifest.version,{'blueprint':'2.1.8','panel-strip':'2.3.6','board-editor':'2.2.9','animated-scene':'2.3.7','editor':'2.2.8'});
|
|
158
|
-
|
|
159
|
-
const
|
|
179
|
+
// 1.16.0: every built-in type ends in the original Editor (1.0), the full video editor.
|
|
180
|
+
const PORTABLE_EDITOR_TYPES=currentBuiltinTypes('1.15.0','2.2.4','2.1.4',{'voiceover':'2.1.4','shot-plan':'2.1.3','blueprint':'2.1.9','panel-strip':'2.3.7','board-editor':'2.2.10','animated-scene':'2.3.8','editor':'2.2.9'});
|
|
181
|
+
export const CURRENT_BUILTIN_TYPES = [...currentBuiltinTypes('1.16.0','2.2.4','2.1.4',{'voiceover':'2.1.4','shot-plan':'2.1.3','blueprint':'2.1.9','panel-strip':'2.3.7','board-editor':'2.2.10','animated-scene':'2.3.8','editor':'1.0.0'}),...REVENUE_REVIEW_PROJECT_TYPES,PRODUCT_AD_VARIANTS_PROJECT_TYPE,...FIRST_REVENUE_PROJECT_TYPES];
|
|
182
|
+
const HISTORICAL_CURRENT_TYPES=[...PORTABLE_EDITOR_TYPES,PRODUCT_AD_VARIANTS_PROJECT_TYPE_1_0_3,PRIOR_PRODUCT_AD_VARIANTS_PROJECT_TYPE,PREVIOUS_PRODUCT_AD_VARIANTS_PROJECT_TYPE,...PREVIOUS_PORTABLE_TYPES,...PRIOR_CURRENT_TYPES,...SHARED_WORKSPACE_TYPES,...SHARED_CANVAS_TYPES,...SHARED_BLUEPRINT_TYPES,...PRIOR_SHARED_TYPES,...MEDIA_LIBRARY_TYPES,...EDITOR_AUDIO_TYPES,...PLAYABLE_EDITOR_TYPES,...ADVANCED_PORTABLE_TYPES,...PORTABLE_MVP_TYPES,...PRE_PROMOTION_CURRENT_TYPES,...PREVIOUS_BUILTIN_TYPES.filter(type=>flows[type.legacyMode].includes('shot-plan')),...currentBuiltinTypes('1.1.0','1.1.0','1.1.0',PRIOR_HOST_WORKSPACE_VERSIONS)];
|
|
160
183
|
export const BUILTIN_TYPES = [...CURRENT_BUILTIN_TYPES,...HISTORICAL_CURRENT_TYPES,...LEGACY_TYPES];
|
|
161
184
|
export const workspaceBlock = (id,version)=>DISCOVERABLE_BLOCKS.find(block=>block.manifest.id===id && (!version || block.manifest.version===version));
|
|
162
185
|
export const discoverableBuiltin = item=>!!workspaceBlock((item.manifest || item).id,(item.manifest || item).version);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {assert,digest} from '../block-sdk/contracts.js';
|
|
1
|
+
import {assert,digest,HOSTED_CAPABILITY_OPERATIONS,PORTABLE_ACTIONS} from '../block-sdk/contracts.js';
|
|
2
2
|
import {semanticValueIsFresh} from '../block-sdk/semantic-freshness.js';
|
|
3
3
|
import {readCanvasSemanticContext} from './semantic-results.js';
|
|
4
4
|
|
|
@@ -24,8 +24,12 @@ export async function pinExplicitFlowSource(pin,edge,sourceNode,canvasEdge,requi
|
|
|
24
24
|
export async function pinCurrentSemanticSource(canvas,stage,node,port,requirement,permissions=[]) {
|
|
25
25
|
const record=readCanvasSemanticContext(canvas).snapshot().values.find(value=>value.source.kind==='block' && value.source.stepId===stage.id && value.source.port===port);
|
|
26
26
|
assert(record && !node.data.blockStale && semanticValueIsFresh(record,requirement),'SEGMENT_SOURCE','Update the source Block before reusing its result.');
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// Hosted operations (text.generate, ...) and portable actions are things the
|
|
28
|
+
// producer did, not data its result carries: a Shots Block downstream of a
|
|
29
|
+
// Script Writer must not need text.generate to reuse the script. Data scopes
|
|
30
|
+
// (project context, network, filesystem, secrets) still have to be held.
|
|
31
|
+
const missingPermissions=(record.permissions||[]).filter(permission=>!permissions.includes(permission)&&!Object.hasOwn(HOSTED_CAPABILITY_OPERATIONS,permission)&&!PORTABLE_ACTIONS.includes(permission));
|
|
32
|
+
assert(!missingPermissions.length,'SEGMENT_SOURCE',`Not applied, for privacy: this saved result was made with project data this Step's Block release isn't allowed to read (${missingPermissions.slice(0,5).join(', ')}${missingPermissions.length>5?' and more':''}). Earlier updates are kept. Run this Step again from its page, or choose a Block release that can read that data.`);
|
|
29
33
|
assert(Object.hasOwn(node.data.blockOutputs || {},port),'SEGMENT_SOURCE','The source Block has no current saved result.');
|
|
30
34
|
const valueDigest=await digest(node.data.blockOutputs[port]);
|
|
31
35
|
assert(valueDigest===await digest(record.value),'SEGMENT_SOURCE','The saved result changed. Update its source before continuing.');
|
|
@@ -11,7 +11,7 @@ import {readCanvasSemanticContext} from './semantic-results.js';
|
|
|
11
11
|
import {stageNodeId} from './type-workspace.js';
|
|
12
12
|
import {nativeHostFor} from './native-hosts.js';
|
|
13
13
|
import {BUILTIN_BLOCKS} from './catalog.js';
|
|
14
|
-
import {ADAPTERS} from '../block-sdk/connection-adapters.js';
|
|
14
|
+
import {ADAPTERS,NARRATION_ADAPTERS} from '../block-sdk/connection-adapters.js';
|
|
15
15
|
import {projectFlowSnapshot,PROJECT_FLOW_CONTRACT_VERSION,PROJECT_FLOW_FAILURE_CODES} from '../block-sdk/project-flow-contract.js';
|
|
16
16
|
import {projectFlowOverrideTarget} from './project-flow-input-override.js';
|
|
17
17
|
import {portablePinnedValue,restorePinnedValue} from './pinned-media-value.js';
|
|
@@ -30,6 +30,9 @@ const mediaAvailability=value=>localMediaUrl(value)?'playable':/^(blob:|data:|fi
|
|
|
30
30
|
const localMediaDetail=value=>{
|
|
31
31
|
const url=value?.url||'',assetId=/^sm-media:\/\/asset\/([a-f0-9-]{36})$/.exec(url)?.[1];
|
|
32
32
|
if(assetId&&value.assetId===assetId&&typeof value.versionId==='string'&&/^[a-f0-9]{64}$/.test(value.versionId||''))return {localSource:'desktop',localReference:{assetId,versionId:value.versionId,kind:value.kind}};
|
|
33
|
+
// Media kept in a browser's own storage (client deviceMedia.js): the address names the device record exactly.
|
|
34
|
+
const device=/^sm-media:\/\/device\/([a-f0-9-]{36})\/([a-f0-9]{64})$/.exec(url);
|
|
35
|
+
if(device)return {localSource:'browser-device',localReference:{assetId:device[1],versionId:device[2],kind:value.kind}};
|
|
33
36
|
return /^(sm-media:|file:)/i.test(url)?{localSource:'device'}:/^blob:/i.test(url)?{localSource:'browser-session'}:/^data:/i.test(url)?{localSource:'inline'}:{};
|
|
34
37
|
};
|
|
35
38
|
const hasLocalMedia=value=>!!value&&(value.availability==='local-only'&&value.localSource!=='inline'||value.kind==='collection'&&(value.items||[]).some(item=>hasLocalMedia(item.preview))||value.kind==='structured'&&(value.fields||[]).some(field=>hasLocalMedia(field.preview)));
|
|
@@ -81,11 +84,18 @@ const pinOptionsFor=(canvas,producer,stage,portKey,inputRequirement,edge,labels,
|
|
|
81
84
|
};
|
|
82
85
|
const adapterDetail=(pin,materialized)=>{
|
|
83
86
|
if(!pin?.id)return null;
|
|
84
|
-
const definition=ADAPTERS.find(item=>item.id===pin.id&&Number(item.version)===Number(pin.version));
|
|
87
|
+
const definition=[...ADAPTERS,...NARRATION_ADAPTERS].find(item=>item.id===pin.id&&Number(item.version)===Number(pin.version));
|
|
85
88
|
if(!definition)return {id:String(pin.id).slice(0,160),version:String(pin.version??'unknown').slice(0,40),available:false,status:'This saved conversion version is unavailable.'};
|
|
86
|
-
const effect=pin.id==='scene-to-shots'?'Uses the selected scene’s ordered project shots.':pin.id==='shot-to-selected-image'?'Uses the project image selected for this shot.':pin.id==='csv-to-table'||pin.id==='csv-file-to-table'?'Reads CSV rows into a table.':pin.id==='text-to-script'?'Wraps text in the Block script shape.':pin.id==='script-to-text'?'Reads text from the Block script shape.':pin.id==='text-file-to-text'?'Reads the selected inline text file.':pin.id==='integer-to-number'?'Converts the integer value to a number.':pin.id.endsWith('-to-asset')?'Keeps the same media reference as a typed asset.':pin.id.startsWith('asset-to-')?'Passes the typed media reference to a matching media input.':'Applies the registered deterministic value conversion.';
|
|
89
|
+
const effect=pin.id==='scene-to-shots'?'Uses the selected scene’s ordered project shots.':pin.id==='shot-to-selected-image'?'Uses the project image selected for this shot.':pin.id==='csv-to-table'||pin.id==='csv-file-to-table'?'Reads CSV rows into a table.':pin.id==='text-to-script'?'Wraps text in the Block script shape.':pin.id==='script-as-narration'||pin.id==='text-as-narration'?'Uses this script as the narration.':pin.id==='transcript-as-narration-timing'?'Uses these word timings as the narration timing.':pin.id==='script-to-text'?'Reads text from the Block script shape.':pin.id==='text-file-to-text'?'Reads the selected inline text file.':pin.id==='integer-to-number'?'Converts the integer value to a number.':pin.id.endsWith('-to-asset')?'Keeps the same media reference as a typed asset.':pin.id.startsWith('asset-to-')?'Passes the typed media reference to a matching media input.':'Applies the registered deterministic value conversion.';
|
|
87
90
|
return {id:definition.id,version:definition.version,available:true,inputType:definition.output,outputType:definition.input,effect,status:materialized?'Source value is available; conversion runs with this input.':'Waiting for the source value; conversion has not run.',steps:definition.steps||[{id:definition.id,version:definition.version}],contextFields:definition.contextFields||[]};
|
|
88
91
|
};
|
|
92
|
+
// A project-context input (Shots' saved script, the voiceover timing) is filled
|
|
93
|
+
// from the project when the project has one; no connection can fill it.
|
|
94
|
+
const CONTEXT_NAMES={script:'saved script',transcript:'voiceover timing',shotPlan:'shot plan',assets:'media',versions:'media versions',editor:'edit',metadata:'details'};
|
|
95
|
+
export function contextInputNote(context){
|
|
96
|
+
const name=CONTEXT_NAMES[context]||String(context||'information').replace(/([a-z0-9])([A-Z])/g,'$1 $2').replace(/[_-]+/g,' ').toLowerCase();
|
|
97
|
+
return `Uses the project’s ${name} when there is one.`;
|
|
98
|
+
}
|
|
89
99
|
const sourceName=(selected,labels)=>{
|
|
90
100
|
if(['conversion-block','conversion-block-path'].includes(selected?.kind))return selected.sourceLabel || 'Add converter Blocks';
|
|
91
101
|
const source=selected?.record?.source;
|
|
@@ -189,7 +199,7 @@ export function inspectProjectFlow(type,canvas={nodes:[],edges:[]},{projectId=nu
|
|
|
189
199
|
const state=optionalUnused?'not-used':item.state==='broken'?'needs-choice':!item.requirement.required&&!item.selected?'not-used':sourceSkipped?'skipped':source?.kind==='block'&&!produced?'waiting':sourceStale?'needs-updating':materialized?'ready':item.state==='needs-user-input'?'needs-choice':'waiting';
|
|
190
200
|
const selectedEdge=canvasEdge&&source?.kind==='block'&&canvasEdge.source===producer?.id&&canvasStage?.id===source.stageId&&canvasPort===source.port?canvasEdge:null;
|
|
191
201
|
const pinOptions=selectedEdge&&!item.selected?.adapter?pinOptionsFor(canvas,producer,sourceStage,source.port,item.requirement,selectedEdge,labels,mediaVersions,restrictedResults):{canPinVersion:false,pinSource:null,pinChoices:[]};
|
|
192
|
-
return {key:item.key,label:item.label,required:item.requirement.required!==false,state,reason:sourceSkipped?'The selected Block skipped this conditional path, so it produced no value.':native?.reason||item.reason,source:sourceName(item.selected,labels),sourceKind:source?.kind||null,sourcePlacementId:source?.stageId||null,sourceOutputKey:source?.port||null,selectedCandidateId:item.selected?.id||null,explicit:item.explicit===true,selectionReason:source?selectionReason(item,source):null,valuePolicy:source?.kind==='block'?(item.explicit?'follow-selected-output':'automatic-resolution'):source?.kind==='default'?'declared-default':source?'current-source-value':null,adapter:item.selected?.adapter?{id:item.selected.adapter.id,version:item.selected.adapter.version}:null,adapterDetail:adapterDetail(item.selected?.adapter,materialized),materialized,...(native?.observation?{sourceObservation:{...native.observation,revision:retainedRevision([native.observation,native.value])}}:{}),preview:materialized&&!sensitive(item.selected?.record)&&!(source?.kind==='block'&&(!(native?.port||producer?.data?.blockPackage?.manifest?.outputs?.[source.port])||sensitive(native?.port||producer.data.blockPackage.manifest.outputs[source.port])))&&!restrictedResults.has(`${source?.stageId}:${source?.port}`)?inputPreview(value,item.requirement):null,...pinOptions,...candidatePageSummary,...overrideMeta};
|
|
202
|
+
return {key:item.key,label:item.label,...(item.requirement.context?{context:item.requirement.context}:{}),required:item.requirement.required!==false,state,reason:sourceSkipped?'The selected Block skipped this conditional path, so it produced no value.':native?.reason||(item.requirement.context&&!materialized?contextInputNote(item.requirement.context):item.reason),source:sourceName(item.selected,labels),sourceKind:source?.kind||null,sourcePlacementId:source?.stageId||null,sourceOutputKey:source?.port||null,selectedCandidateId:item.selected?.id||null,explicit:item.explicit===true,selectionReason:source?selectionReason(item,source):null,valuePolicy:source?.kind==='block'?(item.explicit?'follow-selected-output':'automatic-resolution'):source?.kind==='default'?'declared-default':source?'current-source-value':null,adapter:item.selected?.adapter?{id:item.selected.adapter.id,version:item.selected.adapter.version}:null,adapterDetail:adapterDetail(item.selected?.adapter,materialized),materialized,...(native?.observation?{sourceObservation:{...native.observation,revision:retainedRevision([native.observation,native.value])}}:{}),preview:materialized&&!sensitive(item.selected?.record)&&!(source?.kind==='block'&&(!(native?.port||producer?.data?.blockPackage?.manifest?.outputs?.[source.port])||sensitive(native?.port||producer.data.blockPackage.manifest.outputs[source.port])))&&!restrictedResults.has(`${source?.stageId}:${source?.port}`)?inputPreview(value,item.requirement):null,...pinOptions,...candidatePageSummary,...overrideMeta};
|
|
193
203
|
});
|
|
194
204
|
const last=node?.data?.blockLastRun;
|
|
195
205
|
const usedInputs=inputEvidence(last?.capturedInputs,inputs,[manifest?.inputs]);
|
|
@@ -15,7 +15,7 @@ export const PLAIN_BLOCK_DESCRIPTIONS=Object.freeze({
|
|
|
15
15
|
'stillmade.product-ad-variants':'Make three short product ads with different opening hooks from your own footage. Nothing is invented.',
|
|
16
16
|
'sm.youtube':'Upload a finished video to your connected YouTube channel.',
|
|
17
17
|
'sm.stock-search':'Search free-to-use stock photos and videos and pick one.',
|
|
18
|
-
'sm.export-video':'
|
|
18
|
+
'sm.export-video':'Opens the video editor at its Export: change anything on the timeline, then make the video file.',
|
|
19
19
|
'sm.export-audio':'Turn your timeline into an audio file you can download and keep in the project.',
|
|
20
20
|
'sm.motion-graphics':'Add animated titles, text, charts and transitions to your timeline. You see the change before it applies.',
|
|
21
21
|
'sm.score':'Put music, sound effects and voiceover from your project on the timeline. You see the change before it applies.',
|
|
@@ -115,6 +115,9 @@ export function projectContextSnapshot(data={},options={}) {
|
|
|
115
115
|
const ref={kind:'file',assetId:str(value.assetId),versionId:str(value.versionId),url:value.url,mimeType:value.mimeType,...pick(value,['name','bytes']),...ownership(owner),...(label?{label:str(label).slice(0,200)}:{}),source:'canvas'};
|
|
116
116
|
files.set(JSON.stringify([ref.assetId,ref.versionId,ref.url]),ref);
|
|
117
117
|
};
|
|
118
|
+
// Files a Step's "Render final file" stored (blockRenders): earlier versions only,
|
|
119
|
+
// never a current asset or an input of another Step.
|
|
120
|
+
const addFinishedRenders=(d,label)=>{for(const row of list(d.blockRenders))if(['video','audio'].includes(row?.kind)&&typeof row.url==='string'&&row.url.startsWith('/api/media/'))addMedia({...row,source:'block',owner:d,label,record:{createdAt:row.at},active:false});};
|
|
118
121
|
const canvasNodes=new Map(list(canvas.nodes).map(node=>[node.id,node]));
|
|
119
122
|
for(const node of canvasNodes.values()){
|
|
120
123
|
// outputPort can expose a programmable output named `out` through the
|
|
@@ -153,6 +156,9 @@ export function projectContextSnapshot(data={},options={}) {
|
|
|
153
156
|
for(const ref of current)if(!seen.has(ref.url)){const value=addMedia({...ref,source:'canvas',owner:d,role,label});if(value)selected.push(value);}
|
|
154
157
|
references[referenceField].push(document({id:node.id,name:str(d.styleMeta?.name || d.name || d.label),...pick(d,['description','characterId','locationId']),...ownership(d),source:'canvas',...(referenceField==='styles' ? pick(d.styleMeta,['stylePrompt','description']) : {}),reference:selected[0] || null,references:selected}));
|
|
155
158
|
}
|
|
159
|
+
// A finished render stays a version of the project whether or not its Step is
|
|
160
|
+
// current (October 17, 2026: a stale Export video Step hid its only render).
|
|
161
|
+
if(options.includeBlockOutputs!==false && node.type==='programmableBlockNode')addFinishedRenders(d,label);
|
|
156
162
|
if(options.includeBlockOutputs!==false && node.type==='programmableBlockNode' && !d.blockStale && !d.blockLastRun?.segmentRunId){
|
|
157
163
|
// Batch/single-run sources are captured by the host. Output refs are guest
|
|
158
164
|
// values, so their scene/shot claims must never widen context access.
|
|
@@ -170,6 +176,7 @@ export function projectContextSnapshot(data={},options={}) {
|
|
|
170
176
|
if(options.includeBlockOutputs!==false)for(const entry of list(canvas.workflowArchive)){
|
|
171
177
|
const node=entry.node,d=object(node?.data);
|
|
172
178
|
if(node?.type!=='programmableBlockNode')continue;
|
|
179
|
+
addFinishedRenders(d,entry.step?.label || d.blockPackage?.manifest?.name || node.id);
|
|
173
180
|
const results=[{outputs:d.blockOutputs,source:d.blockLastRun?.batchSource || d.blockLastRun?.source,at:d.blockLastRun?.at},...list(d.blockBatchResults),...list(d.blockSegmentResults),...list(d.blockUnusedResults).map(result=>({...result,active:false}))];
|
|
174
181
|
for(const result of results){
|
|
175
182
|
const owner=result.source ? ownership(result.source) : d;
|
|
@@ -7,7 +7,9 @@ export function projectInputSampleValues(type,samples={}) {
|
|
|
7
7
|
assert(plain(samples),'PROJECT_INPUT','Use a shared sample object keyed by input name.');
|
|
8
8
|
const declarations=validateProjectInputs(type),byKey=new Map(declarations.map(contract=>[contract.key,contract]));
|
|
9
9
|
assert(Object.keys(samples).every(key=>byKey.has(key)),'PROJECT_INPUT','A shared sample refers to an input that is no longer declared.');
|
|
10
|
-
|
|
10
|
+
// The author's rehearsal of the Type uses every shared default, briefs included; a real
|
|
11
|
+
// project never starts with a brief default (project-inputs.js).
|
|
12
|
+
const initial=seedProjectInputDefaults({},type,{briefs:true}).semanticContext;
|
|
11
13
|
const context=createSemanticContext(initial);
|
|
12
14
|
for(const [key,value] of Object.entries(samples)) {
|
|
13
15
|
const contract=byKey.get(key);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {assert,plain,safeDocument,stableStringify} from '../block-sdk/contracts.js';
|
|
2
2
|
import {input,semanticCompatibility} from '../block-sdk/semantic-contracts.js';
|
|
3
3
|
import {createSemanticContext} from './semantic-context.js';
|
|
4
|
+
import {isBriefPort} from './brief-ports.js';
|
|
4
5
|
|
|
5
6
|
const fields=new Set(['key','type','semantic','required','description','accepts','schema','sources','default','sensitivity','freshnessPolicy','cardinality','batchable','min','max']);
|
|
6
7
|
|
|
@@ -45,8 +46,16 @@ export function validateProjectInputs(type) {
|
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
// A shared input that is a brief (brief-ports.js) never starts a project with the Type's
|
|
50
|
+
// default: a brief is the facts about what this project makes, which its own Chat step
|
|
51
|
+
// gathers, and a default was written while the Type was being built (October 16, 2026).
|
|
52
|
+
// briefs:true is only for the author's own rehearsal of the Type (type-preview.js samples).
|
|
53
|
+
// A shared input whose meaning is a brief (project_brief, production_brief, or a
|
|
54
|
+
// Block's own kind of brief such as "noman.vehicle_brief") is one too: its default
|
|
55
|
+
// resolves into every Step brief of that meaning (October 17, 2026).
|
|
56
|
+
export const briefLikeInput=contract=>isBriefPort(contract.key,{...contract,label:contract.description})||/\bbrief\b/i.test(String(contract.semantic||'').replace(/[_\-.]+/g,' '));
|
|
57
|
+
export function seedProjectInputDefaults(canvas,type,{now=Date.now(),briefs=false}={}) {
|
|
58
|
+
const declarations=validateProjectInputs(type).filter(contract=>Object.hasOwn(contract,'default')&&(briefs||!briefLikeInput(contract)));
|
|
50
59
|
if(!declarations.length)return canvas;
|
|
51
60
|
assert(Number.isFinite(now) && now>=0,'PROJECT_INPUT','Use a valid project start time.');
|
|
52
61
|
assert(canvas.semanticContext===undefined || canvas.semanticContext?.schemaVersion===1 && Array.isArray(canvas.semanticContext.values),'PROJECT_CONTEXT','Use a supported project context.');
|