feat(editor): console copy button + collapsible DOM-floor console + one blue family
The first prod blue screen (finally!) also surfaced the console UX gaps: the in-app log had to be hand-copied (and arrived truncated — the flight-recorder dump missing), and the DOM-floor screen's log was a fixed block. - React console: a copy button beside the toggle (writes the full log to the clipboard, confirms in the log); stays collapsible on a fatal as in normal editing. - DOM-floor screen: its console mirrors the editor one — toggle bar (▾/▸ console) + copy, collapsible — and its content is ring + trace dump. - One blue family: both fatal screens now use the boot overlay's #1a1a2e, so every full-screen state (boot, consent, fatal, floor) is coherent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019SE4o46Lnq3hF574FFq8x4
This commit is contained in:
parent
210b079ed9
commit
875195f721
2 changed files with 66 additions and 15 deletions
|
|
@ -2373,7 +2373,7 @@ export function WasmTool({
|
|||
{fatal && (
|
||||
<div
|
||||
data-testid="fatal-overlay"
|
||||
className="absolute inset-0 z-[35] flex flex-col items-center justify-center gap-3 bg-[#1e3a8a] text-white"
|
||||
className="absolute inset-0 z-[35] flex flex-col items-center justify-center gap-3 bg-[#1a1a2e] text-white"
|
||||
>
|
||||
<p className="font-mono text-4xl text-white/90">:(</p>
|
||||
<p className="font-mono text-sm text-white">
|
||||
|
|
@ -2403,13 +2403,28 @@ export function WasmTool({
|
|||
visible on a fatal even with chrome hidden, for the same reason. */}
|
||||
{(!effectiveChromeHidden || fatal) && (
|
||||
<div className="absolute bottom-0 left-0 right-0 z-40">
|
||||
<button
|
||||
className="flex items-center gap-1 bg-black/70 px-3 py-1 font-mono text-xs text-white"
|
||||
onClick={() => setShowLog((s) => !s)}
|
||||
>
|
||||
{showLog ? <ChevronDown size={14} /> : <ChevronUp size={14} />} console
|
||||
({logs.length})
|
||||
</button>
|
||||
<div className="flex items-center bg-black/70">
|
||||
<button
|
||||
className="flex items-center gap-1 px-3 py-1 font-mono text-xs text-white"
|
||||
onClick={() => setShowLog((s) => !s)}
|
||||
>
|
||||
{showLog ? <ChevronDown size={14} /> : <ChevronUp size={14} />} console
|
||||
({logs.length})
|
||||
</button>
|
||||
{showLog && (
|
||||
<button
|
||||
className="ml-auto px-3 py-1 font-mono text-xs text-white/70 hover:text-white"
|
||||
onClick={() => {
|
||||
void navigator.clipboard.writeText(logs.join("\n")).then(
|
||||
() => append("[console] copied to clipboard"),
|
||||
() => append("[console] clipboard copy failed"),
|
||||
);
|
||||
}}
|
||||
>
|
||||
copy
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{showLog && (
|
||||
<pre className="max-h-64 overflow-auto bg-black/85 p-3 font-mono text-[11px] leading-tight text-green-300">
|
||||
{logs.join("\n")}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ function buildDom(): void {
|
|||
const root = doc.createElement("div");
|
||||
root.id = "pcbjam-fatal-screen";
|
||||
root.setAttribute("data-testid", "fatal-screen-dom");
|
||||
// Same blue as the boot overlay / React fatal screen (#1a1a2e) — one
|
||||
// coherent family across every full-screen state.
|
||||
root.style.cssText =
|
||||
"position:fixed;inset:0;z-index:2147483000;background:#1e3a8a;color:#fff;" +
|
||||
"position:fixed;inset:0;z-index:2147483000;background:#1a1a2e;color:#fff;" +
|
||||
"display:flex;flex-direction:column;align-items:center;justify-content:center;" +
|
||||
"gap:12px;font-family:ui-monospace,Menlo,monospace;padding:24px;box-sizing:border-box;";
|
||||
|
||||
|
|
@ -67,14 +69,48 @@ function buildDom(): void {
|
|||
"padding:4px 14px;font-size:12px;cursor:pointer;font-family:inherit;";
|
||||
reload.onclick = () => window.location.reload();
|
||||
|
||||
// Console block mirroring the in-editor one: toggle bar + copy, collapsible.
|
||||
const consoleWrap = doc.createElement("div");
|
||||
consoleWrap.style.cssText = "position:absolute;bottom:0;left:0;right:0;text-align:left;";
|
||||
|
||||
const bar = doc.createElement("div");
|
||||
bar.style.cssText =
|
||||
"display:flex;align-items:center;background:#000000b3;font-size:11px;";
|
||||
|
||||
const logText = () => [...ring, "", dump()].join("\n");
|
||||
|
||||
const toggle = doc.createElement("button");
|
||||
toggle.textContent = "▾ console";
|
||||
toggle.style.cssText =
|
||||
"border:0;background:transparent;color:#fff;padding:4px 12px;cursor:pointer;font:inherit;";
|
||||
|
||||
const copy = doc.createElement("button");
|
||||
copy.textContent = "copy";
|
||||
copy.style.cssText =
|
||||
"border:0;background:transparent;color:#ffffffb3;padding:4px 12px;cursor:pointer;" +
|
||||
"font:inherit;margin-left:auto;";
|
||||
copy.onclick = () => {
|
||||
navigator.clipboard.writeText(logText()).then(
|
||||
() => { copy.textContent = "copied ✓"; },
|
||||
() => { copy.textContent = "copy failed"; },
|
||||
);
|
||||
};
|
||||
|
||||
const log = doc.createElement("pre");
|
||||
log.style.cssText =
|
||||
"position:absolute;bottom:0;left:0;right:0;max-height:38vh;overflow:auto;" +
|
||||
"margin:0;padding:10px 12px;background:#000000d9;color:#86efac;" +
|
||||
"font-size:11px;line-height:1.35;text-align:left;";
|
||||
log.textContent = [...ring, "", dump()].join("\n");
|
||||
"max-height:38vh;overflow:auto;margin:0;padding:10px 12px;background:#000000d9;" +
|
||||
"color:#86efac;font-size:11px;line-height:1.35;";
|
||||
log.textContent = logText();
|
||||
|
||||
root.append(face, title, err, hint, reload, log);
|
||||
toggle.onclick = () => {
|
||||
const hidden = log.style.display === "none";
|
||||
log.style.display = hidden ? "" : "none";
|
||||
toggle.textContent = hidden ? "▾ console" : "▸ console";
|
||||
};
|
||||
|
||||
bar.append(toggle, copy);
|
||||
consoleWrap.append(bar, log);
|
||||
root.append(face, title, err, hint, reload, consoleWrap);
|
||||
doc.body.appendChild(root);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue