@cueloop/client 0.1.0-alpha.11 → 0.1.0-alpha.12

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 (66) hide show
  1. package/package.json +7 -7
  2. package/src/App.autoclose.test.tsx +24 -35
  3. package/src/App.diff.test.tsx +5 -18
  4. package/src/App.export.test.tsx +8 -21
  5. package/src/App.plan-review.test.tsx +21 -52
  6. package/src/App.readonly.test.tsx +3 -18
  7. package/src/App.rendering.test.tsx +5 -13
  8. package/src/App.submit-confirm.test.tsx +7 -25
  9. package/src/App.test.tsx +13 -52
  10. package/src/App.tsx +197 -910
  11. package/src/components/AnnotationCard.stories.tsx +75 -0
  12. package/src/components/AnnotationCard.tsx +136 -0
  13. package/src/components/Breadcrumb.stories.tsx +19 -0
  14. package/src/components/Breadcrumb.tsx +38 -0
  15. package/src/components/CodeBlock.stories.tsx +25 -0
  16. package/src/components/CodeBlock.tsx +68 -0
  17. package/src/components/CompletionOverlay.stories.tsx +23 -0
  18. package/src/components/CompletionOverlay.tsx +50 -0
  19. package/src/components/ComposeBar.stories.tsx +18 -0
  20. package/src/components/ComposeBar.tsx +54 -0
  21. package/src/components/ConfirmCard.stories.tsx +22 -0
  22. package/src/components/ConfirmCard.tsx +130 -0
  23. package/src/components/DiffSheet.stories.tsx +44 -0
  24. package/src/components/DiffSheet.tsx +243 -0
  25. package/src/components/InboxList.stories.tsx +18 -0
  26. package/src/components/InboxList.tsx +42 -0
  27. package/src/components/PlanSheet.stories.tsx +81 -0
  28. package/src/components/PlanSheet.tsx +309 -0
  29. package/src/components/ReviewRail.stories.tsx +103 -0
  30. package/src/components/ReviewRail.tsx +158 -0
  31. package/src/components/SettingsDialog.stories.tsx +63 -0
  32. package/src/components/SettingsDialog.tsx +132 -0
  33. package/src/components/SettingsRows.stories.tsx +18 -0
  34. package/src/components/SettingsRows.tsx +92 -0
  35. package/src/components/__snapshots__/stories.test.tsx.snap +1189 -0
  36. package/src/components/format.ts +6 -0
  37. package/src/components/primitives/Button.stories.tsx +32 -0
  38. package/src/components/primitives/Button.tsx +44 -0
  39. package/src/components/primitives/Card.stories.tsx +25 -0
  40. package/src/components/primitives/Card.tsx +58 -0
  41. package/src/components/primitives/Dialog.stories.tsx +24 -0
  42. package/src/components/primitives/Dialog.tsx +52 -0
  43. package/src/components/primitives/StatusBar.stories.tsx +11 -0
  44. package/src/components/primitives/StatusBar.tsx +22 -0
  45. package/src/components/primitives/Tabs.stories.tsx +29 -0
  46. package/src/components/primitives/Tabs.tsx +97 -0
  47. package/src/components/primitives/Toolbar.stories.tsx +17 -0
  48. package/src/components/primitives/Toolbar.tsx +14 -0
  49. package/src/components/stories-app.tsx +94 -0
  50. package/src/components/stories.test.tsx +51 -0
  51. package/src/components/story-fixtures.ts +90 -0
  52. package/src/components/story.ts +66 -0
  53. package/src/components/syntax-highlight.ts +57 -0
  54. package/src/components/theme-context.stories.tsx +41 -0
  55. package/src/components/theme-context.tsx +30 -0
  56. package/src/key-bindings.test.ts +103 -0
  57. package/src/key-bindings.ts +332 -0
  58. package/src/keymap.ts +8 -2
  59. package/src/run.ts +3 -1
  60. package/src/session-controller.ts +28 -7
  61. package/src/test-support.ts +87 -0
  62. package/src/theme.ts +3 -0
  63. package/src/view.test.ts +35 -13
  64. package/src/view.ts +41 -64
  65. package/src/syntax.test.ts +0 -41
  66. package/src/syntax.ts +0 -88
package/src/App.test.tsx CHANGED
@@ -13,6 +13,7 @@ import { testRender } from "@opentui/react/test-utils";
13
13
  import { DaemonServer } from "@cueloop/daemon";
14
14
  import type { ReviewSession } from "@cueloop/schema";
15
15
  import { App } from "./App";
16
+ import { press, typeText as type, waitForState, waitForText } from "./test-support";
16
17
 
17
18
  const PLAN = `# Migration Plan
18
19
 
@@ -49,35 +50,11 @@ async function renderApp(sessionId?: string) {
49
50
  width: 120,
50
51
  height: 32,
51
52
  });
52
- // let the async daemon connect + first fetch land
53
- for (let i = 0; i < 40 && !setup.captureCharFrame().includes("cueloop"); i++) {
54
- await Bun.sleep(25);
55
- await setup.renderOnce();
56
- }
57
- await setup.renderOnce();
53
+ // the async daemon connect + first fetch land within the frame wait
54
+ await waitForText(setup, "cueloop");
58
55
  return setup;
59
56
  }
60
57
 
61
- type Setup = Awaited<ReturnType<typeof renderApp>>;
62
-
63
- /** Drive one key press: letters type as text; named keys use KeyCodes ids. */
64
- async function press(setup: Setup, k: string): Promise<void> {
65
- if (k === "enter") setup.mockInput.pressKey("RETURN");
66
- else if (k === "escape") setup.mockInput.pressKey("ESCAPE");
67
- else if (k === "backspace") setup.mockInput.pressKey("BACKSPACE");
68
- else if (k === "left") setup.mockInput.pressKey("ARROW_LEFT");
69
- else if (k === "right") setup.mockInput.pressKey("ARROW_RIGHT");
70
- else await type(setup, k);
71
- await Bun.sleep(15);
72
- await setup.renderOnce();
73
- }
74
-
75
- async function type(setup: Setup, text: string): Promise<void> {
76
- await setup.mockInput.typeText(text);
77
- await Bun.sleep(15);
78
- await setup.renderOnce();
79
- }
80
-
81
58
  describe("plan rendering", () => {
82
59
  test("renders the plan with headings, list markers, and the rail", async () => {
83
60
  const setup = await renderApp();
@@ -111,12 +88,10 @@ describe("keyboard grammar", () => {
111
88
  expect(setup.captureCharFrame()).toContain('comment on "The daemon');
112
89
  await type(setup, "Define atomically.");
113
90
  await press(setup, "enter");
114
- await Bun.sleep(80);
115
- await setup.renderOnce();
91
+ await waitForText(setup, "COMMENT");
116
92
  const stored = server.core.sessionGet(session.id);
117
93
  expect(stored.annotations.length).toBe(1);
118
94
  expect(stored.annotations[0]!.body).toBe("Define atomically.");
119
- expect(setup.captureCharFrame()).toContain("COMMENT");
120
95
  });
121
96
 
122
97
  test("span mode: v + l selects words, c anchors the exact span", async () => {
@@ -128,7 +103,7 @@ describe("keyboard grammar", () => {
128
103
  await press(setup, "c");
129
104
  await type(setup, "Which daemon?");
130
105
  await press(setup, "enter");
131
- await Bun.sleep(80);
106
+ await waitForState(setup, () => server.core.sessionGet(session.id).annotations.length === 1);
132
107
  const stored = server.core.sessionGet(session.id);
133
108
  expect(stored.annotations[0]!.anchor.quote).toBe("The daemon");
134
109
  });
@@ -138,14 +113,10 @@ describe("keyboard grammar", () => {
138
113
  // move to "- move the store" (h1, h2, p, h2 = 4 steps in)
139
114
  for (let i = 0; i < 4; i++) await press(setup, "j");
140
115
  await press(setup, "x");
141
- await Bun.sleep(80);
142
- await setup.renderOnce();
116
+ await waitForText(setup, "[cut]");
143
117
  expect(server.core.sessionGet(session.id).workingCopy).not.toContain("move the store");
144
- expect(setup.captureCharFrame()).toContain("[cut]");
145
118
  await press(setup, "x");
146
- await Bun.sleep(80);
147
- await setup.renderOnce();
148
- expect(server.core.sessionGet(session.id).workingCopy).toBeUndefined();
119
+ await waitForState(setup, () => server.core.sessionGet(session.id).workingCopy === undefined);
149
120
  });
150
121
 
151
122
  test("e runs $EDITOR on the working copy and tracks the diff", async () => {
@@ -156,10 +127,8 @@ describe("keyboard grammar", () => {
156
127
  try {
157
128
  const setup = await renderApp();
158
129
  await press(setup, "e");
159
- await Bun.sleep(150);
160
- await setup.renderOnce();
130
+ await waitForText(setup, "[edited]");
161
131
  expect(server.core.sessionGet(session.id).workingCopy).toContain("very atomically");
162
- expect(setup.captureCharFrame()).toContain("[edited]");
163
132
  } finally {
164
133
  delete process.env.CUELOOP_EDITOR;
165
134
  }
@@ -174,14 +143,12 @@ describe("submit", () => {
174
143
  await press(setup, "c");
175
144
  await type(setup, "Needs a phase list.");
176
145
  await press(setup, "enter");
177
- await Bun.sleep(80);
146
+ await waitForText(setup, "Review (1)");
178
147
  await press(setup, "enter"); // open submit
179
- await setup.renderOnce();
180
148
  expect(setup.captureCharFrame()).toContain("[Changes]");
181
149
  await type(setup, "Expand the steps.");
182
150
  await press(setup, "enter");
183
- await Bun.sleep(120);
184
- await setup.renderOnce();
151
+ await waitForText(setup, "✎ feedback sent");
185
152
  const stored = server.core.sessionGet(session.id);
186
153
  expect(stored.status).toBe("resolved");
187
154
  expect(stored.verdict!.kind).toBe("request_changes");
@@ -193,10 +160,9 @@ describe("submit", () => {
193
160
  test("approve via ←/→ verdict cycling", async () => {
194
161
  const setup = await renderApp();
195
162
  await press(setup, "enter");
196
- await setup.renderOnce();
197
163
  expect(setup.captureCharFrame()).toContain("[Approve]"); // no pending items → approve default
198
164
  await press(setup, "enter");
199
- await Bun.sleep(120);
165
+ await waitForState(setup, () => server.core.sessionGet(session.id).verdict !== undefined);
200
166
  expect(server.core.sessionGet(session.id).verdict!.kind).toBe("approve");
201
167
  });
202
168
  });
@@ -204,15 +170,10 @@ describe("submit", () => {
204
170
  describe("inbox", () => {
205
171
  test("inbox mode renders and opens a session", async () => {
206
172
  const setup = await testRender(<App home={home} />, { width: 120, height: 32 });
207
- for (let i = 0; i < 40 && !setup.captureCharFrame().includes("inbox"); i++) {
208
- await Bun.sleep(25);
209
- await setup.renderOnce();
210
- }
173
+ await waitForText(setup, "inbox");
211
174
  expect(setup.captureCharFrame()).toContain("inbox (1 pending)");
212
175
  expect(setup.captureCharFrame()).toContain("Migration Plan");
213
176
  await press(setup, "enter");
214
- await Bun.sleep(80);
215
- await setup.renderOnce();
216
- expect(setup.captureCharFrame()).toContain("Submit review (0)");
177
+ await waitForText(setup, "Submit review (0)");
217
178
  });
218
179
  });