@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.
Files changed (115) hide show
  1. package/package.json +6 -5
  2. package/src/App.autoclose.test.tsx +2 -2
  3. package/src/App.diff.test.tsx +71 -39
  4. package/src/App.plan-review.test.tsx +20 -21
  5. package/src/App.readonly.test.tsx +7 -3
  6. package/src/App.test.tsx +73 -14
  7. package/src/App.tsx +375 -129
  8. package/src/App.walk.test.tsx +14 -15
  9. package/src/App.workbench.test.tsx +390 -0
  10. package/src/__snapshots__/view-plan.regression.test.ts.snap +1 -1
  11. package/src/annotation-palette.ts +61 -0
  12. package/src/app-screens.tsx +176 -45
  13. package/src/app-view-model.ts +7 -65
  14. package/src/components/AnnotationCard.tsx +1 -1
  15. package/src/components/AnnotationCards.stories.tsx +87 -0
  16. package/src/components/AnnotationCards.tsx +303 -0
  17. package/src/components/AppHeader.stories.tsx +50 -0
  18. package/src/components/AppHeader.tsx +83 -0
  19. package/src/components/AppShell.stories.tsx +47 -42
  20. package/src/components/AppShell.tsx +212 -100
  21. package/src/components/AppShell.zoom.test.tsx +94 -0
  22. package/src/components/ChangesColumn.tsx +49 -26
  23. package/src/components/DiffSheet.scroll.test.tsx +38 -55
  24. package/src/components/DiffSheet.split.test.tsx +140 -0
  25. package/src/components/DiffSheet.stories.tsx +55 -53
  26. package/src/components/DiffSheet.syntax.test.tsx +24 -7
  27. package/src/components/DiffSheet.tsx +607 -276
  28. package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
  29. package/src/components/DiscussionMarkerRail.tsx +181 -0
  30. package/src/components/EditorGrid.stories.tsx +59 -0
  31. package/src/components/EditorGrid.tabs.test.tsx +105 -0
  32. package/src/components/EditorGrid.tsx +338 -0
  33. package/src/components/EditorGrid.zoom.test.tsx +68 -0
  34. package/src/components/FileContentsView.stories.tsx +16 -0
  35. package/src/components/FileContentsView.test.tsx +29 -0
  36. package/src/components/FileContentsView.tsx +78 -0
  37. package/src/components/InboxList.tsx +159 -30
  38. package/src/components/PaneGrid.stories.tsx +84 -0
  39. package/src/components/PaneGrid.tsx +128 -0
  40. package/src/components/PanelColumn.stories.tsx +25 -0
  41. package/src/components/PanelColumn.tsx +95 -0
  42. package/src/components/ProjectTreeView.stories.tsx +25 -0
  43. package/src/components/ProjectTreeView.test.tsx +32 -0
  44. package/src/components/ProjectTreeView.tsx +82 -0
  45. package/src/components/PromptDialog.tsx +8 -1
  46. package/src/components/RootOverlay.stories.tsx +44 -0
  47. package/src/components/RootOverlay.tsx +35 -0
  48. package/src/components/SettingsDialog.tsx +2 -1
  49. package/src/components/SurfaceScrollbar.stories.tsx +34 -0
  50. package/src/components/SurfaceScrollbar.test.tsx +111 -0
  51. package/src/components/SurfaceScrollbar.tsx +93 -0
  52. package/src/components/ThreadFooter.stories.tsx +2 -2
  53. package/src/components/ThreadFooter.test.tsx +31 -0
  54. package/src/components/ThreadFooter.tsx +12 -7
  55. package/src/components/ThreadView.tsx +62 -1440
  56. package/src/components/ThreadsSidebar.tsx +14 -1
  57. package/src/components/Tooltip.stories.tsx +26 -0
  58. package/src/components/Tooltip.tsx +72 -0
  59. package/src/components/WelcomeSurface.stories.tsx +12 -0
  60. package/src/components/WelcomeSurface.tsx +52 -0
  61. package/src/components/__snapshots__/stories.test.tsx.snap +509 -559
  62. package/src/components/diff-sheet-layout.test.ts +125 -55
  63. package/src/components/diff-sheet-layout.ts +3 -103
  64. package/src/components/editor-grid.test.ts +86 -0
  65. package/src/components/editor-grid.ts +138 -0
  66. package/src/components/fade-title.test.ts +49 -0
  67. package/src/components/fade-title.ts +61 -0
  68. package/src/components/file-tree.test.ts +15 -0
  69. package/src/components/file-tree.ts +59 -4
  70. package/src/components/pages/Workspace.stories.tsx +2 -2
  71. package/src/components/primitives/IconButton.tsx +21 -2
  72. package/src/components/primitives/Tree.tsx +13 -4
  73. package/src/components/primitives/icons.ts +27 -4
  74. package/src/components/session-tree.test.ts +20 -0
  75. package/src/components/session-tree.ts +16 -2
  76. package/src/components/stories-app.tsx +2 -2
  77. package/src/config.test.ts +20 -49
  78. package/src/config.ts +26 -28
  79. package/src/diff-fold.test.ts +78 -0
  80. package/src/diff-fold.ts +126 -0
  81. package/src/diff-syntax.test.ts +16 -0
  82. package/src/diff-syntax.ts +6 -3
  83. package/src/discussions.ts +79 -0
  84. package/src/intent-dispatch.test.ts +31 -11
  85. package/src/intent-dispatch.ts +93 -48
  86. package/src/key-bindings.test.ts +1 -1
  87. package/src/keymap.test.ts +3 -25
  88. package/src/keymap.ts +17 -18
  89. package/src/mark-runs.ts +120 -0
  90. package/src/pty.ts +10 -2
  91. package/src/run.ts +17 -7
  92. package/src/serve.test.ts +4 -8
  93. package/src/session-controller.curate.test.ts +75 -0
  94. package/src/session-controller.share.test.ts +1 -0
  95. package/src/session-controller.tree.test.ts +26 -0
  96. package/src/session-controller.ts +153 -29
  97. package/src/slash-palette.ts +83 -0
  98. package/src/split-diff.test.ts +110 -0
  99. package/src/split-diff.ts +149 -0
  100. package/src/test-support.test.ts +3 -3
  101. package/src/test-support.ts +39 -8
  102. package/src/thread-chords.test.ts +25 -9
  103. package/src/thread-chords.ts +33 -8
  104. package/src/use-annotation-surface.tsx +837 -0
  105. package/src/use-changes-workbench.ts +151 -0
  106. package/src/use-frame-measure.ts +38 -0
  107. package/src/use-settings-dialog.tsx +14 -24
  108. package/src/use-terminal-virtualizer.ts +233 -0
  109. package/src/view-diff.ts +114 -0
  110. package/src/components/ReviewPanel.stories.tsx +0 -100
  111. package/src/components/ReviewPanel.tsx +0 -103
  112. package/src/components/ReviewRail.stories.tsx +0 -232
  113. package/src/components/ReviewRail.tsx +0 -327
  114. package/src/review-panel.test.ts +0 -113
  115. package/src/review-panel.ts +0 -83
@@ -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 { ThreadsSidebar } from "./components/ThreadsSidebar";
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
- export function InboxScreen(props: {
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 { rows, inboxCursor, mode, theme, controller, setMode, menuChrome, onOpenMenu } = props;
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
- <box
98
- style={{
99
- flexDirection: "column",
100
- width: "100%",
101
- height: "100%",
102
- backgroundColor: theme.background,
103
- }}
104
- >
105
- {/* mirrors the review header row: same box, position, and accent product
106
- word, with a " · resume" separator and no Edit/Share toolbar */}
107
- <box
108
- style={{ flexDirection: "row", height: 2, paddingTop: 1, backgroundColor: theme.panel }}
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
- height: 1,
113
- backgroundColor: theme.panel,
114
- paddingLeft: 1,
115
- flexDirection: "row",
202
+ flexGrow: 1,
203
+ flexDirection: "column",
204
+ alignItems: "center",
205
+ justifyContent: "center",
116
206
  }}
117
207
  >
118
- <box onMouseUp={onOpenMenu} style={{ paddingRight: 2 }}>
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
- <box style={{ flexGrow: 1 }} />
127
- </box>
128
- <box style={{ flexGrow: 1, flexDirection: "row" }}>
129
- <ThreadsSidebar
130
- open
131
- rows={rows}
132
- cursor={inboxCursor}
133
- onSelect={(id) => controller.open(id)}
134
- onRequestDelete={(id, title) =>
135
- setMode({ type: "confirmDelete", sessionId: id, title })
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
- <box style={{ flexGrow: 1, paddingLeft: 2, paddingTop: 1 }}>
140
- <text fg={theme.textDim}>Select a thread</text>
141
- </box>
142
- </box>
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
- </box>
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
@@ -1,16 +1,12 @@
1
1
  import type { Dispatch, MutableRefObject, SetStateAction } from "react";
2
2
  import type { ReviewSession, VerdictKind } from "@cueloop/schema";
3
- import { reviewerAnnotations, type Mode } from "./intent-dispatch";
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 (mode.type === "rename" || mode.type === "nameSelf" || mode.type === "treePrompt")
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, 40)}" `}
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
+ };