diff --git a/system/public/aesthetic.computer/disks/cap.mjs b/system/public/aesthetic.computer/disks/cap.mjs index 80503cec87..256cea3269 100644 --- a/system/public/aesthetic.computer/disks/cap.mjs +++ b/system/public/aesthetic.computer/disks/cap.mjs @@ -20,7 +20,6 @@ #endregion */ import { - SwapButton, RecordingTimer, MicLevel, sounds, @@ -82,6 +81,7 @@ function paint({ rec, painting, recordingUI, + ui, num: { randIntRange, clamp, rand }, }) { // Initialize video feed to match screen dimensions (not painting) @@ -127,12 +127,17 @@ function paint({ const centerX = floor(screen.width / 2); const bottomY = screen.height - 28; - // Swap button (bottom right, only if multiple cameras and not recording) + // Swap-camera TextButton, top-right. Only shown when multiple cameras + // are available and we're not actively (or about to be) recording. + // Lives in the recordingUI overlay so it isn't baked into the tape, + // and is anchored top-right so the bottom edge stays clear for the + // hold-to-record gesture. if (cameras > 1 && !isRecording && !pendingRecordStart) { if (!swapBtn) { - swapBtn = new SwapButton({ x: 0, y: 0, width: 48, height: 20 }); + swapBtn = new ui.TextButton("Swap", { right: 6, top: 6, screen }); + swapBtn.btn.stickyScrubbing = true; } - swapBtn.reposition({ right: 6, bottom: 6, screen }); + swapBtn.reposition({ right: 6, top: 6, screen }, "Swap"); swapBtn.paint($); } @@ -328,7 +333,7 @@ function act({ event: e, jump, video, cameras, sound, rec, notice, leaving, hud // 🎬 Hold-to-record (BakTok pattern): touch anywhere outside the swap // button or HUD label starts recording; releasing stops + jumps to video. if (e.is("touch") && !leaving()) { - const onSwapBtn = swapBtn?.contains(e.x, e.y); + const onSwapBtn = swapBtn?.btn?.box?.contains(e); const onHud = hud?.currentLabel()?.btn?.down; if (!onSwapBtn && !onHud && !isRecording && !pendingRecordStart) { startRecording(rec, sound, notice); diff --git a/system/public/aesthetic.computer/disks/video.mjs b/system/public/aesthetic.computer/disks/video.mjs index 1255e50a6f..98e2ccefdf 100644 --- a/system/public/aesthetic.computer/disks/video.mjs +++ b/system/public/aesthetic.computer/disks/video.mjs @@ -827,8 +827,50 @@ function act({ volume: 0.5, duration: 0.005, }); - // Drop the current tape so cap.mjs starts clean. - rec?.slate?.(); + // Drop the current tape and any cached export / playback state + // so cap.mjs starts clean and stale UI (progress bar, scrub + // strip, post button) doesn't bleed through to the next visit. + try { + rec?.slate?.(); + if (rec) { + rec.tapeProgress = 0; + rec.printing = false; + rec.presenting = false; + rec.recorded = false; + rec.presentProgress = 0; + } + } catch {} + isPrinting = false; + isPostingTape = false; + isExportingGIF = false; + isExportingFrames = false; + isExportingWebP = false; + isExportingAnimWebP = false; + isExportingAPNG = false; + currentExportType = ""; + currentExportPhase = ""; + exportStatusMessage = ""; + printProgress = 0; + progressHistory = []; + exportStartTime = 0; + completionMessage = ""; + completionMessageTimer = 0; + postedTapeCode = null; + tapeInfo = null; + isScrubbing = false; + inertiaActive = false; + scrubSpeed = 0; + scrubMoved = false; + scrubCurrentProgress = 0; + scrubAudioSpeed = 1; + wasPlayingBeforeScrub = false; + tapeWaveform = null; + scrubStripBtn = null; + backBtn = undefined; + postBtn = undefined; + gifBtn = undefined; + mp4Btn = undefined; + zipBtn = undefined; jump("cap"); }, }); diff --git a/system/public/aesthetic.computer/lib/disk.mjs b/system/public/aesthetic.computer/lib/disk.mjs index 846a48c46f..844faaacac 100644 --- a/system/public/aesthetic.computer/lib/disk.mjs +++ b/system/public/aesthetic.computer/lib/disk.mjs @@ -11491,6 +11491,19 @@ async function makeFrame({ data: { type, content } }) { return; } + // 📊 Camera diagnostics from bios — log directly here so they land in + // the piece-runs silo via the patched console. Avoids needing to wire + // every diagnostic type into the actAlerts queue (which only carries + // string keys, not the full content payload we need for debugging). + if (type === "camera:debug" || type === "camera:debug:frame") { + try { + console.log(`📷 ${type}:`, JSON.stringify(content)); + } catch { + console.log(`📷 ${type}:`, content); + } + return; + } + // 1c. Loading from History if (type === "history-load") { if (debug && logs.history) console.log("⏳ History:", content);