diff --git a/demo-sample.mp4 b/demo-sample.mp4 new file mode 100644 index 0000000..c600153 Binary files /dev/null and b/demo-sample.mp4 differ diff --git a/index.html b/index.html index d257cbf..b198830 100644 --- a/index.html +++ b/index.html @@ -1206,6 +1206,8 @@

Top up to keep running

"write a note…":"escribe una nota…", "remove":"quitar", "to run":"para ejecutar", + "to run for real":"para ejecutar de verdad", + "The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.":"Los resultados de muestra son pregrabados, así que esta ejecución no se cobra. Esto es lo que cuesta ejecutar el flujo con tu propia clave.", "Estimated cost to ▶ Run all — {n} paid node":"Coste estimado de ▶ Ejecutar todo — {n} nodo de pago", "Estimated cost to ▶ Run all — {n} paid nodes":"Coste estimado de ▶ Ejecutar todo — {n} nodos de pago", ", plus {n} with no listed price":", más {n} sin precio listado", @@ -1254,6 +1256,7 @@

Top up to keep running

"turns this into a shareable mini app.":"lo convierte en una mini app compartible.", "Sample preview":"Vista previa de muestra", "your edits aren’t reflected yet — results below are still the starter sample. Sign in to run YOUR prompt for real, pay-per-use on your own key.":"tus cambios aún no se reflejan — abajo sigue la muestra del ejemplo inicial. Inicia sesión para ejecutar TU prompt de verdad, pago por uso con tu propia clave.", + "the clip below is a canned example of what Image→Video makes, not your prompt. Sign in to run YOUR graph for real, pay-per-use on your own key.":"el clip de abajo es un ejemplo enlatado de lo que hace Imagen→Vídeo, no tu prompt. Inicia sesión para ejecutar TU flujo de verdad, pago por uso con tu propia clave.", "This is your app.":"Esta es tu app.", "restyles it;":"la rediseña;", "returns to the graph and keeps this app bound.":"vuelve al grafo y mantiene esta app vinculada.", @@ -1445,7 +1448,8 @@

Top up to keep running

"results below are canned examples, not live AI — sign in to run it for real, pay-per-use on your own key.":"los resultados de abajo son ejemplos pregrabados, no IA en vivo — inicia sesión para ejecutarlo de verdad, pago por uso con tu propia clave.", "sample result — sign in to run it for real":"resultado de muestra — inicia sesión para ejecutarlo de verdad", "Run to make your own":"Ejecuta para crear el tuyo", - "samples cover text and images only — sign in to run this node for real":"las muestras solo cubren texto e imágenes — inicia sesión para ejecutar este nodo de verdad", + "the sample doesn't cover this node — sign in to run it for real":"la muestra no cubre este nodo — inicia sesión para ejecutarlo de verdad", + "the sample clip couldn't load — sign in to run this node for real":"no se pudo cargar el clip de muestra — inicia sesión para ejecutar este nodo de verdad", "more sign-in options":"más opciones de inicio de sesión", "sign out":"cerrar sesión", "checking…":"comprobando…", @@ -1721,6 +1725,8 @@

Top up to keep running

"write a note…":"écrire une note…", "remove":"retirer", "to run":"pour exécuter", + "to run for real":"pour exécuter vraiment", + "The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.":"Les résultats d’exemple sont préenregistrés, donc cette exécution n’est pas facturée. Voici ce que coûte l’exécution du graphe avec votre propre clé.", "Estimated cost to ▶ Run all — {n} paid node":"Coût estimé de ▶ Tout exécuter — {n} nœud payant", "Estimated cost to ▶ Run all — {n} paid nodes":"Coût estimé de ▶ Tout exécuter — {n} nœuds payants", ", plus {n} with no listed price":", plus {n} sans prix affiché", @@ -1775,6 +1781,7 @@

Top up to keep running

"turns this into a shareable mini app.":"en fait une mini app partageable.", "Sample preview":"Aperçu d’exemple", "your edits aren’t reflected yet — results below are still the starter sample. Sign in to run YOUR prompt for real, pay-per-use on your own key.":"vos modifications ne sont pas encore prises en compte — les résultats ci-dessous restent l’exemple de départ. Connectez-vous pour exécuter VOTRE prompt pour de vrai, paiement à l’usage avec votre clé.", + "the clip below is a canned example of what Image→Video makes, not your prompt. Sign in to run YOUR graph for real, pay-per-use on your own key.":"le clip ci-dessous est un exemple préenregistré de ce que fait Image→Vidéo, pas votre prompt. Connectez-vous pour exécuter VOTRE graphe pour de vrai, paiement à l’usage avec votre clé.", "This is your app.":"Voici votre app.", "restyles it;":"la restyle ;", "returns to the graph and keeps this app bound.":"revient au graphe et garde cette app liée.", @@ -1960,7 +1967,8 @@

Top up to keep running

"results below are canned examples, not live AI — sign in to run it for real, pay-per-use on your own key.":"les résultats ci-dessous sont des exemples préenregistrés, pas de l’IA en direct — connectez-vous pour l’exécuter vraiment, paiement à l’usage avec votre propre clé.", "sample result — sign in to run it for real":"résultat d’exemple — connectez-vous pour l’exécuter vraiment", "Run to make your own":"Lancez pour créer le vôtre", - "samples cover text and images only — sign in to run this node for real":"les exemples ne couvrent que le texte et les images — connectez-vous pour vraiment exécuter ce nœud", + "the sample doesn't cover this node — sign in to run it for real":"l’exemple ne couvre pas ce nœud — connectez-vous pour l’exécuter pour de vrai", + "the sample clip couldn't load — sign in to run this node for real":"le clip d’exemple n’a pas pu être chargé — connectez-vous pour exécuter ce nœud pour de vrai", "more sign-in options":"autres options de connexion", "sign out":"se déconnecter", "checking…":"vérification…", @@ -2236,6 +2244,8 @@

Top up to keep running

"write a note…":"Notiz schreiben…", "remove":"entfernen", "to run":"zum Ausführen", + "to run for real":"um wirklich auszuführen", + "The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.":"Die Beispielergebnisse sind vorbereitet, deshalb wird dieser Lauf nicht berechnet. So viel kostet es, den Graphen mit eigenem Schlüssel auszuführen.", "Estimated cost to ▶ Run all — {n} paid node":"Geschätzte Kosten für ▶ Alles ausführen — {n} kostenpflichtiger Node", "Estimated cost to ▶ Run all — {n} paid nodes":"Geschätzte Kosten für ▶ Alles ausführen — {n} kostenpflichtige Nodes", ", plus {n} with no listed price":", plus {n} ohne gelisteten Preis", @@ -2286,6 +2296,7 @@

Top up to keep running

"turns this into a shareable mini app.":"macht daraus eine teilbare Mini-App.", "Sample preview":"Beispiel-Vorschau", "your edits aren’t reflected yet — results below are still the starter sample. Sign in to run YOUR prompt for real, pay-per-use on your own key.":"deine Änderungen sind noch nicht drin — unten ist noch das Starter-Beispiel. Melde dich an, um DEINEN Prompt wirklich auszuführen, Bezahlung pro Nutzung mit eigenem Schlüssel.", + "the clip below is a canned example of what Image→Video makes, not your prompt. Sign in to run YOUR graph for real, pay-per-use on your own key.":"der Clip unten ist ein vorgefertigtes Beispiel dafür, was Bild→Video macht, nicht dein Prompt. Melde dich an, um DEINEN Graphen wirklich auszuführen, Bezahlung pro Nutzung mit eigenem Schlüssel.", "This is your app.":"Das ist deine App.", "restyles it;":"gestaltet sie um;", "returns to the graph and keeps this app bound.":"kehrt zum Graph zurück und behält diese App gebunden.", @@ -2475,7 +2486,8 @@

Top up to keep running

"results below are canned examples, not live AI — sign in to run it for real, pay-per-use on your own key.":"die Ergebnisse unten sind vorbereitete Beispiele, keine echte KI — melde dich an, um es wirklich auszuführen, Bezahlung pro Nutzung mit eigenem Schlüssel.", "sample result — sign in to run it for real":"Beispielergebnis — melde dich an, um es wirklich auszuführen", "Run to make your own":"Starte, um dein eigenes zu erstellen", - "samples cover text and images only — sign in to run this node for real":"Beispiele decken nur Text und Bilder ab — melde dich an, um diesen Knoten wirklich auszuführen", + "the sample doesn't cover this node — sign in to run it for real":"das Beispiel deckt diesen Knoten nicht ab — melde dich an, um ihn wirklich auszuführen", + "the sample clip couldn't load — sign in to run this node for real":"der Beispielclip konnte nicht geladen werden — melde dich an, um diesen Knoten wirklich auszuführen", "more sign-in options":"weitere Anmeldeoptionen", "sign out":"abmelden", "checking…":"wird geprüft…", @@ -2751,6 +2763,8 @@

Top up to keep running

"write a note…":"escreva uma nota…", "remove":"remover", "to run":"para executar", + "to run for real":"para executar de verdade", + "The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.":"Os resultados de amostra são pré-gravados, por isso esta execução não é cobrada. Isto é o que custa executar o fluxo com a sua própria chave.", "Estimated cost to ▶ Run all — {n} paid node":"Custo estimado de ▶ Executar tudo — {n} nó pago", "Estimated cost to ▶ Run all — {n} paid nodes":"Custo estimado de ▶ Executar tudo — {n} nós pagos", ", plus {n} with no listed price":", mais {n} sem preço listado", @@ -2800,6 +2814,7 @@

Top up to keep running

"turns this into a shareable mini app.":"transforma isto numa mini app partilhável.", "Sample preview":"Pré-visualização de amostra", "your edits aren’t reflected yet — results below are still the starter sample. Sign in to run YOUR prompt for real, pay-per-use on your own key.":"as suas edições ainda não se reflectem — abaixo continua a amostra inicial. Entre para executar O SEU prompt de verdade, pagamento por uso com a sua própria chave.", + "the clip below is a canned example of what Image→Video makes, not your prompt. Sign in to run YOUR graph for real, pay-per-use on your own key.":"o clipe abaixo é um exemplo pré-gravado do que Imagem→Vídeo faz, não o seu prompt. Entre para executar O SEU fluxo de verdade, pagamento por uso com a sua própria chave.", "This is your app.":"Esta é a sua app.", "restyles it;":"redesenha-a;", "returns to the graph and keeps this app bound.":"volta ao grafo e mantém esta app ligada.", @@ -2990,7 +3005,8 @@

Top up to keep running

"results below are canned examples, not live AI — sign in to run it for real, pay-per-use on your own key.":"os resultados abaixo são exemplos pré-gravados, não IA ao vivo — entre para executar de verdade, pagamento por uso com a sua própria chave.", "sample result — sign in to run it for real":"resultado de amostra — entre para executar de verdade", "Run to make your own":"Execute para criar o seu", - "samples cover text and images only — sign in to run this node for real":"as amostras cobrem apenas texto e imagens — entre para executar este nó de verdade", + "the sample doesn't cover this node — sign in to run it for real":"a amostra não cobre este nó — entre para executá-lo de verdade", + "the sample clip couldn't load — sign in to run this node for real":"não foi possível carregar o clipe de amostra — entre para executar este nó de verdade", "more sign-in options":"mais opções de login", "sign out":"sair", "checking…":"verificando…", @@ -3266,6 +3282,8 @@

Top up to keep running

"write a note…":"メモを書く…", "remove":"削除", "to run":"で実行", + "to run for real":"を実際に実行", + "The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.":"サンプル結果はあらかじめ用意されたものなので、この実行は課金されません。自分のキーでこのグラフを実行するとこの費用がかかります。", "Estimated cost to ▶ Run all — {n} paid node":"▶ すべて実行の推定コスト — 有料ノード {n} 個", "Estimated cost to ▶ Run all — {n} paid nodes":"▶ すべて実行の推定コスト — 有料ノード {n} 個", ", plus {n} with no listed price":"、ほかに価格未掲載 {n} 個", @@ -3311,6 +3329,7 @@

Top up to keep running

"turns this into a shareable mini app.":"で共有できるミニアプリにします。", "Sample preview":"サンプルプレビュー", "your edits aren’t reflected yet — results below are still the starter sample. Sign in to run YOUR prompt for real, pay-per-use on your own key.":"編集はまだ反映されていません — 下はスターターのサンプルのままです。サインインすると自分のプロンプトを本当に実行できます(自分のキーで従量課金)。", + "the clip below is a canned example of what Image→Video makes, not your prompt. Sign in to run YOUR graph for real, pay-per-use on your own key.":"下のクリップは画像→動画の出力例(録画済み)で、あなたのプロンプトの結果ではありません。サインインすると自分のグラフを本当に実行できます(自分のキーで従量課金)。", "This is your app.":"これがあなたのアプリです。", "restyles it;":"見た目を変え、", "returns to the graph and keeps this app bound.":"でグラフに戻り、このアプリの紐づけは保たれます。", @@ -3505,7 +3524,8 @@

Top up to keep running

"results below are canned examples, not live AI — sign in to run it for real, pay-per-use on your own key.":"以下の結果はあらかじめ用意されたサンプルで、実際のAIではありません — サインインすると自分のキーで本当に実行できます(従量課金)。", "sample result — sign in to run it for real":"サンプル結果 — サインインすると本当に実行できます", "Run to make your own":"実行して自分だけの作品を", - "samples cover text and images only — sign in to run this node for real":"サンプルはテキストと画像のみ対応 — サインインするとこのノードを本当に実行できます", + "the sample doesn't cover this node — sign in to run it for real":"このノードはサンプルの対象外です — サインインすると本当に実行できます", + "the sample clip couldn't load — sign in to run this node for real":"サンプル動画を読み込めませんでした — サインインするとこのノードを本当に実行できます", "more sign-in options":"その他のサインイン方法", "sign out":"サインアウト", "checking…":"確認中…", @@ -5748,8 +5768,10 @@

Top up to keep running

return { usd, priced, unpriced, exact }; } const fmtUsd = (v)=> v>=0.01 ? `$${v.toFixed(2)}` : v>0 ? `<$0.01` : `$0.00`; -// True while a signed-out visitor can still free-sample Run (pristine starter OR same topology with -// only field tweaks — see demoMode). We hide the price so the first click reads as "just try it". +// True while a signed-out visitor can still free-sample Run (pristine starter, same topology with +// only field tweaks, or the covered Image→Video extension — see demoMode). The run-cost chip stays +// VISIBLE here, relabelled "to run for real": this click is not billed, but the figure is exactly +// what the visitor wants before they sign in. See refreshRunEstimate. function inDemoRun(){ return !getKey() && !!demoMode(); } // Paint a faded, captioned teaser of what ▶ Run produces into an output node's result area, so a // first-run visitor sees the reward at rest — not just the wired-up apparatus. View-only: never sets @@ -5782,14 +5804,24 @@

Top up to keep running

function refreshRunEstimate(){ refreshDemoGhosts(); // demo teaser shares this function's three trigger sites const el = $("runest"); if(!el) return; - if(inDemoRun()){ el.hidden = true; return; } // free sample run → no price talk yet + // A demo run is not billed, but the chip STAYS UP — relabelled so it clearly prices the real + // run, not this click. Hiding it used to look harmless (nothing is charged, so nothing is + // concealed) and is not: the one thing a signed-out visitor can add to the starter is an + // Image→Video node, the priciest node on the canvas, and the cost figure vanished at exactly + // the moment it became worth reading. A price that disappears when it gets big reads as a dark + // pattern however it got there, so we show it and say which run it is for. + const demo = inDemoRun(); const { usd, priced, unpriced, exact } = workflowCost(); if(!priced){ el.hidden = true; return; } // nothing priced to run → hide the chip entirely el.hidden = false; - el.innerHTML = `${exact?"":"~"}${fmtUsd(usd)} ${esc(t("to run"))}`; - el.title = t(priced>1 ? "Estimated cost to ▶ Run all — {n} paid nodes" : "Estimated cost to ▶ Run all — {n} paid node").replace("{n}",()=>priced) - + (unpriced ? t(", plus {n} with no listed price").replace("{n}",()=>unpriced) : "") - + (exact ? "." : "." + "\n" + t("Video, text & audio are estimates: the real bill depends on clip length, tokens and characters.")); + const amt = `${exact?"":"~"}${fmtUsd(usd)} `; + el.innerHTML = demo ? amt + `${esc(t("to run for real"))}` : amt + `${esc(t("to run"))}`; + el.title = (demo + ? t("The sample results are canned, so this run isn’t billed. This is what running the graph on your own key costs.") + : t(priced>1 ? "Estimated cost to ▶ Run all — {n} paid nodes" : "Estimated cost to ▶ Run all — {n} paid node").replace("{n}",()=>priced) + + (unpriced ? t(", plus {n} with no listed price").replace("{n}",()=>unpriced) : "") + + ".") + + (exact ? "" : "\n" + t("Video, text & audio are estimates: the real bill depends on clip length, tokens and characters.")); } function refreshAllPrices(){ graph.nodes.forEach(n=>{ updateNodePrice(n); refreshDims(n); refreshAudioParams(n); refreshTranscribeLang(n); refreshCivitParams(n); refreshLoraParams(n); refreshModelOpts(n); refreshLlmOpts(n); refreshImageInputs(n); refreshAudioInputs(n); refreshEndFrameInput(n); refreshRefInputs(n); refreshRefHint(n); backfillModel(n); }); refreshRunEstimate(); refreshPromptCaps(); } // refreshPromptCaps: audio caps ARE catalog metadata (max_chars) and every note names the model, so both are blank until the catalog lands. refreshLlmOpts: reconcile knobs kept during a catalog miss (#311) once real capability flags exist; the dynamic port families (img/audio/endframe/ref) are catalog-gated too — without them a cold-cache boot (shared link, cleared storage) paints a saved ref-model graph portless until a reload function backfillModel(n){ // once a catalog arrives, give model nodes a sensible default if none chosen yet @@ -8913,7 +8945,16 @@

Top up to keep running

demoStarterSig — full content (fields + types + links); exact match → sample as-is demoStarterShape — topology only (ids/types/links); same shape with field tweaks still free-samples, but the pill warns that canned results ignore the edit. - Topology changes, share links, examples, etc. still get the honest sign-in wall. */ + + Plus ONE covered extension: the starter with a 📽️ Image→Video node hung off the image + output (see demoVideoNode). Video is the thing people come here to see, and a curious + signed-out visitor who wires one up used to lose the demo entirely — the topology no longer + matched, so the Run click ended on the sign-in wall instead of the wow. That shape now serves + demo-sample.mp4, rendered once from the shipped demo-sample.jpg by a real image→video model + (scripts/gen-demo-clip.mjs records the model and reproduces the file). + + Everything else — other topologies, share links, examples — still gets the honest sign-in + wall, and every canned result carries the sample badge. */ let demoStarterSig = null; let demoStarterShape = null; // Topology fingerprint (ids + types + wire ends). Field edits don't change it; add/remove/rewire do. @@ -8924,11 +8965,33 @@

Top up to keep running

links: (g.links||[]).map(l=>({ f:l.from, t:l.to })), }); } -// 'exact' | 'shape' | null — whether a signed-out Run may free-sample this canvas. +// The ONE covered extension of the starter: an 📽️ Image→Video node fed by the image node's +// output, with nothing downstream of it. Returns that node, or null when the canvas is anything +// else. Structural on purpose (not a second baked signature): the appended node's id depends on +// how many nodes the visitor added and removed first, so an id-for-id comparison would miss the +// very path we are covering. +function demoVideoNode(g){ + if(!demoStarterShape) return null; + g = g || graph; + const nodes = g.nodes || [], links = g.links || []; + const vids = nodes.filter(n=> n.type === "ivideo"); + if(vids.length !== 1) return null; + const v = vids[0]; + if(links.some(l=> l.from.node === v.id)) return null; // nothing downstream — we can only can ITS output + const into = links.filter(l=> l.to.node === v.id); + if(into.length !== 1 || into[0].to.port !== "image") return null; // exactly one wire, into the image port (an end-frame morph is not covered) + const src = nodes.find(n=> n.id === into[0].from.node); + if(!src || src.type !== "image" || into[0].from.port !== "image") return null; + // ...and the rest of the canvas is still the starter, wire for wire. + const rest = { nodes: nodes.filter(n=> n.id !== v.id), links: links.filter(l=> l.to.node !== v.id) }; + return demoShapeSig(rest) === demoStarterShape ? v : null; +} +// 'exact' | 'shape' | 'video' | null — whether a signed-out Run may free-sample this canvas. function demoMode(){ if(!demoStarterShape) return null; if(demoStarterSig && appHandoffSig() === demoStarterSig) return "exact"; if(demoShapeSig() === demoStarterShape) return "shape"; + if(demoVideoNode()) return "video"; return null; } const DEMO_LLM_TEXT = "A cozy, intimate ramen shop on a rainy night, viewed from the quiet street looking slightly through the steamed glass window. Warm, glowing amber light spills out onto the wet pavement, contrasting beautifully with the cool, moody blue and teal tones of the rainy city. Inside, a lone patron in a yellow raincoat sits at a wooden bar, steam rising in thick, inviting plumes from a large bowl of rich tonkotsu ramen. Paper lanterns emit a soft, nostalgic luminescence over wooden interiors. Puddles on the cobblestone street reflect the vibrant shop signs and passing neon city lights. Cinematic lighting, photorealistic, 8k resolution, ultra-detailed, comforting and atmospheric mood, anime-inspired realism."; @@ -8944,6 +9007,25 @@

Top up to keep running

}catch(_){ _demoImgUrl = DEMO_IMG_FALLBACK; } return _demoImgUrl; } +// The clip half of the sample, same contract as demoImage(): shipped next to the page and read +// into a data URL, so ⬇ save / ↗ open behave exactly like a real result. +// No stand-in here, unlike DEMO_IMG_FALLBACK: a