@cueloop/client 0.1.0-alpha.4 → 0.1.0-alpha.41
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/native/build.sh +56 -0
- package/native/darwin-arm64/libcueloopvt.dylib +0 -0
- package/native/src/shim.c +107 -0
- package/package.json +34 -24
- package/src/App.autoclose.test.tsx +137 -0
- package/src/App.diff.test.tsx +158 -22
- package/src/App.export.test.tsx +44 -37
- package/src/App.plan-review.test.tsx +632 -0
- package/src/App.readonly.test.tsx +60 -36
- package/src/App.rendering.test.tsx +132 -0
- package/src/App.review-panel.test.tsx +136 -0
- package/src/App.submit-confirm.test.tsx +220 -0
- package/src/App.test.tsx +128 -69
- package/src/App.toast.test.tsx +75 -0
- package/src/App.tsx +0 -0
- package/src/App.walk.test.tsx +288 -0
- package/src/attribution.test.ts +31 -0
- package/src/attribution.ts +38 -0
- package/src/clipboard.test.ts +23 -0
- package/src/clipboard.ts +30 -0
- package/src/components/AnnotationCard.compose-rows.test.ts +49 -0
- package/src/components/AnnotationCard.stories.tsx +94 -0
- package/src/components/AnnotationCard.tsx +228 -0
- package/src/components/Breadcrumb.stories.tsx +19 -0
- package/src/components/Breadcrumb.tsx +44 -0
- package/src/components/CodeBlock.stories.tsx +42 -0
- package/src/components/CodeBlock.tsx +83 -0
- package/src/components/CompletionOverlay.stories.tsx +23 -0
- package/src/components/CompletionOverlay.tsx +59 -0
- package/src/components/ConfirmCard.stories.tsx +56 -0
- package/src/components/ConfirmCard.tsx +135 -0
- package/src/components/ConfirmDialog.stories.tsx +21 -0
- package/src/components/ConfirmDialog.tsx +64 -0
- package/src/components/DiffSheet.scroll.test.tsx +123 -0
- package/src/components/DiffSheet.stories.tsx +78 -0
- package/src/components/DiffSheet.syntax.test.tsx +62 -0
- package/src/components/DiffSheet.tsx +355 -0
- package/src/components/InboxList.stories.tsx +31 -0
- package/src/components/InboxList.tsx +62 -0
- package/src/components/KeybindsDialog.stories.tsx +31 -0
- package/src/components/KeybindsDialog.tsx +58 -0
- package/src/components/MarkerPopover.stories.tsx +58 -0
- package/src/components/MarkerPopover.tsx +102 -0
- package/src/components/MenuBar.stories.tsx +37 -0
- package/src/components/MenuBar.tsx +74 -0
- package/src/components/PlanSheet.stories.tsx +113 -0
- package/src/components/PlanSheet.tsx +389 -0
- package/src/components/PromptDialog.stories.tsx +36 -0
- package/src/components/PromptDialog.tsx +78 -0
- package/src/components/ReviewPanel.stories.tsx +86 -0
- package/src/components/ReviewPanel.tsx +101 -0
- package/src/components/ReviewRail.stories.tsx +220 -0
- package/src/components/ReviewRail.tsx +324 -0
- package/src/components/SettingsDialog.stories.tsx +111 -0
- package/src/components/SettingsDialog.tsx +141 -0
- package/src/components/SettingsRows.stories.tsx +18 -0
- package/src/components/SettingsRows.tsx +108 -0
- package/src/components/Toast.stories.tsx +12 -0
- package/src/components/Toast.tsx +44 -0
- package/src/components/WalkWizard.stories.tsx +100 -0
- package/src/components/WalkWizard.tsx +152 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +1829 -0
- package/src/components/agent-launcher.stories.tsx +26 -0
- package/src/components/agent-launcher.test.tsx +21 -0
- package/src/components/agent-launcher.tsx +180 -0
- package/src/components/diff-sheet-layout.test.ts +127 -0
- package/src/components/diff-sheet-layout.ts +167 -0
- package/src/components/primitives/Button.stories.tsx +32 -0
- package/src/components/primitives/Button.tsx +48 -0
- package/src/components/primitives/Card.stories.tsx +25 -0
- package/src/components/primitives/Card.tsx +59 -0
- package/src/components/primitives/Dialog.stories.tsx +24 -0
- package/src/components/primitives/Dialog.tsx +64 -0
- package/src/components/primitives/StatusBar.stories.tsx +11 -0
- package/src/components/primitives/StatusBar.tsx +22 -0
- package/src/components/primitives/Tabs.stories.tsx +29 -0
- package/src/components/primitives/Tabs.tsx +98 -0
- package/src/components/primitives/Toolbar.stories.tsx +17 -0
- package/src/components/primitives/Toolbar.tsx +14 -0
- package/src/components/primitives/frame.test.ts +12 -0
- package/src/components/primitives/frame.ts +14 -0
- package/src/components/quick-actions-editor.stories.tsx +54 -0
- package/src/components/quick-actions-editor.tsx +136 -0
- package/src/components/stories-app.tsx +123 -0
- package/src/components/stories.test.tsx +62 -0
- package/src/components/story-fixtures.ts +98 -0
- package/src/components/story.ts +69 -0
- package/src/components/syntax-highlight.ts +92 -0
- package/src/components/terminal-pane.test.tsx +40 -0
- package/src/components/terminal-pane.ts +174 -0
- package/src/components/theme-context.stories.tsx +46 -0
- package/src/components/theme-context.tsx +36 -0
- package/src/components/truncate-text.ts +6 -0
- package/src/config.test.ts +430 -31
- package/src/config.ts +304 -26
- package/src/diff-hunk-curate.test.ts +315 -0
- package/src/diff-hunk-curate.ts +233 -0
- package/src/diff-intraline.test.ts +164 -0
- package/src/diff-intraline.ts +132 -0
- package/src/diff-syntax.test.ts +96 -0
- package/src/diff-syntax.ts +154 -0
- package/src/editor.test.ts +143 -0
- package/src/editor.ts +123 -9
- package/src/ghostty-terminal.test.ts +45 -0
- package/src/ghostty-terminal.ts +168 -0
- package/src/herdr.test.ts +41 -0
- package/src/herdr.ts +27 -0
- package/src/index.ts +10 -0
- package/src/integrations.test.ts +41 -0
- package/src/integrations.ts +30 -0
- package/src/intent-dispatch.test.ts +492 -0
- package/src/intent-dispatch.ts +428 -0
- package/src/key-bindings.test.ts +152 -0
- package/src/key-bindings.ts +424 -0
- package/src/keymap.test.ts +622 -0
- package/src/keymap.ts +339 -0
- package/src/review-panel.test.ts +113 -0
- package/src/review-panel.ts +83 -0
- package/src/run.ts +9 -5
- package/src/serve.test.ts +24 -9
- package/src/serve.ts +7 -7
- package/src/session-controller.curate.test.ts +253 -0
- package/src/session-controller.share.test.ts +309 -0
- package/src/session-controller.ts +900 -0
- package/src/share.ts +113 -0
- package/src/test-support.test.ts +55 -0
- package/src/test-support.ts +187 -0
- package/src/theme-presets.ts +168 -0
- package/src/theme.ts +36 -15
- package/src/version.ts +5 -0
- package/src/view-diff.test.ts +19 -8
- package/src/view-diff.ts +34 -23
- package/src/view-plan.test.ts +294 -0
- package/src/view-plan.ts +408 -0
- package/src/walk.test.ts +121 -0
- package/src/walk.ts +70 -0
- package/src/view.test.ts +0 -157
- package/src/view.ts +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A centered single-field text prompt on the shared Dialog overlay. The focused
|
|
3
|
+
* input owns typing/cursor natively; the reducer's `prompt` overlay drives ⏎
|
|
4
|
+
* save / esc cancel (like the composer). Used to rename an author and to ask a
|
|
5
|
+
* collaborator for their name.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useEffect, useRef } from "react";
|
|
9
|
+
import { useTerminalDimensions } from "@opentui/react";
|
|
10
|
+
import type { KeyBinding, TextareaRenderable } from "@opentui/core";
|
|
11
|
+
import type { Theme } from "../theme";
|
|
12
|
+
import { useComponentTheme } from "./theme-context";
|
|
13
|
+
import { Dialog } from "./primitives/Dialog";
|
|
14
|
+
|
|
15
|
+
// ⏎ submits (suppresses the textarea's default newline); the grammar owns save.
|
|
16
|
+
const PROMPT_KEY_BINDINGS: KeyBinding[] = [{ name: "return", action: "submit" }];
|
|
17
|
+
|
|
18
|
+
export interface PromptDialogProps {
|
|
19
|
+
isOpen: boolean;
|
|
20
|
+
title?: string;
|
|
21
|
+
label: string;
|
|
22
|
+
value: string;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
onInput: (text: string) => void;
|
|
25
|
+
theme?: Theme;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function PromptDialog({
|
|
29
|
+
isOpen,
|
|
30
|
+
title,
|
|
31
|
+
label,
|
|
32
|
+
value,
|
|
33
|
+
placeholder,
|
|
34
|
+
onInput,
|
|
35
|
+
theme,
|
|
36
|
+
}: PromptDialogProps): React.ReactNode {
|
|
37
|
+
const tokens = useComponentTheme(theme);
|
|
38
|
+
const { width: terminalWidth } = useTerminalDimensions();
|
|
39
|
+
const inputRef = useRef<TextareaRenderable | null>(null);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
// open with the caret after the seeded value, like a text field
|
|
42
|
+
if (inputRef.current) inputRef.current.cursorOffset = value.length;
|
|
43
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
44
|
+
}, []);
|
|
45
|
+
if (!isOpen) return null;
|
|
46
|
+
return (
|
|
47
|
+
<Dialog isOpen title={title} width={Math.min(54, terminalWidth - 6)} height={7} theme={theme}>
|
|
48
|
+
<box
|
|
49
|
+
style={{
|
|
50
|
+
flexDirection: "column",
|
|
51
|
+
flexGrow: 1,
|
|
52
|
+
paddingLeft: 1,
|
|
53
|
+
paddingRight: 1,
|
|
54
|
+
paddingTop: 1,
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<text fg={tokens.textDim}>{label}</text>
|
|
58
|
+
<textarea
|
|
59
|
+
ref={inputRef}
|
|
60
|
+
focused
|
|
61
|
+
initialValue={value}
|
|
62
|
+
placeholder={placeholder}
|
|
63
|
+
keyBindings={PROMPT_KEY_BINDINGS}
|
|
64
|
+
onContentChange={() => onInput(inputRef.current?.plainText ?? "")}
|
|
65
|
+
style={{
|
|
66
|
+
height: 1,
|
|
67
|
+
backgroundColor: tokens.elevated,
|
|
68
|
+
focusedBackgroundColor: tokens.elevated,
|
|
69
|
+
textColor: tokens.text,
|
|
70
|
+
focusedTextColor: tokens.text,
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
<box style={{ flexGrow: 1 }} />
|
|
74
|
+
<text fg={tokens.textDim}>enter save · esc cancel</text>
|
|
75
|
+
</box>
|
|
76
|
+
</Dialog>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../theme";
|
|
3
|
+
import { REVIEW_MAX_WIDTH, REVIEW_MIN_WIDTH } from "../review-panel";
|
|
4
|
+
import type { Story, StoryMeta } from "./story";
|
|
5
|
+
import { ReviewPanel } from "./ReviewPanel";
|
|
6
|
+
import { FIXTURE_ANNOTATIONS, fixturePlanSession } from "./story-fixtures";
|
|
7
|
+
|
|
8
|
+
export const meta: StoryMeta = { title: "ReviewPanel" };
|
|
9
|
+
|
|
10
|
+
const rail = {
|
|
11
|
+
session: fixturePlanSession(),
|
|
12
|
+
authorNames: {},
|
|
13
|
+
selectedId: FIXTURE_ANNOTATIONS[0]!.id,
|
|
14
|
+
resolvedIds: new Set(FIXTURE_ANNOTATIONS.map((annotation) => annotation.id)),
|
|
15
|
+
curationItems: [],
|
|
16
|
+
railTab: "review" as const,
|
|
17
|
+
pendingCount: 2,
|
|
18
|
+
cardEdit: null,
|
|
19
|
+
submitConfirm: null,
|
|
20
|
+
onTabChange: () => {},
|
|
21
|
+
onSelectCard: () => {},
|
|
22
|
+
onActivateCard: () => {},
|
|
23
|
+
onSelectCuration: () => {},
|
|
24
|
+
onUndoCuration: () => {},
|
|
25
|
+
onSubmitRequest: () => {},
|
|
26
|
+
onLaunchHarness: () => {},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const panelCallbacks = { onDividerGrab: () => {}, onToggle: () => {} };
|
|
30
|
+
|
|
31
|
+
/** The plan area to the left of the divider, so the layout reads in context. */
|
|
32
|
+
function PanelFrame({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
33
|
+
return (
|
|
34
|
+
<box
|
|
35
|
+
style={{
|
|
36
|
+
flexDirection: "row",
|
|
37
|
+
width: "100%",
|
|
38
|
+
height: "100%",
|
|
39
|
+
backgroundColor: DARK.background,
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<box style={{ flexGrow: 1, paddingLeft: 1 }}>
|
|
43
|
+
<text fg={DARK.textMuted}>The plan takes the width the panel hands back.</text>
|
|
44
|
+
</box>
|
|
45
|
+
{children}
|
|
46
|
+
</box>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const ExpandedNarrow: Story = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<PanelFrame>
|
|
53
|
+
<ReviewPanel mode="expanded" width={REVIEW_MIN_WIDTH} rail={rail} {...panelCallbacks} />
|
|
54
|
+
</PanelFrame>
|
|
55
|
+
),
|
|
56
|
+
size: { width: 80, height: 24 },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const ExpandedWide: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<PanelFrame>
|
|
62
|
+
<ReviewPanel mode="expanded" width={REVIEW_MAX_WIDTH} rail={rail} {...panelCallbacks} />
|
|
63
|
+
</PanelFrame>
|
|
64
|
+
),
|
|
65
|
+
expectedColors: [DARK.accent],
|
|
66
|
+
size: { width: 90, height: 24 },
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Compact: Story = {
|
|
70
|
+
render: () => (
|
|
71
|
+
<PanelFrame>
|
|
72
|
+
<ReviewPanel mode="compact" width={34} rail={rail} {...panelCallbacks} />
|
|
73
|
+
</PanelFrame>
|
|
74
|
+
),
|
|
75
|
+
expectedColors: [DARK.accent],
|
|
76
|
+
size: { width: 60, height: 24 },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Hidden: Story = {
|
|
80
|
+
render: () => (
|
|
81
|
+
<PanelFrame>
|
|
82
|
+
<ReviewPanel mode="hidden" width={34} rail={rail} {...panelCallbacks} />
|
|
83
|
+
</PanelFrame>
|
|
84
|
+
),
|
|
85
|
+
size: { width: 60, height: 24 },
|
|
86
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The review surface's outer shell: the grabbable divider plus the rail in one
|
|
3
|
+
* of three modes - expanded (the full ReviewRail, resizable), compact (a narrow
|
|
4
|
+
* count + dots strip that hands width back to the plan), or hidden (nothing at
|
|
5
|
+
* all, no leftover tab). The keybinding cycles all three; the clickable chevron
|
|
6
|
+
* toggles expanded and compact only. App owns the width/mode state and the drag
|
|
7
|
+
* on the container; this file only reads them. The geometry (width clamp, the
|
|
8
|
+
* divider glyph column, the cycle) lives in review-panel.ts so this stays
|
|
9
|
+
* declarative.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React from "react";
|
|
13
|
+
import type { Annotation } from "@cueloop/schema";
|
|
14
|
+
import type { Theme } from "../theme";
|
|
15
|
+
import { useComponentTheme } from "./theme-context";
|
|
16
|
+
import { ReviewRail, type ReviewRailHandle, type ReviewRailProps } from "./ReviewRail";
|
|
17
|
+
import { REVIEW_COMPACT_WIDTH, type ReviewPanelMode } from "../review-panel";
|
|
18
|
+
|
|
19
|
+
export interface ReviewPanelProps {
|
|
20
|
+
mode: ReviewPanelMode;
|
|
21
|
+
/** Expanded-rail width in columns (already clamped by the app). */
|
|
22
|
+
width: number;
|
|
23
|
+
/** Arm a divider drag; the app tracks the drag itself on the container. */
|
|
24
|
+
onDividerGrab: () => void;
|
|
25
|
+
/** Chevron click: expanded <-> compact (never hidden). */
|
|
26
|
+
onToggle: () => void;
|
|
27
|
+
/** The expanded ReviewRail's props; width and onCollapse are supplied here. */
|
|
28
|
+
rail: Omit<ReviewRailProps, "width" | "onCollapse" | "theme">;
|
|
29
|
+
railRef?: React.Ref<ReviewRailHandle>;
|
|
30
|
+
theme?: Theme;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** An invisible, full-height grab column: the plan's right border is the seam. */
|
|
34
|
+
function ReviewDivider({ onGrab }: { onGrab: () => void }): React.ReactNode {
|
|
35
|
+
// childless: it stretches to the row height like the plan and rail beside it,
|
|
36
|
+
// so the three columns share one bottom edge. Invisible, but full-height grab.
|
|
37
|
+
return <box style={{ width: 1 }} onMouseDown={onGrab} />;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The compact strip: the card count, one kind-colored dot per card, and the
|
|
41
|
+
* `<` expand chevron, bottom-padded to sit at the same row as the expanded `>`. */
|
|
42
|
+
function CompactRail({
|
|
43
|
+
annotations,
|
|
44
|
+
onExpand,
|
|
45
|
+
theme,
|
|
46
|
+
}: {
|
|
47
|
+
annotations: Annotation[];
|
|
48
|
+
onExpand: () => void;
|
|
49
|
+
theme?: Theme;
|
|
50
|
+
}): React.ReactNode {
|
|
51
|
+
const tokens = useComponentTheme(theme);
|
|
52
|
+
return (
|
|
53
|
+
<box
|
|
54
|
+
style={{
|
|
55
|
+
width: REVIEW_COMPACT_WIDTH,
|
|
56
|
+
backgroundColor: tokens.panel,
|
|
57
|
+
flexDirection: "column",
|
|
58
|
+
paddingTop: 1,
|
|
59
|
+
paddingBottom: 1,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<box style={{ alignItems: "center" }}>
|
|
63
|
+
<text fg={tokens.accent}>{String(annotations.length)}</text>
|
|
64
|
+
</box>
|
|
65
|
+
<box style={{ height: 1 }} />
|
|
66
|
+
<box style={{ flexDirection: "column", alignItems: "center" }}>
|
|
67
|
+
{annotations.map((annotation) => (
|
|
68
|
+
<text key={annotation.id} fg={tokens.accent}>
|
|
69
|
+
●
|
|
70
|
+
</text>
|
|
71
|
+
))}
|
|
72
|
+
</box>
|
|
73
|
+
<box style={{ flexGrow: 1 }} />
|
|
74
|
+
<box style={{ paddingLeft: 1 }} onMouseUp={onExpand}>
|
|
75
|
+
<text fg={tokens.textDim}>{"<"}</text>
|
|
76
|
+
</box>
|
|
77
|
+
</box>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function ReviewPanel({
|
|
82
|
+
mode,
|
|
83
|
+
width,
|
|
84
|
+
onDividerGrab,
|
|
85
|
+
onToggle,
|
|
86
|
+
rail,
|
|
87
|
+
railRef,
|
|
88
|
+
theme,
|
|
89
|
+
}: ReviewPanelProps): React.ReactNode {
|
|
90
|
+
if (mode === "hidden") return null;
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
<ReviewDivider onGrab={onDividerGrab} />
|
|
94
|
+
{mode === "expanded" ? (
|
|
95
|
+
<ReviewRail ref={railRef} {...rail} width={width} onCollapse={onToggle} theme={theme} />
|
|
96
|
+
) : (
|
|
97
|
+
<CompactRail annotations={rail.session.annotations} onExpand={onToggle} theme={theme} />
|
|
98
|
+
)}
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "./story";
|
|
4
|
+
import { ReviewRail } from "./ReviewRail";
|
|
5
|
+
import { FIXTURE_ANNOTATIONS, fixtureDiffSession, fixturePlanSession } from "./story-fixtures";
|
|
6
|
+
|
|
7
|
+
export const meta: StoryMeta = { title: "ReviewRail" };
|
|
8
|
+
|
|
9
|
+
const callbacks = {
|
|
10
|
+
curationItems: [],
|
|
11
|
+
onTabChange: () => {},
|
|
12
|
+
onSelectCard: () => {},
|
|
13
|
+
onActivateCard: () => {},
|
|
14
|
+
onSelectCuration: () => {},
|
|
15
|
+
onUndoCuration: () => {},
|
|
16
|
+
onSubmitRequest: () => {},
|
|
17
|
+
onLaunchHarness: () => {},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** The rail expects a height-constrained parent, like the app's main row. */
|
|
21
|
+
function RailFrame({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
22
|
+
return <box style={{ flexDirection: "row", width: "100%", height: "100%" }}>{children}</box>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const AnnotationStack: Story = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<RailFrame>
|
|
28
|
+
<ReviewRail
|
|
29
|
+
session={fixturePlanSession()}
|
|
30
|
+
authorNames={{}}
|
|
31
|
+
selectedId={FIXTURE_ANNOTATIONS[0]!.id}
|
|
32
|
+
resolvedIds={new Set(FIXTURE_ANNOTATIONS.map((annotation) => annotation.id))}
|
|
33
|
+
railTab="review"
|
|
34
|
+
pendingCount={2}
|
|
35
|
+
cardEdit={null}
|
|
36
|
+
submitConfirm={null}
|
|
37
|
+
{...callbacks}
|
|
38
|
+
/>
|
|
39
|
+
</RailFrame>
|
|
40
|
+
),
|
|
41
|
+
expectedColors: [DARK.accent],
|
|
42
|
+
size: { width: 40, height: 24 },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const MixedOwnershipStack: Story = {
|
|
46
|
+
render: () => (
|
|
47
|
+
<RailFrame>
|
|
48
|
+
<ReviewRail
|
|
49
|
+
session={fixturePlanSession({
|
|
50
|
+
participants: [{ id: "SHA256:collab1", provider: "ssh", name: "Priya" }],
|
|
51
|
+
annotations: [
|
|
52
|
+
{
|
|
53
|
+
id: "own_note",
|
|
54
|
+
kind: "comment",
|
|
55
|
+
anchor: { quote: "persists sessions", prefix: "The daemon ", suffix: " to disk" },
|
|
56
|
+
body: "Which daemon owns this?",
|
|
57
|
+
createdAt: "2026-01-01T00:00:00Z",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "collab_note",
|
|
61
|
+
kind: "comment",
|
|
62
|
+
anchor: { quote: "move the store", prefix: "", suffix: "" },
|
|
63
|
+
body: "move it behind one interface",
|
|
64
|
+
author: "SHA256:collab1",
|
|
65
|
+
createdAt: "2026-01-01T00:00:01Z",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
})}
|
|
69
|
+
authorNames={{}}
|
|
70
|
+
selectedId="own_note"
|
|
71
|
+
resolvedIds={new Set(["own_note", "collab_note"])}
|
|
72
|
+
railTab="review"
|
|
73
|
+
pendingCount={2}
|
|
74
|
+
cardEdit={null}
|
|
75
|
+
submitConfirm={null}
|
|
76
|
+
{...callbacks}
|
|
77
|
+
/>
|
|
78
|
+
</RailFrame>
|
|
79
|
+
),
|
|
80
|
+
// every annotation border is accent now
|
|
81
|
+
expectedColors: [DARK.accent],
|
|
82
|
+
size: { width: 40, height: 24 },
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const Removals: Story = {
|
|
86
|
+
render: () => (
|
|
87
|
+
<RailFrame>
|
|
88
|
+
<ReviewRail
|
|
89
|
+
session={fixtureDiffSession()}
|
|
90
|
+
authorNames={{}}
|
|
91
|
+
resolvedIds={null}
|
|
92
|
+
railTab="review"
|
|
93
|
+
pendingCount={0}
|
|
94
|
+
cardEdit={null}
|
|
95
|
+
submitConfirm={null}
|
|
96
|
+
{...callbacks}
|
|
97
|
+
curationItems={[
|
|
98
|
+
{
|
|
99
|
+
id: "diff:src/store.ts#0#1",
|
|
100
|
+
source: "diff",
|
|
101
|
+
preview: ["- private items = [];", "+ private items = new Map();"],
|
|
102
|
+
revealIndex: 3,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "diff:src/store.ts#1#hunk",
|
|
106
|
+
source: "diff",
|
|
107
|
+
preview: ["- return null;", "+ return fallback();", "+ log(fallback);"],
|
|
108
|
+
revealIndex: 9,
|
|
109
|
+
},
|
|
110
|
+
]}
|
|
111
|
+
selectedCurationId="diff:src/store.ts#0#1"
|
|
112
|
+
/>
|
|
113
|
+
</RailFrame>
|
|
114
|
+
),
|
|
115
|
+
expectedColors: [DARK.red],
|
|
116
|
+
size: { width: 40, height: 24 },
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** A plan cut and an annotation interleave in one line-ordered stack. */
|
|
120
|
+
export const InterleavedStack: Story = {
|
|
121
|
+
render: () => (
|
|
122
|
+
<RailFrame>
|
|
123
|
+
<ReviewRail
|
|
124
|
+
session={fixturePlanSession()}
|
|
125
|
+
authorNames={{}}
|
|
126
|
+
selectedId={FIXTURE_ANNOTATIONS[0]!.id}
|
|
127
|
+
resolvedIds={new Set(FIXTURE_ANNOTATIONS.map((annotation) => annotation.id))}
|
|
128
|
+
railTab="review"
|
|
129
|
+
pendingCount={2}
|
|
130
|
+
cardEdit={null}
|
|
131
|
+
submitConfirm={null}
|
|
132
|
+
{...callbacks}
|
|
133
|
+
curationItems={[
|
|
134
|
+
{
|
|
135
|
+
id: "plan:6-6",
|
|
136
|
+
source: "plan",
|
|
137
|
+
preview: ["- add recovery"],
|
|
138
|
+
revealIndex: 1,
|
|
139
|
+
},
|
|
140
|
+
]}
|
|
141
|
+
annotationPositions={new Map([[FIXTURE_ANNOTATIONS[0]!.id, 5]])}
|
|
142
|
+
/>
|
|
143
|
+
</RailFrame>
|
|
144
|
+
),
|
|
145
|
+
expectedColors: [DARK.accent],
|
|
146
|
+
size: { width: 40, height: 24 },
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const AgentTab: Story = {
|
|
150
|
+
render: () => (
|
|
151
|
+
<RailFrame>
|
|
152
|
+
<ReviewRail
|
|
153
|
+
session={fixturePlanSession()}
|
|
154
|
+
authorNames={{}}
|
|
155
|
+
resolvedIds={new Set()}
|
|
156
|
+
railTab="agent"
|
|
157
|
+
pendingCount={2}
|
|
158
|
+
cardEdit={null}
|
|
159
|
+
submitConfirm={null}
|
|
160
|
+
{...callbacks}
|
|
161
|
+
/>
|
|
162
|
+
</RailFrame>
|
|
163
|
+
),
|
|
164
|
+
size: { width: 40, height: 24 },
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const SubmitConfirmOpen: Story = {
|
|
168
|
+
render: () => (
|
|
169
|
+
<RailFrame>
|
|
170
|
+
<ReviewRail
|
|
171
|
+
session={fixturePlanSession()}
|
|
172
|
+
authorNames={{}}
|
|
173
|
+
resolvedIds={new Set()}
|
|
174
|
+
railTab="review"
|
|
175
|
+
pendingCount={2}
|
|
176
|
+
cardEdit={null}
|
|
177
|
+
submitConfirm={{
|
|
178
|
+
verdict: "request_changes",
|
|
179
|
+
summary: "",
|
|
180
|
+
annotationCount: 2,
|
|
181
|
+
blockingCount: 0,
|
|
182
|
+
onInput: () => {},
|
|
183
|
+
onSelectVerdict: () => {},
|
|
184
|
+
onSubmit: () => {},
|
|
185
|
+
onCancel: () => {},
|
|
186
|
+
}}
|
|
187
|
+
{...callbacks}
|
|
188
|
+
/>
|
|
189
|
+
</RailFrame>
|
|
190
|
+
),
|
|
191
|
+
expectedColors: [DARK.red],
|
|
192
|
+
size: { width: 40, height: 30 },
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const Resolved: Story = {
|
|
196
|
+
render: () => (
|
|
197
|
+
<RailFrame>
|
|
198
|
+
<ReviewRail
|
|
199
|
+
session={fixturePlanSession({
|
|
200
|
+
status: "resolved",
|
|
201
|
+
verdict: {
|
|
202
|
+
kind: "approve",
|
|
203
|
+
summary: "",
|
|
204
|
+
feedback: "",
|
|
205
|
+
resolvedAt: "2026-01-01T00:00:00Z",
|
|
206
|
+
},
|
|
207
|
+
})}
|
|
208
|
+
authorNames={{}}
|
|
209
|
+
resolvedIds={new Set()}
|
|
210
|
+
railTab="review"
|
|
211
|
+
pendingCount={0}
|
|
212
|
+
cardEdit={null}
|
|
213
|
+
submitConfirm={null}
|
|
214
|
+
{...callbacks}
|
|
215
|
+
/>
|
|
216
|
+
</RailFrame>
|
|
217
|
+
),
|
|
218
|
+
expectedColors: [DARK.green],
|
|
219
|
+
size: { width: 40, height: 24 },
|
|
220
|
+
};
|