@cueloop/client 0.1.0-alpha.76 → 0.1.0-alpha.77

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 (34) hide show
  1. package/package.json +5 -5
  2. package/src/App.identity.test.tsx +78 -0
  3. package/src/App.share-choice.test.tsx +105 -0
  4. package/src/App.toast.test.tsx +3 -1
  5. package/src/App.tsx +176 -5
  6. package/src/app-screens.tsx +13 -0
  7. package/src/app-view-model.ts +1 -0
  8. package/src/components/AnnotatableFileView.tsx +9 -1
  9. package/src/components/AnnotationCards.tsx +22 -4
  10. package/src/components/DiffContentView.tsx +5 -1
  11. package/src/components/GridTabContent.tsx +2 -0
  12. package/src/components/ManageAccessDialog.stories.tsx +36 -0
  13. package/src/components/ManageAccessDialog.test.tsx +94 -0
  14. package/src/components/ManageAccessDialog.tsx +120 -0
  15. package/src/components/ShareChoiceDialog.stories.tsx +34 -0
  16. package/src/components/ShareChoiceDialog.test.tsx +103 -0
  17. package/src/components/ShareChoiceDialog.tsx +82 -0
  18. package/src/components/ThreadView.tsx +5 -1
  19. package/src/components/__snapshots__/stories.test.tsx.snap +93 -7
  20. package/src/config-identity.test.ts +78 -0
  21. package/src/config.ts +44 -1
  22. package/src/github-identity.test.ts +58 -0
  23. package/src/github-identity.ts +41 -0
  24. package/src/intent-dispatch.test.ts +17 -2
  25. package/src/intent-dispatch.ts +7 -1
  26. package/src/keymap.ts +1 -0
  27. package/src/share.ts +10 -2
  28. package/src/thread-controller.curate.test.ts +1 -0
  29. package/src/thread-controller.live-diff.test.ts +1 -0
  30. package/src/thread-controller.share.test.ts +36 -2
  31. package/src/thread-controller.tree.test.ts +1 -0
  32. package/src/thread-controller.ts +14 -0
  33. package/src/use-annotation-surface.tsx +10 -2
  34. package/src/use-settings-dialog.tsx +24 -0
@@ -10,7 +10,7 @@
10
10
 
11
11
  import React, { useEffect, useMemo, useRef, useState } from "react";
12
12
  import { createTextAttributes, type KeyEvent, type ScrollBoxRenderable } from "@opentui/core";
13
- import type { Thread } from "@cueloop/schema";
13
+ import type { Annotation, Thread } from "@cueloop/schema";
14
14
  import { diffRowText, fileChangeCounts, type DiffRow, type Mark } from "../view-diff";
15
15
  import type { TextSpan } from "../thread-selection";
16
16
  import type { QuickAction } from "../config";
@@ -95,6 +95,8 @@ export interface DiffContentViewProps {
95
95
  onAnnotate: (span: TextSpan, body: string) => void;
96
96
  onReply: (rootAnnotationId: string, body: string) => void;
97
97
  onUpdateAnnotation: (id: string, body: string) => void;
98
+ /** The author's display name for a comment's hover tooltip. */
99
+ resolveAuthorLabel?: (annotation: Annotation) => string | undefined;
98
100
  leaderCombos?: readonly string[];
99
101
  onLeaderCommand?: (key: KeyEvent) => void;
100
102
  onExit: () => void;
@@ -489,6 +491,7 @@ export function DiffContentView({
489
491
  onAnnotate,
490
492
  onReply,
491
493
  onUpdateAnnotation,
494
+ resolveAuthorLabel,
492
495
  leaderCombos,
493
496
  onLeaderCommand,
494
497
  onExit,
@@ -524,6 +527,7 @@ export function DiffContentView({
524
527
  onAnnotate,
525
528
  onReply,
526
529
  onUpdateAnnotation,
530
+ resolveAuthorLabel,
527
531
  leaderCombos,
528
532
  onLeaderCommand,
529
533
  onExit,
@@ -34,6 +34,7 @@ export type DiffSurfaceProps = Pick<
34
34
  | "onAnnotate"
35
35
  | "onReply"
36
36
  | "onUpdateAnnotation"
37
+ | "resolveAuthorLabel"
37
38
  | "leaderCombos"
38
39
  | "onLeaderCommand"
39
40
  | "onExit"
@@ -121,6 +122,7 @@ export function GridTabContent(props: {
121
122
  onAddComment={(anchor, body) => props.onAddFileComment(filePath, anchor, body)}
122
123
  onReply={props.surface.onReply}
123
124
  onUpdateAnnotation={props.surface.onUpdateAnnotation}
125
+ resolveAuthorLabel={props.surface.resolveAuthorLabel}
124
126
  onExit={props.surface.onExit}
125
127
  theme={props.theme}
126
128
  />
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { DARK } from "../theme";
3
+ import type { Story, StoryMeta } from "./story";
4
+ import { ManageAccessDialog } from "./ManageAccessDialog";
5
+
6
+ export const meta: StoryMeta = { title: "Dialogs/ManageAccess" };
7
+
8
+ export const WithHandles: Story = {
9
+ render: () => (
10
+ <ManageAccessDialog
11
+ isOpen
12
+ logins={["octocat", "hubot"]}
13
+ onAdd={() => {}}
14
+ onRemove={() => {}}
15
+ onCreateLink={() => {}}
16
+ onClose={() => {}}
17
+ theme={DARK}
18
+ />
19
+ ),
20
+ size: { width: 60, height: 18 },
21
+ };
22
+
23
+ export const Empty: Story = {
24
+ render: () => (
25
+ <ManageAccessDialog
26
+ isOpen
27
+ logins={[]}
28
+ onAdd={() => {}}
29
+ onRemove={() => {}}
30
+ onCreateLink={() => {}}
31
+ onClose={() => {}}
32
+ theme={DARK}
33
+ />
34
+ ),
35
+ size: { width: 60, height: 18 },
36
+ };
@@ -0,0 +1,94 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import React, { useState } from "react";
3
+ import { testRender } from "@opentui/react/test-utils";
4
+ import { DARK } from "../theme";
5
+ import { clickText, locateTextInFrame, settle, typeText } from "../test-support";
6
+ import { ManageAccessDialog } from "./ManageAccessDialog";
7
+
8
+ function AccessHarness({ initial }: { initial: string[] }): React.ReactNode {
9
+ const [logins, setLogins] = useState<string[]>(initial);
10
+ const [open, setOpen] = useState(true);
11
+
12
+ return (
13
+ <box style={{ width: 60, height: 18 }}>
14
+ <ManageAccessDialog
15
+ isOpen={open}
16
+ logins={logins}
17
+ onAdd={(login) => setLogins((prev) => [...prev, login])}
18
+ onRemove={(login) => setLogins((prev) => prev.filter((entry) => entry !== login))}
19
+ onCreateLink={() => {}}
20
+ onClose={() => setOpen(false)}
21
+ theme={DARK}
22
+ />
23
+ </box>
24
+ );
25
+ }
26
+
27
+ async function renderAccess(initial: string[]): Promise<Awaited<ReturnType<typeof testRender>>> {
28
+ const setup = await testRender(<AccessHarness initial={initial} />, { width: 60, height: 18 });
29
+
30
+ await settle(setup);
31
+
32
+ return setup;
33
+ }
34
+
35
+ describe("ManageAccessDialog", () => {
36
+ test("renders the allowlist of GitHub handles", async () => {
37
+ const setup = await renderAccess(["octocat", "hubot"]);
38
+ const frame = setup.captureCharFrame();
39
+
40
+ expect(locateTextInFrame(frame, "@octocat")).not.toBeNull();
41
+ expect(locateTextInFrame(frame, "@hubot")).not.toBeNull();
42
+ });
43
+
44
+ test("clicking a row removes that handle from the allowlist", async () => {
45
+ const setup = await renderAccess(["octocat", "hubot"]);
46
+
47
+ await clickText(setup, "@octocat");
48
+
49
+ expect(locateTextInFrame(setup.captureCharFrame(), "@octocat")).toBeNull();
50
+ expect(locateTextInFrame(setup.captureCharFrame(), "@hubot")).not.toBeNull();
51
+ });
52
+
53
+ test("typing a handle and pressing add appends it to the allowlist", async () => {
54
+ const setup = await renderAccess([]);
55
+
56
+ await typeText(setup, "octocat");
57
+ await clickText(setup, "add");
58
+
59
+ expect(locateTextInFrame(setup.captureCharFrame(), "@octocat")).not.toBeNull();
60
+ });
61
+
62
+ test("clicking create link publishes the private link", async () => {
63
+ let created = false;
64
+ const setup = await testRender(
65
+ <box style={{ width: 60, height: 18 }}>
66
+ <ManageAccessDialog
67
+ isOpen
68
+ logins={["octocat"]}
69
+ onAdd={() => {}}
70
+ onRemove={() => {}}
71
+ onCreateLink={() => {
72
+ created = true;
73
+ }}
74
+ onClose={() => {}}
75
+ theme={DARK}
76
+ />
77
+ </box>,
78
+ { width: 60, height: 18 },
79
+ );
80
+
81
+ await settle(setup);
82
+ await clickText(setup, "create link");
83
+
84
+ expect(created).toBe(true);
85
+ });
86
+
87
+ test("clicking close dismisses the dialog", async () => {
88
+ const setup = await renderAccess([]);
89
+
90
+ await clickText(setup, "close");
91
+
92
+ expect(locateTextInFrame(setup.captureCharFrame(), "Manage access")).toBeNull();
93
+ });
94
+ });
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Owner surface for a private share's allowlist: the GitHub logins allowed to
3
+ * open the private link. Type a handle and add it; click a row to remove one.
4
+ * Editing persists through the controller, so the list survives a reload.
5
+ */
6
+
7
+ import React, { useEffect, useRef, useState } from "react";
8
+ import { useKeyboard } from "@opentui/react";
9
+ import { useTerminalDimensions } from "@opentui/react";
10
+ import type { KeyBinding, TextareaRenderable } from "@opentui/core";
11
+ import type { Theme } from "../theme";
12
+ import { useComponentTheme } from "./theme-context";
13
+ import { Dialog } from "./primitives/Dialog";
14
+
15
+ // enter adds the handle rather than inserting a newline into the one-line field
16
+ const ADD_KEY_BINDINGS: KeyBinding[] = [{ name: "return", action: "submit" }];
17
+
18
+ export interface ManageAccessDialogProps {
19
+ isOpen: boolean;
20
+ logins: string[];
21
+ onAdd: (login: string) => void;
22
+ onRemove: (login: string) => void;
23
+ /** Publish the private link with the current allowlist and copy its connection line. */
24
+ onCreateLink: () => void;
25
+ onClose: () => void;
26
+ theme?: Theme;
27
+ }
28
+
29
+ export function ManageAccessDialog({
30
+ isOpen,
31
+ logins,
32
+ onAdd,
33
+ onRemove,
34
+ onCreateLink,
35
+ onClose,
36
+ theme,
37
+ }: ManageAccessDialogProps): React.ReactNode {
38
+ const tokens = useComponentTheme(theme);
39
+ const { width: terminalWidth } = useTerminalDimensions();
40
+ const inputRef = useRef<TextareaRenderable | null>(null);
41
+ const [draft, setDraft] = useState("");
42
+ const [inputGeneration, setInputGeneration] = useState(0);
43
+
44
+ useEffect(() => {
45
+ inputRef.current?.focus();
46
+ }, [inputGeneration]);
47
+
48
+ const addDraft = (): void => {
49
+ const handle = draft.trim().replace(/^@/, "").toLowerCase();
50
+
51
+ if (handle && !logins.includes(handle)) onAdd(handle);
52
+ setDraft("");
53
+ setInputGeneration((generation) => generation + 1);
54
+ };
55
+
56
+ useKeyboard((key) => {
57
+ if (isOpen && key.name === "escape") onClose();
58
+ });
59
+
60
+ if (!isOpen) return null;
61
+
62
+ return (
63
+ <Dialog
64
+ isOpen
65
+ title=" Manage access "
66
+ width={Math.min(54, terminalWidth - 6)}
67
+ height={Math.min(18, 9 + logins.length)}
68
+ background={tokens.elevated}
69
+ onDismiss={onClose}
70
+ theme={theme}
71
+ >
72
+ <box style={{ flexDirection: "column", flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>
73
+ <text fg={tokens.textDim}>Only these GitHub users can open the private link.</text>
74
+ <box style={{ height: 1 }} />
75
+ {logins.length === 0 ? (
76
+ <text fg={tokens.textDim}>no one yet - add a handle below</text>
77
+ ) : (
78
+ logins.map((login) => (
79
+ <box key={login} onMouseUp={() => onRemove(login)} style={{ flexDirection: "row" }}>
80
+ <text fg={tokens.text}>{`@${login}`}</text>
81
+ <box style={{ flexGrow: 1 }} />
82
+ <text fg={tokens.textDim}>remove</text>
83
+ </box>
84
+ ))
85
+ )}
86
+ <box style={{ flexGrow: 1 }} />
87
+ <box style={{ flexDirection: "row" }}>
88
+ <text fg={tokens.textDim}>{"@"}</text>
89
+ <textarea
90
+ key={inputGeneration}
91
+ ref={inputRef}
92
+ focused
93
+ placeholder="handle"
94
+ keyBindings={ADD_KEY_BINDINGS}
95
+ onContentChange={() => setDraft(inputRef.current?.plainText ?? "")}
96
+ style={{
97
+ height: 1,
98
+ flexGrow: 1,
99
+ backgroundColor: tokens.elevated,
100
+ focusedBackgroundColor: tokens.elevated,
101
+ textColor: tokens.text,
102
+ focusedTextColor: tokens.text,
103
+ }}
104
+ />
105
+ </box>
106
+ <box style={{ flexDirection: "row" }}>
107
+ <box onMouseUp={addDraft} style={{ marginRight: 3 }}>
108
+ <text fg={tokens.accent}>add</text>
109
+ </box>
110
+ <box onMouseUp={onCreateLink} style={{ marginRight: 3 }}>
111
+ <text fg={tokens.accent}>create link</text>
112
+ </box>
113
+ <box onMouseUp={onClose}>
114
+ <text fg={tokens.textDim}>close</text>
115
+ </box>
116
+ </box>
117
+ </box>
118
+ </Dialog>
119
+ );
120
+ }
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { DARK } from "../theme";
3
+ import type { Story, StoryMeta } from "./story";
4
+ import { ShareChoiceDialog } from "./ShareChoiceDialog";
5
+
6
+ export const meta: StoryMeta = { title: "Dialogs/ShareChoice" };
7
+
8
+ export const PublicSelected: Story = {
9
+ render: () => (
10
+ <ShareChoiceDialog
11
+ isOpen
12
+ selectedIndex={0}
13
+ onPublicShare={() => {}}
14
+ onPrivateShare={() => {}}
15
+ onClose={() => {}}
16
+ theme={DARK}
17
+ />
18
+ ),
19
+ size: { width: 60, height: 12 },
20
+ };
21
+
22
+ export const PrivateSelected: Story = {
23
+ render: () => (
24
+ <ShareChoiceDialog
25
+ isOpen
26
+ selectedIndex={1}
27
+ onPublicShare={() => {}}
28
+ onPrivateShare={() => {}}
29
+ onClose={() => {}}
30
+ theme={DARK}
31
+ />
32
+ ),
33
+ size: { width: 60, height: 12 },
34
+ };
@@ -0,0 +1,103 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import React from "react";
3
+ import { testRender } from "@opentui/react/test-utils";
4
+ import { DARK } from "../theme";
5
+ import { clickText, settle } from "../test-support";
6
+ import { ShareChoiceDialog } from "./ShareChoiceDialog";
7
+
8
+ interface ShareCounts {
9
+ publicShares: number;
10
+ privateShares: number;
11
+ closes: number;
12
+ }
13
+
14
+ async function renderDialog(selectedIndex = 0): Promise<{
15
+ setup: Awaited<ReturnType<typeof testRender>>;
16
+ counts: ShareCounts;
17
+ }> {
18
+ const counts: ShareCounts = { publicShares: 0, privateShares: 0, closes: 0 };
19
+ const setup = await testRender(
20
+ <box style={{ width: 60, height: 12 }}>
21
+ <ShareChoiceDialog
22
+ isOpen
23
+ selectedIndex={selectedIndex}
24
+ onPublicShare={() => {
25
+ counts.publicShares += 1;
26
+ }}
27
+ onPrivateShare={() => {
28
+ counts.privateShares += 1;
29
+ }}
30
+ onClose={() => {
31
+ counts.closes += 1;
32
+ }}
33
+ theme={DARK}
34
+ />
35
+ </box>,
36
+ { width: 60, height: 12 },
37
+ );
38
+
39
+ await settle(setup);
40
+
41
+ return { setup, counts };
42
+ }
43
+
44
+ describe("ShareChoiceDialog", () => {
45
+ test("offers both the public and private choice", async () => {
46
+ // Arrange + Act
47
+ const { setup } = await renderDialog();
48
+
49
+ // Assert
50
+ const frame = setup.captureCharFrame();
51
+
52
+ expect(frame).toContain("public link");
53
+ expect(frame).toContain("private link");
54
+ });
55
+
56
+ test("clicking the public choice publishes and closes", async () => {
57
+ // Arrange
58
+ const { setup, counts } = await renderDialog();
59
+
60
+ // Act
61
+ await clickText(setup, "public link");
62
+
63
+ // Assert
64
+ expect(counts.publicShares).toBe(1);
65
+ expect(counts.privateShares).toBe(0);
66
+ expect(counts.closes).toBe(1);
67
+ });
68
+
69
+ test("clicking the private choice opens manage-access and closes", async () => {
70
+ // Arrange
71
+ const { setup, counts } = await renderDialog();
72
+
73
+ // Act
74
+ await clickText(setup, "private link");
75
+
76
+ // Assert
77
+ expect(counts.privateShares).toBe(1);
78
+ expect(counts.publicShares).toBe(0);
79
+ expect(counts.closes).toBe(1);
80
+ });
81
+
82
+ test("renders nothing while closed", async () => {
83
+ // Arrange + Act
84
+ const setup = await testRender(
85
+ <box style={{ width: 60, height: 12 }}>
86
+ <ShareChoiceDialog
87
+ isOpen={false}
88
+ selectedIndex={0}
89
+ onPublicShare={() => {}}
90
+ onPrivateShare={() => {}}
91
+ onClose={() => {}}
92
+ theme={DARK}
93
+ />
94
+ </box>,
95
+ { width: 60, height: 12 },
96
+ );
97
+
98
+ await settle(setup);
99
+
100
+ // Assert
101
+ expect(setup.captureCharFrame()).not.toContain("public link");
102
+ });
103
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The share public/private choice, as an app-level overlay the share shortcut
3
+ * opens from any view. Public publishes a link for anyone; private opens the
4
+ * manage-access allowlist surface. The header share button and the shortcut both
5
+ * open this one surface. Selection moves with the keyboard (owned by the App) or
6
+ * a pointer click on a row.
7
+ */
8
+
9
+ import React from "react";
10
+ import { useTerminalDimensions } from "@opentui/react";
11
+ import type { Theme } from "../theme";
12
+ import { useComponentTheme } from "./theme-context";
13
+ import { Dialog } from "./primitives/Dialog";
14
+
15
+ export type ShareChoice = "public" | "private";
16
+
17
+ interface ShareChoiceRow {
18
+ choice: ShareChoice;
19
+ label: string;
20
+ }
21
+
22
+ export const SHARE_CHOICES: ShareChoiceRow[] = [
23
+ { choice: "public", label: "public link" },
24
+ { choice: "private", label: "private link" },
25
+ ];
26
+
27
+ export interface ShareChoiceDialogProps {
28
+ isOpen: boolean;
29
+ /** The highlighted row; the App drives it from the shareChoice mode. */
30
+ selectedIndex: number;
31
+ /** Publish a public link and copy its connection line. */
32
+ onPublicShare: () => void;
33
+ /** Open the private-share manage-access allowlist surface. */
34
+ onPrivateShare: () => void;
35
+ onClose: () => void;
36
+ theme?: Theme;
37
+ }
38
+
39
+ export function ShareChoiceDialog({
40
+ isOpen,
41
+ selectedIndex,
42
+ onPublicShare,
43
+ onPrivateShare,
44
+ onClose,
45
+ theme,
46
+ }: ShareChoiceDialogProps): React.ReactNode {
47
+ const tokens = useComponentTheme(theme);
48
+ const { width: terminalWidth } = useTerminalDimensions();
49
+
50
+ const pick = (choice: ShareChoice): void => {
51
+ onClose();
52
+ if (choice === "public") onPublicShare();
53
+ else onPrivateShare();
54
+ };
55
+
56
+ if (!isOpen) return null;
57
+
58
+ return (
59
+ <Dialog
60
+ isOpen
61
+ title=" Share "
62
+ width={Math.min(46, terminalWidth - 6)}
63
+ height={7}
64
+ background={tokens.elevated}
65
+ onDismiss={onClose}
66
+ theme={theme}
67
+ >
68
+ <box style={{ flexDirection: "column", flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>
69
+ {SHARE_CHOICES.map((row, index) => (
70
+ <box key={row.choice} onMouseUp={() => pick(row.choice)} style={{ flexDirection: "row" }}>
71
+ <text fg={index === selectedIndex ? tokens.accent : tokens.text}>
72
+ {index === selectedIndex ? "› " : " "}
73
+ {row.label}
74
+ </text>
75
+ </box>
76
+ ))}
77
+ <box style={{ flexGrow: 1 }} />
78
+ <text fg={tokens.textDim}>↑↓ choose · enter select · esc cancel</text>
79
+ </box>
80
+ </Dialog>
81
+ );
82
+ }
@@ -14,7 +14,7 @@
14
14
 
15
15
  import React, { useEffect, useRef } from "react";
16
16
  import type { KeyEvent, ScrollBoxRenderable } from "@opentui/core";
17
- import type { Thread } from "@cueloop/schema";
17
+ import type { Annotation, Thread } from "@cueloop/schema";
18
18
  import { displayText, type DisplayBlock, type Mark } from "../view-plan";
19
19
  import type { TextSpan } from "../thread-selection";
20
20
  import type { QuickAction } from "../config";
@@ -127,6 +127,8 @@ export interface ThreadViewProps {
127
127
  onAnnotate: (span: TextSpan, body: string) => void;
128
128
  onReply: (rootAnnotationId: string, body: string) => void;
129
129
  onUpdateAnnotation: (id: string, body: string) => void;
130
+ /** The author's display name for a comment's hover tooltip. */
131
+ resolveAuthorLabel?: (annotation: Annotation) => string | undefined;
130
132
  leaderCombos?: readonly string[];
131
133
  onLeaderCommand?: (key: KeyEvent) => void;
132
134
  onExit: () => void;
@@ -150,6 +152,7 @@ export function ThreadView({
150
152
  onAnnotate,
151
153
  onReply,
152
154
  onUpdateAnnotation,
155
+ resolveAuthorLabel,
153
156
  leaderCombos,
154
157
  onLeaderCommand,
155
158
  onExit,
@@ -178,6 +181,7 @@ export function ThreadView({
178
181
  onAnnotate,
179
182
  onReply,
180
183
  onUpdateAnnotation,
184
+ resolveAuthorLabel,
181
185
  leaderCombos,
182
186
  onLeaderCommand,
183
187
  onExit,
@@ -1,4 +1,4 @@
1
- // Bun Snapshot v1, https://goo.gl/fbAQLP
1
+ // Bun Snapshot v1, https://bun.sh/docs/test/snapshots
2
2
 
3
3
  exports[`stories catalog Surfaces/AnnotatableFileView/Loaded renders, snapshots, and carries its colors: Surfaces/AnnotatableFileView/Loaded 1`] = `
4
4
  " ▎1 export function add(a: number, b: number): number {
@@ -667,6 +667,50 @@ exports[`stories catalog Overlays/KeybindsDialog/Cheatsheet renders, snapshots,
667
667
 
668
668
 
669
669
 
670
+ "
671
+ `;
672
+
673
+ exports[`stories catalog Dialogs/ManageAccess/Empty renders, snapshots, and carries its colors: Dialogs/ManageAccess/Empty 1`] = `
674
+ "
675
+
676
+
677
+
678
+
679
+ ┌─ Manage access ────────────────────────────────────┐
680
+ │ Only these GitHub users can open the private link. │
681
+ │ │
682
+ │ no one yet - add a handle below │
683
+ │ │
684
+ │ │
685
+ │ @handle │
686
+ │ add create link close │
687
+ └────────────────────────────────────────────────────┘
688
+
689
+
690
+
691
+
692
+ "
693
+ `;
694
+
695
+ exports[`stories catalog Dialogs/ManageAccess/WithHandles renders, snapshots, and carries its colors: Dialogs/ManageAccess/WithHandles 1`] = `
696
+ "
697
+
698
+
699
+
700
+ ┌─ Manage access ────────────────────────────────────┐
701
+ │ Only these GitHub users can open the private link. │
702
+ │ │
703
+ │ @octocat remove │
704
+ │ @hubot remove │
705
+ │ │
706
+ │ │
707
+ │ │
708
+ │ @handle │
709
+ │ add create link close │
710
+ └────────────────────────────────────────────────────┘
711
+
712
+
713
+
670
714
  "
671
715
  `;
672
716
 
@@ -999,6 +1043,16 @@ exports[`stories catalog Chrome/SettingsRows/AllKinds renders, snapshots, and ca
999
1043
 
1000
1044
 
1001
1045
 
1046
+ "
1047
+ `;
1048
+
1049
+ exports[`stories catalog Chrome/ShareMenu/Closed renders, snapshots, and carries its colors: Chrome/ShareMenu/Closed 1`] = `
1050
+ " share
1051
+
1052
+
1053
+
1054
+
1055
+
1002
1056
  "
1003
1057
  `;
1004
1058
 
@@ -1367,6 +1421,15 @@ exports[`stories catalog Chrome/AgentLauncher/Launcher renders, snapshots, and c
1367
1421
 
1368
1422
 
1369
1423
 
1424
+ "
1425
+ `;
1426
+
1427
+ exports[`stories catalog Layout/MenuControl/OneOpenAtATime renders, snapshots, and carries its colors: Layout/MenuControl/OneOpenAtATime 1`] = `
1428
+ "
1429
+ alpha: open
1430
+ beta: closed
1431
+
1432
+
1370
1433
  "
1371
1434
  `;
1372
1435
 
@@ -1888,11 +1951,34 @@ exports[`stories catalog Foundations/Theme/ProviderSwap renders, snapshots, and
1888
1951
  "
1889
1952
  `;
1890
1953
 
1891
- exports[`stories catalog Layout/MenuControl/OneOpenAtATime renders, snapshots, and carries its colors: Layout/MenuControl/OneOpenAtATime 1`] = `
1892
- "
1893
- alpha: open
1894
- beta: closed
1895
-
1896
-
1954
+ exports[`stories catalog Dialogs/ShareChoice/PrivateSelected renders, snapshots, and carries its colors: Dialogs/ShareChoice/PrivateSelected 1`] = `
1955
+ "
1956
+
1957
+
1958
+ ┌─ Share ────────────────────────────────────┐
1959
+ │ public link │
1960
+ │ › private link │
1961
+ │ │
1962
+ │ │
1963
+ │ ↑↓ choose · enter select · esc cancel │
1964
+ └────────────────────────────────────────────┘
1965
+
1966
+
1967
+ "
1968
+ `;
1969
+
1970
+ exports[`stories catalog Dialogs/ShareChoice/PublicSelected renders, snapshots, and carries its colors: Dialogs/ShareChoice/PublicSelected 1`] = `
1971
+ "
1972
+
1973
+
1974
+ ┌─ Share ────────────────────────────────────┐
1975
+ │ › public link │
1976
+ │ private link │
1977
+ │ │
1978
+ │ │
1979
+ │ ↑↓ choose · enter select · esc cancel │
1980
+ └────────────────────────────────────────────┘
1981
+
1982
+
1897
1983
  "
1898
1984
  `;