@cueloop/client 0.1.0-alpha.65 → 0.1.0-alpha.66
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/package.json +5 -5
- package/src/App.diff.test.tsx +8 -73
- package/src/App.plan-review.test.tsx +4 -185
- package/src/App.prototype.test.tsx +1 -8
- package/src/App.readonly.test.tsx +6 -28
- package/src/App.submit-confirm.test.tsx +19 -72
- package/src/App.test.tsx +70 -24
- package/src/App.tsx +177 -254
- package/src/App.walk.test.tsx +1 -1
- package/src/app-screens.tsx +40 -51
- package/src/app-view-model.ts +2 -4
- package/src/components/AnnotationCard.stories.tsx +1 -1
- package/src/components/AppShell.stories.tsx +84 -0
- package/src/components/AppShell.tsx +127 -0
- package/src/components/Breadcrumb.stories.tsx +1 -1
- package/src/components/ChangesColumn.stories.tsx +39 -0
- package/src/components/ChangesColumn.tsx +147 -0
- package/src/components/CodeBlock.stories.tsx +1 -1
- package/src/components/CompletionOverlay.stories.tsx +1 -1
- package/src/components/ConfirmCard.stories.tsx +1 -1
- package/src/components/ConfirmCard.tsx +3 -4
- package/src/components/ConfirmDialog.stories.tsx +1 -1
- package/src/components/DiffSheet.stories.tsx +1 -1
- package/src/components/InboxList.stories.tsx +23 -8
- package/src/components/InboxList.tsx +45 -19
- package/src/components/KeybindsDialog.stories.tsx +1 -1
- package/src/components/MarkerPopover.stories.tsx +1 -1
- package/src/components/MenuBar.stories.tsx +3 -12
- package/src/components/MenuBar.tsx +25 -56
- package/src/components/PromptDialog.stories.tsx +1 -1
- package/src/components/PrototypeSheet.stories.tsx +1 -1
- package/src/components/ReviewPanel.stories.tsx +1 -1
- package/src/components/ReviewRail.stories.tsx +1 -1
- package/src/components/SettingsDialog.stories.tsx +7 -1
- package/src/components/SettingsDialog.tsx +112 -66
- package/src/components/SettingsRows.stories.tsx +1 -1
- package/src/components/ShellHeader.stories.tsx +50 -0
- package/src/components/ShellHeader.tsx +137 -0
- package/src/components/ThreadFooter.stories.tsx +30 -0
- package/src/components/ThreadFooter.tsx +49 -0
- package/src/components/ThreadView.slash.test.tsx +113 -0
- package/src/components/ThreadView.stories.tsx +1 -1
- package/src/components/ThreadView.tsx +99 -11
- package/src/components/ThreadsSidebar.stories.tsx +36 -0
- package/src/components/ThreadsSidebar.tsx +57 -0
- package/src/components/Toast.stories.tsx +1 -1
- package/src/components/TreePane.stories.tsx +1 -1
- package/src/components/WalkWizard.stories.tsx +1 -1
- package/src/components/__snapshots__/stories.test.tsx.snap +348 -138
- package/src/components/agent-launcher.stories.tsx +1 -1
- package/src/components/file-tree.test.ts +45 -0
- package/src/components/file-tree.ts +57 -0
- package/src/components/pages/Workspace.stories.tsx +181 -0
- package/src/components/primitives/Button.stories.tsx +1 -1
- package/src/components/primitives/Card.stories.tsx +1 -1
- package/src/components/primitives/Dialog.stories.tsx +1 -1
- package/src/components/primitives/IconButton.stories.tsx +36 -0
- package/src/components/primitives/IconButton.tsx +43 -0
- package/src/components/primitives/StatusBar.stories.tsx +1 -1
- package/src/components/primitives/Tabs.stories.tsx +1 -1
- package/src/components/primitives/Toolbar.stories.tsx +1 -1
- package/src/components/primitives/Tree.stories.tsx +124 -0
- package/src/components/primitives/Tree.tsx +87 -0
- package/src/components/primitives/icons.ts +38 -0
- package/src/components/primitives/tree-model.test.ts +99 -0
- package/src/components/primitives/tree-model.ts +137 -0
- package/src/components/quick-actions-editor.stories.tsx +1 -1
- package/src/components/session-tree.test.ts +114 -0
- package/src/components/session-tree.ts +94 -0
- package/src/components/stories-app.tsx +142 -82
- package/src/components/story-tree.test.ts +100 -0
- package/src/components/story.ts +55 -0
- package/src/components/theme-context.stories.tsx +1 -1
- package/src/session-controller.ts +9 -4
- package/src/theme.ts +4 -4
- package/src/use-settings-dialog.tsx +9 -0
- package/src/App.review-panel.test.tsx +0 -140
- package/src/App.tree.test.tsx +0 -224
package/src/App.test.tsx
CHANGED
|
@@ -71,7 +71,7 @@ async function renderApp(sessionId?: string) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
describe("plan rendering", () => {
|
|
74
|
-
test("renders the plan with headings, list markers, and the
|
|
74
|
+
test("renders the plan with headings, list markers, and the footer", async () => {
|
|
75
75
|
// Arrange
|
|
76
76
|
const setup = await renderApp();
|
|
77
77
|
|
|
@@ -82,8 +82,22 @@ describe("plan rendering", () => {
|
|
|
82
82
|
expect(frame).toContain("Context");
|
|
83
83
|
expect(frame).toContain("persists sessions to disk atomically");
|
|
84
84
|
expect(frame).toContain("· move the store");
|
|
85
|
-
expect(frame).toContain("
|
|
86
|
-
|
|
85
|
+
expect(frame).toContain("send message");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("a direct open still populates the Threads sidebar with other pending reviews", async () => {
|
|
89
|
+
// Arrange - a second pending review exists alongside the one opened directly
|
|
90
|
+
server.core.sessionCreate({
|
|
91
|
+
workspace: { repoRoot: "/repo", branch: "main" },
|
|
92
|
+
artifact: { type: "plan", content: "# Other Plan\n", meta: { title: "Other Plan" } },
|
|
93
|
+
});
|
|
94
|
+
const setup = await renderApp();
|
|
95
|
+
|
|
96
|
+
// Act - open the collapsed Threads sidebar (the mirrored panel toggle by the gear)
|
|
97
|
+
await setup.mockMouse.click(3, 1);
|
|
98
|
+
|
|
99
|
+
// Assert - the sidebar lists the other pending review, so you can jump to it
|
|
100
|
+
await waitForText(setup, "Other Plan");
|
|
87
101
|
});
|
|
88
102
|
});
|
|
89
103
|
|
|
@@ -113,7 +127,7 @@ describe("thread view grammar", () => {
|
|
|
113
127
|
await pressKey(setup, "RETURN", { meta: true });
|
|
114
128
|
|
|
115
129
|
// Assert
|
|
116
|
-
await
|
|
130
|
+
await waitForState(setup, () => server.core.sessionGet(session.id).annotations.length === 1);
|
|
117
131
|
const stored = server.core.sessionGet(session.id);
|
|
118
132
|
|
|
119
133
|
expect(stored.annotations.length).toBe(1);
|
|
@@ -144,9 +158,11 @@ describe("thread view grammar", () => {
|
|
|
144
158
|
// Act
|
|
145
159
|
await pressKey(setup, "x", { meta: true });
|
|
146
160
|
|
|
147
|
-
// Assert - the
|
|
148
|
-
await
|
|
149
|
-
|
|
161
|
+
// Assert - the cut lands in the working copy
|
|
162
|
+
await waitForState(
|
|
163
|
+
setup,
|
|
164
|
+
() => !(server.core.sessionGet(session.id).workingCopy ?? "").includes("move the store"),
|
|
165
|
+
);
|
|
150
166
|
|
|
151
167
|
// Act
|
|
152
168
|
await pressKey(setup, "x", { meta: true });
|
|
@@ -228,25 +244,24 @@ describe("submit", () => {
|
|
|
228
244
|
});
|
|
229
245
|
|
|
230
246
|
describe("inbox", () => {
|
|
231
|
-
test("inbox mode wears the review
|
|
247
|
+
test("inbox mode wears the review header and opens a session", async () => {
|
|
232
248
|
// Arrange
|
|
233
249
|
const setup = await testRender(<App home={home} />, { width: 120, height: 32 });
|
|
234
250
|
|
|
235
251
|
await waitForText(setup, "cueloop");
|
|
236
252
|
|
|
237
|
-
// Assert - the same header
|
|
253
|
+
// Assert - the same header as review, plus the session row
|
|
238
254
|
const frame = setup.captureCharFrame();
|
|
239
255
|
|
|
240
256
|
expect(frame).toContain("cueloop · resume");
|
|
241
257
|
expect(frame).toContain("Migration Plan");
|
|
242
|
-
expect(frame).toContain("menu"); // the shared MenuBar sits at the bottom
|
|
243
258
|
expect(frame).not.toContain("inbox ("); // the old inline header is gone
|
|
244
259
|
|
|
245
260
|
// Act
|
|
246
261
|
await press(setup, "enter");
|
|
247
262
|
|
|
248
263
|
// Assert
|
|
249
|
-
await waitForText(setup, "
|
|
264
|
+
await waitForText(setup, "send message");
|
|
250
265
|
});
|
|
251
266
|
|
|
252
267
|
test("the menu opens from the inbox and escape is not a trap", async () => {
|
|
@@ -254,12 +269,9 @@ describe("inbox", () => {
|
|
|
254
269
|
const setup = await testRender(<App home={home} />, { width: 120, height: 32 });
|
|
255
270
|
|
|
256
271
|
await waitForText(setup, "cueloop · resume");
|
|
257
|
-
const lines = setup.captureCharFrame().split("\n");
|
|
258
|
-
const menuRow = lines.findIndex((line) => line.includes("menu"));
|
|
259
|
-
const menuColumn = lines[menuRow]!.indexOf("menu");
|
|
260
272
|
|
|
261
|
-
// Act - open the drop-
|
|
262
|
-
await setup.mockMouse.click(
|
|
273
|
+
// Act - open the drop-down from the top-left settings gear (header content row)
|
|
274
|
+
await setup.mockMouse.click(1, 1);
|
|
263
275
|
|
|
264
276
|
// Assert - the drop-up options appear
|
|
265
277
|
await waitForText(setup, "Keybinds");
|
|
@@ -271,7 +283,7 @@ describe("inbox", () => {
|
|
|
271
283
|
await press(setup, "enter");
|
|
272
284
|
|
|
273
285
|
// Assert
|
|
274
|
-
await waitForText(setup, "
|
|
286
|
+
await waitForText(setup, "send message");
|
|
275
287
|
});
|
|
276
288
|
});
|
|
277
289
|
|
|
@@ -281,12 +293,9 @@ describe("the thread view and the menu", () => {
|
|
|
281
293
|
const setup = await renderApp();
|
|
282
294
|
|
|
283
295
|
await waitForText(setup, "The daemon persists");
|
|
284
|
-
const lines = setup.captureCharFrame().split("\n");
|
|
285
|
-
const menuRow = lines.findIndex((line) => line.includes("menu"));
|
|
286
|
-
const menuColumn = lines[menuRow]!.indexOf("menu");
|
|
287
296
|
|
|
288
|
-
// Act - open the menu, then the keybinds dialog
|
|
289
|
-
await setup.mockMouse.click(
|
|
297
|
+
// Act - open the menu from the top-left settings gear, then the keybinds dialog
|
|
298
|
+
await setup.mockMouse.click(1, 1);
|
|
290
299
|
await waitForText(setup, "Keybinds");
|
|
291
300
|
const dropUp = setup.captureCharFrame().split("\n");
|
|
292
301
|
const keybindsRow = dropUp.findIndex((line) => line.includes("Keybinds"));
|
|
@@ -298,7 +307,7 @@ describe("the thread view and the menu", () => {
|
|
|
298
307
|
const dialog = setup.captureCharFrame();
|
|
299
308
|
|
|
300
309
|
expect(dialog).toContain("⌘⌥m");
|
|
301
|
-
expect(dialog).toContain("
|
|
310
|
+
expect(dialog).toContain("place the caret");
|
|
302
311
|
expect(dialog).not.toContain("grow/shrink");
|
|
303
312
|
|
|
304
313
|
// Act - a printable behind the dialog must not open a comment; escape closes it
|
|
@@ -307,7 +316,44 @@ describe("the thread view and the menu", () => {
|
|
|
307
316
|
await press(setup, "escape");
|
|
308
317
|
|
|
309
318
|
// Assert
|
|
310
|
-
await waitForState(setup, () => !setup.captureCharFrame().includes("
|
|
319
|
+
await waitForState(setup, () => !setup.captureCharFrame().includes("mark text, across blocks"));
|
|
311
320
|
expect(setup.captureCharFrame()).not.toContain("● x");
|
|
312
321
|
});
|
|
322
|
+
|
|
323
|
+
test("clicking the Settings nav group is inert and never corrupts the active category", async () => {
|
|
324
|
+
// Arrange - open the settings dialog from the gear
|
|
325
|
+
const setup = await renderApp();
|
|
326
|
+
|
|
327
|
+
await setup.mockMouse.click(1, 1);
|
|
328
|
+
await waitForText(setup, "Keybinds");
|
|
329
|
+
|
|
330
|
+
// the nav folder is the last "Settings" on screen (the first is the dialog title)
|
|
331
|
+
const lines = setup.captureCharFrame().split("\n");
|
|
332
|
+
let folderRow = -1;
|
|
333
|
+
let folderColumn = -1;
|
|
334
|
+
|
|
335
|
+
lines.forEach((line, row) => {
|
|
336
|
+
const column = line.indexOf("Settings");
|
|
337
|
+
|
|
338
|
+
if (column !== -1) {
|
|
339
|
+
folderColumn = column;
|
|
340
|
+
folderRow = row;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Act - click the synthetic Settings group folder, then press a nav key. The
|
|
345
|
+
// key handler dereferences the active category, so a corrupt id would throw
|
|
346
|
+
// (the global key handler swallows the throw into console.error).
|
|
347
|
+
const keyHandlerErrors: string[] = [];
|
|
348
|
+
const originalConsoleError = console.error;
|
|
349
|
+
|
|
350
|
+
console.error = (...args: unknown[]) => keyHandlerErrors.push(args.map(String).join(" "));
|
|
351
|
+
await setup.mockMouse.click(folderColumn + 1, folderRow);
|
|
352
|
+
await pressKey(setup, "ARROW_DOWN");
|
|
353
|
+
console.error = originalConsoleError;
|
|
354
|
+
|
|
355
|
+
// Assert - no key-handler crash, and the dialog is still up
|
|
356
|
+
expect(keyHandlerErrors.filter((line) => line.includes("keypress handler"))).toEqual([]);
|
|
357
|
+
expect(setup.captureCharFrame()).toContain("Keybinds");
|
|
358
|
+
});
|
|
313
359
|
});
|