Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
372edbf
Name the code-review:read bridge channel (CR-2)
BunsDev Oct 20, 2026
22f5335
Name the code-review:read bridge channel (CR-2)
BunsDev Oct 20, 2026
a34ef72
Extend the read contract for what GitHub's REST answers say (CR-2)
BunsDev Oct 20, 2026
bac6db4
Extend the read contract for what GitHub's REST answers say (CR-2)
BunsDev Oct 20, 2026
fbd9be1
Add the Code Review workspace's reader, navigation and pins (CR-3)
BunsDev Oct 20, 2026
8c76bfc
Add the Code Review destination, inbox and workspace host (CR-3)
BunsDev Oct 20, 2026
1b40a5b
Test Code Review navigation, races and states through main's channel …
BunsDev Oct 20, 2026
3d5e31b
Record the Code Review workspace's geometry, controls and deviations …
BunsDev Oct 20, 2026
ae6ea30
Answer code-review:read in the Store demo (CR-2)
BunsDev Oct 20, 2026
8883428
Answer code-review:read in the Store demo (CR-2)
BunsDev Oct 20, 2026
197276e
Add main's Code Review read service through gh (CR-2)
BunsDev Oct 20, 2026
f2c2de7
Fix the CR-2 review findings: account switches, stale fallback, coverage
BunsDev Oct 20, 2026
d31f661
Refuse a read whose gh sign-in moved under it (CR-2 re-review)
BunsDev Oct 20, 2026
2ba1462
Invalidate retained PR data after visibility loss (#354)
BunsDev Oct 21, 2026
ec498e7
Preserve inbox page states and integrate authenticated CR3 navigation…
BunsDev Oct 21, 2026
8882aad
Exercise CR2 reads through production Electron IPC (#361)
BunsDev Oct 21, 2026
e5ce423
Integrate CR2 reads and Electron IPC coverage with current main
BunsDev Oct 21, 2026
54c46f1
Integrate Code Review inbox with validated producer and current main
BunsDev Oct 21, 2026
0866265
Reconcile Code Review inbox with landed producer and Note fixes
BunsDev Oct 21, 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
529 changes: 529 additions & 0 deletions apps/desktop/e2e/code-review-navigation.spec.ts

Large diffs are not rendered by default.

22 changes: 15 additions & 7 deletions apps/desktop/src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import { PANEL_WIDTHS, usePanelSizing } from "./panel-sizing";
import { useLayout } from "./layout";
import { applyTheme, restoreTheme } from "./theme";
import { applyItemEvent, type ItemEvent } from "./transcript";
import { codeReviewEnabled } from "./code-review/feature.js";
import type { ReviewNavigation } from "./code-review/CodeReviewView";

/** ⌃` shows or hides the thread's terminal. xterm maps no byte to it, so it reaches this handler even from inside the shell. */
const isTerminalToggle = (e: KeyboardEvent) => e.ctrlKey && !e.metaKey && !e.altKey && e.code === "Backquote";
Expand All @@ -47,6 +49,7 @@ const ImagesView = lazy(() => import("./images/ImagesView").then(m => ({ default
const RoutinesView = lazy(() => import("./routines/RoutinesView").then(m => ({ default: m.RoutinesView })));
const PluginSurface = lazy(() => import("./plugins/PluginSurface").then(m => ({ default: m.PluginSurface })));
const SpaceView = lazy(() => import("./space/SpaceView").then(m => ({ default: m.SpaceView })));
const CodeReviewView = lazy(() => import("./code-review/CodeReviewView"));

export function App({ persistPreferences = true, spaceEnabled = true, analyticsEnabled = true, suspended = false }: { persistPreferences?: boolean; spaceEnabled?: boolean; analyticsEnabled?: boolean; suspended?: boolean } = {}) {
const updater = useUpdateState();
Expand Down Expand Up @@ -84,7 +87,11 @@ export function App({ persistPreferences = true, spaceEnabled = true, analyticsE
window.addEventListener("modex:open-page", open);
return () => window.removeEventListener("modex:open-page", open);
}, []);
const [surface, setSurface] = useState<"chat" | "space" | "analytics" | "images" | "plugins" | "routines">("chat");
const [surface, setSurface] = useState<"chat" | "space" | "analytics" | "images" | "plugins" | "routines" | "code-review">("chat");
// Code Review (#319) stays hidden in built apps until Gate A is done; see code-review/feature.ts.
const [reviewEnabled] = useState(() => codeReviewEnabled(localStorage, import.meta.env.DEV));
const [reviewOpened, setReviewOpened] = useState(false);
const [reviewNav, setReviewNav] = useState<ReviewNavigation | null>(null);
const [mediaFeatures, setMediaFeatures] = useState({ images: false, plugins: false });
const [imagesOpened, setImagesOpened] = useState(false);
useEffect(() => { let live = true; void bridge.invoke("features:media", undefined).then(features => { if (live) setMediaFeatures(features); }).catch(() => {}); return () => { live = false; }; }, []);
Expand Down Expand Up @@ -896,10 +903,10 @@ export function App({ persistPreferences = true, spaceEnabled = true, analyticsE
<TitleBar
thread={surface === "chat" ? thread : null}
onRename={(title) => void updateThread({ title })}
canBack={surface === "chat" && history.canBack}
canForward={surface === "chat" && history.canForward}
onBack={history.back}
onForward={history.forward}
canBack={surface === "chat" ? history.canBack : surface === "code-review" && Boolean(reviewNav?.canBack)}
canForward={surface === "chat" ? history.canForward : surface === "code-review" && Boolean(reviewNav?.canForward)}
onBack={surface === "code-review" ? () => reviewNav?.back() : history.back}
onForward={surface === "code-review" ? () => reviewNav?.forward() : history.forward}
sidebarOpen={sidebarOpen}
onToggleSidebar={() => setSidebarOpen((v) => !v)}
showChanges={showChanges}
Expand All @@ -913,13 +920,14 @@ export function App({ persistPreferences = true, spaceEnabled = true, analyticsE
onArchive={archiveThread}
platform={bridge.platform}
/>
<Rail surface={surface} onOpenRoutines={() => setSurface("routines")} onOpenAnalytics={showAnalytics ? () => setSurface("analytics") : undefined} onOpenImages={mediaFeatures.images ? openImages : undefined} onOpenPlugins={mediaFeatures.plugins ? () => setSurface("plugins") : undefined} onOpenChat={() => setSurface("chat")} onOpenSpace={spaceEnabled ? () => { setSpaceOpened(true); setSurface("space"); } : undefined} activeWork={activeWork} onOpenActiveWork={openActiveWork} onOpenSettings={() => setShowSettings(true)} onOpenCompanion={() => setShowCompanion(true)} streamerMode={streamerMode} onEnableStreamerMode={() => setLayout({ streamerMode: !streamerMode })} />
<Rail surface={surface} onOpenRoutines={() => setSurface("routines")} onOpenAnalytics={showAnalytics ? () => setSurface("analytics") : undefined} onOpenImages={mediaFeatures.images ? openImages : undefined} onOpenPlugins={mediaFeatures.plugins ? () => setSurface("plugins") : undefined} onOpenCodeReview={reviewEnabled ? () => { setReviewOpened(true); setSurface("code-review"); } : undefined} onOpenChat={() => setSurface("chat")} onOpenSpace={spaceEnabled ? () => { setSpaceOpened(true); setSurface("space"); } : undefined} activeWork={activeWork} onOpenActiveWork={openActiveWork} onOpenSettings={() => setShowSettings(true)} onOpenCompanion={() => setShowCompanion(true)} streamerMode={streamerMode} onEnableStreamerMode={() => setLayout({ streamerMode: !streamerMode })} />
<div ref={panels.setSheet} className="sheet" data-testid="sheet" data-surface={surface}>
{(surface === "chat" || surface === "space") && sidebarOpen && <PanelResizeHandle side="sidebar" width={panels.sidebarWidth} min={PANEL_WIDTHS.sidebar.min} max={panels.sidebarMax}
{(surface === "chat" || surface === "space" || surface === "code-review") && sidebarOpen && <PanelResizeHandle side="sidebar" width={panels.sidebarWidth} min={PANEL_WIDTHS.sidebar.min} max={panels.sidebarMax}
onResize={sidebarWidth => setLayout({ sidebarWidth })} onReset={() => setLayout({ sidebarWidth: panels.sidebarDefault })} />}
{surface === "chat" && thread && showChanges && <PanelResizeHandle side="workspace" width={panels.workspaceWidth} min={PANEL_WIDTHS.workspace.min} max={panels.workspaceMax}
onResize={workspaceWidth => setLayout({ workspaceWidth })} onReset={() => setLayout({ workspaceWidth: PANEL_WIDTHS.workspace.default })} />}
{surface === "analytics" && <AnalyticsView active={!showSettings && !showCompanion} />}
{reviewOpened && <Suspense fallback={surface === "code-review" ? <div role="status">Opening Code Review…</div> : null}><CodeReviewView active={surface === "code-review"} inboxOpen={sidebarOpen} layout={layout} setLayout={setLayout} onNavigation={setReviewNav} /></Suspense>}
{imagesOpened && <Suspense fallback={<div role="status">Opening Images…</div>}><ImagesView active={surface === "images"} projects={state.projects} onPlugins={() => setSurface("plugins")} onOpenPage={id => { setOpenPage({ id }); setSpaceOpened(true); setSurface("space"); }} onUseInChat={async id => {
const key = thread?.id ?? draftKey;
if (!thread && !draft) throw new Error("Choose a project and open a chat before attaching an image.");
Expand Down
Loading
Loading