feat(theseus/screenshot): 0.5.0 — sidebar-first editor, direct save/copy, sounds
Two problems the old editor kept hitting:
- __pending drain race: opening the editor a second time (refresh, back-and-
forth navigation) found the storage entry already consumed and bailed to
a blank canvas silently.
- Cross-origin img loading: editor.html at file:///…/addons/screenshot/
loading a scratch PNG at file:///…/addons-data/ counts as cross-origin
under Chromium's file-URL policy; setting crossOrigin="anonymous" made
the load fail outright.
Rebuilt editor v2:
- Load path is idempotent: silentmode.invoke("getBytes", {name}) → addon
reads the scratch file and returns a data URL. No __pending drain, no
cross-origin trickery — data: URLs are same-origin and never taint the
canvas, so getImageData / toBlob keep working.
- Two-canvas model (#base + #over, over is pointer-events:none) so live
previews don't cost a full re-composite per mousemove.
- Tools: cursor, arrow, rect, ellipse, pen, text. 6 swatches, 3 widths,
undo / redo (25-deep). Copy + Save at the top bar. Back and Maximize
buttons in the same top bar so navigation controls stay reachable when
the toolbar wraps at narrow widths.
- Keyboard: A/R/O/P/T select tool, Esc = cursor, Ctrl+Z/Shift+Z undo/redo,
Ctrl+S save, Ctrl+C copy.
- Toast surface for save/copy/error feedback.
Sidebar panel gains a direct raw-save path so the user can copy or save the
capture without entering the editor:
- Two-row actions: [Copy] [Save] on top, [Discard] [Edit] below.
- Copy uses navigator.clipboard.write(ClipboardItem); Save uses
<a download> with a Blob URL — same path Chromium's will-download
tracker already handles, so the file lands in Downloads and the chip
updates like any other save.
Inline "clear all" confirmation replaces the native confirm() — the old
system-modal opened over the tab area (out of the sidebar's visual
context) and looked like Windows 95. Now a compact red strip appears
under the Recent header with Cancel / Delete buttons.
Sounds + a sound-on/off toggle in both surfaces:
- Web Audio oscillator-synthesized (no .wav shipped): shutter click on
capture, two-tone bloop on copy, descending pair on discard/back,
ascending triad on save.
- Preference stored in silentmode.storage under "soundOn" (default on),
shared between the panel and the editor.
Simplifications:
- Dropped the addon's "arm" onMessage handler (superseded by getBytes).
- Manifest capabilities: sidebar-panel + capture-tab (no open-tab,
no toolbar-menu).
Bundled but not shipped — parent session handles the OTA sign + push.
2026-09-09 00:56:41 +02:00
|
|
|
<!doctype html>
|
|
|
|
|
<html lang="en">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<title>Screenshot editor</title>
|
|
|
|
|
<link rel="stylesheet" href="editor.css">
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<div class="topbar">
|
|
|
|
|
<button class="btn" id="back" title="Back to sidebar panel">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M10 3L5 8l5 5"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn" id="toggle-max" title="Expand the sidebar to full window / restore">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 6V2h4M14 6V2h-4M2 10v4h4M14 10v4h-4"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn" id="toggle-sound" title="Toggle sound"></button>
|
|
|
|
|
<span class="name" id="name">screenshot</span>
|
|
|
|
|
<span class="spacer"></span>
|
|
|
|
|
<button class="btn wide" id="copy" title="Copy PNG to clipboard (Ctrl+C)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="4" y="4" width="9" height="10"/><path d="M3 12V3h9"/></svg>
|
|
|
|
|
<span>Copy</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn wide primary" id="save" title="Save PNG (Ctrl+S)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v9M4 7l4 4 4-4M2 14h12"/></svg>
|
|
|
|
|
<span>Save</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="toolbar">
|
|
|
|
|
<button class="tool active" data-tool="select" title="Select (no tool)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M3 2l10 5-4 1-1 4z"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="tool" data-tool="arrow" title="Arrow (A)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13L13 3M13 3H8M13 3v5"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="tool" data-tool="rect" title="Rectangle (R)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2.5" y="3.5" width="11" height="9"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="tool" data-tool="ellipse" title="Ellipse (O)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><ellipse cx="8" cy="8" rx="5.5" ry="4"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="tool" data-tool="pen" title="Pen (P)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 14c1-3 3-6 6-8s5-3 6-3l-3 4c-2 1-4 3-6 4s-2 2-3 3z"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="tool" data-tool="text" title="Text (T)">
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M3 3h10M8 3v10M6 13h4"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<span class="sep"></span>
|
|
|
|
|
|
|
|
|
|
<button class="swatch active" data-color="#ff3b30" style="background:#ff3b30" title="Red"></button>
|
|
|
|
|
<button class="swatch" data-color="#d6ff3d" style="background:#d6ff3d" title="Acid"></button>
|
|
|
|
|
<button class="swatch" data-color="#ffcc00" style="background:#ffcc00" title="Yellow"></button>
|
|
|
|
|
<button class="swatch" data-color="#0aa8ff" style="background:#0aa8ff" title="Blue"></button>
|
|
|
|
|
<button class="swatch" data-color="#ffffff" style="background:#ffffff" title="White"></button>
|
|
|
|
|
<button class="swatch" data-color="#000000" style="background:#000000" title="Black"></button>
|
|
|
|
|
|
|
|
|
|
<span class="sep"></span>
|
|
|
|
|
|
|
|
|
|
<button class="width" data-width="3" title="Thin"><span class="dot" style="width:4px;height:4px"></span></button>
|
|
|
|
|
<button class="width active" data-width="5" title="Medium"><span class="dot" style="width:7px;height:7px"></span></button>
|
|
|
|
|
<button class="width" data-width="9" title="Thick"><span class="dot" style="width:11px;height:11px"></span></button>
|
|
|
|
|
|
|
|
|
|
<span class="sep"></span>
|
|
|
|
|
|
|
|
|
|
<button class="btn" id="undo" title="Undo (Ctrl+Z)" disabled>
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M3 8c0-3 2-5 5-5s5 2 5 5-2 5-5 5"/><path d="M6 5L3 8l3 3"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn" id="redo" title="Redo (Ctrl+Shift+Z)" disabled>
|
|
|
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M13 8c0-3-2-5-5-5S3 5 3 8s2 5 5 5"/><path d="M10 5l3 3-3 3"/></svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="board" id="board">
|
|
|
|
|
<div class="empty" id="empty">
|
|
|
|
|
<div>Loading capture…</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stage" id="stage" hidden>
|
|
|
|
|
<canvas id="base"></canvas>
|
|
|
|
|
<canvas id="over"></canvas>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="toast" id="toast"></div>
|
|
|
|
|
<a id="download-link" style="display:none"></a>
|
|
|
|
|
|
|
|
|
|
<script src="editor.js"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|