diff --git a/bundled-addons/screenshot/addon.json b/bundled-addons/screenshot/addon.json
index 247a4aa..f0089d2 100644
--- a/bundled-addons/screenshot/addon.json
+++ b/bundled-addons/screenshot/addon.json
@@ -1,11 +1,11 @@
{
"id": "screenshot",
"name": "Screenshot",
- "version": "0.3.0",
- "description": "Capture the current tab — visible viewport, full page, or a rectangle you draw. Preview lands in the sidebar; open in a new tab for the full editor (crop, annotate, redact, save).",
+ "version": "0.4.0",
+ "description": "Capture the current tab — visible viewport, full page, or a rectangle you draw. Preview + full editor (crop, annotate, redact, save) live inside the sidebar. Expand the sidebar to full window for a canvas-sized editor.",
"author": "Silent Mode",
"icon": "📸",
"main": "index.js",
- "capabilities": ["sidebar-panel", "capture-tab", "open-tab"],
+ "capabilities": ["sidebar-panel", "capture-tab"],
"updateURL": "https://navigate.st/bns/theseus.x/extensions/screenshot/updates.json"
}
diff --git a/bundled-addons/screenshot/editor.html b/bundled-addons/screenshot/editor.html
index 6b43400..bfa8732 100644
--- a/bundled-addons/screenshot/editor.html
+++ b/bundled-addons/screenshot/editor.html
@@ -7,6 +7,12 @@
+
+
screenshot
diff --git a/bundled-addons/screenshot/editor.js b/bundled-addons/screenshot/editor.js
index fd48a37..021337f 100644
--- a/bundled-addons/screenshot/editor.js
+++ b/bundled-addons/screenshot/editor.js
@@ -588,24 +588,34 @@ $("save").addEventListener("click", save);
$("copy").addEventListener("click", copy);
$("discard").addEventListener("click", discard);
-// Drop the working screenshot and close the editor tab. Used by the
-// Discard button and the top-level Escape shortcut. window.close() on a
-// tab opened via api.openTab lands as the tab's close request; Theseus's
-// tab manager honors it just like a normal cross-origin window.close().
+// Back button — navigate the sidebar view back to panel.html. Same
+// webContents, so it's just a location swap; no IPC needed.
+const backBtn = $("back");
+if (backBtn) backBtn.addEventListener("click", () => { location.href = "panel.html"; });
+
+// Maximize / restore — asks the sidebar host to widen its view to the full
+// window and back. Icon reflects state via silentmode.sidebar.onMaxChange.
+const maxBtn = $("toggle-max");
+if (maxBtn && window.silentmode?.sidebar) {
+ maxBtn.addEventListener("click", async () => {
+ try { await window.silentmode.sidebar.toggleMax(); }
+ catch (e) { console.warn("toggleMax failed:", e); }
+ });
+ const paint = (isMax) => {
+ maxBtn.title = isMax ? "Restore sidebar width" : "Expand the sidebar to full window";
+ maxBtn.innerHTML = isMax
+ ? ''
+ : '';
+ };
+ window.silentmode.sidebar.onMaxChange(paint);
+ window.silentmode.sidebar.isMax().then(paint).catch(() => {});
+}
+
+// Drop the working screenshot and go back to the sidebar panel. Used by
+// the Discard button and the top-level Escape shortcut.
async function discard() {
- // Preferred path: ask main to close this tab. window.close() on a page
- // Chromium didn't open via script is a no-op by default, and we don't
- // want the editor stuck if the user has changed nothing to save.
- try {
- if (window.silentmode?.closeTab) {
- const ok = await window.silentmode.closeTab();
- if (ok) return;
- }
- } catch { /* fall through */ }
- // Fallbacks: window.close() (works when the tab was opened via JS), then
- // a hard navigation so at minimum the stale screenshot is gone.
- try { window.close(); } catch {}
- location.replace("about:blank");
+ try { if (window.silentmode?.storage) await window.silentmode.storage.set("__pending", null); } catch {}
+ location.href = "panel.html";
}
init();
diff --git a/bundled-addons/screenshot/index.js b/bundled-addons/screenshot/index.js
index 03805bf..70255ad 100644
--- a/bundled-addons/screenshot/index.js
+++ b/bundled-addons/screenshot/index.js
@@ -101,22 +101,22 @@ module.exports = {
};
});
- // panel invokes "openInTab" once the user is happy with the preview.
- // We rewrite __pending fresh (the editor consumes it on load) so the
- // editor is always populated from the most-recent capture — even if a
- // prior editor tab already drained the pending entry.
- api.onMessage("openInTab", (payload) => {
+ // panel invokes "arm" right before it navigates itself to editor.html
+ // (in-sidebar navigation — SAME webContents, so the editor's storage-
+ // based load path Just Works). We rewrite __pending fresh here so
+ // whichever capture is currently previewed becomes the one the editor
+ // draws, even if a previous edit session already drained the entry.
+ api.onMessage("arm", (payload) => {
const wantedName = payload && payload.name ? String(payload.name) : "";
let recent = api.storage.get("recent", []);
if (!Array.isArray(recent)) recent = [];
recent = pruneRecent(recent);
api.storage.set("recent", recent);
const hit = wantedName ? recent.find((r) => r.id === wantedName) : recent[0];
- if (!hit) throw new Error("no capture to open — take one first");
+ if (!hit) throw new Error("no capture to edit — take one first");
const dataUrl = readAsDataUrl(hit.path);
api.storage.set("__pending", { name: hit.name, dataUrl, at: Date.now() });
- api.openTab("editor.html", { query: { name: hit.name } });
- api.log(`openInTab → editor.html?name=${hit.name}`);
+ api.log(`arm → editor.html?name=${hit.name}`);
return { ok: true, name: hit.name };
});
diff --git a/bundled-addons/screenshot/panel.html b/bundled-addons/screenshot/panel.html
index b3bd844..7f2275c 100644
--- a/bundled-addons/screenshot/panel.html
+++ b/bundled-addons/screenshot/panel.html
@@ -20,10 +20,16 @@
display: flex; flex-direction: column; }
header { display: flex; align-items: center; justify-content: space-between;
padding: 10px 14px; border-bottom: 1px solid var(--line);
- background: var(--panel); }
- header .t { font-weight: 600; display: flex; gap: 8px; align-items: center; }
+ background: var(--panel); gap: 8px; }
+ header .t { font-weight: 600; display: flex; gap: 8px; align-items: center; min-width: 0; }
header .t .em { font-size: 15px; }
- header .m { color: var(--dim); font-size: 11.5px; min-height: 15px; }
+ header .m { color: var(--dim); font-size: 11.5px; min-height: 15px; flex: 1; text-align: right; }
+ header .max {
+ border: 1px solid var(--line); border-radius: 6px; background: var(--btn);
+ color: var(--ink); cursor: pointer; padding: 4px 6px; font: inherit; line-height: 0;
+ }
+ header .max:hover { background: var(--btn-h); }
+ header .max svg { width: 14px; height: 14px; display: block; }
main { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 12px; }
.modes { display: flex; gap: 6px; }
button.mode {
@@ -67,7 +73,7 @@
.status.ok { color: var(--acid); }
.recent { display: none; }
.recent.on { display: block; }
- .recent .lbl { color: var(--dim); font-size: 11.5px; text-transform: uppercase; letter-spacing: .5px; margin: 4px 0 6px; }
+ .recent .lbl { color: var(--dim); font-size: 11.5px; text-transform: uppercase; letter-spacing: .5px; margin: 4px 0 6px; display: flex; justify-content: space-between; }
.recent .strip { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; }
.recent .tile {
flex: 0 0 auto; width: 78px; height: 52px; border-radius: 4px;
@@ -77,7 +83,7 @@
}
.recent .tile img { width: 100%; height: 100%; object-fit: cover; background: #fff; }
.recent .tile:hover { border-color: var(--acid); }
- .recent .clear { background: none; border: 0; color: var(--dim); font: inherit; cursor: pointer; float: right; }
+ .recent .clear { background: none; border: 0; color: var(--dim); font: inherit; cursor: pointer; text-transform: none; letter-spacing: 0; }
.recent .clear:hover { color: var(--err); }
@@ -85,6 +91,11 @@