diff --git a/system/public/aesthetic.computer/bios.mjs b/system/public/aesthetic.computer/bios.mjs index 70453a619d..7a7d3a4d6c 100644 --- a/system/public/aesthetic.computer/bios.mjs +++ b/system/public/aesthetic.computer/bios.mjs @@ -816,6 +816,7 @@ async function boot(parsed, bpm = 60, resolution, debug) { preservedParams = {}; if (resolution.gap === 0) preservedParams.nogap = "true"; // gap: 0 means nogap was true if (resolution.nolabel === true) preservedParams.nolabel = "true"; + if (resolution.shellhtml === true) preservedParams.shellhtml = "true"; if (resolution.tv === true) preservedParams.tv = "true"; if (resolution.device === true) preservedParams.device = "true"; if (resolution.solo === true) preservedParams.solo = "true"; @@ -13866,6 +13867,9 @@ async function boot(parsed, bpm = 60, resolution, debug) { if (preservedParams.nolabel) { currentUrl.searchParams.set("nolabel", preservedParams.nolabel); } + if (preservedParams.shellhtml) { + currentUrl.searchParams.set("shellhtml", preservedParams.shellhtml); + } if (preservedParams.duration) { currentUrl.searchParams.set("duration", preservedParams.duration); } diff --git a/system/public/aesthetic.computer/boot.mjs b/system/public/aesthetic.computer/boot.mjs index 85a8699f3b..6ed3db1c97 100644 --- a/system/public/aesthetic.computer/boot.mjs +++ b/system/public/aesthetic.computer/boot.mjs @@ -528,7 +528,7 @@ if (window.max?.outlet) { // List of legitimate query parameters that should be preserved const LEGITIMATE_PARAMS = [ 'icon', 'preview', 'signup', 'supportSignUp', 'success', 'code', - 'supportForgotPassword', 'message', 'vscode', 'nogap', 'nolabel', + 'supportForgotPassword', 'message', 'vscode', 'nogap', 'nolabel', 'shellhtml', 'density', 'zoom', 'duration', 'session-aesthetic', 'session-sotce', 'notice', 'tv', 'highlight', 'noauth', 'nocache', 'daw', 'width', 'height', 'desktop', 'device', 'perf', 'auto-scale', 'solo', 'crossfade', 'maxfps' @@ -1109,6 +1109,10 @@ const nogap = params.has("nogap") || params.has("desktop") || location.search.in // Check for nolabel parameter (no localStorage persistence) const nolabel = params.has("nolabel") || params.has("desktop") || location.search.includes("nolabel"); +// 🐚 shellhtml β€” an HTML shell (prompt.ac) hosts the runtime and owns the +// prompt/corner chrome in DOM; the composited equivalents stand down. +const shellhtml = params.has("shellhtml") || location.search.includes("shellhtml"); + // Check for desktop mode (Electron app) - combines nogap + nolabel const desktop = params.has("desktop") || location.search.includes("desktop"); @@ -1237,7 +1241,7 @@ if (window.acVSCODE) { // Pass the parameters directly without stripping them bootLog(`booting: ${parsed?.text || 'prompt'}`); -boot(parsed, bpm, { gap: nogap ? 0 : undefined, nolabel, density, zoom, duration, tv, highlight, desktop, device, perf, autoScale, solo, maxfps, spoofaudio }, debug); +boot(parsed, bpm, { gap: nogap ? 0 : undefined, nolabel, shellhtml, density, zoom, duration, tv, highlight, desktop, device, perf, autoScale, solo, maxfps, spoofaudio }, debug); // Start processing any early kidlisp messages that arrived before boot completed processEarlyKidlispQueue(); diff --git a/system/public/aesthetic.computer/lib/disk.mjs b/system/public/aesthetic.computer/lib/disk.mjs index e6297bc780..ab055aa19c 100644 --- a/system/public/aesthetic.computer/lib/disk.mjs +++ b/system/public/aesthetic.computer/lib/disk.mjs @@ -970,6 +970,10 @@ function fillExpandedWithFadePixels(screen, width, height, oldWidth, oldHeight, } const projectionMode = location.search.indexOf("nolabel") > -1; // Skip loading noise. +// 🐚 shellhtml mode β€” the runtime is hosted by an HTML shell (prompt.ac) that +// owns the prompt + return-to-prompt chrome in crisp DOM above the pixel +// buffer. The composited typable prompt and corner label stand down here. +const shellHTMLMode = location.search.indexOf("shellhtml") > -1; import { setDebug } from "../disks/common/debug.mjs"; import { customAlphabet } from "../dep/nanoid/nanoid.js"; @@ -3675,6 +3679,9 @@ const $commonApi = { data: {}, }, system: { + // 🐚 True when an HTML shell (prompt.ac) hosts the runtime and owns the + // prompt / corner chrome in DOM β€” pieces skip their own low-res chrome. + shellhtml: shellHTMLMode, // prompt: { input: undefined }, Gets set in `prompt_boot`. // 🎡 ac-electron drag-drop: set when a file is dropped onto the app // (Dock icon or running window). Pieces (notably `play`) read this on @@ -9662,7 +9669,7 @@ async function load( sim = ($) => { module.sim?.($); - prompt.prompt_sim($); + if (!shellHTMLMode) prompt.prompt_sim($); }; paint = ($) => { @@ -9675,7 +9682,9 @@ async function load( noPaint = true; } - const promptPainted = prompt.prompt_paint($); + // shellhtml: the host page's DOM bar IS the prompt β€” never composite + // the low-res typable input over the buffer. + const promptPainted = shellHTMLMode ? false : prompt.prompt_paint($); return noPaint || promptPainted; }; @@ -9683,7 +9692,7 @@ async function load( act = ($) => { module.act?.($); - prompt.prompt_act($); + if (!shellHTMLMode) prompt.prompt_act($); // shell DOM owns typing }; leave = ($) => { @@ -9884,6 +9893,7 @@ async function load( const searchParams = new URLSearchParams(parsed.search); if (searchParams.has("nolabel")) hideLabel = true; } + if (shellHTMLMode) hideLabel = true; // the shell's DOM corner overlay replaces it currentColon = colon; currentParams = params; diff --git a/system/public/aesthetic.computer/lib/parse.mjs b/system/public/aesthetic.computer/lib/parse.mjs index f33c56b75c..d63cdcdc8d 100644 --- a/system/public/aesthetic.computer/lib/parse.mjs +++ b/system/public/aesthetic.computer/lib/parse.mjs @@ -18,6 +18,7 @@ const LEGITIMATE_PARAMS = [ "vscode", "nogap", "nolabel", + "shellhtml", "noboot", "density", "zoom", diff --git a/system/public/prompt.ac/index.html b/system/public/prompt.ac/index.html index 232172b13d..03739f6958 100644 --- a/system/public/prompt.ac/index.html +++ b/system/public/prompt.ac/index.html @@ -50,6 +50,22 @@ } .loading.gone { opacity: 0; } + /* ── DOM corner overlay β€” the crisp return-to-prompt chip that replaces the + runtime's composited corner label while in shellhtml mode. ───────────── */ + .corner { + position: absolute; top: 8px; left: 8px; z-index: 2; + display: none; align-items: center; gap: 6px; + padding: 4px 9px; cursor: pointer; user-select: none; + background: color-mix(in srgb, var(--bar) 82%, transparent); + border: 1px solid var(--edge); border-radius: 6px; + color: var(--text); font-family: var(--font); font-size: 13px; + backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); + text-shadow: 0 1px 2px rgba(0,0,0,.6); + } + .corner.on { display: flex; } + .corner:hover { border-color: var(--accent); } + .corner .back { color: var(--accent); font-weight: 700; } + /* token colors (for the suggestion/echo line, shared class names with moat) */ .t-handle { color: #ffb0a0; } .t-kidlisp { color: #ff8cc8; } .t-clock { color: #ffd24b; } @@ -63,6 +79,7 @@
+
‹
booting aesthetic computer…
@@ -73,6 +90,8 @@ const $cmd = document.getElementById("cmd"); const $here = document.getElementById("here"); const $dot = document.getElementById("dot"); const $loading = document.getElementById("loading"); +const $corner = document.getElementById("corner"); +const $cornerName = document.getElementById("cornerName"); const frame = new ACFrame(document.getElementById("ac")); // Deep-link support that doesn't fight Caddy: prompt.ac/#clock stays on the @@ -80,7 +99,9 @@ const frame = new ACFrame(document.getElementById("ac")); const initial = decodeURIComponent(location.hash.slice(1)) || "prompt"; $dot.title = "booting…"; -frame.boot(initial); +// shellhtml: the runtime's composited prompt + corner label stand down β€” this +// page's DOM bar and corner chip are that chrome, at native resolution. +frame.boot(initial, "nogap=true&nolabel=true&shellhtml=true"); frame.onReady(() => { $dot.className = "dot ok"; $dot.title = "live"; @@ -88,14 +109,23 @@ frame.onReady(() => { $cmd.focus(); }); -// Keep the bar + URL hash in sync with whatever the runtime is showing. +// Keep the bar + URL hash + corner chip in sync with the runtime. frame.onPiece((slug) => { const name = slug || "prompt"; $here.textContent = name; document.title = name === "prompt" ? "prompt.ac" : name + " Β· prompt.ac"; + const onPiece = name !== "prompt"; + $corner.classList.toggle("on", onPiece); // chip only overlays pieces + $cornerName.textContent = name; const h = "#" + name; - if (location.hash !== h && name !== "prompt") history.replaceState(null, "", h); - else if (name === "prompt" && location.hash) history.replaceState(null, "", location.pathname); + if (location.hash !== h && onPiece) history.replaceState(null, "", h); + else if (!onPiece && location.hash) history.replaceState(null, "", location.pathname); +}); + +// Corner chip β†’ back to the prompt (the DOM twin of the composited label). +$corner.addEventListener("click", () => { + frame.load("prompt"); + $cmd.focus(); }); // Command bar β†’ in-place piece load.