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
|
|
|
// Screenshot editor v2. Lives inside the sidebar view; no separate tab.
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
//
|
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
|
|
|
// Load path (idempotent — this is the key change from v1's __pending drain):
|
|
|
|
|
// editor.html?name=<scratchfile.png>
|
|
|
|
|
// → silentmode.invoke("getBytes", {name})
|
|
|
|
|
// → addon reads scratch/<name>.png from disk and returns
|
|
|
|
|
// { name, dataUrl:"data:image/png;base64,…", bytes, at, mode }
|
|
|
|
|
// → we set an <img> src to that data URL and draw it onto #base
|
|
|
|
|
//
|
|
|
|
|
// Two canvases: #base holds the committed image; #over is a preview layer
|
|
|
|
|
// that hosts the live drag preview for each drawing tool. On mouseup the
|
|
|
|
|
// tool commits its result by drawing #over onto #base, clearing #over,
|
|
|
|
|
// and pushing a fresh ImageData snapshot onto the undo stack.
|
|
|
|
|
//
|
|
|
|
|
// No cross-origin img loading (data: URLs are same-origin in Chromium), so
|
|
|
|
|
// the canvas never gets tainted — getImageData / toBlob keep working.
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
|
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
|
|
|
const $ = (id) => document.getElementById(id);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
const UNDO_MAX = 25;
|
|
|
|
|
|
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
|
|
|
// ---- audio -----------------------------------------------------------
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
// Same synth engine as the sidebar panel: preference stored under
|
|
|
|
|
// silentmode.storage as "soundOn" (default true), no .wav ships in the
|
|
|
|
|
// tarball. Photoshoot-style shutter for capture, quick "printer chirp"
|
|
|
|
|
// for copy — modeled on Firefox's Screenshots feedback tones.
|
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
|
|
|
let soundOn = true;
|
|
|
|
|
let _audio = null;
|
|
|
|
|
function _actx() {
|
|
|
|
|
if (!_audio) { try { _audio = new (window.AudioContext || window.webkitAudioContext)(); } catch {} }
|
|
|
|
|
if (_audio && _audio.state === "suspended") _audio.resume().catch(() => {});
|
|
|
|
|
return _audio;
|
|
|
|
|
}
|
|
|
|
|
function _tone(freq, dur, type, vol, at) {
|
|
|
|
|
if (!soundOn) return;
|
|
|
|
|
const c = _actx(); if (!c) return;
|
|
|
|
|
const t0 = c.currentTime + (at || 0);
|
|
|
|
|
const osc = c.createOscillator();
|
|
|
|
|
const g = c.createGain();
|
|
|
|
|
osc.type = type || "sine";
|
|
|
|
|
osc.frequency.setValueAtTime(freq, t0);
|
|
|
|
|
g.gain.setValueAtTime(vol || 0.15, t0);
|
|
|
|
|
g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
|
|
|
|
|
osc.connect(g); g.connect(c.destination);
|
|
|
|
|
osc.start(t0);
|
|
|
|
|
osc.stop(t0 + dur + 0.02);
|
|
|
|
|
}
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
// A short filtered noise burst — the mechanical component of the shutter
|
|
|
|
|
// click and (paired lower down) the "paper feed" texture of the print
|
|
|
|
|
// sound. `bp` is the band-pass centre frequency, `q` how tight the band.
|
|
|
|
|
function _noise(dur, vol, bp, q, at) {
|
|
|
|
|
if (!soundOn) return;
|
|
|
|
|
const c = _actx(); if (!c) return;
|
|
|
|
|
const t0 = c.currentTime + (at || 0);
|
|
|
|
|
const n = Math.max(1, Math.floor(c.sampleRate * dur));
|
|
|
|
|
const buf = c.createBuffer(1, n, c.sampleRate);
|
|
|
|
|
const d = buf.getChannelData(0);
|
|
|
|
|
for (let i = 0; i < n; i++) d[i] = Math.random() * 2 - 1;
|
|
|
|
|
const src = c.createBufferSource(); src.buffer = buf;
|
|
|
|
|
const flt = c.createBiquadFilter(); flt.type = "bandpass";
|
|
|
|
|
flt.frequency.setValueAtTime(bp || 2000, t0);
|
|
|
|
|
flt.Q.setValueAtTime(q || 4, t0);
|
|
|
|
|
const g = c.createGain();
|
|
|
|
|
g.gain.setValueAtTime(vol || 0.2, t0);
|
|
|
|
|
g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
|
|
|
|
|
src.connect(flt); flt.connect(g); g.connect(c.destination);
|
|
|
|
|
src.start(t0);
|
|
|
|
|
src.stop(t0 + dur + 0.02);
|
|
|
|
|
}
|
|
|
|
|
// Photoshoot: two clicks — mirror-slap up, mirror-slap down. A short
|
|
|
|
|
// high-band noise burst is the metallic ping, a low-band burst is the
|
|
|
|
|
// mechanical thud, and a very short low tone fills in the body of the
|
|
|
|
|
// shutter closing.
|
|
|
|
|
function playShutter() {
|
|
|
|
|
_noise(0.03, 0.28, 4200, 6, 0); // first click, up
|
|
|
|
|
_tone(180, 0.04, "square", 0.10, 0.005);
|
|
|
|
|
_noise(0.04, 0.32, 3600, 5, 0.08); // second click, down
|
|
|
|
|
_tone(140, 0.05, "square", 0.12, 0.09);
|
|
|
|
|
}
|
|
|
|
|
// Copy: two-chirp "printer feed" — matches Firefox's Easy Screenshot copy
|
|
|
|
|
// feedback (staccato ascending pair, no residual hum).
|
|
|
|
|
function playCopy() {
|
|
|
|
|
_noise(0.02, 0.14, 3200, 8, 0);
|
|
|
|
|
_tone(1400, 0.05, "sine", 0.14, 0);
|
|
|
|
|
_noise(0.02, 0.14, 4200, 8, 0.06);
|
|
|
|
|
_tone(2000, 0.06, "sine", 0.14, 0.06);
|
|
|
|
|
}
|
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
|
|
|
function playSave() { _tone(700, 0.06, "sine", 0.16, 0); _tone(1100, 0.08, "sine", 0.16, 0.05); _tone(1500, 0.09, "sine", 0.16, 0.10); }
|
|
|
|
|
function playDiscard() { _tone(500, 0.06, "sine", 0.18, 0); _tone(280, 0.10, "sine", 0.14, 0.05); }
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
function playCropApply() { _tone(1200, 0.05, "sine", 0.14, 0); _tone(1600, 0.07, "sine", 0.14, 0.04); }
|
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
|
|
|
|
|
|
|
|
const base = $("base");
|
|
|
|
|
const over = $("over");
|
|
|
|
|
const stage = $("stage");
|
|
|
|
|
const board = $("board");
|
|
|
|
|
const empty = $("empty");
|
|
|
|
|
const nameEl = $("name");
|
|
|
|
|
const undoBtn = $("undo");
|
|
|
|
|
const redoBtn = $("redo");
|
|
|
|
|
const toastEl = $("toast");
|
|
|
|
|
const dl = $("download-link");
|
|
|
|
|
|
|
|
|
|
const bctx = base.getContext("2d");
|
|
|
|
|
const octx = over.getContext("2d");
|
|
|
|
|
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
const params = new URLSearchParams(location.search);
|
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
|
|
|
const NAME = params.get("name") || "";
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
|
|
|
|
|
let state = {
|
|
|
|
|
tool: "select",
|
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
|
|
|
color: "#ff3b30",
|
|
|
|
|
width: 5,
|
|
|
|
|
drag: null, // {x0,y0,x,y}
|
|
|
|
|
pen: null, // [{x,y}, …]
|
|
|
|
|
textInput: null, // {x, y, el}
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
cropRect: null, // committed-crop rectangle in canvas coords, {x,y,w,h}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
};
|
|
|
|
|
|
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
|
|
|
let undo = [];
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
let redo = [];
|
|
|
|
|
|
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
|
|
|
// -- utilities --------------------------------------------------------------
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
function toast(msg, err) {
|
|
|
|
|
toastEl.textContent = msg;
|
|
|
|
|
toastEl.classList.toggle("err", !!err);
|
|
|
|
|
toastEl.classList.add("on");
|
|
|
|
|
clearTimeout(toast._t);
|
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
|
|
|
toast._t = setTimeout(() => toastEl.classList.remove("on"), 1800);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
function updateUndoRedo() {
|
|
|
|
|
undoBtn.disabled = undo.length <= 1; // baseline snapshot always at index 0
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
redoBtn.disabled = redo.length === 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pushSnapshot() {
|
|
|
|
|
try {
|
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
|
|
|
const snap = bctx.getImageData(0, 0, base.width, base.height);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
undo.push(snap);
|
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
|
|
|
if (undo.length > UNDO_MAX) undo.shift();
|
|
|
|
|
redo = [];
|
|
|
|
|
updateUndoRedo();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn("snapshot failed:", e);
|
|
|
|
|
}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function restoreSnapshot(snap) {
|
|
|
|
|
if (!snap) return;
|
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
|
|
|
if (base.width !== snap.width || base.height !== snap.height) {
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
sizeCanvases(snap.width, snap.height);
|
|
|
|
|
}
|
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
|
|
|
bctx.putImageData(snap, 0, 0);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sizeCanvases(w, h) {
|
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
|
|
|
for (const c of [base, over]) {
|
|
|
|
|
c.width = w;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
c.height = h;
|
|
|
|
|
c.style.width = w + "px";
|
|
|
|
|
c.style.height = h + "px";
|
|
|
|
|
}
|
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
|
|
|
stage.style.width = w + "px";
|
|
|
|
|
stage.style.height = h + "px";
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Scale the stage down to fit inside the board when the image is larger
|
|
|
|
|
// than the visible area. Purely visual — drawing math stays in natural px.
|
|
|
|
|
function fitBoard() {
|
|
|
|
|
const availW = Math.max(50, board.clientWidth - 24);
|
|
|
|
|
const availH = Math.max(50, board.clientHeight - 24);
|
|
|
|
|
const s = Math.min(1, availW / base.width, availH / base.height);
|
|
|
|
|
const scale = s > 0 && Number.isFinite(s) ? s : 1;
|
|
|
|
|
stage.style.transform = `scale(${scale})`;
|
|
|
|
|
stage.style.width = (base.width * scale) + "px";
|
|
|
|
|
stage.style.height = (base.height * scale) + "px";
|
|
|
|
|
stage._scale = scale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert a viewport-relative pointer event into natural canvas coords.
|
|
|
|
|
function pointToCanvas(ev) {
|
|
|
|
|
const rect = base.getBoundingClientRect();
|
|
|
|
|
const scale = stage._scale || 1;
|
|
|
|
|
const cssW = rect.width, cssH = rect.height;
|
|
|
|
|
const x = (ev.clientX - rect.left) * (base.width / (cssW || 1));
|
|
|
|
|
const y = (ev.clientY - rect.top) * (base.height / (cssH || 1));
|
|
|
|
|
return { x, y };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -- loading ---------------------------------------------------------------
|
|
|
|
|
async function loadFromAddon(name) {
|
|
|
|
|
if (!window.silentmode?.invoke) {
|
|
|
|
|
throw new Error("silentmode API not available in this view");
|
|
|
|
|
}
|
|
|
|
|
const res = await window.silentmode.invoke("getBytes", { name });
|
|
|
|
|
if (!res || !res.dataUrl) throw new Error("addon returned no data URL");
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadImage(dataUrl) {
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const img = new Image();
|
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
|
|
|
img.onload = () => resolve(img);
|
|
|
|
|
img.onerror = () => reject(new Error("image decode failed"));
|
|
|
|
|
// NB: no crossOrigin — data: URLs are same-origin, and setting it
|
|
|
|
|
// to anonymous would require CORS headers that a data URL can't carry,
|
|
|
|
|
// which is precisely what tripped up v1.
|
|
|
|
|
img.src = dataUrl;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function init() {
|
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
|
|
|
document.title = NAME ? NAME + " — editor" : "Screenshot editor";
|
|
|
|
|
nameEl.textContent = NAME || "screenshot";
|
|
|
|
|
if (!NAME) {
|
|
|
|
|
empty.classList.add("err");
|
|
|
|
|
empty.innerHTML = "<div>Missing <code>?name</code> — open a capture from the Screenshot sidebar.</div>";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let res;
|
|
|
|
|
try { res = await loadFromAddon(NAME); }
|
|
|
|
|
catch (e) {
|
|
|
|
|
console.warn("editor load failed:", e);
|
|
|
|
|
empty.classList.add("err");
|
|
|
|
|
empty.innerHTML = `<div>Couldn't load capture: ${(e && e.message || e)}</div>`;
|
|
|
|
|
return;
|
2026-09-08 12:57:46 +02:00
|
|
|
}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
let img;
|
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
|
|
|
try { img = await loadImage(res.dataUrl); }
|
|
|
|
|
catch (e) {
|
|
|
|
|
empty.classList.add("err");
|
|
|
|
|
empty.innerHTML = `<div>Couldn't decode capture bytes (${res.bytes || 0} B)</div>`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
sizeCanvases(img.naturalWidth, img.naturalHeight);
|
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
|
|
|
bctx.drawImage(img, 0, 0);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
undo = [];
|
|
|
|
|
redo = [];
|
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
|
|
|
pushSnapshot();
|
|
|
|
|
empty.hidden = true;
|
|
|
|
|
stage.hidden = false;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
fitBoard();
|
|
|
|
|
}
|
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
|
|
|
window.addEventListener("resize", () => { if (!stage.hidden) fitBoard(); });
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
|
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
|
|
|
// -- tool selection --------------------------------------------------------
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
function setTool(name) {
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
// Leaving crop mode with an unapplied marquee drops it.
|
|
|
|
|
if (state.tool === "crop" && name !== "crop" && state.cropRect) {
|
|
|
|
|
state.cropRect = null; clearOver();
|
|
|
|
|
}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
state.tool = name;
|
|
|
|
|
stage.dataset.tool = name;
|
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
|
|
|
for (const b of document.querySelectorAll(".tool")) {
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
b.classList.toggle("active", b.dataset.tool === name);
|
|
|
|
|
}
|
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
|
|
|
cancelTextInput();
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
updateCropUI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show/hide the Apply crop / Cancel crop buttons in the top bar. Visible
|
|
|
|
|
// only while the tool is "crop" AND a rectangle has been drawn.
|
|
|
|
|
function updateCropUI() {
|
|
|
|
|
const on = state.tool === "crop" && !!state.cropRect;
|
|
|
|
|
const ac = document.getElementById("apply-crop");
|
|
|
|
|
const cc = document.getElementById("cancel-crop");
|
|
|
|
|
if (ac) ac.hidden = !on;
|
|
|
|
|
if (cc) cc.hidden = !on;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply the crop: resize the base canvas to the rect's size, draw the
|
|
|
|
|
// cropped region onto it, then clear undo/redo (the coordinate system has
|
|
|
|
|
// changed — pre-crop snapshots would restore into the wrong dimensions).
|
|
|
|
|
// Baseline snapshot for the cropped canvas becomes the new floor.
|
|
|
|
|
function applyCrop() {
|
|
|
|
|
const r = state.cropRect;
|
|
|
|
|
if (!r) return;
|
|
|
|
|
const w = Math.max(1, Math.round(r.w));
|
|
|
|
|
const h = Math.max(1, Math.round(r.h));
|
|
|
|
|
const x = Math.max(0, Math.round(r.x));
|
|
|
|
|
const y = Math.max(0, Math.round(r.y));
|
|
|
|
|
const tmp = document.createElement("canvas");
|
|
|
|
|
tmp.width = w; tmp.height = h;
|
|
|
|
|
tmp.getContext("2d").drawImage(base, x, y, w, h, 0, 0, w, h);
|
|
|
|
|
sizeCanvases(w, h);
|
|
|
|
|
bctx.drawImage(tmp, 0, 0);
|
|
|
|
|
state.cropRect = null;
|
|
|
|
|
clearOver();
|
|
|
|
|
undo = [];
|
|
|
|
|
redo = [];
|
|
|
|
|
pushSnapshot();
|
|
|
|
|
fitBoard();
|
|
|
|
|
updateCropUI();
|
|
|
|
|
playCropApply();
|
|
|
|
|
setTool("select");
|
|
|
|
|
}
|
|
|
|
|
function cancelCrop() {
|
|
|
|
|
state.cropRect = null;
|
|
|
|
|
clearOver();
|
|
|
|
|
updateCropUI();
|
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
|
|
|
}
|
|
|
|
|
for (const b of document.querySelectorAll(".tool")) {
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
b.addEventListener("click", () => setTool(b.dataset.tool));
|
|
|
|
|
}
|
|
|
|
|
for (const b of document.querySelectorAll(".swatch")) {
|
|
|
|
|
b.addEventListener("click", () => {
|
|
|
|
|
state.color = b.dataset.color;
|
|
|
|
|
for (const x of document.querySelectorAll(".swatch")) x.classList.toggle("active", x === b);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
for (const b of document.querySelectorAll(".width")) {
|
|
|
|
|
b.addEventListener("click", () => {
|
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
|
|
|
state.width = Number(b.dataset.width) || 5;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
for (const x of document.querySelectorAll(".width")) x.classList.toggle("active", x === b);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// -- drawing ---------------------------------------------------------------
|
|
|
|
|
function clearOver() { octx.clearRect(0, 0, over.width, over.height); }
|
|
|
|
|
|
|
|
|
|
function strokeStyle() {
|
|
|
|
|
octx.strokeStyle = state.color;
|
|
|
|
|
octx.fillStyle = state.color;
|
|
|
|
|
octx.lineWidth = state.width;
|
|
|
|
|
octx.lineCap = "round";
|
|
|
|
|
octx.lineJoin = "round";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawArrow(a, b) {
|
|
|
|
|
strokeStyle();
|
|
|
|
|
octx.beginPath();
|
|
|
|
|
octx.moveTo(a.x, a.y); octx.lineTo(b.x, b.y); octx.stroke();
|
|
|
|
|
// arrowhead
|
|
|
|
|
const dx = b.x - a.x, dy = b.y - a.y;
|
|
|
|
|
const len = Math.hypot(dx, dy);
|
|
|
|
|
if (len < 1) return;
|
|
|
|
|
const head = Math.max(10, state.width * 3);
|
|
|
|
|
const ang = Math.atan2(dy, dx);
|
|
|
|
|
const spread = Math.PI / 6;
|
|
|
|
|
octx.beginPath();
|
|
|
|
|
octx.moveTo(b.x, b.y);
|
|
|
|
|
octx.lineTo(b.x - head * Math.cos(ang - spread), b.y - head * Math.sin(ang - spread));
|
|
|
|
|
octx.moveTo(b.x, b.y);
|
|
|
|
|
octx.lineTo(b.x - head * Math.cos(ang + spread), b.y - head * Math.sin(ang + spread));
|
|
|
|
|
octx.stroke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawRect(a, b) {
|
|
|
|
|
strokeStyle();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
const x = Math.min(a.x, b.x), y = Math.min(a.y, b.y);
|
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
|
|
|
const w = Math.abs(b.x - a.x), h = Math.abs(b.y - a.y);
|
|
|
|
|
octx.strokeRect(x, y, w, h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawEllipse(a, b) {
|
|
|
|
|
strokeStyle();
|
|
|
|
|
const cx = (a.x + b.x) / 2, cy = (a.y + b.y) / 2;
|
|
|
|
|
const rx = Math.abs(b.x - a.x) / 2, ry = Math.abs(b.y - a.y) / 2;
|
|
|
|
|
octx.beginPath();
|
|
|
|
|
octx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
|
|
|
octx.stroke();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
function drawPen(points) {
|
|
|
|
|
if (!points || points.length < 2) return;
|
|
|
|
|
strokeStyle();
|
|
|
|
|
octx.beginPath();
|
|
|
|
|
octx.moveTo(points[0].x, points[0].y);
|
|
|
|
|
for (let i = 1; i < points.length; i++) octx.lineTo(points[i].x, points[i].y);
|
|
|
|
|
octx.stroke();
|
|
|
|
|
}
|
|
|
|
|
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
// Crop overlay — dim the area outside the rect, thin dashed border. This
|
|
|
|
|
// is a live preview during drag AND the settled marquee while apply-crop
|
|
|
|
|
// is pending.
|
|
|
|
|
function drawCropRect(a, b) {
|
|
|
|
|
const x = Math.min(a.x, b.x), y = Math.min(a.y, b.y);
|
|
|
|
|
const w = Math.abs(b.x - a.x), h = Math.abs(b.y - a.y);
|
|
|
|
|
clearOver();
|
|
|
|
|
octx.save();
|
|
|
|
|
octx.fillStyle = "rgba(10,13,19,0.55)";
|
|
|
|
|
octx.fillRect(0, 0, over.width, over.height);
|
|
|
|
|
octx.clearRect(x, y, w, h);
|
|
|
|
|
octx.strokeStyle = "#d6ff3d";
|
|
|
|
|
octx.lineWidth = 1.5;
|
|
|
|
|
octx.setLineDash([6, 4]);
|
|
|
|
|
octx.strokeRect(x + 0.5, y + 0.5, w - 1, h - 1);
|
|
|
|
|
octx.restore();
|
|
|
|
|
return { x, y, w, h };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mosaic redaction: downsample the selected region of #base to blocks of
|
|
|
|
|
// ~<blockSize> px, then paint the blocks back. The whole thing commits
|
|
|
|
|
// straight to #base (there's no live preview — the drag rect uses the
|
|
|
|
|
// crop-style marquee to show the target region).
|
|
|
|
|
function commitBlur(a, b) {
|
|
|
|
|
const x = Math.max(0, Math.min(a.x, b.x)) | 0;
|
|
|
|
|
const y = Math.max(0, Math.min(a.y, b.y)) | 0;
|
|
|
|
|
const w = Math.min(base.width - x, Math.abs(b.x - a.x) | 0);
|
|
|
|
|
const h = Math.min(base.height - y, Math.abs(b.y - a.y) | 0);
|
|
|
|
|
if (w < 4 || h < 4) return false;
|
|
|
|
|
const block = Math.max(6, Math.round(Math.min(w, h) / 12));
|
|
|
|
|
const tmp = document.createElement("canvas");
|
|
|
|
|
const scaleW = Math.max(1, Math.floor(w / block));
|
|
|
|
|
const scaleH = Math.max(1, Math.floor(h / block));
|
|
|
|
|
tmp.width = scaleW; tmp.height = scaleH;
|
|
|
|
|
const tctx = tmp.getContext("2d");
|
|
|
|
|
tctx.imageSmoothingEnabled = true;
|
|
|
|
|
tctx.drawImage(base, x, y, w, h, 0, 0, scaleW, scaleH);
|
|
|
|
|
bctx.imageSmoothingEnabled = false;
|
|
|
|
|
bctx.drawImage(tmp, 0, 0, scaleW, scaleH, x, y, w, h);
|
|
|
|
|
bctx.imageSmoothingEnabled = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
function commitOver() {
|
|
|
|
|
bctx.drawImage(over, 0, 0);
|
|
|
|
|
clearOver();
|
|
|
|
|
pushSnapshot();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -- input --------------------------------------------------------------
|
|
|
|
|
over.style.pointerEvents = "none"; // draw layer never blocks input; base receives
|
|
|
|
|
base.addEventListener("pointerdown", (ev) => {
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
if (state.tool === "select") return;
|
2026-09-09 10:55:21 +02:00
|
|
|
if (state.tool === "text") {
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
openTextInput(pointToCanvas(ev));
|
|
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
base.setPointerCapture(ev.pointerId);
|
|
|
|
|
const p = pointToCanvas(ev);
|
|
|
|
|
if (state.tool === "pen") { state.pen = [p]; }
|
|
|
|
|
else { state.drag = { x0: p.x, y0: p.y, x: p.x, y: p.y }; }
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
});
|
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
|
|
|
base.addEventListener("pointermove", (ev) => {
|
|
|
|
|
const p = pointToCanvas(ev);
|
|
|
|
|
if (state.tool === "pen" && state.pen) {
|
|
|
|
|
state.pen.push(p);
|
|
|
|
|
clearOver();
|
|
|
|
|
drawPen(state.pen);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
if (!state.drag) return;
|
|
|
|
|
state.drag.x = p.x; state.drag.y = p.y;
|
|
|
|
|
const a = { x: state.drag.x0, y: state.drag.y0 }, b = { x: p.x, y: p.y };
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
if (state.tool === "arrow") { clearOver(); drawArrow(a, b); }
|
|
|
|
|
else if (state.tool === "rect") { clearOver(); drawRect(a, b); }
|
|
|
|
|
else if (state.tool === "ellipse") { clearOver(); drawEllipse(a, b); }
|
|
|
|
|
else if (state.tool === "crop"
|
|
|
|
|
|| state.tool === "blur") { drawCropRect(a, b); }
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
});
|
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
|
|
|
base.addEventListener("pointerup", (ev) => {
|
|
|
|
|
try { base.releasePointerCapture(ev.pointerId); } catch {}
|
|
|
|
|
if (state.tool === "pen" && state.pen) {
|
|
|
|
|
if (state.pen.length >= 2) commitOver();
|
|
|
|
|
else clearOver();
|
|
|
|
|
state.pen = null;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
if (!state.drag) return;
|
|
|
|
|
const dx = state.drag.x - state.drag.x0, dy = state.drag.y - state.drag.y0;
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
if (Math.hypot(dx, dy) < 2) { clearOver(); state.drag = null; state.cropRect = null; updateCropUI(); return; }
|
|
|
|
|
const a = { x: state.drag.x0, y: state.drag.y0 }, b = { x: state.drag.x, y: state.drag.y };
|
|
|
|
|
if (state.tool === "crop") {
|
|
|
|
|
// Don't commit yet — the marquee stays up until the user hits Apply.
|
|
|
|
|
state.cropRect = drawCropRect(a, b);
|
|
|
|
|
state.drag = null;
|
|
|
|
|
updateCropUI();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (state.tool === "blur") {
|
|
|
|
|
if (commitBlur(a, b)) { clearOver(); pushSnapshot(); }
|
|
|
|
|
else clearOver();
|
|
|
|
|
state.drag = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
commitOver();
|
|
|
|
|
state.drag = null;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
});
|
|
|
|
|
|
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
|
|
|
// -- text tool -----------------------------------------------------------
|
2026-09-09 10:55:21 +02:00
|
|
|
// A little <input> floats over the canvas at the click point; Enter commits
|
|
|
|
|
// as fillText, Escape drops. Chases a few browser quirks:
|
|
|
|
|
// - Focus after the DOM mutation, not before: some Chromium builds
|
|
|
|
|
// drop focus() when the element hasn't yet been laid out.
|
|
|
|
|
// - Contain the input's own pointer events so a mousedown inside it
|
|
|
|
|
// doesn't bubble to the canvas and immediately re-fire openTextInput,
|
|
|
|
|
// spawning a fresh empty box.
|
|
|
|
|
// - Track height instead of a hard-coded 14 px offset so tall fonts sit
|
|
|
|
|
// on the click's baseline rather than 14 px above it.
|
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
|
|
|
function openTextInput(pt) {
|
|
|
|
|
cancelTextInput();
|
2026-09-09 10:55:21 +02:00
|
|
|
const size = Math.max(16, state.width * 4);
|
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
|
|
|
const el = document.createElement("input");
|
|
|
|
|
el.type = "text";
|
|
|
|
|
el.className = "text-input";
|
|
|
|
|
el.placeholder = "text…";
|
|
|
|
|
el.style.color = state.color;
|
2026-09-09 10:55:21 +02:00
|
|
|
el.style.font = `${size}px system-ui, -apple-system, Segoe UI, Roboto, sans-serif`;
|
|
|
|
|
el.style.lineHeight = "1.15";
|
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
|
|
|
const rect = base.getBoundingClientRect();
|
|
|
|
|
const scale = stage._scale || 1;
|
|
|
|
|
const cssX = pt.x * scale + rect.left;
|
|
|
|
|
const cssY = pt.y * scale + rect.top;
|
|
|
|
|
el.style.left = cssX + "px";
|
2026-09-09 10:55:21 +02:00
|
|
|
el.style.top = (cssY - size) + "px";
|
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
|
|
|
document.body.appendChild(el);
|
2026-09-09 10:55:21 +02:00
|
|
|
// Focus after a paint so Chromium reliably picks it up (the input goes
|
|
|
|
|
// from `display:absolute` to laid-out; focus() called synchronously
|
|
|
|
|
// right after appendChild races that in some builds).
|
|
|
|
|
requestAnimationFrame(() => el.focus());
|
|
|
|
|
state.textInput = { x: pt.x, y: pt.y, size, el };
|
|
|
|
|
// Don't let the input's own pointerdown / mousedown / click bubble to
|
|
|
|
|
// the canvas — otherwise every keystroke click-through re-fires
|
|
|
|
|
// openTextInput on the base and spawns duplicate boxes.
|
|
|
|
|
const swallow = (ev) => ev.stopPropagation();
|
|
|
|
|
for (const t of ["pointerdown", "mousedown", "click"]) el.addEventListener(t, swallow);
|
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
|
|
|
el.addEventListener("keydown", (ev) => {
|
2026-09-09 10:55:21 +02:00
|
|
|
if (ev.key === "Enter") { commitTextInput(); ev.preventDefault(); ev.stopPropagation(); }
|
|
|
|
|
else if (ev.key === "Escape") { cancelTextInput(); ev.preventDefault(); ev.stopPropagation(); }
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
});
|
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
|
|
|
el.addEventListener("blur", commitTextInput);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
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
|
|
|
function commitTextInput() {
|
|
|
|
|
const ti = state.textInput;
|
|
|
|
|
if (!ti) return;
|
2026-09-09 10:55:21 +02:00
|
|
|
const value = ti.el.value;
|
|
|
|
|
const trimmed = value.trim();
|
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
|
|
|
ti.el.remove();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
state.textInput = null;
|
2026-09-09 10:55:21 +02:00
|
|
|
if (!trimmed) return;
|
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
|
|
|
bctx.fillStyle = state.color;
|
2026-09-09 10:55:21 +02:00
|
|
|
bctx.font = `${ti.size}px system-ui, -apple-system, Segoe UI, Roboto, sans-serif`;
|
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
|
|
|
bctx.textBaseline = "alphabetic";
|
|
|
|
|
bctx.fillText(value, ti.x, ti.y);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
pushSnapshot();
|
|
|
|
|
}
|
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
|
|
|
function cancelTextInput() {
|
|
|
|
|
if (!state.textInput) return;
|
|
|
|
|
state.textInput.el.remove();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
state.textInput = null;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// -- undo / redo -------------------------------------------------------
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
function doUndo() {
|
|
|
|
|
if (undo.length <= 1) return;
|
|
|
|
|
const cur = undo.pop();
|
|
|
|
|
redo.push(cur);
|
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
|
|
|
restoreSnapshot(undo[undo.length - 1]);
|
|
|
|
|
updateUndoRedo();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
function doRedo() {
|
|
|
|
|
const snap = redo.pop();
|
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
|
|
|
if (!snap) return;
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
undo.push(snap);
|
|
|
|
|
restoreSnapshot(snap);
|
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
|
|
|
updateUndoRedo();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
undoBtn.addEventListener("click", doUndo);
|
|
|
|
|
redoBtn.addEventListener("click", doRedo);
|
|
|
|
|
|
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
|
|
|
// -- save / copy ------------------------------------------------------
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
function canvasBlob() {
|
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
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
|
base.toBlob((b) => b ? resolve(b) : reject(new Error("toBlob returned null")), "image/png"));
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
async function save() {
|
|
|
|
|
try {
|
|
|
|
|
const blob = await canvasBlob();
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
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
|
|
|
dl.href = url;
|
|
|
|
|
dl.download = NAME || "screenshot.png";
|
|
|
|
|
dl.click();
|
|
|
|
|
setTimeout(() => URL.revokeObjectURL(url), 5000);
|
|
|
|
|
playSave();
|
|
|
|
|
toast(`Saved ${dl.download} (${(blob.size / 1024).toFixed(1)} KB)`);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
} catch (e) {
|
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
|
|
|
toast("Save failed: " + (e && e.message || e), true);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
async function copy() {
|
|
|
|
|
try {
|
|
|
|
|
const blob = await canvasBlob();
|
|
|
|
|
await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
|
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
|
|
|
playCopy();
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
toast("Copied to clipboard");
|
|
|
|
|
} catch (e) {
|
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
|
|
|
toast("Copy failed: " + (e && e.message || e), true);
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$("save").addEventListener("click", save);
|
|
|
|
|
$("copy").addEventListener("click", copy);
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
const applyCropBtn = document.getElementById("apply-crop");
|
|
|
|
|
const cancelCropBtn = document.getElementById("cancel-crop");
|
|
|
|
|
if (applyCropBtn) applyCropBtn.addEventListener("click", applyCrop);
|
|
|
|
|
if (cancelCropBtn) cancelCropBtn.addEventListener("click", cancelCrop);
|
2026-09-08 02:27:36 +02:00
|
|
|
|
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
|
|
|
// -- top-bar navigation ----------------------------------------------
|
|
|
|
|
$("back").addEventListener("click", () => { playDiscard(); location.href = "panel.html"; });
|
|
|
|
|
|
2026-09-09 10:55:21 +02:00
|
|
|
// Close the sidebar entirely — same IPC the dock icon toggles. We stop
|
|
|
|
|
// on the panel side (see panel.html); the editor mirrors the affordance
|
|
|
|
|
// so users don't need to navigate back before hiding the addon.
|
|
|
|
|
const closeSbBtn = document.getElementById("close-sidebar");
|
|
|
|
|
if (closeSbBtn && window.silentmode?.sidebar) {
|
|
|
|
|
closeSbBtn.addEventListener("click", () => {
|
|
|
|
|
try { window.silentmode.sidebar.close(); } catch {}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Sound-toggle button — mirrors the sidebar panel's, both persist via
|
|
|
|
|
// silentmode.storage so opening either surface reflects the same setting.
|
|
|
|
|
const soundBtn = $("toggle-sound");
|
|
|
|
|
function paintSoundIcon() {
|
|
|
|
|
if (!soundBtn) return;
|
|
|
|
|
soundBtn.title = soundOn ? "Sounds on — click to mute" : "Sounds off — click to enable";
|
|
|
|
|
soundBtn.innerHTML = soundOn
|
|
|
|
|
? '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6v4h3l3.5 3V3L5.5 6z"/><path d="M11 5.5c1 .8 1 4.2 0 5"/><path d="M12.5 4c2 1.5 2 6.5 0 8"/></svg>'
|
|
|
|
|
: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 6v4h3l3.5 3V3L5.5 6z"/><path d="M11 5l4 6M15 5l-4 6"/></svg>';
|
|
|
|
|
}
|
|
|
|
|
(async () => {
|
|
|
|
|
try {
|
|
|
|
|
const v = await window.silentmode?.storage?.get("soundOn", true);
|
|
|
|
|
soundOn = v !== false;
|
|
|
|
|
} catch {}
|
|
|
|
|
paintSoundIcon();
|
|
|
|
|
})();
|
|
|
|
|
if (soundBtn) soundBtn.addEventListener("click", async () => {
|
|
|
|
|
soundOn = !soundOn;
|
|
|
|
|
paintSoundIcon();
|
|
|
|
|
try { await window.silentmode?.storage?.set("soundOn", soundOn); } catch {}
|
|
|
|
|
if (soundOn) playSave();
|
|
|
|
|
});
|
feat(theseus/screenshot): 0.4.0 — editor lives inside the sidebar, maximizable
User report: the sidebar preview lands correctly, but the moment the editor
opens in its own tab the picture is blank. Rather than chase that class of
handoff race again, put the editor in the same webContents as the panel:
the sidebar view navigates panel.html ↔ editor.html in place. Same
document object, same silentmode.storage surface, no cross-tab __pending
transfer at all.
- panel.html "Edit" button now calls silentmode.invoke("arm", …) — the
add-on rewrites __pending with the currently-previewed capture's bytes,
and the panel does location.href = "editor.html?name=…". Sidebar view
loads the editor with the same preload; editor.js's storage-based load
path pulls the pending entry out and paints.
- editor.html gains a "Back" arrow (returns to panel.html) and a
maximize / restore icon.
- discard() now navigates to panel.html instead of closeTab() — there is
no tab to close.
- Manifest drops the "open-tab" capability entirely (no more full-tab
editor); keeps sidebar-panel + capture-tab.
Framework: new silentmode.sidebar.{maximize, restore, toggleMax, isMax,
onMaxChange}. main.js honours them via new sidebar-maximize / -restore /
-toggle-max / -is-max IPCs, remembering the pre-maximize width so a
restore drops back exactly. The sidebar drag-grip auto-exits maximize
mode on any user drag, so pulling the edge always lands on the pre-max
value plus/minus the delta. sidebar-preload exposes the surface;
chrome.html renderer is untouched — this is a per-panel affordance.
Editor tools (crop / arrow / rect / ellipse / pen / text / mosaic /
undo / redo / copy / save) unchanged. Save still goes through Chromium's
<a download> path, so the file lands in Downloads and appears in the
download chip like any other save.
Bundled but not shipped — leaving version bump + deploy to parent session.
2026-09-08 22:18:41 +02:00
|
|
|
|
|
|
|
|
const maxBtn = $("toggle-max");
|
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
|
|
|
async function paintMaxIcon(isMax) {
|
|
|
|
|
maxBtn.title = isMax ? "Restore sidebar width" : "Expand the sidebar to full window";
|
|
|
|
|
maxBtn.innerHTML = isMax
|
|
|
|
|
? '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 2v4H2M10 2v4h4M6 14v-4H2M10 14v-4h4"/></svg>'
|
|
|
|
|
: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 6V2h4M14 6V2h-4M2 10v4h4M14 10v4h-4"/></svg>';
|
|
|
|
|
}
|
|
|
|
|
if (window.silentmode?.sidebar) {
|
feat(theseus/screenshot): 0.4.0 — editor lives inside the sidebar, maximizable
User report: the sidebar preview lands correctly, but the moment the editor
opens in its own tab the picture is blank. Rather than chase that class of
handoff race again, put the editor in the same webContents as the panel:
the sidebar view navigates panel.html ↔ editor.html in place. Same
document object, same silentmode.storage surface, no cross-tab __pending
transfer at all.
- panel.html "Edit" button now calls silentmode.invoke("arm", …) — the
add-on rewrites __pending with the currently-previewed capture's bytes,
and the panel does location.href = "editor.html?name=…". Sidebar view
loads the editor with the same preload; editor.js's storage-based load
path pulls the pending entry out and paints.
- editor.html gains a "Back" arrow (returns to panel.html) and a
maximize / restore icon.
- discard() now navigates to panel.html instead of closeTab() — there is
no tab to close.
- Manifest drops the "open-tab" capability entirely (no more full-tab
editor); keeps sidebar-panel + capture-tab.
Framework: new silentmode.sidebar.{maximize, restore, toggleMax, isMax,
onMaxChange}. main.js honours them via new sidebar-maximize / -restore /
-toggle-max / -is-max IPCs, remembering the pre-maximize width so a
restore drops back exactly. The sidebar drag-grip auto-exits maximize
mode on any user drag, so pulling the edge always lands on the pre-max
value plus/minus the delta. sidebar-preload exposes the surface;
chrome.html renderer is untouched — this is a per-panel affordance.
Editor tools (crop / arrow / rect / ellipse / pen / text / mosaic /
undo / redo / copy / save) unchanged. Save still goes through Chromium's
<a download> path, so the file lands in Downloads and appears in the
download chip like any other save.
Bundled but not shipped — leaving version bump + deploy to parent session.
2026-09-08 22:18:41 +02:00
|
|
|
maxBtn.addEventListener("click", async () => {
|
|
|
|
|
try { await window.silentmode.sidebar.toggleMax(); }
|
|
|
|
|
catch (e) { console.warn("toggleMax failed:", e); }
|
|
|
|
|
});
|
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
|
|
|
window.silentmode.sidebar.onMaxChange(paintMaxIcon);
|
|
|
|
|
window.silentmode.sidebar.isMax().then(paintMaxIcon).catch(() => {});
|
2026-09-08 02:27:36 +02:00
|
|
|
}
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
|
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
|
|
|
// -- keyboard shortcuts ---------------------------------------------
|
|
|
|
|
window.addEventListener("keydown", (ev) => {
|
|
|
|
|
if (state.textInput) return;
|
|
|
|
|
const meta = ev.ctrlKey || ev.metaKey;
|
|
|
|
|
if (meta && ev.key.toLowerCase() === "z" && !ev.shiftKey) { doUndo(); ev.preventDefault(); return; }
|
|
|
|
|
if (meta && ev.key.toLowerCase() === "z" && ev.shiftKey) { doRedo(); ev.preventDefault(); return; }
|
|
|
|
|
if (meta && ev.key.toLowerCase() === "s") { save(); ev.preventDefault(); return; }
|
|
|
|
|
if (meta && ev.key.toLowerCase() === "c") { copy(); ev.preventDefault(); return; }
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
if (state.tool === "crop" && state.cropRect) {
|
|
|
|
|
if (ev.key === "Enter") { applyCrop(); ev.preventDefault(); return; }
|
|
|
|
|
if (ev.key === "Escape") { cancelCrop(); ev.preventDefault(); return; }
|
|
|
|
|
}
|
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
|
|
|
if (ev.key === "a") setTool("arrow");
|
|
|
|
|
else if (ev.key === "r") setTool("rect");
|
|
|
|
|
else if (ev.key === "o") setTool("ellipse");
|
|
|
|
|
else if (ev.key === "p") setTool("pen");
|
|
|
|
|
else if (ev.key === "t") setTool("text");
|
feat(theseus/screenshot): 0.6.0 — crop + mosaic redaction, right-anchored sidebar controls, real shutter+print sounds
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
2026-09-09 10:24:03 +02:00
|
|
|
else if (ev.key === "c") setTool("crop");
|
|
|
|
|
else if (ev.key === "b") setTool("blur");
|
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
|
|
|
else if (ev.key === "Escape") setTool("select");
|
|
|
|
|
});
|
|
|
|
|
|
feat(theseus/screenshot): full-tab editor + toolbar-menu + open-tab capabilities
Reworks the screenshot addon into the flow the user asked for: the
dock icon opens a small dropdown menu (Visible viewport / Full page /
Region…) instead of the sidebar picker, and each capture opens a
full browser tab hosting an editor.
Two new addon-host capabilities land alongside:
- toolbar-menu: the addon declares an icon + item list in its manifest;
the chrome dock renders a button that, on click, opens a small menu
and dispatches the selection to the addon via addon-menu-select IPC.
- open-tab: api.openTab(path) opens a browser tab whose URL is the
addon's local file. Origin-gated per addon; the editor uses a
dedicated addon-tab-preload for its main → renderer bridge.
Editor page (editor.html/js/css):
- Crop, arrow, rectangle, circle, freehand pen, text, blur
- Colour swatches (red / yellow / acid / white / black), 3 stroke widths
- Undo/redo command stack, zoom controls
- Save PNG (goes through the download pipeline, chip picks it up)
- Copy to clipboard via ClipboardItem
2026-09-07 00:56:57 +02:00
|
|
|
init();
|