@cueloop/client 0.1.0-alpha.33 → 0.1.0-alpha.35

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 (94) hide show
  1. package/package.json +27 -26
  2. package/src/App.autoclose.test.tsx +7 -4
  3. package/src/App.diff.test.tsx +130 -4
  4. package/src/App.export.test.tsx +22 -4
  5. package/src/App.plan-review.test.tsx +127 -27
  6. package/src/App.readonly.test.tsx +31 -8
  7. package/src/App.rendering.test.tsx +12 -3
  8. package/src/App.review-panel.test.tsx +11 -3
  9. package/src/App.submit-confirm.test.tsx +12 -7
  10. package/src/App.test.tsx +46 -9
  11. package/src/App.toast.test.tsx +10 -3
  12. package/src/App.tsx +400 -100
  13. package/src/App.walk.test.tsx +30 -10
  14. package/src/attribution.ts +8 -2
  15. package/src/clipboard.ts +5 -1
  16. package/src/components/AnnotationCard.stories.tsx +28 -9
  17. package/src/components/AnnotationCard.tsx +53 -38
  18. package/src/components/Breadcrumb.tsx +7 -1
  19. package/src/components/CodeBlock.stories.tsx +19 -2
  20. package/src/components/CodeBlock.tsx +24 -9
  21. package/src/components/CompletionOverlay.tsx +13 -3
  22. package/src/components/ConfirmCard.stories.tsx +22 -3
  23. package/src/components/ConfirmCard.tsx +22 -5
  24. package/src/components/ConfirmDialog.tsx +9 -1
  25. package/src/components/DiffSheet.scroll.test.tsx +123 -0
  26. package/src/components/DiffSheet.stories.tsx +18 -2
  27. package/src/components/DiffSheet.syntax.test.tsx +62 -0
  28. package/src/components/DiffSheet.tsx +184 -98
  29. package/src/components/InboxList.stories.tsx +16 -3
  30. package/src/components/InboxList.tsx +17 -10
  31. package/src/components/KeybindsDialog.tsx +19 -3
  32. package/src/components/MarkerPopover.stories.tsx +58 -0
  33. package/src/components/MarkerPopover.tsx +102 -0
  34. package/src/components/MenuBar.stories.tsx +5 -1
  35. package/src/components/MenuBar.tsx +18 -2
  36. package/src/components/PlanSheet.stories.tsx +34 -2
  37. package/src/components/PlanSheet.tsx +141 -32
  38. package/src/components/PromptDialog.tsx +18 -2
  39. package/src/components/ReviewPanel.stories.tsx +13 -3
  40. package/src/components/ReviewPanel.tsx +8 -4
  41. package/src/components/ReviewRail.stories.tsx +145 -72
  42. package/src/components/ReviewRail.tsx +177 -38
  43. package/src/components/SettingsDialog.stories.tsx +51 -3
  44. package/src/components/SettingsDialog.tsx +8 -3
  45. package/src/components/SettingsRows.tsx +19 -3
  46. package/src/components/Toast.tsx +17 -2
  47. package/src/components/WalkWizard.stories.tsx +13 -2
  48. package/src/components/WalkWizard.tsx +9 -3
  49. package/src/components/__snapshots__/stories.test.tsx.snap +281 -83
  50. package/src/components/diff-sheet-layout.test.ts +127 -0
  51. package/src/components/diff-sheet-layout.ts +167 -0
  52. package/src/components/primitives/Button.tsx +5 -1
  53. package/src/components/primitives/Dialog.tsx +9 -1
  54. package/src/components/primitives/Tabs.tsx +24 -4
  55. package/src/components/stories-app.tsx +34 -6
  56. package/src/components/story-fixtures.ts +11 -3
  57. package/src/components/story.ts +3 -1
  58. package/src/components/syntax-highlight.ts +47 -12
  59. package/src/components/theme-context.stories.tsx +6 -1
  60. package/src/components/theme-context.tsx +7 -1
  61. package/src/config.test.ts +178 -2
  62. package/src/config.ts +121 -23
  63. package/src/diff-hunk-curate.test.ts +315 -0
  64. package/src/diff-hunk-curate.ts +233 -0
  65. package/src/diff-intraline.test.ts +164 -0
  66. package/src/diff-intraline.ts +132 -0
  67. package/src/diff-syntax.test.ts +96 -0
  68. package/src/diff-syntax.ts +154 -0
  69. package/src/editor.test.ts +13 -4
  70. package/src/editor.ts +31 -5
  71. package/src/herdr.ts +8 -2
  72. package/src/index.ts +9 -1
  73. package/src/integrations.ts +3 -1
  74. package/src/intent-dispatch.test.ts +193 -9
  75. package/src/intent-dispatch.ts +151 -18
  76. package/src/key-bindings.test.ts +19 -9
  77. package/src/key-bindings.ts +36 -14
  78. package/src/keymap.test.ts +171 -26
  79. package/src/keymap.ts +83 -16
  80. package/src/review-panel.test.ts +3 -1
  81. package/src/review-panel.ts +9 -2
  82. package/src/serve.test.ts +11 -3
  83. package/src/session-controller.curate.test.ts +253 -0
  84. package/src/session-controller.share.test.ts +65 -17
  85. package/src/session-controller.ts +301 -26
  86. package/src/share.ts +41 -8
  87. package/src/test-support.ts +16 -6
  88. package/src/theme-presets.ts +168 -0
  89. package/src/theme.ts +1 -3
  90. package/src/view-diff.ts +15 -4
  91. package/src/view-plan.test.ts +40 -6
  92. package/src/view-plan.ts +71 -28
  93. package/src/walk.test.ts +7 -2
  94. package/src/walk.ts +7 -2
@@ -8,9 +8,9 @@
8
8
  * which keeps quote anchors char-precise.
9
9
  */
10
10
 
11
- import React, { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
11
+ import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
12
12
  import { useRenderer } from "@opentui/react";
13
- import type { ScrollBoxRenderable, TextRenderable } from "@opentui/core";
13
+ import { createTextAttributes, type ScrollBoxRenderable, type TextRenderable } from "@opentui/core";
14
14
  import type { ReviewSession } from "@cueloop/schema";
15
15
  import {
16
16
  blockRuns,
@@ -27,8 +27,12 @@ import type { Theme } from "../theme";
27
27
  import { useComponentTheme } from "./theme-context";
28
28
  import { CodeBlock } from "./CodeBlock";
29
29
  import { AnnotationCard, type AnnotationDraft } from "./AnnotationCard";
30
+ import { MarkerPopover, type MarkerPopoverProps } from "./MarkerPopover";
30
31
  import { FRAME_BORDER_STYLE } from "./primitives/frame";
31
32
 
33
+ /** A cut block reads as removed: struck through and grayed, never red. */
34
+ const CUT_ATTRIBUTES = createTextAttributes({ strikethrough: true, dim: true });
35
+
32
36
  export interface PlanSelection {
33
37
  displayIndex: number;
34
38
  start: number;
@@ -37,7 +41,8 @@ export interface PlanSelection {
37
41
 
38
42
  export interface PlanSheetHandle {
39
43
  /** Work-text range of the current native (mouse) selection, if any. */
40
- readSelection(): PlanSelection | null;
44
+ /** The current native selection; `preferredIndex` (the mouse-up block) re-anchors a fresh drag. */
45
+ readSelection(preferredIndex?: number): PlanSelection | null;
41
46
  /** Anchor/extend the renderer's native selection from keyboard span offsets. */
42
47
  driveSpanSelection(span: SpanState): void;
43
48
  clearSelection(): void;
@@ -45,12 +50,17 @@ export interface PlanSheetHandle {
45
50
  }
46
51
 
47
52
  export interface PlanComposeState {
48
- kind: "comment" | "suggestion";
53
+ kind: "comment";
49
54
  displayIndex: number;
50
55
  quote: string;
51
56
  draft: AnnotationDraft;
52
57
  }
53
58
 
59
+ /** The marker-actions popover, rendered inline at its block while span mode is live. */
60
+ export interface PlanPopoverState extends MarkerPopoverProps {
61
+ displayIndex: number;
62
+ }
63
+
54
64
  export interface PlanSheetProps {
55
65
  session: ReviewSession;
56
66
  display: DisplayBlock[];
@@ -59,6 +69,8 @@ export interface PlanSheetProps {
59
69
  /** Extra selection-style paint on one block (keyboard span or compose anchor). */
60
70
  activeSpan: { displayIndex: number; start: number; end: number } | null;
61
71
  compose: PlanComposeState | null;
72
+ /** The marker-actions popover for the span's block; null when not in span mode. */
73
+ popover: PlanPopoverState | null;
62
74
  editOrphanCount: number;
63
75
  onLineActivate: (displayIndex: number) => void;
64
76
  theme?: Theme;
@@ -70,7 +82,10 @@ interface BlockRef {
70
82
  }
71
83
 
72
84
  /** Screen position of a rendered-text offset inside a wrapped text. */
73
- function positionOfRenderedOffset(renderable: TextRenderable, renderedOffset: number): { x: number; y: number } {
85
+ function positionOfRenderedOffset(
86
+ renderable: TextRenderable,
87
+ renderedOffset: number,
88
+ ): { x: number; y: number } {
74
89
  const info = renderable.lineInfo;
75
90
  let lineIndex = 0;
76
91
  for (let index = 0; index < info.lineStartCols.length; index++) {
@@ -85,12 +100,12 @@ function positionOfRenderedOffset(renderable: TextRenderable, renderedOffset: nu
85
100
 
86
101
  export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function PlanSheet(
87
102
  {
88
- session,
89
103
  display,
90
104
  marks,
91
105
  cursor,
92
106
  activeSpan,
93
107
  compose,
108
+ popover,
94
109
  editOrphanCount,
95
110
  onLineActivate,
96
111
  theme,
@@ -103,14 +118,27 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
103
118
  const scrollRef = useRef<ScrollBoxRenderable | null>(null);
104
119
 
105
120
  useImperativeHandle(handleRef, () => ({
106
- readSelection: (): PlanSelection | null => {
121
+ readSelection: (preferredIndex?: number): PlanSelection | null => {
107
122
  if (!renderer?.hasSelection) return null;
108
- const ordered = [...blockRefs.current.entries()].sort(([a], [b]) => a - b);
109
- for (const [displayIndex, blockRef] of ordered) {
123
+ const readBlock = (displayIndex: number): PlanSelection | null => {
124
+ const blockRef = blockRefs.current.get(displayIndex);
125
+ if (!blockRef) return null;
110
126
  const selection = blockRef.renderable.getSelection();
111
- if (!selection || selection.end <= selection.start) continue;
127
+ if (!selection || selection.end <= selection.start) return null;
112
128
  const range = workRangeForRendered(blockRef.runs, selection.start, selection.end);
113
- if (range) return { displayIndex, ...range };
129
+ return range ? { displayIndex, ...range } : null;
130
+ };
131
+ // the block the mouse released on wins, so a fresh drag re-anchors the span
132
+ // (one marker at a time) instead of sticking to the topmost prior selection
133
+ if (preferredIndex !== undefined) {
134
+ const preferred = readBlock(preferredIndex);
135
+ if (preferred) return preferred;
136
+ }
137
+ for (const displayIndex of [...blockRefs.current.keys()].sort(
138
+ (left, right) => left - right,
139
+ )) {
140
+ const found = readBlock(displayIndex);
141
+ if (found) return found;
114
142
  }
115
143
  return null;
116
144
  },
@@ -138,7 +166,30 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
138
166
  },
139
167
  }));
140
168
 
141
- const registerBlock = (displayIndex: number, renderable: TextRenderable | null, runs: StyleRun[]): void => {
169
+ // the popover floats above its block, anchored to the selection start; flip
170
+ // below when the block sits too near the viewport top to fit above it
171
+ const [popoverFlipBelow, setPopoverFlipBelow] = useState(false);
172
+ const [popoverLeft, setPopoverLeft] = useState(2);
173
+ useEffect(() => {
174
+ if (!popover) return;
175
+ const blockRef = blockRefs.current.get(popover.displayIndex);
176
+ const scrollbox = scrollRef.current;
177
+ if (!blockRef || !scrollbox) return;
178
+ // only the toolbar (3 rows) + gap must fit above; the dropdown flows down
179
+ setPopoverFlipBelow(blockRef.renderable.y - scrollbox.y < 4);
180
+ // left-anchor to the start of the selection: gutter width + its column
181
+ const startColumn =
182
+ activeSpan && activeSpan.displayIndex === popover.displayIndex
183
+ ? (renderedOffsetFor(blockRef.runs, activeSpan.start) ?? 0)
184
+ : 0;
185
+ setPopoverLeft(2 + startColumn);
186
+ }, [popover, activeSpan, display, cursor]);
187
+
188
+ const registerBlock = (
189
+ displayIndex: number,
190
+ renderable: TextRenderable | null,
191
+ runs: StyleRun[],
192
+ ): void => {
142
193
  if (renderable) blockRefs.current.set(displayIndex, { renderable, runs });
143
194
  else blockRefs.current.delete(displayIndex);
144
195
  };
@@ -164,7 +215,8 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
164
215
  isCursor={isCursor}
165
216
  marginTop={gap}
166
217
  isAnnotated={(marks.get(displayIndex) ?? []).length > 0}
167
- changeTag={block.type !== "same" ? tagLabel(block) : undefined}
218
+ changeTag={showsChangeTag(block) ? tagLabel(block) : undefined}
219
+ cut={block.type === "del"}
168
220
  theme={theme}
169
221
  />,
170
222
  );
@@ -174,11 +226,23 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
174
226
  blockMarks.push({ start: activeSpan.start, end: activeSpan.end, role: "kspan" });
175
227
  }
176
228
  const runs = overlayMarks(blockRuns(block, true), blockMarks);
177
- // the change tag participates in the rendered text but carries no offsets
178
- const mappedRuns: StyleRun[] =
179
- block.type !== "same" ? [...runs, { text: ` [${tagLabel(block)}]`, role: "plain", start: null }] : runs;
229
+ // the change tag participates in the rendered text but carries no offsets;
230
+ // a cut block shows no tag (its strikethrough already reads as removed)
231
+ const mappedRuns: StyleRun[] = showsChangeTag(block)
232
+ ? [...runs, { text: ` [${tagLabel(block)}]`, role: "plain", start: null }]
233
+ : runs;
180
234
  children.push(
181
- <box key={displayIndex} id={`plan-block-${displayIndex}`} style={{ flexDirection: "row", marginTop: gap }}>
235
+ <box
236
+ key={displayIndex}
237
+ id={`plan-block-${displayIndex}`}
238
+ // raise the block holding the popover so its overlay paints over the
239
+ // later blocks it floats across (siblings paint in z-index order)
240
+ style={{
241
+ flexDirection: "row",
242
+ marginTop: gap,
243
+ zIndex: popover?.displayIndex === displayIndex ? 10 : undefined,
244
+ }}
245
+ >
182
246
  <text selectable={false}>
183
247
  <span fg={isCursor ? tokens.accent : tokens.textDim}>{isCursor ? "▎ " : " "}</span>
184
248
  <span fg={tokens.textDim}>{marker(block)}</span>
@@ -189,7 +253,9 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
189
253
  selectionBg={tokens.accent}
190
254
  selectionFg={tokens.accentInk}
191
255
  style={{ wrapMode: "word", flexGrow: 1, flexShrink: 1 }}
192
- ref={(renderable: TextRenderable | null) => registerBlock(displayIndex, renderable, mappedRuns)}
256
+ ref={(renderable: TextRenderable | null) =>
257
+ registerBlock(displayIndex, renderable, mappedRuns)
258
+ }
193
259
  onMouseUp={() => onLineActivate(displayIndex)}
194
260
  >
195
261
  {runs.map((run, runIndex) => (
@@ -197,14 +263,38 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
197
263
  {run.text}
198
264
  </span>
199
265
  ))}
200
- {block.type !== "same" ? <span fg={tagColor(block, tokens)}> [{tagLabel(block)}]</span> : null}
266
+ {showsChangeTag(block) ? (
267
+ <span fg={tagColor(block, tokens)}> [{tagLabel(block)}]</span>
268
+ ) : null}
201
269
  </text>
270
+ {popover && popover.displayIndex === displayIndex ? (
271
+ // float the card over the block, centered, with a one-row gap; flip
272
+ // below only when there is no room above (near the viewport top)
273
+ <box
274
+ style={{
275
+ position: "absolute",
276
+ left: popoverLeft,
277
+ // fixed offset: the toolbar sits one row above the selection and
278
+ // stays put; the dropdown flows down from it, over the selection
279
+ top: popoverFlipBelow ? 1 : -4,
280
+ flexDirection: "column",
281
+ }}
282
+ >
283
+ <MarkerPopover {...popover} theme={theme} />
284
+ </box>
285
+ ) : null}
202
286
  </box>,
203
287
  );
204
288
  }
205
289
  if (compose && compose.displayIndex === displayIndex) {
206
290
  children.push(
207
- <AnnotationCard key={`compose-${displayIndex}`} kind={compose.kind} quote={compose.quote} draft={compose.draft} theme={theme} />,
291
+ <AnnotationCard
292
+ key={`compose-${displayIndex}`}
293
+ kind={compose.kind}
294
+ quote={compose.quote}
295
+ draft={compose.draft}
296
+ theme={theme}
297
+ />,
208
298
  );
209
299
  }
210
300
  }
@@ -223,11 +313,16 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
223
313
  {editOrphanCount > 0 ? (
224
314
  <box style={{ height: 1, backgroundColor: tokens.markCommentBackground, paddingLeft: 1 }}>
225
315
  <text fg={tokens.red}>
226
- {editOrphanCount} annotation{editOrphanCount === 1 ? "" : "s"} no longer match - the passage was removed.
316
+ {editOrphanCount} annotation{editOrphanCount === 1 ? "" : "s"} no longer match - the
317
+ passage was removed.
227
318
  </text>
228
319
  </box>
229
320
  ) : null}
230
- <scrollbox ref={scrollRef} style={{ flexGrow: 1, paddingLeft: 1, paddingTop: 0 }} focused={false}>
321
+ <scrollbox
322
+ ref={scrollRef}
323
+ style={{ flexGrow: 1, paddingLeft: 1, paddingTop: 0 }}
324
+ focused={false}
325
+ >
231
326
  {children}
232
327
  </scrollbox>
233
328
  </box>
@@ -239,7 +334,8 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
239
334
  function topGap(previous: DisplayBlock | undefined, current: DisplayBlock): number {
240
335
  if (!previous) return 0;
241
336
  const tightPair =
242
- (current.kind === "li" && previous.kind === "li") || (current.kind === "oli" && previous.kind === "oli");
337
+ (current.kind === "li" && previous.kind === "li") ||
338
+ (current.kind === "oli" && previous.kind === "oli");
243
339
  return tightPair ? 0 : 1;
244
340
  }
245
341
 
@@ -250,17 +346,32 @@ function marker(block: DisplayBlock): string {
250
346
  return "";
251
347
  }
252
348
 
253
- function tagLabel(block: DisplayBlock): "cut" | "new" | "edited" {
254
- return block.type === "del" ? "cut" : block.type === "add" ? "new" : "edited";
349
+ /** An added or edited block earns a tag; a cut block relies on its strikethrough. */
350
+ function showsChangeTag(block: DisplayBlock): boolean {
351
+ return block.type === "add" || block.type === "mod";
352
+ }
353
+
354
+ function tagLabel(block: DisplayBlock): "new" | "edited" {
355
+ return block.type === "add" ? "new" : "edited";
255
356
  }
256
357
 
257
358
  function tagColor(block: DisplayBlock, tokens: Theme): string {
258
- return block.type === "del" ? tokens.red : block.type === "add" ? tokens.green : tokens.accent;
359
+ return block.type === "add" ? tokens.green : tokens.accent;
259
360
  }
260
361
 
261
- function runStyle(run: StyleRun, block: DisplayBlock, tokens: Theme): { fg?: string; bg?: string } {
262
- const headingFg = block.kind === "h1" ? tokens.text : block.kind === "h2" || block.kind === "h3" ? tokens.accent : undefined;
263
- const struck = block.type === "del";
362
+ function runStyle(
363
+ run: StyleRun,
364
+ block: DisplayBlock,
365
+ tokens: Theme,
366
+ ): { fg?: string; bg?: string; attributes?: number } {
367
+ // a cut block reads as removed: every run struck through and grayed, never red
368
+ if (block.type === "del") return { fg: tokens.textDim, attributes: CUT_ATTRIBUTES };
369
+ const headingFg =
370
+ block.kind === "h1"
371
+ ? tokens.text
372
+ : block.kind === "h2" || block.kind === "h3"
373
+ ? tokens.accent
374
+ : undefined;
264
375
  switch (run.role) {
265
376
  case "ins":
266
377
  return { fg: tokens.insertedForeground };
@@ -268,13 +379,11 @@ function runStyle(run: StyleRun, block: DisplayBlock, tokens: Theme): { fg?: str
268
379
  return { fg: tokens.deletedForeground };
269
380
  case "mark-comment":
270
381
  return { fg: tokens.text, bg: tokens.markCommentBackground };
271
- case "mark-suggestion":
272
- return { fg: tokens.text, bg: tokens.markSuggestionBackground };
273
382
  case "mark-focus":
274
383
  return { fg: tokens.accentInk, bg: tokens.accent };
275
384
  case "kspan":
276
385
  return { fg: tokens.accentInk, bg: tokens.accent };
277
386
  default:
278
- return { fg: struck ? tokens.red : (headingFg ?? tokens.text) };
387
+ return { fg: headingFg ?? tokens.text };
279
388
  }
280
389
  }
@@ -25,7 +25,15 @@ export interface PromptDialogProps {
25
25
  theme?: Theme;
26
26
  }
27
27
 
28
- export function PromptDialog({ isOpen, title, label, value, placeholder, onInput, theme }: PromptDialogProps): React.ReactNode {
28
+ export function PromptDialog({
29
+ isOpen,
30
+ title,
31
+ label,
32
+ value,
33
+ placeholder,
34
+ onInput,
35
+ theme,
36
+ }: PromptDialogProps): React.ReactNode {
29
37
  const tokens = useComponentTheme(theme);
30
38
  const { width: terminalWidth } = useTerminalDimensions();
31
39
  const inputRef = useRef<TextareaRenderable | null>(null);
@@ -37,7 +45,15 @@ export function PromptDialog({ isOpen, title, label, value, placeholder, onInput
37
45
  if (!isOpen) return null;
38
46
  return (
39
47
  <Dialog isOpen title={title} width={Math.min(54, terminalWidth - 6)} height={7} theme={theme}>
40
- <box style={{ flexDirection: "column", flexGrow: 1, paddingLeft: 1, paddingRight: 1, paddingTop: 1 }}>
48
+ <box
49
+ style={{
50
+ flexDirection: "column",
51
+ flexGrow: 1,
52
+ paddingLeft: 1,
53
+ paddingRight: 1,
54
+ paddingTop: 1,
55
+ }}
56
+ >
41
57
  <text fg={tokens.textDim}>{label}</text>
42
58
  <textarea
43
59
  ref={inputRef}
@@ -12,6 +12,7 @@ const rail = {
12
12
  authorNames: {},
13
13
  selectedId: FIXTURE_ANNOTATIONS[0]!.id,
14
14
  resolvedIds: new Set(FIXTURE_ANNOTATIONS.map((annotation) => annotation.id)),
15
+ curationItems: [],
15
16
  railTab: "review" as const,
16
17
  pendingCount: 2,
17
18
  cardEdit: null,
@@ -19,6 +20,8 @@ const rail = {
19
20
  onTabChange: () => {},
20
21
  onSelectCard: () => {},
21
22
  onActivateCard: () => {},
23
+ onSelectCuration: () => {},
24
+ onUndoCuration: () => {},
22
25
  onSubmitRequest: () => {},
23
26
  };
24
27
 
@@ -27,7 +30,14 @@ const panelCallbacks = { onDividerGrab: () => {}, onToggle: () => {} };
27
30
  /** The plan area to the left of the divider, so the layout reads in context. */
28
31
  function PanelFrame({ children }: { children: React.ReactNode }): React.ReactNode {
29
32
  return (
30
- <box style={{ flexDirection: "row", width: "100%", height: "100%", backgroundColor: DARK.background }}>
33
+ <box
34
+ style={{
35
+ flexDirection: "row",
36
+ width: "100%",
37
+ height: "100%",
38
+ backgroundColor: DARK.background,
39
+ }}
40
+ >
31
41
  <box style={{ flexGrow: 1, paddingLeft: 1 }}>
32
42
  <text fg={DARK.textMuted}>The plan takes the width the panel hands back.</text>
33
43
  </box>
@@ -51,7 +61,7 @@ export const ExpandedWide: Story = {
51
61
  <ReviewPanel mode="expanded" width={REVIEW_MAX_WIDTH} rail={rail} {...panelCallbacks} />
52
62
  </PanelFrame>
53
63
  ),
54
- expectedColors: [DARK.green],
64
+ expectedColors: [DARK.accent],
55
65
  size: { width: 90, height: 24 },
56
66
  };
57
67
 
@@ -61,7 +71,7 @@ export const Compact: Story = {
61
71
  <ReviewPanel mode="compact" width={34} rail={rail} {...panelCallbacks} />
62
72
  </PanelFrame>
63
73
  ),
64
- expectedColors: [DARK.accent, DARK.green],
74
+ expectedColors: [DARK.accent],
65
75
  size: { width: 60, height: 24 },
66
76
  };
67
77
 
@@ -49,11 +49,15 @@ function CompactRail({
49
49
  theme?: Theme;
50
50
  }): React.ReactNode {
51
51
  const tokens = useComponentTheme(theme);
52
- const dotColor = (annotation: Annotation): string =>
53
- annotation.kind === "suggestion" ? tokens.green : tokens.accent;
54
52
  return (
55
53
  <box
56
- style={{ width: REVIEW_COMPACT_WIDTH, backgroundColor: tokens.panel, flexDirection: "column", paddingTop: 1, paddingBottom: 1 }}
54
+ style={{
55
+ width: REVIEW_COMPACT_WIDTH,
56
+ backgroundColor: tokens.panel,
57
+ flexDirection: "column",
58
+ paddingTop: 1,
59
+ paddingBottom: 1,
60
+ }}
57
61
  >
58
62
  <box style={{ alignItems: "center" }}>
59
63
  <text fg={tokens.accent}>{String(annotations.length)}</text>
@@ -61,7 +65,7 @@ function CompactRail({
61
65
  <box style={{ height: 1 }} />
62
66
  <box style={{ flexDirection: "column", alignItems: "center" }}>
63
67
  {annotations.map((annotation) => (
64
- <text key={annotation.id} fg={dotColor(annotation)}>
68
+ <text key={annotation.id} fg={tokens.accent}>
65
69
  ●
66
70
  </text>
67
71
  ))}