@cueloop/client 0.1.0-alpha.66 → 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 +71 -39
- package/src/App.plan-review.test.tsx +20 -21
- package/src/App.readonly.test.tsx +7 -3
- package/src/App.test.tsx +73 -14
- package/src/App.tsx +375 -129
- package/src/App.walk.test.tsx +14 -15
- 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 +176 -45
- package/src/app-view-model.ts +7 -65
- 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 +47 -42
- package/src/components/AppShell.tsx +212 -100
- package/src/components/AppShell.zoom.test.tsx +94 -0
- package/src/components/ChangesColumn.tsx +49 -26
- package/src/components/DiffSheet.scroll.test.tsx +38 -55
- package/src/components/DiffSheet.split.test.tsx +140 -0
- package/src/components/DiffSheet.stories.tsx +55 -53
- 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.tsx +159 -30
- 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.tsx +8 -1
- package/src/components/RootOverlay.stories.tsx +44 -0
- package/src/components/RootOverlay.tsx +35 -0
- package/src/components/SettingsDialog.tsx +2 -1
- 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 +2 -2
- package/src/components/ThreadFooter.test.tsx +31 -0
- package/src/components/ThreadFooter.tsx +12 -7
- package/src/components/ThreadView.tsx +62 -1440
- package/src/components/ThreadsSidebar.tsx +14 -1
- package/src/components/Tooltip.stories.tsx +26 -0
- package/src/components/Tooltip.tsx +72 -0
- package/src/components/WelcomeSurface.stories.tsx +12 -0
- package/src/components/WelcomeSurface.tsx +52 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +509 -559
- 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 +15 -0
- package/src/components/file-tree.ts +59 -4
- package/src/components/pages/Workspace.stories.tsx +2 -2
- package/src/components/primitives/IconButton.tsx +21 -2
- package/src/components/primitives/Tree.tsx +13 -4
- package/src/components/primitives/icons.ts +27 -4
- package/src/components/session-tree.test.ts +20 -0
- package/src/components/session-tree.ts +16 -2
- package/src/components/stories-app.tsx +2 -2
- 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 +153 -29
- 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/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 +14 -24
- package/src/use-terminal-virtualizer.ts +233 -0
- package/src/view-diff.ts +114 -0
- 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 { NERD } from "./components/primitives/icons";
|
|
16
15
|
import type { InboxRow } from "./components/session-tree";
|
|
17
16
|
import { SettingsDialog } from "./components/SettingsDialog";
|
|
18
17
|
import { CompletionOverlay } from "./components/CompletionOverlay";
|
|
19
|
-
import {
|
|
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";
|
|
@@ -79,7 +85,57 @@ export function MenuChrome(props: {
|
|
|
79
85
|
);
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
|
|
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
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
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: {
|
|
83
139
|
rows: InboxRow[];
|
|
84
140
|
inboxCursor: number;
|
|
85
141
|
mode: Mode;
|
|
@@ -88,58 +144,111 @@ export function InboxScreen(props: {
|
|
|
88
144
|
setMode: Dispatch<SetStateAction<Mode>>;
|
|
89
145
|
menuChrome: React.ReactNode;
|
|
90
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;
|
|
91
153
|
}): React.ReactNode {
|
|
92
|
-
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;
|
|
93
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");
|
|
94
174
|
|
|
95
175
|
return (
|
|
96
176
|
<ThemeProvider theme={theme}>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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={
|
|
110
200
|
<box
|
|
111
201
|
style={{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
202
|
+
flexGrow: 1,
|
|
203
|
+
flexDirection: "column",
|
|
204
|
+
alignItems: "center",
|
|
205
|
+
justifyContent: "center",
|
|
116
206
|
}}
|
|
117
207
|
>
|
|
118
|
-
<
|
|
119
|
-
<text fg={theme.textMuted}>{NERD.settings}</text>
|
|
120
|
-
</box>
|
|
121
|
-
<text>
|
|
122
|
-
<span fg={theme.accent}>cueloop</span>
|
|
123
|
-
<span fg={theme.textDim}> · resume</span>
|
|
124
|
-
</text>
|
|
208
|
+
<text fg={theme.textDim}>Select a thread</text>
|
|
125
209
|
</box>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
)
|
|
136
238
|
}
|
|
137
239
|
theme={theme}
|
|
138
240
|
/>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
241
|
+
}
|
|
242
|
+
projectPanel={
|
|
243
|
+
<WelcomeProjectPanel
|
|
244
|
+
mode={workbench.projectMode}
|
|
245
|
+
controller={controller}
|
|
246
|
+
onOpenFile={openRepoFile}
|
|
247
|
+
theme={theme}
|
|
248
|
+
/>
|
|
249
|
+
}
|
|
250
|
+
theme={theme}
|
|
251
|
+
>
|
|
143
252
|
{menuChrome}
|
|
144
253
|
<ConfirmDialog
|
|
145
254
|
isOpen={confirming !== null}
|
|
@@ -154,7 +263,18 @@ export function InboxScreen(props: {
|
|
|
154
263
|
onCancel={() => setMode({ type: "normal" })}
|
|
155
264
|
theme={theme}
|
|
156
265
|
/>
|
|
157
|
-
|
|
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>
|
|
158
278
|
</ThemeProvider>
|
|
159
279
|
);
|
|
160
280
|
}
|
|
@@ -257,6 +377,17 @@ export function TrailingOverlays(props: {
|
|
|
257
377
|
theme={theme}
|
|
258
378
|
/>
|
|
259
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}
|
|
260
391
|
{mode.type === "nameSelf" ? (
|
|
261
392
|
<PromptDialog
|
|
262
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;
|
|
@@ -132,29 +114,6 @@ interface DraftHandlerDeps {
|
|
|
132
114
|
dispatch: (intent: Intent) => void;
|
|
133
115
|
}
|
|
134
116
|
|
|
135
|
-
export function buildDiffComposeState(
|
|
136
|
-
deps: DraftHandlerDeps & { mode: Mode; isDiff: boolean; rows: DiffRow[] },
|
|
137
|
-
): DiffComposeState | null {
|
|
138
|
-
const { mode, isDiff, rows, liveInput, setMode, dispatch } = deps;
|
|
139
|
-
|
|
140
|
-
if (mode.type !== "compose" || !isDiff) return null;
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
kind: mode.kind,
|
|
144
|
-
rowIndex: mode.displayIndex,
|
|
145
|
-
quote: rows[mode.displayIndex]?.text.replace(/\n$/, "") ?? "",
|
|
146
|
-
draft: {
|
|
147
|
-
text: mode.text,
|
|
148
|
-
onInput: (text: string) => {
|
|
149
|
-
liveInput.current = text;
|
|
150
|
-
setMode({ ...mode, text });
|
|
151
|
-
},
|
|
152
|
-
onSave: () => dispatch({ type: "saveCompose" }),
|
|
153
|
-
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
117
|
export function buildSubmitConfirmState(
|
|
159
118
|
deps: DraftHandlerDeps & {
|
|
160
119
|
mode: Mode;
|
|
@@ -184,20 +143,3 @@ export function buildSubmitConfirmState(
|
|
|
184
143
|
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
185
144
|
};
|
|
186
145
|
}
|
|
187
|
-
|
|
188
|
-
export function buildCardEditState(deps: DraftHandlerDeps & { mode: Mode }): RailCardEdit | null {
|
|
189
|
-
const { mode, liveInput, setMode, dispatch } = deps;
|
|
190
|
-
|
|
191
|
-
if (mode.type !== "railEdit") return null;
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
id: mode.id,
|
|
195
|
-
text: mode.text,
|
|
196
|
-
onInput: (text: string) => {
|
|
197
|
-
liveInput.current = text;
|
|
198
|
-
setMode({ type: "railEdit", id: mode.id, text });
|
|
199
|
-
},
|
|
200
|
-
onSave: () => dispatch({ type: "saveCompose" }),
|
|
201
|
-
onCancel: () => dispatch({ type: "closeOverlay" }),
|
|
202
|
-
};
|
|
203
|
-
}
|
|
@@ -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
|
+
};
|