Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7b2a86d
feat: auto-detect Claude Code plans and surface inline commenting UI
srid Mar 31, 2026
68aaba9
refactor: elegance pass on plan feature
srid Mar 31, 2026
34f0118
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Mar 31, 2026
5769e66
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Mar 31, 2026
4aceba6
refactor: scope plans to Claude sessions via JSONL slug
srid Mar 31, 2026
0994e02
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Mar 31, 2026
cc84432
feat: resizable plan pane, full markdown via marked, inline feedback,…
srid Mar 31, 2026
c97fa5b
fix: plan pane overflow + clamp feedback line numbers
srid Mar 31, 2026
28552b1
feat: add Review and Proceed action buttons to plan pane
srid Mar 31, 2026
8bd0479
fix: use \r (carriage return) for PTY Enter, not \n
srid Mar 31, 2026
41ca4be
fix: improve Review prompt to work with Claude's plan confirmation
srid Mar 31, 2026
0f625c1
fix: prevent table/content overflow in plan pane
srid Mar 31, 2026
a21ce68
fix: poll plan content every 3s to pick up Claude's live edits
srid Mar 31, 2026
544e954
refactor: push-based plan content updates via planModifiedAt
srid Mar 31, 2026
3d77f7a
fix: update pnpmDeps hash for marked dependency
srid Mar 31, 2026
d03cb5a
fix: render feedback inline via post-processing marked HTML
srid Mar 31, 2026
3fb3d55
fix: accurate feedback placement via data-line source annotations
srid Mar 31, 2026
ebe8bc9
fix: plan pane initial width + selection popover on unannotated elements
srid Mar 31, 2026
accc51a
fix: invert feedback callout colors for contrast (light-on-dark, dark…
srid Mar 31, 2026
c2e8b58
feat: remove and edit feedback on plan callouts
srid Mar 31, 2026
ac82832
fix: optimistic locking on plan file mutations to prevent overwriting…
srid Mar 31, 2026
b97d3df
feat: highlight changed sections when Claude updates the plan
srid Mar 31, 2026
35c13d9
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Mar 31, 2026
729bceb
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Mar 31, 2026
f93502c
fix: table-layout fixed to eliminate plan pane whitespace gap
srid Mar 31, 2026
c345aef
feat: show feedback comment count badge in plan pane header
srid Mar 31, 2026
eb0a5ff
refactor: unbraid Hickey findings — extract pipeline, highlight, fix …
srid Mar 31, 2026
bd0e433
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Apr 10, 2026
2256591
fix: update pnpmDeps hash after merge
srid Apr 10, 2026
4c8a692
Merge remote-tracking branch 'origin/master' into plan-commenting
srid Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"marked": "^17.0.5",
"partysocket": "^1.1.16",
"solid-js": "^1.9.0",
"solid-sonner": "^0.2.0",
Expand Down
161 changes: 124 additions & 37 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import ClaudeTranscriptDialog from "./ClaudeTranscriptDialog";
import ModalDialog, { refocusTerminal } from "./ModalDialog";
import Dialog from "@corvu/dialog";
import EmptyState from "./EmptyState";
import PlanPane from "./PlanPane";
import Resizable from "@corvu/resizable";
import CloseConfirm, { type CloseConfirmTarget } from "./CloseConfirm";
import { createCommands } from "./commands";
import { exportSessionAsPdf } from "./exportSessionAsPdf";
Expand All @@ -35,6 +37,7 @@ import { useShortcuts } from "./useShortcuts";
import { useSubPanel } from "./useSubPanel";
import { useColorScheme } from "./useColorScheme";
import { useTips } from "./useTips";
import { usePlans } from "./usePlans";

const App: Component = () => {
const { preferences, updatePreferences } = useServerState();
Expand Down Expand Up @@ -107,6 +110,15 @@ const App: Component = () => {
const [searchOpen, setSearchOpen] = createSignal(false);
createEffect(on(store.activeId, () => setSearchOpen(false), { defer: true }));

const {
activePlanPath,
planName,
planContent,
isPlanContentLoading,
addFeedback,
removeFeedback,
} = usePlans({ activeMeta: store.activeMeta });

const { initTipTriggers, startupTips, setStartupTips } = useTips();
initTipTriggers({ terminalIds: store.terminalIds });

Expand Down Expand Up @@ -375,46 +387,121 @@ const App: Component = () => {
/>
{/* min-w-0: override flex min-width:auto so terminal area shrinks below canvas intrinsic size */}
<div class="flex-1 min-h-0 min-w-0 flex flex-col">
<div
class="flex-1 min-h-0 overflow-hidden"
style={{ "background-color": activeTheme().background }}
data-testid="terminal-viewport"
<Show
when={activePlanPath()}
fallback={
<div
class="flex-1 min-h-0 overflow-hidden"
style={{ "background-color": activeTheme().background }}
data-testid="terminal-viewport"
>
<Show
when={!session.isLoading()}
fallback={
<div class="flex items-center justify-center h-full text-fg-3 text-sm">
Connecting...
</div>
}
>
<Show when={store.terminalIds().length === 0}>
<EmptyState
savedSession={session.savedSession() ?? undefined}
onRestore={() => void session.handleRestoreSession()}
/>
</Show>
<For each={store.terminalIds()}>
{(id) => (
<TerminalPane
terminalId={id}
visible={store.activeId() === id}
theme={getTerminalTheme(id)}
searchOpen={searchOpen()}
onSearchOpenChange={setSearchOpen}
subTerminalIds={store.getSubTerminalIds(id)}
getMetadata={store.getMetadata}
onCreateSubTerminal={(parentId, cwd) =>
void crud.handleCreateSubTerminal(parentId, cwd)
}
onCloseTerminal={closeTerminal}
activeMeta={store.activeMeta()}
scrollLockEnabled={scrollLock()}
/>
)}
</For>
</Show>
</div>
}
>
<Show
when={!session.isLoading()}
fallback={
<div class="flex items-center justify-center h-full text-fg-3 text-sm">
Connecting...
{/* Plan pane open — resizable horizontal split: terminal left, plan right */}
<Resizable orientation="horizontal" class="flex-1 min-h-0">
<Resizable.Panel
as="div"
class="min-w-0 overflow-hidden"
minSize={0.3}
>
<div
class="h-full overflow-hidden"
style={{ "background-color": activeTheme().background }}
data-testid="terminal-viewport"
>
<Show
when={!session.isLoading()}
fallback={
<div class="flex items-center justify-center h-full text-fg-3 text-sm">
Connecting...
</div>
}
>
<For each={store.terminalIds()}>
{(id) => (
<TerminalPane
terminalId={id}
visible={store.activeId() === id}
theme={getTerminalTheme(id)}
searchOpen={searchOpen()}
onSearchOpenChange={setSearchOpen}
subTerminalIds={store.getSubTerminalIds(id)}
getMetadata={store.getMetadata}
onCreateSubTerminal={(parentId, cwd) =>
void crud.handleCreateSubTerminal(parentId, cwd)
}
onCloseTerminal={closeTerminal}
activeMeta={store.activeMeta()}
scrollLockEnabled={scrollLock()}
/>
)}
</For>
</Show>
</div>
}
>
<Show when={store.terminalIds().length === 0}>
<EmptyState
savedSession={session.savedSession() ?? undefined}
onRestore={() => void session.handleRestoreSession()}
/>
</Show>
<For each={store.terminalIds()}>
{(id) => (
<TerminalPane
terminalId={id}
visible={store.activeId() === id}
theme={getTerminalTheme(id)}
searchOpen={searchOpen()}
onSearchOpenChange={setSearchOpen}
subTerminalIds={store.getSubTerminalIds(id)}
getMetadata={store.getMetadata}
onCreateSubTerminal={(parentId, cwd) =>
void crud.handleCreateSubTerminal(parentId, cwd)
</Resizable.Panel>
<Resizable.Handle class="w-1 bg-edge hover:bg-accent-bright cursor-col-resize shrink-0" />
<Resizable.Panel
as="div"
class="min-w-0 h-full overflow-hidden"
minSize={0.15}
initialSize={0.35}
>
<PlanPane
content={planContent()}
loading={isPlanContentLoading()}
planName={planName()}
planPath={activePlanPath()}
onAddFeedback={addFeedback}
onRemoveFeedback={removeFeedback}
onSendToTerminal={(text) => {
const id = store.activeId();
if (id) {
// \r = Enter in PTY (carriage return, not newline)
void client.terminal.sendInput({
id,
data: text + "\r",
});
}
onCloseTerminal={closeTerminal}
activeMeta={store.activeMeta()}
scrollLockEnabled={scrollLock()}
/>
)}
</For>
</Show>
</div>
}}
/>
</Resizable.Panel>
</Resizable>
</Show>
<MobileKeyBar activeId={store.activeId} />
</div>
</div>
Expand Down
Loading