@cueloop/client 0.1.0-alpha.78 → 0.1.0-alpha.79
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 +14 -13
- package/src/App.diff.test.tsx +9 -4
- package/src/App.focus.test.ts +1 -59
- package/src/App.identity.test.tsx +3 -3
- package/src/App.plan-render.test.tsx +209 -0
- package/src/App.plan-review.test.tsx +75 -26
- package/src/App.readonly.test.tsx +6 -5
- package/src/App.share-dialog.test.tsx +172 -0
- package/src/App.submit-confirm.test.tsx +6 -4
- package/src/App.test.tsx +31 -28
- package/src/App.toast.test.tsx +11 -4
- package/src/App.tsx +259 -274
- package/src/App.walk.test.tsx +9 -8
- package/src/App.workbench.test.tsx +86 -0
- package/src/annotation-palette.ts +1 -0
- package/src/app-screens.tsx +12 -6
- package/src/app-view-model.ts +0 -1
- package/src/components/ConfirmDialog.stories.tsx +3 -3
- package/src/components/DiffContentView.tsx +14 -7
- package/src/components/DiscussionMarkerRail.tsx +9 -4
- package/src/components/GridTabContent.tsx +8 -2
- package/src/components/HandleChipsInput.stories.tsx +24 -0
- package/src/components/HandleChipsInput.test.tsx +35 -0
- package/src/components/HandleChipsInput.tsx +145 -0
- package/src/components/MarkdownGridBlock.stories.tsx +33 -0
- package/src/components/MarkdownGridBlock.tsx +72 -0
- package/src/components/MarkdownThreadEditor.stories.tsx +15 -0
- package/src/components/MarkdownThreadEditor.test.tsx +98 -0
- package/src/components/MarkdownThreadEditor.tsx +162 -0
- package/src/components/NavModeHint.stories.tsx +40 -0
- package/src/components/NavModeHint.tsx +31 -0
- package/src/components/SettingsDialog.tsx +1 -0
- package/src/components/ShareDialog.stories.tsx +121 -0
- package/src/components/ShareDialog.tsx +552 -0
- package/src/components/ThreadFooter.tsx +4 -1
- package/src/components/ThreadTree.tsx +1 -1
- package/src/components/ThreadView.slash.test.tsx +0 -35
- package/src/components/ThreadView.tsx +142 -68
- package/src/components/Toast.stories.tsx +7 -0
- package/src/components/Toast.tsx +15 -3
- package/src/components/__snapshots__/stories.test.tsx.snap +258 -134
- package/src/components/primitives/Button.tsx +6 -2
- package/src/components/primitives/Card.tsx +2 -2
- package/src/components/primitives/Dialog.tsx +2 -2
- package/src/components/primitives/Switch.stories.tsx +17 -0
- package/src/components/primitives/Switch.tsx +32 -0
- package/src/components/primitives/Tree.tsx +5 -1
- package/src/components/primitives/frame.ts +5 -0
- package/src/components/share-dialog-store.ts +58 -0
- package/src/components/stories.test.tsx +4 -3
- package/src/config.test.ts +18 -0
- package/src/config.ts +8 -1
- package/src/diff-fold.ts +1 -1
- package/src/intent-dispatch.test.ts +12 -3
- package/src/intent-dispatch.ts +10 -25
- package/src/key-bindings.ts +0 -6
- package/src/keymap.ts +0 -1
- package/src/markdown-editor-syntax-style.ts +50 -0
- package/src/markdown-grid.test.ts +101 -0
- package/src/markdown-grid.ts +203 -0
- package/src/markdown-highlight.test.ts +61 -0
- package/src/markdown-highlight.ts +122 -0
- package/src/prototype-browser.test.ts +9 -1
- package/src/prototype-browser.ts +22 -0
- package/src/run.ts +8 -6
- package/src/share.ts +15 -1
- package/src/test-support.ts +14 -0
- package/src/thread-chords.test.ts +55 -178
- package/src/thread-chords.ts +78 -169
- package/src/thread-controller.curate.test.ts +1 -0
- package/src/thread-controller.live-diff.test.ts +3 -0
- package/src/thread-controller.share.test.ts +60 -5
- package/src/thread-controller.switch-cache.test.ts +30 -0
- package/src/thread-controller.tree.test.ts +6 -1
- package/src/thread-controller.ts +229 -54
- package/src/use-annotation-surface.tsx +45 -36
- package/src/use-settings-dialog.tsx +14 -3
- package/src/use-thread-body-editing.tsx +58 -0
- package/src/view-plan.test.ts +56 -6
- package/src/view-plan.ts +154 -5
- package/src/App.share-choice.test.tsx +0 -105
- package/src/components/ManageAccessDialog.stories.tsx +0 -36
- package/src/components/ManageAccessDialog.test.tsx +0 -94
- package/src/components/ManageAccessDialog.tsx +0 -122
- package/src/components/ShareChoiceDialog.stories.tsx +0 -34
- package/src/components/ShareChoiceDialog.test.tsx +0 -103
- package/src/components/ShareChoiceDialog.tsx +0 -80
- package/src/components/ThreadsSidebar.stories.tsx +0 -36
- package/src/components/ThreadsSidebar.tsx +0 -71
- package/src/components/agent-launcher.stories.tsx +0 -26
- package/src/components/agent-launcher.test.tsx +0 -50
- package/src/components/agent-launcher.tsx +0 -149
|
@@ -17,16 +17,20 @@ const pushShare = mock(
|
|
|
17
17
|
async (
|
|
18
18
|
_shareId: string,
|
|
19
19
|
_annotations: Array<Omit<Annotation, "createdAt">>,
|
|
20
|
-
_access?: ShareAccess,
|
|
20
|
+
_access?: ShareAccess | "public",
|
|
21
21
|
) => {},
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
+
const revokeShare = mock(async (_shareId: string) => {});
|
|
25
|
+
|
|
24
26
|
const shareTransport: ShareTransport = {
|
|
25
27
|
publish: publishShare,
|
|
26
28
|
pull: pullShare,
|
|
27
29
|
push: pushShare,
|
|
28
30
|
watch: () => () => {},
|
|
31
|
+
revoke: revokeShare,
|
|
29
32
|
parseShareId: (line) => line.match(/^ssh (\S+)@/)?.[1],
|
|
33
|
+
formatShareLine: (id: string) => "ssh " + id + "@cueloop.dev",
|
|
30
34
|
collaboratorAnnotations: (session) => session.annotations.filter((entry) => entry.author),
|
|
31
35
|
mergeFromShare,
|
|
32
36
|
};
|
|
@@ -88,6 +92,12 @@ function fakeClient(session: Thread): FakeSessionClient {
|
|
|
88
92
|
sessionSetShareId: mock(
|
|
89
93
|
async (_id: string, shareId: string) => ((session.shareId = shareId), session),
|
|
90
94
|
),
|
|
95
|
+
sessionSetShares: mock(
|
|
96
|
+
async (_id: string, shares: import("@cueloop/schema").ShareLink[]) => (
|
|
97
|
+
(session.shares = shares),
|
|
98
|
+
session
|
|
99
|
+
),
|
|
100
|
+
),
|
|
91
101
|
sessionMergeShared: mock(async (_id: string, incoming: { annotations: Annotation[] }) => {
|
|
92
102
|
const known = new Set(session.annotations.map((existing) => existing.id));
|
|
93
103
|
|
|
@@ -125,7 +135,7 @@ async function connectedController(
|
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
describe("share", () => {
|
|
128
|
-
test("
|
|
138
|
+
test("publishes a public link and records it on the session", async () => {
|
|
129
139
|
// Arrange
|
|
130
140
|
const { controller, client } = await connectedController(sessionFixture());
|
|
131
141
|
|
|
@@ -133,8 +143,10 @@ describe("share", () => {
|
|
|
133
143
|
controller.share();
|
|
134
144
|
await tick();
|
|
135
145
|
|
|
136
|
-
// Assert
|
|
137
|
-
expect(client.
|
|
146
|
+
// Assert - a public link (no auth) is persisted with the returned id
|
|
147
|
+
expect(client.sessionSetShares).toHaveBeenCalledWith("ses_1", [
|
|
148
|
+
{ id: "p_abc123xy", name: undefined, requireAuth: false, allowlist: [], shareBranch: "main" },
|
|
149
|
+
]);
|
|
138
150
|
});
|
|
139
151
|
|
|
140
152
|
test("surfaces the ssh line as a toast, not an inline status", async () => {
|
|
@@ -148,7 +160,7 @@ describe("share", () => {
|
|
|
148
160
|
// Assert
|
|
149
161
|
expect(controller.getSnapshot().toast).toEqual({
|
|
150
162
|
body: "ssh p_abc123xy@cueloop.dev",
|
|
151
|
-
title: "
|
|
163
|
+
title: "link copied",
|
|
152
164
|
});
|
|
153
165
|
expect(controller.getSnapshot().status).not.toContain("ssh p_abc123xy@cueloop.dev");
|
|
154
166
|
});
|
|
@@ -504,3 +516,46 @@ describe("startShareSync", () => {
|
|
|
504
516
|
expect(streams[1]!.stopped).toBe(false);
|
|
505
517
|
});
|
|
506
518
|
});
|
|
519
|
+
|
|
520
|
+
describe("revoke", () => {
|
|
521
|
+
test("unshare revokes the gateway blob for the shared thread", async () => {
|
|
522
|
+
revokeShare.mockClear();
|
|
523
|
+
const { controller } = await connectedController(sessionFixture({ shareId: "p_abc123xy" }));
|
|
524
|
+
|
|
525
|
+
controller.unshare();
|
|
526
|
+
await tick();
|
|
527
|
+
|
|
528
|
+
expect(revokeShare).toHaveBeenCalledWith("p_abc123xy");
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("unshare on an unshared thread is a no-op", async () => {
|
|
532
|
+
revokeShare.mockClear();
|
|
533
|
+
const { controller } = await connectedController(sessionFixture());
|
|
534
|
+
|
|
535
|
+
controller.unshare();
|
|
536
|
+
await tick();
|
|
537
|
+
|
|
538
|
+
expect(revokeShare).not.toHaveBeenCalled();
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("deleting a shared thread revokes its share first", async () => {
|
|
542
|
+
revokeShare.mockClear();
|
|
543
|
+
const session = sessionFixture({ shareId: "p_abc123xy" });
|
|
544
|
+
const client = fakeClient(session);
|
|
545
|
+
|
|
546
|
+
client.sessionDelete = mock(async () => {});
|
|
547
|
+
const controller = createReviewController({
|
|
548
|
+
sessionId: session.id,
|
|
549
|
+
openClient: async () => client,
|
|
550
|
+
shareTransport,
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
controller.connect();
|
|
554
|
+
await tick();
|
|
555
|
+
controller.deleteSession(session.id);
|
|
556
|
+
await tick();
|
|
557
|
+
|
|
558
|
+
expect(revokeShare).toHaveBeenCalledWith("p_abc123xy");
|
|
559
|
+
expect(client.sessionDelete).toHaveBeenCalledWith(session.id);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
@@ -104,6 +104,7 @@ function fakeClient(sessions: Thread[]): SessionClient {
|
|
|
104
104
|
sessionSetViewed: unimplemented("sessionSetViewed"),
|
|
105
105
|
sessionSetTitle: unimplemented("sessionSetTitle"),
|
|
106
106
|
sessionSetShareId: unimplemented("sessionSetShareId"),
|
|
107
|
+
sessionSetShares: unimplemented("sessionSetShares"),
|
|
107
108
|
sessionMergeShared: unimplemented("sessionMergeShared"),
|
|
108
109
|
sessionDelete: unimplemented("sessionDelete"),
|
|
109
110
|
sessionSetSelfName: unimplemented("sessionSetSelfName"),
|
|
@@ -143,4 +144,33 @@ describe("switching threads keeps the cached projection curatable", () => {
|
|
|
143
144
|
expect(controller.getSnapshot().status).toContain("change rejected");
|
|
144
145
|
expect(controller.getSnapshot().session?.workingCopy).toBe("");
|
|
145
146
|
});
|
|
147
|
+
|
|
148
|
+
test("returning to a diff thread shows the daemon's latest, not the stale sidebar copy", async () => {
|
|
149
|
+
// The sidebar list trails the daemon for a hot-reloading diff (its session.updated
|
|
150
|
+
// refreshes the open thread, not the list). Returning to it must revalidate.
|
|
151
|
+
const staleA = diffSession("ses_a", PATCH_A, FILES_A);
|
|
152
|
+
const freshA = diffSession("ses_a", PATCH_B, FILES_B);
|
|
153
|
+
const other = diffSession("ses_b", PATCH_B, FILES_B);
|
|
154
|
+
const base = fakeClient([staleA, other]);
|
|
155
|
+
const controller = createReviewController({
|
|
156
|
+
sessionId: "ses_a",
|
|
157
|
+
openClient: async () => ({
|
|
158
|
+
...base,
|
|
159
|
+
// the sidebar keeps the stale copy; the daemon serves the fresh one
|
|
160
|
+
sessionList: async () => [staleA, other],
|
|
161
|
+
sessionGet: async (id: string) => (id === "ses_a" ? freshA : other),
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
controller.connect();
|
|
166
|
+
await tick();
|
|
167
|
+
|
|
168
|
+
// Visit B, then return to A - open() paints the stale sidebar copy, then revalidates
|
|
169
|
+
controller.open("ses_b");
|
|
170
|
+
await tick();
|
|
171
|
+
controller.open("ses_a");
|
|
172
|
+
await tick();
|
|
173
|
+
|
|
174
|
+
expect(controller.getSnapshot().session?.artifact.content).toBe(PATCH_B);
|
|
175
|
+
});
|
|
146
176
|
});
|
|
@@ -58,7 +58,9 @@ const shareTransport: ShareTransport = {
|
|
|
58
58
|
pull: mock(async () => sessionFixture()),
|
|
59
59
|
push: mock(async () => {}),
|
|
60
60
|
watch: () => () => {},
|
|
61
|
+
revoke: async () => {},
|
|
61
62
|
parseShareId: (line) => line.match(/^ssh (\S+)@/)?.[1],
|
|
63
|
+
formatShareLine: (id: string) => "ssh " + id + "@cueloop.dev",
|
|
62
64
|
collaboratorAnnotations: (session) => session.annotations.filter((entry) => entry.author),
|
|
63
65
|
mergeFromShare,
|
|
64
66
|
};
|
|
@@ -95,6 +97,7 @@ function fakeClient(session: Thread) {
|
|
|
95
97
|
sessionSetViewed: unimplemented("sessionSetViewed"),
|
|
96
98
|
sessionSetTitle: unimplemented("sessionSetTitle"),
|
|
97
99
|
sessionSetShareId: mock(async (_id: string, _shareId: string) => session),
|
|
100
|
+
sessionSetShares: mock(async () => session),
|
|
98
101
|
sessionMergeShared: unimplemented("sessionMergeShared"),
|
|
99
102
|
sessionDelete: unimplemented("sessionDelete"),
|
|
100
103
|
sessionSetSelfName: unimplemented("sessionSetSelfName"),
|
|
@@ -250,7 +253,9 @@ describe("tree primitives", () => {
|
|
|
250
253
|
expect(forked.controller.getSnapshot().session!.id).toBe("ses_1_fork");
|
|
251
254
|
expect(shared.controller.getSnapshot().session!.id).toBe("ses_1");
|
|
252
255
|
expect(publish).toHaveBeenCalledWith(expect.objectContaining({ id: "ses_1_fork" }));
|
|
253
|
-
expect(shared.client.
|
|
256
|
+
expect(shared.client.sessionSetShares).toHaveBeenCalledWith("ses_1_fork", [
|
|
257
|
+
{ id: "p_ses_1_fork", requireAuth: false, allowlist: [], shareBranch: "main" },
|
|
258
|
+
]);
|
|
254
259
|
expect(shared.controller.getSnapshot().toast?.title).toBe("fork shared - link copied");
|
|
255
260
|
});
|
|
256
261
|
});
|