From 32db9b7663793ec0967fb4ab55a61da21fae2847 Mon Sep 17 00:00:00 2001 From: Eliot Hedeman Date: Thu, 23 Apr 2026 15:04:43 -0400 Subject: [PATCH 1/2] feat(toolpath-desktop): replace session checkboxes with Select button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multi-select UX forced users to scroll to a footer Preview button to advance; merging traces isn't well supported yet, so the checkboxes had no real payoff. Each Claude/Pi session row now has a per-row "Select →" button that derives that one session and jumps straight to Preview. Drops the now-unused `selected` state, `ClaudeToggleSession`/ `PiToggleSession` messages, and the footer bar. `ClaudeDerive`/`PiDerive` now carry `{ path, sid }` inline. --- .../frontend/src/lib/types.ts | 8 +--- .../frontend/src/lib/update.ts | 44 ++++-------------- .../frontend/src/routes/BrowseClaude.svelte | 45 ++++--------------- .../frontend/src/routes/BrowsePi.svelte | 45 ++++--------------- 4 files changed, 26 insertions(+), 116 deletions(-) diff --git a/crates/toolpath-desktop/frontend/src/lib/types.ts b/crates/toolpath-desktop/frontend/src/lib/types.ts index e764103..5f9d34d 100644 --- a/crates/toolpath-desktop/frontend/src/lib/types.ts +++ b/crates/toolpath-desktop/frontend/src/lib/types.ts @@ -121,7 +121,6 @@ export interface ClaudeSlice { sessionsByPath: Record; sessionsLoading: Record; titles: Record; // `${path}|${sid}` → title - selected: Record>; // path → sid set deriving: boolean; } @@ -132,7 +131,6 @@ export interface PiSlice { expanded: string | null; sessionsByPath: Record; sessionsLoading: Record; - selected: Record>; deriving: boolean; } @@ -202,9 +200,8 @@ export type Msg = | { t: "ClaudeExpandProject"; path: string } | { t: "ClaudeSessionReceived"; session: ClaudeSession } | { t: "ClaudeSessionsDone"; path: string } - | { t: "ClaudeToggleSession"; path: string; sid: string } | { t: "ClaudeTitleLoaded"; path: string; sid: string; title: string | null } - | { t: "ClaudeDerive" } + | { t: "ClaudeDerive"; path: string; sid: string } // Pi | { t: "PiEnsureProjects" } @@ -214,8 +211,7 @@ export type Msg = | { t: "PiExpandProject"; path: string } | { t: "PiSessionReceived"; session: PiSession } | { t: "PiSessionsDone"; path: string } - | { t: "PiToggleSession"; path: string; sid: string } - | { t: "PiDerive" } + | { t: "PiDerive"; path: string; sid: string } // Git | { t: "GitSetRepoPath"; value: string } diff --git a/crates/toolpath-desktop/frontend/src/lib/update.ts b/crates/toolpath-desktop/frontend/src/lib/update.ts index bd2ce34..906a138 100644 --- a/crates/toolpath-desktop/frontend/src/lib/update.ts +++ b/crates/toolpath-desktop/frontend/src/lib/update.ts @@ -18,7 +18,6 @@ export function initialModel(): Model { sessionsByPath: {}, sessionsLoading: {}, titles: {}, - selected: {}, deriving: false, }, pi: { @@ -28,7 +27,6 @@ export function initialModel(): Model { expanded: null, sessionsByPath: {}, sessionsLoading: {}, - selected: {}, deriving: false, }, git: { @@ -190,34 +188,21 @@ export function update(msg: Msg, m: Model): [Model, Cmd | null] { { ...m, claude: { ...m.claude, sessionsLoading: { ...m.claude.sessionsLoading, [msg.path]: false } } }, null, ]; - case "ClaudeToggleSession": { - const sel = { ...(m.claude.selected[msg.path] ?? {}) }; - if (sel[msg.sid]) delete sel[msg.sid]; - else sel[msg.sid] = true; - return [ - { ...m, claude: { ...m.claude, selected: { ...m.claude.selected, [msg.path]: sel } } }, - null, - ]; - } case "ClaudeTitleLoaded": { const titles = { ...m.claude.titles, [`${msg.path}|${msg.sid}`]: msg.title ?? "" }; return [{ ...m, claude: { ...m.claude, titles } }, null]; } case "ClaudeDerive": { - const path = Object.keys(m.claude.selected).find( - (p) => Object.keys(m.claude.selected[p] ?? {}).length > 0, - ); - if (!path) return [m, null]; - const sessionIds = Object.keys(m.claude.selected[path] ?? {}); + const { path, sid } = msg; const displayName = path.split("/").filter(Boolean).pop() ?? "claude"; - const shortId = sessionIds[0]?.slice(0, 8) ?? ""; - const filename = `${displayName}${shortId ? `-${shortId}` : ""}.path.json`; + const shortId = sid.slice(0, 8); + const filename = `${displayName}-${shortId}.path.json`; return [ { ...m, claude: { ...m.claude, deriving: true }, error: null }, { type: "invoke", name: "derive_claude", - args: { projectPath: path, sessionIds, includeThinking: false }, + args: { projectPath: path, sessionIds: [sid], includeThinking: false }, onOk: (doc) => ({ t: "DeriveSucceeded", doc: doc as import("./types").Document, source: `Claude: ${displayName}`, filename }), onErr: (e) => ({ t: "DeriveFailed", error: e }), }, @@ -276,30 +261,17 @@ export function update(msg: Msg, m: Model): [Model, Cmd | null] { { ...m, pi: { ...m.pi, sessionsLoading: { ...m.pi.sessionsLoading, [msg.path]: false } } }, null, ]; - case "PiToggleSession": { - const sel = { ...(m.pi.selected[msg.path] ?? {}) }; - if (sel[msg.sid]) delete sel[msg.sid]; - else sel[msg.sid] = true; - return [ - { ...m, pi: { ...m.pi, selected: { ...m.pi.selected, [msg.path]: sel } } }, - null, - ]; - } case "PiDerive": { - const path = Object.keys(m.pi.selected).find( - (p) => Object.keys(m.pi.selected[p] ?? {}).length > 0, - ); - if (!path) return [m, null]; - const sessionIds = Object.keys(m.pi.selected[path] ?? {}); + const { path, sid } = msg; const displayName = path.split("/").filter(Boolean).pop() ?? "pi"; - const shortId = sessionIds[0]?.slice(0, 8) ?? ""; - const filename = `${displayName}${shortId ? `-${shortId}` : ""}.path.json`; + const shortId = sid.slice(0, 8); + const filename = `${displayName}-${shortId}.path.json`; return [ { ...m, pi: { ...m.pi, deriving: true }, error: null }, { type: "invoke", name: "derive_pi", - args: { projectPath: path, sessionIds, includeThinking: false }, + args: { projectPath: path, sessionIds: [sid], includeThinking: false }, onOk: (doc) => ({ t: "DeriveSucceeded", doc: doc as import("./types").Document, diff --git a/crates/toolpath-desktop/frontend/src/routes/BrowseClaude.svelte b/crates/toolpath-desktop/frontend/src/routes/BrowseClaude.svelte index 7f51621..51e3bbc 100644 --- a/crates/toolpath-desktop/frontend/src/routes/BrowseClaude.svelte +++ b/crates/toolpath-desktop/frontend/src/routes/BrowseClaude.svelte @@ -82,9 +82,6 @@ const claude = $derived(store.m.claude); const projectCount = $derived(claude.projects.length); - const selectedCount = $derived( - Object.values(claude.selected).reduce((acc, s) => acc + Object.keys(s || {}).length, 0), - );
@@ -115,7 +112,6 @@
{#each claude.projects as p (p.project_path)} {@const isExpanded = claude.expanded === p.project_path} - {@const selectedForProject = Object.keys(claude.selected[p.project_path] ?? {}).length}
{#if isExpanded} @@ -143,25 +138,9 @@
No sessions in this project.
{:else} {#each sessions as s (s.session_id)} - {@const isChecked = !!(claude.selected[p.project_path] ?? {})[s.session_id]} {@const title = claude.titles[`${p.project_path}|${s.session_id}`]} -
diff --git a/crates/toolpath-desktop/frontend/src/routes/BrowsePi.svelte b/crates/toolpath-desktop/frontend/src/routes/BrowsePi.svelte index 0b70ba3..2273d82 100644 --- a/crates/toolpath-desktop/frontend/src/routes/BrowsePi.svelte +++ b/crates/toolpath-desktop/frontend/src/routes/BrowsePi.svelte @@ -99,9 +99,6 @@ const pi = $derived(store.m.pi); const projectCount = $derived(pi.projects.length); - const selectedCount = $derived( - Object.values(pi.selected).reduce((acc, s) => acc + Object.keys(s || {}).length, 0), - );
@@ -132,7 +129,6 @@
{#each pi.projects as p (p.project_path)} {@const isExpanded = pi.expanded === p.project_path} - {@const selectedForProject = Object.keys(pi.selected[p.project_path] ?? {}).length}
{#if isExpanded} @@ -160,24 +155,8 @@
No sessions in this project.
{:else} {#each sessions as s (s.session_id)} - {@const isChecked = !!(pi.selected[p.project_path] ?? {})[s.session_id]} -
From d646100a8e21db7a5d39d8f113740b39ac58f6f1 Mon Sep 17 00:00:00 2001 From: Eliot Hedeman Date: Thu, 23 Apr 2026 15:04:52 -0400 Subject: [PATCH 2/2] fix(toolpath-desktop): stop preview transcript from forcing horizontal scroll `.preview-body--split` used `grid-template-columns: 380px 1fr`. Grid 1fr defaults to `minmax(auto, 1fr)`, so an unbreakable string in the transcript (a long file path, a wide pre block) would grow the right track past the viewport and drag the whole preview page into horizontal scroll. Switch to `minmax(0, 1fr)` so the column is capped at the available width and `.chat-view`'s `overflow-x: hidden` can actually clip overflowing bubbles. --- crates/toolpath-desktop/frontend/src/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/toolpath-desktop/frontend/src/styles.css b/crates/toolpath-desktop/frontend/src/styles.css index b66a678..785b53c 100644 --- a/crates/toolpath-desktop/frontend/src/styles.css +++ b/crates/toolpath-desktop/frontend/src/styles.css @@ -825,7 +825,7 @@ html, body { padding: 18px 28px 28px; } .preview-body--split { - grid-template-columns: 380px 1fr; + grid-template-columns: 380px minmax(0, 1fr); padding: 0; } .preview-body--split > .preview-body__left {