@@ -2676,6 +2679,94 @@ function HandoffDivider({ block }: { block: Block }) {
);
}
+/** A mid-turn interjection, e.g. OMP advisor notes: a labeled boundary with
+ * a collapsible advisory body below it. */
+function InterjectionDivider({ block }: { block: Block }) {
+ const [expanded, setExpanded] = useState(false);
+ const [overflows, setOverflows] = useState(false);
+ const textRef = useRef(null);
+
+ useLayoutEffect(() => {
+ const el = textRef.current;
+ if (!el || !block.text) {
+ setOverflows(false);
+ return;
+ }
+ const measure = () => {
+ if (!expanded) {
+ setOverflows(el.scrollHeight > el.clientHeight + 1);
+ }
+ };
+ measure();
+ const observer = new ResizeObserver(measure);
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [block.text, expanded]);
+
+ const meta = block.interjection;
+ if (!meta) return null;
+ const label =
+ meta.customType === "advisor"
+ ? "Advisor"
+ : meta.customType === "custom"
+ ? "Notice"
+ : meta.customType;
+ const severityText =
+ meta.severity === "blocker"
+ ? "Blocker"
+ : meta.severity === "concern"
+ ? "Concern"
+ : meta.severity === "nit"
+ ? "Nit"
+ : undefined;
+ const severityClass =
+ meta.severity === "blocker"
+ ? "text-red-400"
+ : meta.severity === "concern"
+ ? "text-amber-400"
+ : "text-content/55";
+ return (
+
+
+
+
+ {label}
+ {severityText ? (
+
+ {severityText}
+
+ ) : null}
+
+
+
+ {block.text ? (
+
+
+ {block.text}
+
+ {overflows ? (
+
+ ) : null}
+
+ ) : null}
+
+ );
+}
+
function lastUserBlockId(blocks: Block[]): string | undefined {
return turnUserBlock(blocks)?.id;
}
diff --git a/src/surfaces/transcriptActivity.test.ts b/src/surfaces/transcriptActivity.test.ts
index c20ad288..d3fe6a8b 100644
--- a/src/surfaces/transcriptActivity.test.ts
+++ b/src/surfaces/transcriptActivity.test.ts
@@ -791,6 +791,27 @@ describe("foldableWork", () => {
expect(foldableWork(turn)).toEqual({ start: 3, end: 3 });
});
+ it("uses an interjection as a hard boundary between answered work phases", () => {
+ const turn = items([
+ shell("before"),
+ note("answer", "The complete answer."),
+ {
+ id: "advisor",
+ role: "system",
+ text: "Check the fallback.",
+ interjection: { customType: "advisor", severity: "nit" },
+ },
+ shell("after"),
+ note("ack", "Checked."),
+ ]);
+ const fold = foldableWork(turn)!;
+
+ expect(fold).toEqual({ start: 3, end: 3 });
+ expect(foldedBlocks(turn, fold).map((block) => block.id)).toEqual([
+ "after",
+ ]);
+ });
+
it("leaves an approval attached to earlier work outside the fold", () => {
const turn = items([
shell("pending", "pending", { requestId: 1 }),
diff --git a/src/surfaces/transcriptActivity.ts b/src/surfaces/transcriptActivity.ts
index d7e4abac..3b2ba00b 100644
--- a/src/surfaces/transcriptActivity.ts
+++ b/src/surfaces/transcriptActivity.ts
@@ -682,6 +682,10 @@ export type WorkFold = { start: number; end: number };
* approval. As the turn streams, each new paragraph folds the work and running
* commentary before it, leaving the final answer visible. A late approval can
* reopen that boundary so its controls remain available.
+ *
+ * Persisted interjections (system blocks with interjection chrome) are neither
+ * prose nor work, so they stop the fold: an answer the harness already showed
+ * never folds behind an interjection that arrived after it.
*/
export function foldableWork(items: TurnItem[]): WorkFold | undefined {
let end = -1;
From 79facefdf96f6b9b2bfa66248af96462a40430bd Mon Sep 17 00:00:00 2001
From: Andrew
Date: Mon, 14 Sep 2026 10:31:19 +0300
Subject: [PATCH 13/17] Add `Close All Tabs` on `Cmd+Shift+W` (#215)
* Add `Close All Tabs` on `Cmd+Shift+W`
* Address review feedback on Close All Tabs
---
src-tauri/src/menu.rs | 51 ++++++++------
src/App.tsx | 150 +++++++++++++++++++++++++++++++++++++++-
src/chrome/MenuBar.tsx | 12 ++++
src/lib/layout.test.ts | 65 +++++++++++++++++
src/lib/layout.ts | 28 ++++++++
src/lib/settings.ts | 1 +
src/lib/tabKeys.test.ts | 10 +++
src/lib/tabKeys.ts | 3 +
8 files changed, 297 insertions(+), 23 deletions(-)
diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs
index 6c58ff7d..af79def9 100644
--- a/src-tauri/src/menu.rs
+++ b/src-tauri/src/menu.rs
@@ -25,30 +25,33 @@ pub fn dispatch(app: &AppHandle, id: &str) {
| "open_model_picker" | "open_settings" | "check_for_updates" => {
let _ = app.emit(id, ());
}
- "zoom_in" | "zoom_out" | "zoom_reset" => {
- // Zoom targets one window: a broadcast would make every window
- // increment the shared scale setting on a single menu click.
- let mut windows: Vec<_> = app.webview_windows().into_values().collect();
- windows.sort_by(|a, b| a.label().cmp(b.label()));
- let target = windows
+ // Zoom and Close All Tabs target one window: a broadcast would make
+ // every window act on a single menu click.
+ "zoom_in" | "zoom_out" | "zoom_reset" | "close_all_tabs" => emit_to_focused(app, id),
+ _ => {}
+ }
+}
+
+/// Emit `id` to the focused window, falling back to a visible one, then any.
+fn emit_to_focused(app: &AppHandle, id: &str) {
+ let mut windows: Vec<_> = app.webview_windows().into_values().collect();
+ windows.sort_by(|a, b| a.label().cmp(b.label()));
+ let target = windows
+ .iter()
+ .find(|window| window.is_focused().unwrap_or(false))
+ .or_else(|| {
+ windows
.iter()
- .find(|window| window.is_focused().unwrap_or(false))
- .or_else(|| {
- windows
- .iter()
- .find(|window| window.is_visible().unwrap_or(false))
- })
- .or(windows.first());
- match target {
- Some(window) => {
- let _ = app.emit_to(window.label(), id, ());
- }
- None => {
- let _ = app.emit(id, ());
- }
- }
+ .find(|window| window.is_visible().unwrap_or(false))
+ })
+ .or(windows.first());
+ match target {
+ Some(window) => {
+ let _ = app.emit_to(window.label(), id, ());
+ }
+ None => {
+ let _ = app.emit(id, ());
}
- _ => {}
}
}
@@ -97,6 +100,9 @@ fn build(app: &AppHandle) -> tauri::Result