Files
studio/branding/hanzo-deeplink.js
Hanzo AI 2ea07a2678 feat(studio): deep-link opener — ?org=&workflow=&run=1 opens (and runs) a saved workflow
A gallery/console/chat link lands you on the graph ready to render: sets the
session org (so a re-run dispatches to that org's BYO-GPU and outputs to its
gallery), loads the saved workflow, and optionally auto-queues. Injected as a
frontend shim (patch_deeplink.py, step 13 of apply-branding), guarded end-to-end.
2026-07-14 06:06:24 -07:00

104 lines
4.0 KiB
JavaScript

/* Hanzo Studio deep-link — open a saved workflow (and optionally run it) straight
* from a URL, so a link in the gallery / console / chat lands you on the graph
* ready to render.
*
* Params (query string or hash, e.g. ?org=karma&workflow=fashion/shoot_valentina.json&run=1):
* org active org to scope userdata + render output to (sets the studio
* session org cookie BEFORE loading, so a re-run dispatches to the
* org's BYO-GPU and lands the output in that org's gallery).
* workflow path under the user's workflows/ dir (e.g. "fashion/shoot_x.json").
* run "1" to auto-queue the graph once loaded (re-run in one click).
*
* Injected as the last <script> by branding/patch_deeplink.py so window.app
* exists. Guarded end-to-end: a bad param logs and no-ops, never throws.
*/
(function () {
"use strict";
var VERSION = "1";
function app() { return window.app; }
function params() {
var out = {};
var q = new URLSearchParams(window.location.search);
q.forEach(function (v, k) { out[k] = v; });
if (window.location.hash && window.location.hash.length > 1) {
var h = new URLSearchParams(window.location.hash.slice(1));
h.forEach(function (v, k) { if (out[k] == null) out[k] = v; });
}
return out;
}
function waitFor(cond, ms) {
return new Promise(function (resolve, reject) {
var t0 = Date.now();
(function poll() {
var v; try { v = cond(); } catch (e) { v = null; }
if (v) return resolve(v);
if (Date.now() - t0 > ms) return reject(new Error("timeout"));
setTimeout(poll, 150);
})();
});
}
function toast(msg, ok) {
var el = document.createElement("div");
el.textContent = msg;
el.style.cssText =
"position:fixed;top:14px;left:50%;transform:translateX(-50%);z-index:99999;" +
"padding:8px 14px;border-radius:8px;font:13px system-ui;color:#fff;" +
"background:" + (ok ? "#1f7a4d" : "#8a1f1f") + ";box-shadow:0 4px 16px rgba(0,0,0,.35)";
document.body.appendChild(el);
setTimeout(function () { el.style.transition = "opacity .4s"; el.style.opacity = "0"; }, 3200);
setTimeout(function () { el.remove(); }, 3700);
}
async function setOrg(org) {
// Set the studio active org so userdata + render output scope to it. Same-origin
// fetch carries cookies; the response Set-Cookie updates studio_active_org.
var r = await fetch("/v1/session/org", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({ org: org }),
});
if (!r.ok) throw new Error("org switch failed (" + r.status + ")");
}
async function loadWorkflow(wf) {
var key = "workflows/" + String(wf).replace(/^\/+/, "");
var r = await fetch("/userdata/" + encodeURIComponent(key), { credentials: "same-origin" });
if (!r.ok) throw new Error("workflow not found: " + wf + " (" + r.status + ")");
var data = await r.json();
await app().loadGraphData(data);
return key;
}
async function run() {
var p = params();
if (!p.workflow && !p.org) return; // nothing to do — normal studio load
await waitFor(function () { return app() && typeof app().loadGraphData === "function"; }, 30000);
try {
if (p.org) { await setOrg(p.org); }
if (p.workflow) {
await loadWorkflow(p.workflow);
toast("Opened " + p.workflow + (p.org ? " · " + p.org : ""), true);
if (String(p.run) === "1" && typeof app().queuePrompt === "function") {
// Give the graph a tick to settle before queuing.
setTimeout(function () {
try { app().queuePrompt(0); toast("Queued render → your GPU", true); }
catch (e) { toast("Auto-run failed: " + e.message, false); }
}, 400);
}
} else if (p.org) {
toast("Switched to " + p.org, true);
}
} catch (e) {
console.warn("[Hanzo deep-link]", e);
toast(e.message, false);
}
}
run();
console.log("[Hanzo deep-link] v" + VERSION + " ready");
})();