@cueloop/client 0.1.0-alpha.4 → 0.1.0-alpha.40

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 (131) hide show
  1. package/package.json +32 -24
  2. package/src/App.autoclose.test.tsx +137 -0
  3. package/src/App.diff.test.tsx +158 -22
  4. package/src/App.export.test.tsx +44 -37
  5. package/src/App.plan-review.test.tsx +632 -0
  6. package/src/App.readonly.test.tsx +60 -36
  7. package/src/App.rendering.test.tsx +132 -0
  8. package/src/App.review-panel.test.tsx +136 -0
  9. package/src/App.submit-confirm.test.tsx +220 -0
  10. package/src/App.test.tsx +128 -69
  11. package/src/App.toast.test.tsx +75 -0
  12. package/src/App.tsx +0 -0
  13. package/src/App.walk.test.tsx +288 -0
  14. package/src/attribution.test.ts +31 -0
  15. package/src/attribution.ts +38 -0
  16. package/src/clipboard.test.ts +23 -0
  17. package/src/clipboard.ts +30 -0
  18. package/src/components/AnnotationCard.compose-rows.test.ts +49 -0
  19. package/src/components/AnnotationCard.stories.tsx +94 -0
  20. package/src/components/AnnotationCard.tsx +228 -0
  21. package/src/components/Breadcrumb.stories.tsx +19 -0
  22. package/src/components/Breadcrumb.tsx +44 -0
  23. package/src/components/CodeBlock.stories.tsx +42 -0
  24. package/src/components/CodeBlock.tsx +83 -0
  25. package/src/components/CompletionOverlay.stories.tsx +23 -0
  26. package/src/components/CompletionOverlay.tsx +59 -0
  27. package/src/components/ConfirmCard.stories.tsx +56 -0
  28. package/src/components/ConfirmCard.tsx +135 -0
  29. package/src/components/ConfirmDialog.stories.tsx +21 -0
  30. package/src/components/ConfirmDialog.tsx +64 -0
  31. package/src/components/DiffSheet.scroll.test.tsx +123 -0
  32. package/src/components/DiffSheet.stories.tsx +78 -0
  33. package/src/components/DiffSheet.syntax.test.tsx +62 -0
  34. package/src/components/DiffSheet.tsx +355 -0
  35. package/src/components/InboxList.stories.tsx +31 -0
  36. package/src/components/InboxList.tsx +62 -0
  37. package/src/components/KeybindsDialog.stories.tsx +31 -0
  38. package/src/components/KeybindsDialog.tsx +58 -0
  39. package/src/components/MarkerPopover.stories.tsx +58 -0
  40. package/src/components/MarkerPopover.tsx +102 -0
  41. package/src/components/MenuBar.stories.tsx +37 -0
  42. package/src/components/MenuBar.tsx +74 -0
  43. package/src/components/PlanSheet.stories.tsx +113 -0
  44. package/src/components/PlanSheet.tsx +389 -0
  45. package/src/components/PromptDialog.stories.tsx +36 -0
  46. package/src/components/PromptDialog.tsx +78 -0
  47. package/src/components/ReviewPanel.stories.tsx +86 -0
  48. package/src/components/ReviewPanel.tsx +101 -0
  49. package/src/components/ReviewRail.stories.tsx +220 -0
  50. package/src/components/ReviewRail.tsx +316 -0
  51. package/src/components/SettingsDialog.stories.tsx +111 -0
  52. package/src/components/SettingsDialog.tsx +141 -0
  53. package/src/components/SettingsRows.stories.tsx +18 -0
  54. package/src/components/SettingsRows.tsx +108 -0
  55. package/src/components/Toast.stories.tsx +12 -0
  56. package/src/components/Toast.tsx +44 -0
  57. package/src/components/WalkWizard.stories.tsx +100 -0
  58. package/src/components/WalkWizard.tsx +152 -0
  59. package/src/components/__snapshots__/stories.test.tsx.snap +1829 -0
  60. package/src/components/agent-launcher.stories.tsx +26 -0
  61. package/src/components/agent-launcher.test.tsx +21 -0
  62. package/src/components/agent-launcher.tsx +123 -0
  63. package/src/components/diff-sheet-layout.test.ts +127 -0
  64. package/src/components/diff-sheet-layout.ts +167 -0
  65. package/src/components/primitives/Button.stories.tsx +32 -0
  66. package/src/components/primitives/Button.tsx +48 -0
  67. package/src/components/primitives/Card.stories.tsx +25 -0
  68. package/src/components/primitives/Card.tsx +59 -0
  69. package/src/components/primitives/Dialog.stories.tsx +24 -0
  70. package/src/components/primitives/Dialog.tsx +64 -0
  71. package/src/components/primitives/StatusBar.stories.tsx +11 -0
  72. package/src/components/primitives/StatusBar.tsx +22 -0
  73. package/src/components/primitives/Tabs.stories.tsx +29 -0
  74. package/src/components/primitives/Tabs.tsx +98 -0
  75. package/src/components/primitives/Toolbar.stories.tsx +17 -0
  76. package/src/components/primitives/Toolbar.tsx +14 -0
  77. package/src/components/primitives/frame.test.ts +12 -0
  78. package/src/components/primitives/frame.ts +14 -0
  79. package/src/components/quick-actions-editor.stories.tsx +54 -0
  80. package/src/components/quick-actions-editor.tsx +136 -0
  81. package/src/components/stories-app.tsx +123 -0
  82. package/src/components/stories.test.tsx +62 -0
  83. package/src/components/story-fixtures.ts +98 -0
  84. package/src/components/story.ts +69 -0
  85. package/src/components/syntax-highlight.ts +92 -0
  86. package/src/components/theme-context.stories.tsx +46 -0
  87. package/src/components/theme-context.tsx +36 -0
  88. package/src/components/truncate-text.ts +6 -0
  89. package/src/config.test.ts +430 -31
  90. package/src/config.ts +304 -26
  91. package/src/diff-hunk-curate.test.ts +315 -0
  92. package/src/diff-hunk-curate.ts +233 -0
  93. package/src/diff-intraline.test.ts +164 -0
  94. package/src/diff-intraline.ts +132 -0
  95. package/src/diff-syntax.test.ts +96 -0
  96. package/src/diff-syntax.ts +154 -0
  97. package/src/editor.test.ts +143 -0
  98. package/src/editor.ts +123 -9
  99. package/src/herdr.test.ts +41 -0
  100. package/src/herdr.ts +27 -0
  101. package/src/index.ts +10 -0
  102. package/src/integrations.test.ts +41 -0
  103. package/src/integrations.ts +30 -0
  104. package/src/intent-dispatch.test.ts +492 -0
  105. package/src/intent-dispatch.ts +428 -0
  106. package/src/key-bindings.test.ts +152 -0
  107. package/src/key-bindings.ts +424 -0
  108. package/src/keymap.test.ts +622 -0
  109. package/src/keymap.ts +339 -0
  110. package/src/review-panel.test.ts +113 -0
  111. package/src/review-panel.ts +83 -0
  112. package/src/run.ts +9 -5
  113. package/src/serve.test.ts +24 -9
  114. package/src/serve.ts +7 -7
  115. package/src/session-controller.curate.test.ts +253 -0
  116. package/src/session-controller.share.test.ts +309 -0
  117. package/src/session-controller.ts +900 -0
  118. package/src/share.ts +113 -0
  119. package/src/test-support.test.ts +55 -0
  120. package/src/test-support.ts +187 -0
  121. package/src/theme-presets.ts +168 -0
  122. package/src/theme.ts +36 -15
  123. package/src/version.ts +5 -0
  124. package/src/view-diff.test.ts +19 -8
  125. package/src/view-diff.ts +34 -23
  126. package/src/view-plan.test.ts +294 -0
  127. package/src/view-plan.ts +408 -0
  128. package/src/walk.test.ts +121 -0
  129. package/src/walk.ts +70 -0
  130. package/src/view.test.ts +0 -157
  131. package/src/view.ts +0 -0
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { DARK } from "../theme";
3
+ import type { Story, StoryMeta } from "./story";
4
+ import { Toast } from "./Toast";
5
+
6
+ export const meta: StoryMeta = { title: "Toast" };
7
+
8
+ export const ShareLink: Story = {
9
+ render: () => <Toast title="share link copied" body="ssh review-4f2a@cueloop.dev" />,
10
+ expectedColors: [DARK.accent],
11
+ size: { width: 60, height: 12 },
12
+ };
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import { useTerminalDimensions } from "@opentui/react";
3
+ import type { Theme } from "../theme";
4
+ import { useComponentTheme } from "./theme-context";
5
+ import { Dialog } from "./primitives/Dialog";
6
+
7
+ export interface ToastProps {
8
+ title?: string;
9
+ body: string;
10
+ theme?: Theme;
11
+ }
12
+
13
+ export function Toast({ title, body, theme }: ToastProps): React.ReactNode {
14
+ const tokens = useComponentTheme(theme);
15
+ const { width: terminalWidth } = useTerminalDimensions();
16
+ const width = Math.min(terminalWidth - 6, Math.max(28, body.length + 4));
17
+ return (
18
+ <Dialog
19
+ isOpen
20
+ title={title ? ` ${title} ` : undefined}
21
+ width={width}
22
+ height={6}
23
+ background={tokens.elevated}
24
+ theme={theme}
25
+ >
26
+ <box
27
+ style={{
28
+ flexDirection: "column",
29
+ flexGrow: 1,
30
+ paddingLeft: 1,
31
+ paddingRight: 1,
32
+ paddingTop: 1,
33
+ }}
34
+ >
35
+ <text fg={tokens.text}>{body}</text>
36
+ <box style={{ flexGrow: 1 }} />
37
+ <box style={{ flexDirection: "row" }}>
38
+ <box style={{ flexGrow: 1 }} />
39
+ <text fg={tokens.textDim}>[esc]</text>
40
+ </box>
41
+ </box>
42
+ </Dialog>
43
+ );
44
+ }
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import { DARK } from "../theme";
3
+ import type { WalkFile } from "../walk";
4
+ import type { Story, StoryMeta } from "./story";
5
+ import { WalkWizard } from "./WalkWizard";
6
+
7
+ export const meta: StoryMeta = { title: "WalkWizard" };
8
+
9
+ const FILES: WalkFile[] = [
10
+ {
11
+ path: "packages/daemon/src/store.ts",
12
+ added: 12,
13
+ removed: 3,
14
+ preview: [
15
+ { sign: "+", text: "const viewed = new Set<string>();" },
16
+ { sign: "+", text: "record.viewedPaths = [...viewed];" },
17
+ { sign: "-", text: "// viewed tracking lives in the client only" },
18
+ ],
19
+ },
20
+ {
21
+ path: "packages/client/src/walk.ts",
22
+ added: 48,
23
+ removed: 0,
24
+ preview: [{ sign: "+", text: "export function firstUnviewedIndex(files, viewed) {" }],
25
+ },
26
+ {
27
+ path: "packages/schema/src/types.ts",
28
+ added: 3,
29
+ removed: 0,
30
+ preview: [{ sign: "+", text: "viewedPaths?: string[];" }],
31
+ },
32
+ ];
33
+
34
+ const noop = (): void => {};
35
+
36
+ export const FirstFile: Story = {
37
+ render: () => (
38
+ <WalkWizard
39
+ files={FILES}
40
+ index={0}
41
+ viewedPaths={new Set()}
42
+ terminalWidth={80}
43
+ onSubmitRequest={noop}
44
+ onBack={noop}
45
+ />
46
+ ),
47
+ // the main card wears the accent border; stats carry the +/- colors
48
+ expectedColors: [
49
+ DARK.accent,
50
+ DARK.green,
51
+ DARK.red,
52
+ DARK.insertedForeground,
53
+ DARK.deletedForeground,
54
+ ],
55
+ };
56
+
57
+ export const WithAgentNote: Story = {
58
+ render: () => (
59
+ <WalkWizard
60
+ files={FILES}
61
+ index={1}
62
+ viewedPaths={new Set(["packages/daemon/src/store.ts"])}
63
+ note="New module: pure helpers that pick the next unviewed file. No IO, unit tested."
64
+ terminalWidth={80}
65
+ onSubmitRequest={noop}
66
+ onBack={noop}
67
+ />
68
+ ),
69
+ // the agent-note block wears the plain gray border token, not the accent
70
+ expectedColors: [DARK.accent, DARK.border, DARK.textMuted],
71
+ };
72
+
73
+ export const EndCard: Story = {
74
+ render: () => (
75
+ <WalkWizard
76
+ files={FILES}
77
+ index={3}
78
+ viewedPaths={new Set(FILES.map((file) => file.path))}
79
+ terminalWidth={80}
80
+ onSubmitRequest={noop}
81
+ onBack={noop}
82
+ />
83
+ ),
84
+ // the end card wears the green border and the solid submit button
85
+ expectedColors: [DARK.green, DARK.accent, DARK.accentInk],
86
+ };
87
+
88
+ export const EndCardPartialPass: Story = {
89
+ render: () => (
90
+ <WalkWizard
91
+ files={FILES}
92
+ index={3}
93
+ viewedPaths={new Set(["packages/daemon/src/store.ts"])}
94
+ terminalWidth={80}
95
+ onSubmitRequest={noop}
96
+ onBack={noop}
97
+ />
98
+ ),
99
+ expectedColors: [DARK.green],
100
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * The guided walk's focused card wizard: one card per changed file
3
+ * floating over the dimmed diff, with a plain step count in the title - no
4
+ * progress bar. Below the main card a separate gray-bordered block carries
5
+ * the submitting agent's note for the file, when one exists. Past the last
6
+ * file the end card offers the submit action directly with honest counts.
7
+ * The keys live in the keymap's walk overlay; this component only renders.
8
+ */
9
+
10
+ import React from "react";
11
+ import type { Theme } from "../theme";
12
+ import { useComponentTheme } from "./theme-context";
13
+ import type { WalkFile } from "../walk";
14
+ import { viewedCount } from "../walk";
15
+ import { Card } from "./primitives/Card";
16
+ import { Button } from "./primitives/Button";
17
+ import { Toolbar } from "./primitives/Toolbar";
18
+
19
+ export interface WalkWizardProps {
20
+ files: WalkFile[];
21
+ /** The wizard step; index === files.length renders the end card. */
22
+ index: number;
23
+ viewedPaths: ReadonlySet<string>;
24
+ /** The agent's note for the current file; the gray block renders only when set. */
25
+ note?: string;
26
+ /** Terminal width bounds the card width. */
27
+ terminalWidth: number;
28
+ /** End-card submit: leaves the walk and opens the rail confirm. */
29
+ onSubmitRequest: () => void;
30
+ onBack: () => void;
31
+ theme?: Theme;
32
+ }
33
+
34
+ /** The card never crowds the terminal edge nor stretches past reading width. */
35
+ function wizardWidth(terminalWidth: number): number {
36
+ return Math.min(72, Math.max(24, terminalWidth - 8));
37
+ }
38
+
39
+ /** Note rows from a crude wrap estimate, capped so the block stays a block. */
40
+ function noteRows(note: string, innerWidth: number): number {
41
+ return Math.min(3, Math.max(1, Math.ceil(note.length / Math.max(1, innerWidth))));
42
+ }
43
+
44
+ export function WalkWizard({
45
+ files,
46
+ index,
47
+ viewedPaths,
48
+ note,
49
+ terminalWidth,
50
+ onSubmitRequest,
51
+ onBack,
52
+ theme,
53
+ }: WalkWizardProps): React.ReactNode {
54
+ const tokens = useComponentTheme(theme);
55
+ const width = wizardWidth(terminalWidth);
56
+ const viewed = viewedCount(files, viewedPaths);
57
+ const atEnd = index >= files.length;
58
+ const currentFile = files[index];
59
+
60
+ return (
61
+ <box
62
+ style={{
63
+ position: "absolute",
64
+ left: 0,
65
+ top: 0,
66
+ width: "100%",
67
+ height: "100%",
68
+ alignItems: "center",
69
+ justifyContent: "center",
70
+ flexDirection: "column",
71
+ }}
72
+ >
73
+ <box style={{ width, flexDirection: "column" }}>
74
+ {atEnd || !currentFile ? (
75
+ <Card
76
+ title=" walk complete "
77
+ contentRows={3}
78
+ borderColor={tokens.green}
79
+ backgroundColor={tokens.elevated}
80
+ theme={theme}
81
+ >
82
+ <text fg={tokens.text}>
83
+ {viewed === files.length
84
+ ? `every file viewed (${viewed}/${files.length})`
85
+ : `${viewed} of ${files.length} files viewed`}
86
+ </text>
87
+ <box style={{ height: 1 }} />
88
+ <Toolbar>
89
+ <Button variant="solid" marginRight={2} onPress={onSubmitRequest} theme={theme}>
90
+ {" Submit review "}
91
+ </Button>
92
+ <Button onPress={onBack} theme={theme}>
93
+ {" [ back "}
94
+ </Button>
95
+ </Toolbar>
96
+ </Card>
97
+ ) : (
98
+ <>
99
+ <Card
100
+ title={` file ${index + 1} of ${files.length} · ${viewed} viewed `}
101
+ contentRows={3 + Math.max(1, currentFile.preview.length)}
102
+ borderColor={tokens.accent}
103
+ backgroundColor={tokens.elevated}
104
+ theme={theme}
105
+ >
106
+ <text fg={tokens.text}>{currentFile.path}</text>
107
+ <text>
108
+ <span fg={tokens.green}>{`+${currentFile.added}`}</span>
109
+ <span fg={tokens.textDim}> </span>
110
+ <span fg={tokens.red}>{`-${currentFile.removed}`}</span>
111
+ <span fg={tokens.textDim}>
112
+ {viewedPaths.has(currentFile.path) ? " · viewed" : ""}
113
+ </span>
114
+ </text>
115
+ <box style={{ height: 1 }} />
116
+ {currentFile.preview.length === 0 ? (
117
+ <text fg={tokens.textDim}>(no changed lines)</text>
118
+ ) : (
119
+ currentFile.preview.map((previewLine, previewIndex) => (
120
+ <text
121
+ key={previewIndex}
122
+ style={{ wrapMode: "none" }}
123
+ fg={
124
+ previewLine.sign === "+"
125
+ ? tokens.insertedForeground
126
+ : tokens.deletedForeground
127
+ }
128
+ >
129
+ {previewLine.sign + previewLine.text}
130
+ </text>
131
+ ))
132
+ )}
133
+ </Card>
134
+ {note !== undefined ? (
135
+ <box style={{ marginTop: 1, flexDirection: "column" }}>
136
+ <Card
137
+ title=" agent note "
138
+ contentRows={noteRows(note, width - 4)}
139
+ borderColor={tokens.border}
140
+ backgroundColor={tokens.elevated}
141
+ theme={theme}
142
+ >
143
+ <text fg={tokens.textMuted}>{note}</text>
144
+ </Card>
145
+ </box>
146
+ ) : null}
147
+ </>
148
+ )}
149
+ </box>
150
+ </box>
151
+ );
152
+ }