@cueloop/client 0.1.0-alpha.65 → 0.1.0-alpha.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -5
- package/src/App.autoclose.test.tsx +2 -2
- package/src/App.diff.test.tsx +79 -112
- package/src/App.plan-review.test.tsx +23 -205
- package/src/App.prototype.test.tsx +1 -8
- package/src/App.readonly.test.tsx +9 -27
- package/src/App.submit-confirm.test.tsx +19 -72
- package/src/App.test.tsx +136 -31
- package/src/App.tsx +491 -322
- package/src/App.walk.test.tsx +15 -16
- package/src/App.workbench.test.tsx +390 -0
- package/src/__snapshots__/view-plan.regression.test.ts.snap +1 -1
- package/src/annotation-palette.ts +61 -0
- package/src/app-screens.tsx +197 -77
- package/src/app-view-model.ts +9 -69
- package/src/components/AnnotationCard.stories.tsx +1 -1
- package/src/components/AnnotationCard.tsx +1 -1
- package/src/components/AnnotationCards.stories.tsx +87 -0
- package/src/components/AnnotationCards.tsx +303 -0
- package/src/components/AppHeader.stories.tsx +50 -0
- package/src/components/AppHeader.tsx +83 -0
- package/src/components/AppShell.stories.tsx +89 -0
- package/src/components/AppShell.tsx +239 -0
- package/src/components/AppShell.zoom.test.tsx +94 -0
- package/src/components/Breadcrumb.stories.tsx +1 -1
- package/src/components/ChangesColumn.stories.tsx +39 -0
- package/src/components/ChangesColumn.tsx +170 -0
- package/src/components/CodeBlock.stories.tsx +1 -1
- package/src/components/CompletionOverlay.stories.tsx +1 -1
- package/src/components/ConfirmCard.stories.tsx +1 -1
- package/src/components/ConfirmCard.tsx +3 -4
- package/src/components/ConfirmDialog.stories.tsx +1 -1
- package/src/components/DiffSheet.scroll.test.tsx +38 -55
- package/src/components/DiffSheet.split.test.tsx +140 -0
- package/src/components/DiffSheet.stories.tsx +56 -54
- package/src/components/DiffSheet.syntax.test.tsx +24 -7
- package/src/components/DiffSheet.tsx +607 -276
- package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
- package/src/components/DiscussionMarkerRail.tsx +181 -0
- package/src/components/EditorGrid.stories.tsx +59 -0
- package/src/components/EditorGrid.tabs.test.tsx +105 -0
- package/src/components/EditorGrid.tsx +338 -0
- package/src/components/EditorGrid.zoom.test.tsx +68 -0
- package/src/components/FileContentsView.stories.tsx +16 -0
- package/src/components/FileContentsView.test.tsx +29 -0
- package/src/components/FileContentsView.tsx +78 -0
- package/src/components/InboxList.stories.tsx +23 -8
- package/src/components/InboxList.tsx +188 -33
- package/src/components/KeybindsDialog.stories.tsx +1 -1
- package/src/components/MarkerPopover.stories.tsx +1 -1
- package/src/components/MenuBar.stories.tsx +3 -12
- package/src/components/MenuBar.tsx +25 -56
- package/src/components/PaneGrid.stories.tsx +84 -0
- package/src/components/PaneGrid.tsx +128 -0
- package/src/components/PanelColumn.stories.tsx +25 -0
- package/src/components/PanelColumn.tsx +95 -0
- package/src/components/ProjectTreeView.stories.tsx +25 -0
- package/src/components/ProjectTreeView.test.tsx +32 -0
- package/src/components/ProjectTreeView.tsx +82 -0
- package/src/components/PromptDialog.stories.tsx +1 -1
- package/src/components/PromptDialog.tsx +8 -1
- package/src/components/PrototypeSheet.stories.tsx +1 -1
- package/src/components/RootOverlay.stories.tsx +44 -0
- package/src/components/RootOverlay.tsx +35 -0
- package/src/components/SettingsDialog.stories.tsx +7 -1
- package/src/components/SettingsDialog.tsx +114 -67
- package/src/components/SettingsRows.stories.tsx +1 -1
- package/src/components/ShellHeader.stories.tsx +50 -0
- package/src/components/ShellHeader.tsx +137 -0
- package/src/components/SurfaceScrollbar.stories.tsx +34 -0
- package/src/components/SurfaceScrollbar.test.tsx +111 -0
- package/src/components/SurfaceScrollbar.tsx +93 -0
- package/src/components/ThreadFooter.stories.tsx +30 -0
- package/src/components/ThreadFooter.test.tsx +31 -0
- package/src/components/ThreadFooter.tsx +54 -0
- package/src/components/ThreadView.slash.test.tsx +113 -0
- package/src/components/ThreadView.stories.tsx +1 -1
- package/src/components/ThreadView.tsx +62 -1352
- package/src/components/ThreadsSidebar.stories.tsx +36 -0
- package/src/components/ThreadsSidebar.tsx +70 -0
- package/src/components/Toast.stories.tsx +1 -1
- package/src/components/Tooltip.stories.tsx +26 -0
- package/src/components/Tooltip.tsx +72 -0
- package/src/components/TreePane.stories.tsx +1 -1
- package/src/components/WalkWizard.stories.tsx +1 -1
- package/src/components/WelcomeSurface.stories.tsx +12 -0
- package/src/components/WelcomeSurface.tsx +52 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +732 -572
- package/src/components/agent-launcher.stories.tsx +1 -1
- package/src/components/diff-sheet-layout.test.ts +125 -55
- package/src/components/diff-sheet-layout.ts +3 -103
- package/src/components/editor-grid.test.ts +86 -0
- package/src/components/editor-grid.ts +138 -0
- package/src/components/fade-title.test.ts +49 -0
- package/src/components/fade-title.ts +61 -0
- package/src/components/file-tree.test.ts +60 -0
- package/src/components/file-tree.ts +112 -0
- package/src/components/pages/Workspace.stories.tsx +181 -0
- package/src/components/primitives/Button.stories.tsx +1 -1
- package/src/components/primitives/Card.stories.tsx +1 -1
- package/src/components/primitives/Dialog.stories.tsx +1 -1
- package/src/components/primitives/IconButton.stories.tsx +36 -0
- package/src/components/primitives/IconButton.tsx +62 -0
- package/src/components/primitives/StatusBar.stories.tsx +1 -1
- package/src/components/primitives/Tabs.stories.tsx +1 -1
- package/src/components/primitives/Toolbar.stories.tsx +1 -1
- package/src/components/primitives/Tree.stories.tsx +124 -0
- package/src/components/primitives/Tree.tsx +96 -0
- package/src/components/primitives/icons.ts +61 -0
- package/src/components/primitives/tree-model.test.ts +99 -0
- package/src/components/primitives/tree-model.ts +137 -0
- package/src/components/quick-actions-editor.stories.tsx +1 -1
- package/src/components/session-tree.test.ts +134 -0
- package/src/components/session-tree.ts +108 -0
- package/src/components/stories-app.tsx +142 -82
- package/src/components/story-tree.test.ts +100 -0
- package/src/components/story.ts +55 -0
- package/src/components/theme-context.stories.tsx +1 -1
- package/src/config.test.ts +20 -49
- package/src/config.ts +26 -28
- package/src/diff-fold.test.ts +78 -0
- package/src/diff-fold.ts +126 -0
- package/src/diff-syntax.test.ts +16 -0
- package/src/diff-syntax.ts +6 -3
- package/src/discussions.ts +79 -0
- package/src/intent-dispatch.test.ts +31 -11
- package/src/intent-dispatch.ts +93 -48
- package/src/key-bindings.test.ts +1 -1
- package/src/keymap.test.ts +3 -25
- package/src/keymap.ts +17 -18
- package/src/mark-runs.ts +120 -0
- package/src/pty.ts +10 -2
- package/src/run.ts +17 -7
- package/src/serve.test.ts +4 -8
- package/src/session-controller.curate.test.ts +75 -0
- package/src/session-controller.share.test.ts +1 -0
- package/src/session-controller.tree.test.ts +26 -0
- package/src/session-controller.ts +160 -31
- package/src/slash-palette.ts +83 -0
- package/src/split-diff.test.ts +110 -0
- package/src/split-diff.ts +149 -0
- package/src/test-support.test.ts +3 -3
- package/src/test-support.ts +39 -8
- package/src/theme.ts +4 -4
- package/src/thread-chords.test.ts +25 -9
- package/src/thread-chords.ts +33 -8
- package/src/use-annotation-surface.tsx +837 -0
- package/src/use-changes-workbench.ts +151 -0
- package/src/use-frame-measure.ts +38 -0
- package/src/use-settings-dialog.tsx +23 -24
- package/src/use-terminal-virtualizer.ts +233 -0
- package/src/view-diff.ts +114 -0
- package/src/App.review-panel.test.tsx +0 -140
- package/src/App.tree.test.tsx +0 -224
- package/src/components/ReviewPanel.stories.tsx +0 -100
- package/src/components/ReviewPanel.tsx +0 -103
- package/src/components/ReviewRail.stories.tsx +0 -232
- package/src/components/ReviewRail.tsx +0 -327
- package/src/review-panel.test.ts +0 -113
- package/src/review-panel.ts +0 -83
package/src/app-screens.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { type Dispatch, type SetStateAction } from "react";
|
|
2
|
-
import type { ReviewSession, VerdictKind } from "@cueloop/schema";
|
|
1
|
+
import React, { useEffect, useState, type Dispatch, type SetStateAction } from "react";
|
|
2
|
+
import type { DiffFileContents, ReviewSession, VerdictKind } from "@cueloop/schema";
|
|
3
3
|
import { returnPaneFor } from "@cueloop/schema";
|
|
4
4
|
import type { Theme } from "./theme";
|
|
5
5
|
import type { Mode, TreeAsk } from "./intent-dispatch";
|
|
@@ -12,11 +12,17 @@ import type { SettingsCategory, SettingsValues } from "./components/SettingsDial
|
|
|
12
12
|
import type { SettingsNav } from "./use-settings-dialog";
|
|
13
13
|
import { CLIENT_VERSION } from "./version";
|
|
14
14
|
import { ThemeProvider } from "./components/theme-context";
|
|
15
|
-
import {
|
|
16
|
-
import { KeybindsDialog } from "./components/KeybindsDialog";
|
|
15
|
+
import type { InboxRow } from "./components/session-tree";
|
|
17
16
|
import { SettingsDialog } from "./components/SettingsDialog";
|
|
18
17
|
import { CompletionOverlay } from "./components/CompletionOverlay";
|
|
19
18
|
import { InboxList } from "./components/InboxList";
|
|
19
|
+
import { WelcomeSurface } from "./components/WelcomeSurface";
|
|
20
|
+
import { AppShell, type ProjectPanelMode } from "./components/AppShell";
|
|
21
|
+
import { EditorGrid } from "./components/EditorGrid";
|
|
22
|
+
import { ProjectTreeView } from "./components/ProjectTreeView";
|
|
23
|
+
import { ChangesFileTree } from "./components/ChangesColumn";
|
|
24
|
+
import { FileContentsView } from "./components/FileContentsView";
|
|
25
|
+
import { useChangesWorkbench } from "./use-changes-workbench";
|
|
20
26
|
import { ConfirmDialog } from "./components/ConfirmDialog";
|
|
21
27
|
import { PromptDialog } from "./components/PromptDialog";
|
|
22
28
|
import { WalkWizard } from "./components/WalkWizard";
|
|
@@ -39,12 +45,8 @@ export function ConnectingScreen({ theme }: { theme: Theme }): React.ReactNode {
|
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
export function MenuChrome(props: {
|
|
42
|
-
menuOpen: boolean;
|
|
43
48
|
menuDialog: "keybinds" | "settings" | null;
|
|
44
|
-
status: string;
|
|
45
49
|
theme: Theme;
|
|
46
|
-
setMenuOpen: Dispatch<SetStateAction<boolean>>;
|
|
47
|
-
setMenuDialog: Dispatch<SetStateAction<"keybinds" | "settings" | null>>;
|
|
48
50
|
keybindsSections: CheatsheetSection[];
|
|
49
51
|
settingsCategories: SettingsCategory[];
|
|
50
52
|
settingsValues: SettingsValues;
|
|
@@ -53,12 +55,8 @@ export function MenuChrome(props: {
|
|
|
53
55
|
cycleSetting: (rowKey: string) => void;
|
|
54
56
|
}): React.ReactNode {
|
|
55
57
|
const {
|
|
56
|
-
menuOpen,
|
|
57
58
|
menuDialog,
|
|
58
|
-
status,
|
|
59
59
|
theme,
|
|
60
|
-
setMenuOpen,
|
|
61
|
-
setMenuDialog,
|
|
62
60
|
keybindsSections,
|
|
63
61
|
settingsCategories,
|
|
64
62
|
settingsValues,
|
|
@@ -67,90 +65,190 @@ export function MenuChrome(props: {
|
|
|
67
65
|
cycleSetting,
|
|
68
66
|
} = props;
|
|
69
67
|
|
|
68
|
+
// the gear opens the settings dialog directly; Keybinds is a leaf in its tree nav
|
|
69
|
+
if (menuDialog !== "settings") return null;
|
|
70
|
+
|
|
70
71
|
return (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
72
|
+
<SettingsDialog
|
|
73
|
+
isOpen
|
|
74
|
+
version={CLIENT_VERSION}
|
|
75
|
+
keybindsSections={keybindsSections}
|
|
76
|
+
categories={settingsCategories}
|
|
77
|
+
values={settingsValues}
|
|
78
|
+
activeCategoryId={settingsNav.categoryId}
|
|
79
|
+
activeRowIndex={settingsNav.rowIndex}
|
|
80
|
+
activeZone={settingsNav.zone}
|
|
81
|
+
onCategorySelect={onCategorySelect}
|
|
82
|
+
onRowActivate={(row) => cycleSetting(row.key)}
|
|
83
|
+
theme={theme}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The welcome shell's Project pane: the launch repo's changed files in changes mode, its full tree otherwise. */
|
|
89
|
+
function WelcomeProjectPanel({
|
|
90
|
+
mode,
|
|
91
|
+
controller,
|
|
92
|
+
onOpenFile,
|
|
93
|
+
theme,
|
|
94
|
+
}: {
|
|
95
|
+
mode: ProjectPanelMode;
|
|
96
|
+
controller: ReviewController;
|
|
97
|
+
onOpenFile: (path: string) => void;
|
|
98
|
+
theme: Theme;
|
|
99
|
+
}): React.ReactNode {
|
|
100
|
+
const [changes, setChanges] = useState<readonly DiffFileContents[]>([]);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
let alive = true;
|
|
104
|
+
|
|
105
|
+
void controller.repoChanges().then(
|
|
106
|
+
(files) => {
|
|
107
|
+
if (alive) setChanges(files);
|
|
108
|
+
},
|
|
109
|
+
() => {
|
|
110
|
+
if (alive) setChanges([]);
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
alive = false;
|
|
116
|
+
};
|
|
117
|
+
}, [controller]);
|
|
118
|
+
|
|
119
|
+
if (mode === "changes") {
|
|
120
|
+
return <ChangesFileTree files={changes} onSelectFile={onOpenFile} theme={theme} />;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<ProjectTreeView
|
|
125
|
+
loadFiles={() => controller.repoFiles()}
|
|
126
|
+
onSelectFile={onOpenFile}
|
|
127
|
+
theme={theme}
|
|
128
|
+
/>
|
|
104
129
|
);
|
|
105
130
|
}
|
|
106
131
|
|
|
107
|
-
|
|
108
|
-
|
|
132
|
+
/**
|
|
133
|
+
* The shell with no thread open: the same header and Projects/Threads sidebar as
|
|
134
|
+
* the thread view, and a disposable Welcome tab in the center. There is no
|
|
135
|
+
* separate inbox screen - opening the app lands here, and picking a thread swaps
|
|
136
|
+
* the center for it. Closing the Welcome tab leaves a bare "select a thread" hint.
|
|
137
|
+
*/
|
|
138
|
+
export function NoThreadShell(props: {
|
|
139
|
+
rows: InboxRow[];
|
|
109
140
|
inboxCursor: number;
|
|
110
141
|
mode: Mode;
|
|
111
142
|
theme: Theme;
|
|
112
143
|
controller: ReviewController;
|
|
113
144
|
setMode: Dispatch<SetStateAction<Mode>>;
|
|
114
145
|
menuChrome: React.ReactNode;
|
|
146
|
+
onOpenMenu: () => void;
|
|
147
|
+
/** Shared with the thread view, so picking a thread preserves the sidebar. */
|
|
148
|
+
sidebarOpen: boolean;
|
|
149
|
+
onToggleSidebar: () => void;
|
|
150
|
+
pinnedIds: ReadonlySet<string>;
|
|
151
|
+
onPin: (id: string) => void;
|
|
152
|
+
onRename: (id: string, title: string) => void;
|
|
115
153
|
}): React.ReactNode {
|
|
116
|
-
const {
|
|
154
|
+
const {
|
|
155
|
+
rows,
|
|
156
|
+
inboxCursor,
|
|
157
|
+
mode,
|
|
158
|
+
theme,
|
|
159
|
+
controller,
|
|
160
|
+
setMode,
|
|
161
|
+
menuChrome,
|
|
162
|
+
onOpenMenu,
|
|
163
|
+
sidebarOpen,
|
|
164
|
+
onToggleSidebar,
|
|
165
|
+
pinnedIds,
|
|
166
|
+
onPin,
|
|
167
|
+
onRename,
|
|
168
|
+
} = props;
|
|
117
169
|
const confirming = mode.type === "confirmDelete" ? mode : null;
|
|
170
|
+
// The bare-launch shell is the same four panes as a thread: the Thread pane waits in its empty state
|
|
171
|
+
// and a disposable Welcome tab rides in the Changes editor until a thread or diff is opened.
|
|
172
|
+
const workbench = useChangesWorkbench({ seed: "welcome" });
|
|
173
|
+
const openRepoFile = (path: string): void => workbench.openFile(path, "contents");
|
|
118
174
|
|
|
119
175
|
return (
|
|
120
176
|
<ThemeProvider theme={theme}>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
177
|
+
<AppShell
|
|
178
|
+
sidebarOpen={sidebarOpen}
|
|
179
|
+
onToggleSidebar={onToggleSidebar}
|
|
180
|
+
onOpenMenu={onOpenMenu}
|
|
181
|
+
threadsPanel={
|
|
182
|
+
<scrollbox style={{ flexGrow: 1 }} focused={false}>
|
|
183
|
+
<InboxList
|
|
184
|
+
rows={rows}
|
|
185
|
+
cursor={inboxCursor}
|
|
186
|
+
pinnedIds={pinnedIds}
|
|
187
|
+
width={30}
|
|
188
|
+
onSelect={(id) => controller.open(id)}
|
|
189
|
+
onRequestDelete={(id, title) =>
|
|
190
|
+
setMode({ type: "confirmDelete", sessionId: id, title })
|
|
191
|
+
}
|
|
192
|
+
onPin={onPin}
|
|
193
|
+
onRename={onRename}
|
|
194
|
+
theme={theme}
|
|
195
|
+
/>
|
|
196
|
+
</scrollbox>
|
|
197
|
+
}
|
|
198
|
+
threadTitle=""
|
|
199
|
+
threadPanel={
|
|
134
200
|
<box
|
|
135
201
|
style={{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
202
|
+
flexGrow: 1,
|
|
203
|
+
flexDirection: "column",
|
|
204
|
+
alignItems: "center",
|
|
205
|
+
justifyContent: "center",
|
|
140
206
|
}}
|
|
141
207
|
>
|
|
142
|
-
<text>
|
|
143
|
-
<span fg={theme.accent}>cueloop</span>
|
|
144
|
-
<span fg={theme.textDim}> · resume</span>
|
|
145
|
-
</text>
|
|
208
|
+
<text fg={theme.textDim}>Select a thread</text>
|
|
146
209
|
</box>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
210
|
+
}
|
|
211
|
+
changesOpen={workbench.changesOpen}
|
|
212
|
+
projectOpen={workbench.projectOpen}
|
|
213
|
+
onToggleChanges={workbench.toggleChanges}
|
|
214
|
+
onToggleProject={workbench.toggleProject}
|
|
215
|
+
onToggleRight={workbench.toggleRight}
|
|
216
|
+
projectMode={workbench.projectMode}
|
|
217
|
+
zoomHideThread={workbench.zoomed}
|
|
218
|
+
changesPanel={
|
|
219
|
+
<EditorGrid
|
|
220
|
+
tree={workbench.grid}
|
|
221
|
+
focusedGroupId={workbench.activeGroup}
|
|
222
|
+
onFocusGroup={workbench.focusGroup}
|
|
223
|
+
onActivateTab={workbench.activate}
|
|
224
|
+
onCloseTab={workbench.close}
|
|
225
|
+
onSplit={workbench.split}
|
|
226
|
+
onZoom={workbench.toggleZoom}
|
|
227
|
+
zoomed={workbench.zoomed}
|
|
228
|
+
renderTab={(tab) =>
|
|
229
|
+
tab.kind === "welcome" ? (
|
|
230
|
+
<WelcomeSurface version={CLIENT_VERSION} theme={theme} />
|
|
231
|
+
) : (
|
|
232
|
+
<FileContentsView
|
|
233
|
+
path={tab.path ?? ""}
|
|
234
|
+
loadContents={(path) => controller.repoReadFile(path)}
|
|
235
|
+
theme={theme}
|
|
236
|
+
/>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
theme={theme}
|
|
240
|
+
/>
|
|
241
|
+
}
|
|
242
|
+
projectPanel={
|
|
243
|
+
<WelcomeProjectPanel
|
|
244
|
+
mode={workbench.projectMode}
|
|
245
|
+
controller={controller}
|
|
246
|
+
onOpenFile={openRepoFile}
|
|
247
|
+
theme={theme}
|
|
248
|
+
/>
|
|
249
|
+
}
|
|
250
|
+
theme={theme}
|
|
251
|
+
>
|
|
154
252
|
{menuChrome}
|
|
155
253
|
<ConfirmDialog
|
|
156
254
|
isOpen={confirming !== null}
|
|
@@ -165,7 +263,18 @@ export function InboxScreen(props: {
|
|
|
165
263
|
onCancel={() => setMode({ type: "normal" })}
|
|
166
264
|
theme={theme}
|
|
167
265
|
/>
|
|
168
|
-
|
|
266
|
+
{mode.type === "renameThread" ? (
|
|
267
|
+
<PromptDialog
|
|
268
|
+
isOpen
|
|
269
|
+
title=" rename thread "
|
|
270
|
+
label="new title for this thread:"
|
|
271
|
+
value={mode.text}
|
|
272
|
+
placeholder="a short title"
|
|
273
|
+
onInput={(text) => setMode({ ...mode, text })}
|
|
274
|
+
theme={theme}
|
|
275
|
+
/>
|
|
276
|
+
) : null}
|
|
277
|
+
</AppShell>
|
|
169
278
|
</ThemeProvider>
|
|
170
279
|
);
|
|
171
280
|
}
|
|
@@ -268,6 +377,17 @@ export function TrailingOverlays(props: {
|
|
|
268
377
|
theme={theme}
|
|
269
378
|
/>
|
|
270
379
|
) : null}
|
|
380
|
+
{mode.type === "renameThread" ? (
|
|
381
|
+
<PromptDialog
|
|
382
|
+
isOpen
|
|
383
|
+
title=" rename thread "
|
|
384
|
+
label="new title for this thread:"
|
|
385
|
+
value={mode.text}
|
|
386
|
+
placeholder="a short title"
|
|
387
|
+
onInput={(text) => setMode({ ...mode, text })}
|
|
388
|
+
theme={theme}
|
|
389
|
+
/>
|
|
390
|
+
) : null}
|
|
271
391
|
{mode.type === "nameSelf" ? (
|
|
272
392
|
<PromptDialog
|
|
273
393
|
isOpen
|
package/src/app-view-model.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
|
|
2
2
|
import type { ReviewSession, VerdictKind } from "@cueloop/schema";
|
|
3
|
-
import {
|
|
4
|
-
import type { DiffRow } from "./view-diff";
|
|
3
|
+
import type { Mode } from "./intent-dispatch";
|
|
5
4
|
import type { Intent, KeyState } from "./keymap";
|
|
6
5
|
import type { Completion } from "./session-controller";
|
|
7
6
|
import type { WalkFile } from "./walk";
|
|
8
7
|
import { viewedCount } from "./walk";
|
|
9
|
-
import type { DiffComposeState } from "./components/DiffSheet";
|
|
10
8
|
import type { ConfirmCardProps } from "./components/ConfirmCard";
|
|
11
|
-
import type { RailCardEdit } from "./components/ReviewRail";
|
|
12
9
|
import type { BreadcrumbItem } from "./components/Breadcrumb";
|
|
13
|
-
import { REVIEW_COMPACT_WIDTH, resolveReviewWidth, type ReviewPanelMode } from "./review-panel";
|
|
14
10
|
|
|
15
11
|
export function computeRoleCapabilities(
|
|
16
12
|
readOnly: boolean,
|
|
@@ -41,7 +37,12 @@ export function resolveOverlay(
|
|
|
41
37
|
if (mode.type === "compose" || mode.type === "railEdit") return "compose";
|
|
42
38
|
if (mode.type === "submit") return "submit";
|
|
43
39
|
if (mode.type === "confirmDelete") return "confirm";
|
|
44
|
-
if (
|
|
40
|
+
if (
|
|
41
|
+
mode.type === "rename" ||
|
|
42
|
+
mode.type === "renameThread" ||
|
|
43
|
+
mode.type === "nameSelf" ||
|
|
44
|
+
mode.type === "treePrompt"
|
|
45
|
+
)
|
|
45
46
|
return "prompt";
|
|
46
47
|
if (mode.type === "spanActions") return "spanActions";
|
|
47
48
|
if (completionPhase === "prompt") return "completion-prompt";
|
|
@@ -57,25 +58,6 @@ export function isCompletionOverlayPhase(
|
|
|
57
58
|
return completion.phase === "prompt" || completion.phase === "counting";
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
export function computePendingCount(session: ReviewSession): number {
|
|
61
|
-
return reviewerAnnotations(session).length + (session.workingCopy !== undefined ? 1 : 0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function computeRailFootprint(
|
|
65
|
-
reviewMode: ReviewPanelMode,
|
|
66
|
-
reviewWidth: number,
|
|
67
|
-
terminalWidth: number,
|
|
68
|
-
): number {
|
|
69
|
-
if (reviewMode === "hidden") return 0;
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
1 +
|
|
73
|
-
(reviewMode === "compact"
|
|
74
|
-
? REVIEW_COMPACT_WIDTH
|
|
75
|
-
: resolveReviewWidth(reviewWidth, terminalWidth))
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
61
|
export function buildHeaderItems(params: {
|
|
80
62
|
session: ReviewSession;
|
|
81
63
|
resolved: boolean;
|
|
@@ -112,17 +94,15 @@ export function buildRenderFlags(params: {
|
|
|
112
94
|
isDiff: boolean;
|
|
113
95
|
isPrototype: boolean;
|
|
114
96
|
resolved: boolean;
|
|
115
|
-
menuOpen: boolean;
|
|
116
97
|
menuDialog: "keybinds" | "settings" | null;
|
|
117
98
|
resolvedIds: Set<string>;
|
|
118
99
|
}) {
|
|
119
|
-
const { session, isOwner, isDiff, isPrototype, resolved,
|
|
120
|
-
params;
|
|
100
|
+
const { session, isOwner, isDiff, isPrototype, resolved, menuDialog, resolvedIds } = params;
|
|
121
101
|
|
|
122
102
|
return {
|
|
123
103
|
showOwnerActions: isOwner && !isDiff && !resolved,
|
|
124
104
|
prototypeCanComment: isOwner && !resolved,
|
|
125
|
-
chromeHidden:
|
|
105
|
+
chromeHidden: menuDialog !== null,
|
|
126
106
|
prototypePath: session.artifact.meta.prototypePath ?? "",
|
|
127
107
|
railResolvedIds: isDiff || isPrototype ? null : resolvedIds,
|
|
128
108
|
};
|
|
@@ -134,29 +114,6 @@ interface DraftHandlerDeps {
|
|
|
134
114
|
dispatch: (intent: Intent) => void;
|
|
135
115
|
}
|
|
136
116
|
|
|
137
|
-
export function buildDiffComposeState(
|
|
138
|
-
deps: DraftHandlerDeps & { mode: Mode; isDiff: boolean; rows: DiffRow[] },
|
|
139
|
-
): DiffComposeState | null {
|
|
140
|
-
const { mode, isDiff, rows, liveInput, setMode, dispatch } = deps;
|
|
141
|
-
|
|
142
|
-
if (mode.type !== "compose" || !isDiff) return null;
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
kind: mode.kind,
|
|
146
|
-
rowIndex: mode.displayIndex,
|
|
147
|
-
quote: rows[mode.displayIndex]?.text.replace(/\n$/, "") ?? "",
|
|
148
|
-
draft: {
|
|
149
|
-
text: mode.text,
|
|
150
|
-
onInput: (text: string) => {
|
|
151
|
-
liveInput.current = text;
|
|
152
|
-
setMode({ ...mode, text });
|
|
153
|
-
},
|
|
154
|
-
onSave: () => dispatch({ type: "saveCompose" }),
|
|
155
|
-
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
117
|
export function buildSubmitConfirmState(
|
|
161
118
|
deps: DraftHandlerDeps & {
|
|
162
119
|
mode: Mode;
|
|
@@ -186,20 +143,3 @@ export function buildSubmitConfirmState(
|
|
|
186
143
|
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
187
144
|
};
|
|
188
145
|
}
|
|
189
|
-
|
|
190
|
-
export function buildCardEditState(deps: DraftHandlerDeps & { mode: Mode }): RailCardEdit | null {
|
|
191
|
-
const { mode, liveInput, setMode, dispatch } = deps;
|
|
192
|
-
|
|
193
|
-
if (mode.type !== "railEdit") return null;
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
id: mode.id,
|
|
197
|
-
text: mode.text,
|
|
198
|
-
onInput: (text: string) => {
|
|
199
|
-
liveInput.current = text;
|
|
200
|
-
setMode({ type: "railEdit", id: mode.id, text });
|
|
201
|
-
},
|
|
202
|
-
onSave: () => dispatch({ type: "saveCompose" }),
|
|
203
|
-
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
204
|
-
};
|
|
205
|
-
}
|
|
@@ -3,7 +3,7 @@ import { DARK } from "../theme";
|
|
|
3
3
|
import type { Story, StoryMeta } from "./story";
|
|
4
4
|
import { AnnotationCard } from "./AnnotationCard";
|
|
5
5
|
|
|
6
|
-
export const meta: StoryMeta = { title: "AnnotationCard" };
|
|
6
|
+
export const meta: StoryMeta = { title: "Cards/AnnotationCard" };
|
|
7
7
|
|
|
8
8
|
const draftCallbacks = { onInput: () => {}, onSave: () => {}, onCancel: () => {} };
|
|
9
9
|
|
|
@@ -185,7 +185,7 @@ export function AnnotationCard({
|
|
|
185
185
|
|
|
186
186
|
return (
|
|
187
187
|
<Card
|
|
188
|
-
title={` ${primitive} "${truncateToSingleLine(quote,
|
|
188
|
+
title={` ${primitive} "${truncateToSingleLine(quote, 20)}" `}
|
|
189
189
|
contentRows={editorRowCount + 1}
|
|
190
190
|
borderColor={kindColor}
|
|
191
191
|
backgroundColor="transparent"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Annotation } from "@cueloop/schema";
|
|
3
|
+
import { DARK } from "../theme";
|
|
4
|
+
import { annotationPaletteFor } from "../annotation-palette";
|
|
5
|
+
import { slashItemsFrom } from "../slash-palette";
|
|
6
|
+
import { DEFAULT_QUICK_ACTIONS } from "../config";
|
|
7
|
+
import type { Story, StoryMeta } from "./story";
|
|
8
|
+
import { CommentRow, ComposerPalette, DiscussionCard } from "./AnnotationCards";
|
|
9
|
+
|
|
10
|
+
export const meta: StoryMeta = { title: "Chrome/AnnotationCards" };
|
|
11
|
+
|
|
12
|
+
const palette = annotationPaletteFor(DARK);
|
|
13
|
+
|
|
14
|
+
function annotation(id: string, body: string, author?: string): Annotation {
|
|
15
|
+
const comment: Annotation = {
|
|
16
|
+
id,
|
|
17
|
+
kind: "comment",
|
|
18
|
+
anchor: { quote: "the daemon", prefix: "", suffix: "" },
|
|
19
|
+
body,
|
|
20
|
+
createdAt: "2026-09-07T10:00:00.000Z",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (author !== undefined) comment.author = author;
|
|
24
|
+
|
|
25
|
+
return comment;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A two-voice discussion: the owner's comment then a collaborator's reply, each
|
|
29
|
+
// segment wearing its own accent edge.
|
|
30
|
+
export const TwoVoices: Story = {
|
|
31
|
+
render: () => (
|
|
32
|
+
<DiscussionCard
|
|
33
|
+
tokens={DARK}
|
|
34
|
+
segments={[
|
|
35
|
+
{
|
|
36
|
+
color: palette.cardEdge,
|
|
37
|
+
node: <CommentRow annotation={annotation("a", "Which daemon?")} tokens={DARK} />,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
color: DARK.text,
|
|
41
|
+
node: (
|
|
42
|
+
<CommentRow
|
|
43
|
+
annotation={annotation("b", "The one the CLI spawns.", "fp:collab")}
|
|
44
|
+
tokens={DARK}
|
|
45
|
+
/>
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
/>
|
|
50
|
+
),
|
|
51
|
+
expectedColors: [palette.cardEdge, DARK.text],
|
|
52
|
+
size: { width: 60, height: 8 },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const FocusedCard: Story = {
|
|
56
|
+
render: () => (
|
|
57
|
+
<DiscussionCard
|
|
58
|
+
focused
|
|
59
|
+
tokens={DARK}
|
|
60
|
+
segments={[
|
|
61
|
+
{
|
|
62
|
+
color: palette.cardEdge,
|
|
63
|
+
node: (
|
|
64
|
+
<CommentRow annotation={annotation("a", "Needs an eviction story.")} tokens={DARK} />
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
]}
|
|
68
|
+
/>
|
|
69
|
+
),
|
|
70
|
+
expectedColors: [DARK.text],
|
|
71
|
+
size: { width: 60, height: 6 },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// The palette a leading "/" opens under the composer, with the second item selected.
|
|
75
|
+
export const SlashPaletteOpen: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<ComposerPalette
|
|
78
|
+
slashActive
|
|
79
|
+
slashItems={slashItemsFrom(DEFAULT_QUICK_ACTIONS)}
|
|
80
|
+
slashIndex={1}
|
|
81
|
+
inline={null}
|
|
82
|
+
tokens={DARK}
|
|
83
|
+
/>
|
|
84
|
+
),
|
|
85
|
+
expectedColors: [DARK.accent, DARK.textDim],
|
|
86
|
+
size: { width: 90, height: 8 },
|
|
87
|
+
};
|