@cueloop/client 0.1.0-alpha.32 → 0.1.0-alpha.34

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 (101) hide show
  1. package/package.json +27 -26
  2. package/src/App.autoclose.test.tsx +7 -4
  3. package/src/App.diff.test.tsx +131 -5
  4. package/src/App.export.test.tsx +23 -5
  5. package/src/App.plan-review.test.tsx +133 -32
  6. package/src/App.readonly.test.tsx +35 -13
  7. package/src/App.rendering.test.tsx +14 -4
  8. package/src/App.review-panel.test.tsx +27 -15
  9. package/src/App.submit-confirm.test.tsx +14 -13
  10. package/src/App.test.tsx +50 -13
  11. package/src/App.toast.test.tsx +75 -0
  12. package/src/App.tsx +505 -101
  13. package/src/App.walk.test.tsx +31 -11
  14. package/src/attribution.ts +8 -2
  15. package/src/clipboard.ts +5 -1
  16. package/src/components/AnnotationCard.stories.tsx +29 -10
  17. package/src/components/AnnotationCard.tsx +57 -37
  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 +23 -5
  24. package/src/components/ConfirmDialog.tsx +10 -2
  25. package/src/components/DiffSheet.scroll.test.tsx +123 -0
  26. package/src/components/DiffSheet.stories.tsx +35 -1
  27. package/src/components/DiffSheet.syntax.test.tsx +62 -0
  28. package/src/components/DiffSheet.tsx +213 -101
  29. package/src/components/InboxList.stories.tsx +16 -3
  30. package/src/components/InboxList.tsx +17 -10
  31. package/src/components/KeybindsDialog.stories.tsx +31 -0
  32. package/src/components/KeybindsDialog.tsx +58 -0
  33. package/src/components/MarkerPopover.stories.tsx +58 -0
  34. package/src/components/MarkerPopover.tsx +102 -0
  35. package/src/components/MenuBar.stories.tsx +37 -0
  36. package/src/components/MenuBar.tsx +74 -0
  37. package/src/components/PlanSheet.stories.tsx +35 -3
  38. package/src/components/PlanSheet.tsx +160 -100
  39. package/src/components/PromptDialog.tsx +19 -3
  40. package/src/components/ReviewPanel.stories.tsx +17 -19
  41. package/src/components/ReviewPanel.tsx +17 -32
  42. package/src/components/ReviewRail.stories.tsx +159 -47
  43. package/src/components/ReviewRail.tsx +206 -62
  44. package/src/components/SettingsDialog.stories.tsx +51 -3
  45. package/src/components/SettingsDialog.tsx +11 -5
  46. package/src/components/SettingsRows.stories.tsx +1 -1
  47. package/src/components/SettingsRows.tsx +19 -3
  48. package/src/components/Toast.stories.tsx +12 -0
  49. package/src/components/Toast.tsx +44 -0
  50. package/src/components/WalkWizard.stories.tsx +13 -2
  51. package/src/components/WalkWizard.tsx +9 -3
  52. package/src/components/__snapshots__/stories.test.tsx.snap +853 -623
  53. package/src/components/diff-sheet-layout.test.ts +127 -0
  54. package/src/components/diff-sheet-layout.ts +167 -0
  55. package/src/components/primitives/Button.stories.tsx +2 -2
  56. package/src/components/primitives/Button.tsx +5 -1
  57. package/src/components/primitives/Dialog.tsx +12 -1
  58. package/src/components/primitives/Tabs.tsx +40 -39
  59. package/src/components/stories-app.tsx +34 -6
  60. package/src/components/story-fixtures.ts +11 -3
  61. package/src/components/story.ts +3 -1
  62. package/src/components/syntax-highlight.ts +47 -12
  63. package/src/components/theme-context.stories.tsx +6 -1
  64. package/src/components/theme-context.tsx +7 -1
  65. package/src/config.test.ts +178 -2
  66. package/src/config.ts +121 -23
  67. package/src/diff-hunk-curate.test.ts +315 -0
  68. package/src/diff-hunk-curate.ts +233 -0
  69. package/src/diff-intraline.test.ts +164 -0
  70. package/src/diff-intraline.ts +132 -0
  71. package/src/diff-syntax.test.ts +96 -0
  72. package/src/diff-syntax.ts +154 -0
  73. package/src/editor.test.ts +13 -4
  74. package/src/editor.ts +31 -5
  75. package/src/herdr.ts +8 -2
  76. package/src/index.ts +9 -1
  77. package/src/integrations.ts +3 -1
  78. package/src/intent-dispatch.test.ts +193 -9
  79. package/src/intent-dispatch.ts +151 -18
  80. package/src/key-bindings.test.ts +19 -9
  81. package/src/key-bindings.ts +77 -16
  82. package/src/keymap.test.ts +171 -26
  83. package/src/keymap.ts +83 -16
  84. package/src/review-panel.test.ts +3 -1
  85. package/src/review-panel.ts +9 -2
  86. package/src/serve.test.ts +13 -5
  87. package/src/session-controller.curate.test.ts +253 -0
  88. package/src/session-controller.share.test.ts +77 -16
  89. package/src/session-controller.ts +319 -26
  90. package/src/share.ts +41 -8
  91. package/src/test-support.ts +97 -23
  92. package/src/theme-presets.ts +168 -0
  93. package/src/theme.ts +1 -3
  94. package/src/version.ts +5 -0
  95. package/src/view-diff.ts +15 -4
  96. package/src/view-plan.test.ts +40 -6
  97. package/src/view-plan.ts +71 -28
  98. package/src/walk.test.ts +7 -2
  99. package/src/walk.ts +7 -2
  100. package/src/components/ComposeBar.stories.tsx +0 -18
  101. package/src/components/ComposeBar.tsx +0 -54
@@ -8,13 +8,16 @@ import type { SessionClient } from "@cueloop/daemon/client";
8
8
  const publishShare = mock(async () => ({ line: "ssh p_abc123xy@cueloop.dev", copied: true }));
9
9
  let remote: ReviewSession;
10
10
  const pullShare = mock(async () => remote);
11
- const pushShare = mock(async (_shareId: string, _annotations: Array<Omit<Annotation, "createdAt">>) => {});
11
+ const pushShare = mock(
12
+ async (_shareId: string, _annotations: Array<Omit<Annotation, "createdAt">>) => {},
13
+ );
12
14
  mock.module("./share", () => ({
13
15
  publishShare,
14
16
  pullShare,
15
17
  pushShare,
16
18
  shareIdFromLine: (line: string) => line.match(/^ssh (\S+)@/)?.[1],
17
- collaboratorAnnotations: (session: ReviewSession) => session.annotations.filter((annotation) => annotation.author),
19
+ collaboratorAnnotations: (session: ReviewSession) =>
20
+ session.annotations.filter((annotation) => annotation.author),
18
21
  }));
19
22
 
20
23
  const { createReviewController, SHARE_POLL_MS } = await import("./session-controller");
@@ -35,7 +38,14 @@ function sessionFixture(overrides: Partial<ReviewSession> = {}): ReviewSession {
35
38
  }
36
39
 
37
40
  function annotation(id: string, author: string): Annotation {
38
- return { id, kind: "comment", anchor: { quote: "Plan", prefix: "", suffix: "" }, body: "note", author, createdAt: "2026-01-01T00:00:00.000Z" };
41
+ return {
42
+ id,
43
+ kind: "comment",
44
+ anchor: { quote: "Plan", prefix: "", suffix: "" },
45
+ body: "note",
46
+ author,
47
+ createdAt: "2026-01-01T00:00:00.000Z",
48
+ };
39
49
  }
40
50
 
41
51
  function fakeClient(session: ReviewSession): SessionClient {
@@ -45,10 +55,13 @@ function fakeClient(session: ReviewSession): SessionClient {
45
55
  sessionGet: async () => session,
46
56
  sessionList: async () => [session],
47
57
  sessionAnnotate: mock(async () => session),
48
- sessionSetShareId: mock(async (_id: string, shareId: string) => ((session.shareId = shareId), session)),
58
+ sessionSetShareId: mock(
59
+ async (_id: string, shareId: string) => ((session.shareId = shareId), session),
60
+ ),
49
61
  sessionMergeShared: mock(async (_id: string, incoming: { annotations: Annotation[] }) => {
50
62
  const known = new Set(session.annotations.map((existing) => existing.id));
51
- for (const note of incoming.annotations) if (!known.has(note.id)) session.annotations.push(note);
63
+ for (const note of incoming.annotations)
64
+ if (!known.has(note.id)) session.annotations.push(note);
52
65
  return session;
53
66
  }),
54
67
  close: () => {},
@@ -62,7 +75,11 @@ async function connectedController(
62
75
  clock?: ManualClock,
63
76
  ): Promise<{ controller: ReturnType<typeof createReviewController>; client: SessionClient }> {
64
77
  const client = fakeClient(session);
65
- const controller = createReviewController({ sessionId: session.id, openClient: async () => client, clock });
78
+ const controller = createReviewController({
79
+ sessionId: session.id,
80
+ openClient: async () => client,
81
+ clock,
82
+ });
66
83
  controller.connect();
67
84
  await tick();
68
85
  return { controller, client };
@@ -80,6 +97,22 @@ describe("share", () => {
80
97
  // Assert
81
98
  expect(client.sessionSetShareId).toHaveBeenCalledWith("ses_1", "p_abc123xy");
82
99
  });
100
+
101
+ test("surfaces the ssh line as a toast, not an inline status", async () => {
102
+ // Arrange
103
+ const { controller } = await connectedController(sessionFixture());
104
+
105
+ // Act
106
+ controller.share();
107
+ await tick();
108
+
109
+ // Assert
110
+ expect(controller.getSnapshot().toast).toEqual({
111
+ body: "ssh p_abc123xy@cueloop.dev",
112
+ title: "share link copied",
113
+ });
114
+ expect(controller.getSnapshot().status).not.toContain("ssh p_abc123xy@cueloop.dev");
115
+ });
83
116
  });
84
117
 
85
118
  describe("pullShared", () => {
@@ -89,7 +122,9 @@ describe("pullShared", () => {
89
122
  annotations: [annotation("a1", "SHA256:mate")],
90
123
  participants: [{ id: "SHA256:mate", provider: "ssh", name: "Sam" }],
91
124
  });
92
- const { controller, client } = await connectedController(sessionFixture({ shareId: "p_abc123xy" }));
125
+ const { controller, client } = await connectedController(
126
+ sessionFixture({ shareId: "p_abc123xy" }),
127
+ );
93
128
 
94
129
  // Act
95
130
  controller.pullShared();
@@ -122,7 +157,9 @@ describe("mirror on annotate", () => {
122
157
  test("pushes an edited note up when the plan is shared", async () => {
123
158
  // Arrange
124
159
  pushShare.mockClear();
125
- const { controller } = await connectedController(sessionFixture({ shareId: "p_abc123xy", annotations: [annotation("a1", "SHA256:me")] }));
160
+ const { controller } = await connectedController(
161
+ sessionFixture({ shareId: "p_abc123xy", annotations: [annotation("a1", "SHA256:me")] }),
162
+ );
126
163
 
127
164
  // Act
128
165
  controller.updateAnnotation("a1", "revised body");
@@ -131,13 +168,22 @@ describe("mirror on annotate", () => {
131
168
  // Assert
132
169
  expect(pushShare).toHaveBeenCalledTimes(1);
133
170
  expect(pushShare.mock.calls[0]?.[0]).toBe("p_abc123xy");
134
- expect(pushShare.mock.calls[0]?.[1]).toEqual([{ id: "a1", kind: "comment", anchor: { quote: "Plan", prefix: "", suffix: "" }, body: "revised body" }]);
171
+ expect(pushShare.mock.calls[0]?.[1]).toEqual([
172
+ {
173
+ id: "a1",
174
+ kind: "comment",
175
+ anchor: { quote: "Plan", prefix: "", suffix: "" },
176
+ body: "revised body",
177
+ },
178
+ ]);
135
179
  });
136
180
 
137
181
  test("does not push when the plan was never shared", async () => {
138
182
  // Arrange
139
183
  pushShare.mockClear();
140
- const { controller } = await connectedController(sessionFixture({ annotations: [annotation("a1", "SHA256:me")] }));
184
+ const { controller } = await connectedController(
185
+ sessionFixture({ annotations: [annotation("a1", "SHA256:me")] }),
186
+ );
141
187
 
142
188
  // Act
143
189
  controller.updateAnnotation("a1", "revised body");
@@ -150,7 +196,9 @@ describe("mirror on annotate", () => {
150
196
  test("does not push when the local write is rejected", async () => {
151
197
  // Arrange - the daemon write fails (e.g. a resolved session)
152
198
  pushShare.mockClear();
153
- const { controller, client } = await connectedController(sessionFixture({ shareId: "p_abc123xy", annotations: [annotation("a1", "SHA256:me")] }));
199
+ const { controller, client } = await connectedController(
200
+ sessionFixture({ shareId: "p_abc123xy", annotations: [annotation("a1", "SHA256:me")] }),
201
+ );
154
202
  (client.sessionAnnotate as ReturnType<typeof mock>).mockImplementationOnce(async () => {
155
203
  throw new Error("session is resolved");
156
204
  });
@@ -170,7 +218,10 @@ describe("startSharePoll", () => {
170
218
  pullShare.mockClear();
171
219
  remote = sessionFixture({ annotations: [] });
172
220
  const clock = new ManualClock();
173
- const { controller } = await connectedController(sessionFixture({ shareId: "p_abc123xy" }), clock);
221
+ const { controller } = await connectedController(
222
+ sessionFixture({ shareId: "p_abc123xy" }),
223
+ clock,
224
+ );
174
225
 
175
226
  // Act - immediate pull
176
227
  const stop = controller.startSharePoll();
@@ -200,9 +251,14 @@ describe("startSharePoll", () => {
200
251
  pullShare.mockClear();
201
252
  remote = sessionFixture({ annotations: [] });
202
253
  let release: () => void = () => {};
203
- pullShare.mockImplementationOnce(() => new Promise<ReviewSession>((resolve) => (release = () => resolve(remote))));
254
+ pullShare.mockImplementationOnce(
255
+ () => new Promise<ReviewSession>((resolve) => (release = () => resolve(remote))),
256
+ );
204
257
  const clock = new ManualClock();
205
- const { controller } = await connectedController(sessionFixture({ shareId: "p_abc123xy" }), clock);
258
+ const { controller } = await connectedController(
259
+ sessionFixture({ shareId: "p_abc123xy" }),
260
+ clock,
261
+ );
206
262
 
207
263
  // Act - start (pull is in flight), leave, then let the pull settle
208
264
  const stop = controller.startSharePoll();
@@ -223,9 +279,14 @@ describe("startSharePoll", () => {
223
279
  pullShare.mockClear();
224
280
  remote = sessionFixture({ annotations: [] });
225
281
  let release: () => void = () => {};
226
- pullShare.mockImplementationOnce(() => new Promise<ReviewSession>((resolve) => (release = () => resolve(remote))));
282
+ pullShare.mockImplementationOnce(
283
+ () => new Promise<ReviewSession>((resolve) => (release = () => resolve(remote))),
284
+ );
227
285
  const clock = new ManualClock();
228
- const { controller } = await connectedController(sessionFixture({ shareId: "p_abc123xy" }), clock);
286
+ const { controller } = await connectedController(
287
+ sessionFixture({ shareId: "p_abc123xy" }),
288
+ clock,
289
+ );
229
290
 
230
291
  // Act - run A starts (pull hangs), then run B restarts while A is in flight
231
292
  controller.startSharePoll();