visual-comments 0.0.0-stage → 0.2.0

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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -2
  3. package/dist/cli/index.d.ts +2 -0
  4. package/dist/cli/index.js +477 -0
  5. package/dist/core/aim.d.ts +21 -0
  6. package/dist/core/aim.js +21 -0
  7. package/dist/core/api.d.ts +5 -0
  8. package/dist/core/api.js +11 -0
  9. package/dist/core/metaball.d.ts +15 -0
  10. package/dist/core/metaball.js +42 -0
  11. package/dist/core/presence.d.ts +13 -0
  12. package/dist/core/presence.js +25 -0
  13. package/dist/core/surfaces.d.ts +95 -0
  14. package/dist/core/surfaces.js +197 -0
  15. package/dist/core/target.d.ts +59 -0
  16. package/dist/core/target.js +382 -0
  17. package/dist/core/threads.svelte.d.ts +80 -0
  18. package/dist/core/threads.svelte.js +336 -0
  19. package/dist/core/time.d.ts +3 -0
  20. package/dist/core/time.js +25 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.js +6 -0
  23. package/dist/server/env.d.ts +78 -0
  24. package/dist/server/env.js +94 -0
  25. package/dist/server/index.d.ts +34 -0
  26. package/dist/server/index.js +74 -0
  27. package/dist/server/variables.d.ts +28 -0
  28. package/dist/server/variables.js +73 -0
  29. package/dist/shared/limits.d.ts +9 -0
  30. package/dist/shared/limits.js +10 -0
  31. package/dist/shared/protocol.d.ts +6 -0
  32. package/dist/shared/protocol.js +7 -0
  33. package/dist/shared/types.d.ts +157 -0
  34. package/dist/shared/types.js +2 -0
  35. package/dist/sveltekit/CommentTools.svelte +98 -0
  36. package/dist/sveltekit/CommentTools.svelte.d.ts +6 -0
  37. package/dist/sveltekit/Comments.svelte +104 -0
  38. package/dist/sveltekit/Comments.svelte.d.ts +25 -0
  39. package/dist/sveltekit/Toolbar.svelte +64 -0
  40. package/dist/sveltekit/Toolbar.svelte.d.ts +13 -0
  41. package/dist/sveltekit/ToolbarButton.svelte +66 -0
  42. package/dist/sveltekit/ToolbarButton.svelte.d.ts +12 -0
  43. package/dist/sveltekit/context.d.ts +16 -0
  44. package/dist/sveltekit/context.js +9 -0
  45. package/dist/sveltekit/index.d.ts +6 -0
  46. package/dist/sveltekit/index.js +6 -0
  47. package/dist/ui/Account.svelte +106 -0
  48. package/dist/ui/Account.svelte.d.ts +8 -0
  49. package/dist/ui/App.svelte +77 -0
  50. package/dist/ui/App.svelte.d.ts +7 -0
  51. package/dist/ui/Avatar.svelte +64 -0
  52. package/dist/ui/Avatar.svelte.d.ts +11 -0
  53. package/dist/ui/Composer.svelte +267 -0
  54. package/dist/ui/Composer.svelte.d.ts +20 -0
  55. package/dist/ui/Connect.svelte +128 -0
  56. package/dist/ui/Connect.svelte.d.ts +6 -0
  57. package/dist/ui/Icon.svelte +44 -0
  58. package/dist/ui/Icon.svelte.d.ts +29 -0
  59. package/dist/ui/Menu.svelte +69 -0
  60. package/dist/ui/Menu.svelte.d.ts +15 -0
  61. package/dist/ui/MenuItem.svelte +38 -0
  62. package/dist/ui/MenuItem.svelte.d.ts +12 -0
  63. package/dist/ui/Overlay.svelte +502 -0
  64. package/dist/ui/Overlay.svelte.d.ts +3 -0
  65. package/dist/ui/Panel.svelte +220 -0
  66. package/dist/ui/Panel.svelte.d.ts +10 -0
  67. package/dist/ui/Pin.svelte +156 -0
  68. package/dist/ui/Pin.svelte.d.ts +15 -0
  69. package/dist/ui/Presence.svelte +43 -0
  70. package/dist/ui/Presence.svelte.d.ts +8 -0
  71. package/dist/ui/ThreadCard.svelte +201 -0
  72. package/dist/ui/ThreadCard.svelte.d.ts +13 -0
  73. package/dist/ui/Toolbar.svelte +35 -0
  74. package/dist/ui/Toolbar.svelte.d.ts +18 -0
  75. package/dist/ui/context.d.ts +3 -0
  76. package/dist/ui/context.js +8 -0
  77. package/dist/ui/generated/styles.css +2 -0
  78. package/dist/ui/generated/styles.d.ts +2 -0
  79. package/dist/ui/generated/styles.js +3 -0
  80. package/dist/ui/styles.css +113 -0
  81. package/dist/ui/text.d.ts +5 -0
  82. package/dist/ui/text.js +11 -0
  83. package/dist/visual-comments.svelte.d.ts +230 -0
  84. package/dist/visual-comments.svelte.js +693 -0
  85. package/dist/vite/index.d.ts +20 -0
  86. package/dist/vite/index.js +78 -0
  87. package/dist-widget/visual-comments.js +58 -0
  88. package/package.json +86 -3
@@ -0,0 +1,20 @@
1
+ import type { CommentImage } from '../shared/types.js';
2
+ type $$ComponentProps = {
3
+ /** `new` floats beside its pin; `reply` and `edit` sit inside a thread. */
4
+ variant?: 'new' | 'reply' | 'edit';
5
+ placeholder?: string;
6
+ initial?: string;
7
+ /** The images of a comment being edited. */
8
+ initialImages?: CommentImage[];
9
+ autofocus?: boolean;
10
+ onsubmit: (body: string, images: CommentImage[]) => Promise<unknown> | unknown;
11
+ oncancel?: () => void;
12
+ oninput?: (value: string) => void;
13
+ /** How many images the field holds, as it changes. */
14
+ onimages?: (count: number) => void;
15
+ };
16
+ declare const Composer: import("svelte").Component<$$ComponentProps, {
17
+ focus: () => void;
18
+ }, "">;
19
+ type Composer = ReturnType<typeof Composer>;
20
+ export default Composer;
@@ -0,0 +1,128 @@
1
+ <svelte:options css="injected" />
2
+
3
+ <!--
4
+ Approving an agent. `visual-comments connect` sends the person to
5
+ `#vc-connect=<id>`; Allow adds the agent to the space, under its own name.
6
+ Signed out, it offers to sign in first; the address keeps the request.
7
+ -->
8
+ <script lang="ts">
9
+ import type { Agent, AgentRequest } from '../shared/types.js';
10
+ import { useComments } from './context.js';
11
+
12
+ let { id }: { id: string } = $props();
13
+
14
+ const vc = useComments();
15
+ const space = $derived(vc.store.spaceName || 'this space');
16
+ const signedIn = $derived(Boolean(vc.viewer));
17
+ let request = $state<AgentRequest>();
18
+ let joined = $state<Agent>();
19
+ let failed = $state<string>();
20
+ let busy = $state(false);
21
+
22
+ $effect(() => {
23
+ if (!signedIn) return;
24
+ let current = true;
25
+ request = undefined;
26
+ failed = undefined;
27
+ vc.store.agentRequest(id).then(
28
+ (found) => { if (current) request = found; },
29
+ (error: Error) => {
30
+ if (!current) return;
31
+ failed = error.message;
32
+ // The sign-in may have lapsed since the page loaded; then this offers it again.
33
+ void vc.store.refresh();
34
+ }
35
+ );
36
+ return () => { current = false; };
37
+ });
38
+
39
+ async function answer(allow: boolean) {
40
+ busy = true;
41
+ failed = undefined;
42
+ try {
43
+ if (allow) joined = await vc.store.approveAgent(id);
44
+ else {
45
+ await vc.store.denyAgent(id);
46
+ vc.endConnect();
47
+ }
48
+ } catch (error) {
49
+ failed = (error as Error).message;
50
+ } finally {
51
+ busy = false;
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <dialog
57
+ {@attach (node) => node.showModal()}
58
+ onclose={() => vc.endConnect()}
59
+ aria-labelledby="vc-connect-title"
60
+ class="vc-ui pointer-events-auto m-auto w-[360px] max-w-[calc(100vw-24px)] rounded-[12px] bg-white p-5 text-[13px] text-vc-ink shadow-[0_8px_40px_rgb(0_0_0/0.28)] outline-none backdrop:bg-black/30"
61
+ >
62
+ {#if !vc.viewer}
63
+ <h2 id="vc-connect-title" class="text-[15px] font-semibold">An agent wants to join</h2>
64
+ {#if vc.loading}
65
+ <p class="mt-2 text-vc-muted">Connecting…</p>
66
+ {:else}
67
+ <p class="mt-2 text-vc-muted">Sign in to see which agent asks, and to let it read and write comments here.</p>
68
+ <div class="mt-5 flex justify-end gap-2">
69
+ <form method="dialog"><button class="vc-dialog-button">Cancel</button></form>
70
+ <button type="button" class="vc-dialog-button vc-dialog-primary" onclick={() => vc.signIn()}>Sign in</button>
71
+ </div>
72
+ {/if}
73
+ {:else if joined}
74
+ <h2 id="vc-connect-title" class="text-[15px] font-semibold">{joined.name} joined {space}</h2>
75
+ <p class="mt-2 text-vc-muted">It sees new comments and replies under its own name. Remove it from your account menu at any time.</p>
76
+ <form method="dialog" class="mt-5 flex justify-end">
77
+ <button class="vc-dialog-button vc-dialog-primary">Done</button>
78
+ </form>
79
+ {:else if request}
80
+ <h2 id="vc-connect-title" class="text-[15px] font-semibold">{request.name} wants to join {space} as an agent</h2>
81
+ <p class="mt-2 text-vc-muted">
82
+ It will see every comment here, and may reply and act on them under its own name. Allow it only if you started it.
83
+ </p>
84
+ <p class="mt-4 text-vc-muted">The agent shows this code:</p>
85
+ <p class="mt-1 rounded-[8px] bg-vc-surface py-2 text-center font-mono text-xl tracking-[0.2em]">{request.code}</p>
86
+ {#if failed}<p role="alert" class="mt-3 text-red-600">{failed}</p>{/if}
87
+ <div class="mt-5 flex justify-end gap-2">
88
+ <button type="button" class="vc-dialog-button" disabled={busy} onclick={() => answer(false)}>Deny</button>
89
+ <button type="button" class="vc-dialog-button vc-dialog-primary" disabled={busy} onclick={() => answer(true)}>Allow</button>
90
+ </div>
91
+ {:else if failed}
92
+ <h2 id="vc-connect-title" class="text-[15px] font-semibold">This request cannot be answered</h2>
93
+ <p class="mt-2 text-vc-muted">{failed}</p>
94
+ <form method="dialog" class="mt-5 flex justify-end">
95
+ <button class="vc-dialog-button">Close</button>
96
+ </form>
97
+ {:else}
98
+ <h2 id="vc-connect-title" class="text-[15px] font-semibold">An agent wants to join</h2>
99
+ <p class="mt-2 text-vc-muted">Loading…</p>
100
+ {/if}
101
+ </dialog>
102
+
103
+ <style>
104
+ .vc-dialog-button {
105
+ height: 2rem;
106
+ border-radius: 9999px;
107
+ padding: 0 0.875rem;
108
+ font-weight: 500;
109
+ background: var(--color-vc-wash);
110
+ outline: none;
111
+ }
112
+ .vc-dialog-button:hover:not(:disabled) {
113
+ filter: brightness(0.96);
114
+ }
115
+ .vc-dialog-button:focus-visible {
116
+ box-shadow: 0 0 0 2px var(--color-vc-comment);
117
+ }
118
+ .vc-dialog-button:disabled {
119
+ opacity: 0.6;
120
+ }
121
+ .vc-dialog-primary {
122
+ background: var(--color-vc-comment);
123
+ color: white;
124
+ }
125
+ .vc-dialog-primary:focus-visible {
126
+ box-shadow: 0 0 0 2px white, 0 0 0 4px var(--color-vc-comment);
127
+ }
128
+ </style>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ id: string;
3
+ };
4
+ declare const Connect: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Connect = ReturnType<typeof Connect>;
6
+ export default Connect;
@@ -0,0 +1,44 @@
1
+ <script lang="ts" module>
2
+ // 24×24 stroke icons.
3
+ const PATHS = {
4
+ canvas:
5
+ 'M3 6.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M14 6.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M3 17.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M14 17.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0',
6
+ 'chevron-left': 'M15 6l-6 6 6 6',
7
+ 'chevron-right': 'M9 6l6 6-6 6',
8
+ 'chevron-down': 'M6 9l6 6 6-6',
9
+ 'chevron-up': 'M6 15l6-6 6 6',
10
+ play: 'M7 5l12 7-12 7z',
11
+ external: 'M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5',
12
+ restart: 'M4 12a8 8 0 1 0 2.4-5.7M4 4v4h4',
13
+ check: 'M5 12.5l4.5 4.5L19 7.5',
14
+ plus: 'M12 5v14M5 12h14',
15
+ minus: 'M5 12h14',
16
+ fit: 'M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5',
17
+ comment: 'M12 3.5a8.5 8.5 0 1 1 0 17H3.5V12A8.5 8.5 0 0 1 12 3.5z',
18
+ search: 'M11 4.5a6.5 6.5 0 1 0 0 13a6.5 6.5 0 1 0 0-13zM20 20l-4.4-4.4',
19
+ filter: 'M4 7h16M7 12h10M10 17h4',
20
+ more: 'M5 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2zM12 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2zM19 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2z',
21
+ resolve: 'M12 3a9 9 0 1 0 0 18a9 9 0 1 0 0-18zM8 12.2l2.7 2.7L16 9.6',
22
+ close: 'M6 6l12 12M18 6L6 18',
23
+ send: 'M12 19V5M6.5 10.5L12 5l5.5 5.5'
24
+ } as const;
25
+ export type IconName = keyof typeof PATHS;
26
+ </script>
27
+
28
+ <script lang="ts">
29
+ let { name, size = 16 }: { name: IconName; size?: number } = $props();
30
+ </script>
31
+
32
+ <svg
33
+ width={size}
34
+ height={size}
35
+ viewBox="0 0 24 24"
36
+ fill="none"
37
+ stroke="currentColor"
38
+ stroke-width="1.75"
39
+ stroke-linecap="round"
40
+ stroke-linejoin="round"
41
+ aria-hidden="true"
42
+ >
43
+ <path d={PATHS[name]} />
44
+ </svg>
@@ -0,0 +1,29 @@
1
+ declare const PATHS: {
2
+ readonly canvas: "M3 6.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M14 6.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M3 17.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M14 17.5a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0";
3
+ readonly 'chevron-left': "M15 6l-6 6 6 6";
4
+ readonly 'chevron-right': "M9 6l6 6-6 6";
5
+ readonly 'chevron-down': "M6 9l6 6 6-6";
6
+ readonly 'chevron-up': "M6 15l6-6 6 6";
7
+ readonly play: "M7 5l12 7-12 7z";
8
+ readonly external: "M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5";
9
+ readonly restart: "M4 12a8 8 0 1 0 2.4-5.7M4 4v4h4";
10
+ readonly check: "M5 12.5l4.5 4.5L19 7.5";
11
+ readonly plus: "M12 5v14M5 12h14";
12
+ readonly minus: "M5 12h14";
13
+ readonly fit: "M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5";
14
+ readonly comment: "M12 3.5a8.5 8.5 0 1 1 0 17H3.5V12A8.5 8.5 0 0 1 12 3.5z";
15
+ readonly search: "M11 4.5a6.5 6.5 0 1 0 0 13a6.5 6.5 0 1 0 0-13zM20 20l-4.4-4.4";
16
+ readonly filter: "M4 7h16M7 12h10M10 17h4";
17
+ readonly more: "M5 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2zM12 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2zM19 11a1 1 0 1 0 0 2a1 1 0 1 0 0-2z";
18
+ readonly resolve: "M12 3a9 9 0 1 0 0 18a9 9 0 1 0 0-18zM8 12.2l2.7 2.7L16 9.6";
19
+ readonly close: "M6 6l12 12M18 6L6 18";
20
+ readonly send: "M12 19V5M6.5 10.5L12 5l5.5 5.5";
21
+ };
22
+ export type IconName = keyof typeof PATHS;
23
+ type $$ComponentProps = {
24
+ name: IconName;
25
+ size?: number;
26
+ };
27
+ declare const Icon: import("svelte").Component<$$ComponentProps, {}, "">;
28
+ type Icon = ReturnType<typeof Icon>;
29
+ export default Icon;
@@ -0,0 +1,69 @@
1
+ <!-- A button that opens a popover menu below it. Items close the menu with `close()`. -->
2
+ <script lang="ts">
3
+ import type { Snippet } from 'svelte';
4
+
5
+ let {
6
+ label,
7
+ trigger,
8
+ children,
9
+ align = 'start',
10
+ tone = 'light',
11
+ class: className = '',
12
+ onopen
13
+ }: {
14
+ /** Accessible name for the trigger. */
15
+ label: string;
16
+ trigger: Snippet;
17
+ children: Snippet<[close: () => void]>;
18
+ align?: 'start' | 'end';
19
+ tone?: 'light' | 'dark';
20
+ class?: string;
21
+ /** Called as it opens, e.g. to load what it lists. */
22
+ onopen?: () => void;
23
+ } = $props();
24
+
25
+ const id = $props.id();
26
+ let button = $state<HTMLButtonElement>();
27
+ let popover = $state<HTMLDivElement>();
28
+ let open = $state(false);
29
+
30
+ function place(event: ToggleEvent) {
31
+ open = event.newState === 'open';
32
+ if (event.newState !== 'open' || !button || !popover) return;
33
+ onopen?.();
34
+ const rect = button.getBoundingClientRect();
35
+ const gutter = 12;
36
+ const left = align === 'start' ? rect.left : rect.right - popover.offsetWidth;
37
+ popover.style.left = `${Math.max(gutter, Math.min(left, window.innerWidth - popover.offsetWidth - gutter))}px`;
38
+ popover.style.top = `${Math.max(gutter, Math.min(rect.bottom + 6, window.innerHeight - popover.offsetHeight - gutter))}px`;
39
+ }
40
+
41
+ const close = () => popover?.hidePopover();
42
+ </script>
43
+
44
+ <button
45
+ bind:this={button}
46
+ type="button"
47
+ popovertarget="vc-menu-{id}"
48
+ aria-label={label}
49
+ aria-expanded={open}
50
+ aria-controls="vc-menu-{id}"
51
+ title={label}
52
+ class={className}
53
+ >
54
+ {@render trigger()}
55
+ </button>
56
+ <div
57
+ bind:this={popover}
58
+ id="vc-menu-{id}"
59
+ popover="auto"
60
+ ontoggle={place}
61
+ class={[
62
+ 'vc-menu vc-ui fixed inset-auto m-0 max-h-[min(70vh,560px)] w-max min-w-52 max-w-[calc(100vw-24px)] overflow-y-auto rounded border p-1 text-[13px] shadow-lg',
63
+ tone === 'dark'
64
+ ? 'border-white/10 bg-vc-ink text-white/90'
65
+ : 'border-vc-line bg-vc-surface text-vc-ink'
66
+ ]}
67
+ >
68
+ {@render children(close)}
69
+ </div>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ /** Accessible name for the trigger. */
4
+ label: string;
5
+ trigger: Snippet;
6
+ children: Snippet<[close: () => void]>;
7
+ align?: 'start' | 'end';
8
+ tone?: 'light' | 'dark';
9
+ class?: string;
10
+ /** Called as it opens, e.g. to load what it lists. */
11
+ onopen?: () => void;
12
+ };
13
+ declare const Menu: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type Menu = ReturnType<typeof Menu>;
15
+ export default Menu;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Icon from './Icon.svelte';
4
+
5
+ let {
6
+ onselect,
7
+ checked,
8
+ children,
9
+ hint,
10
+ tone = 'light'
11
+ }: {
12
+ onselect: () => void;
13
+ /** Shows a check mark column when not undefined. */
14
+ checked?: boolean;
15
+ children: Snippet;
16
+ hint?: string;
17
+ tone?: 'light' | 'dark';
18
+ } = $props();
19
+ </script>
20
+
21
+ <button
22
+ type="button"
23
+ role="menuitem"
24
+ onclick={onselect}
25
+ class={[
26
+ 'flex w-full items-center gap-2 rounded-[2px] px-2 py-1.5 text-left',
27
+ tone === 'dark' ? 'hover:bg-white/10 focus-visible:bg-white/10' : 'hover:bg-vc-wash focus-visible:bg-vc-wash',
28
+ 'outline-none'
29
+ ]}
30
+ >
31
+ {#if checked !== undefined}
32
+ <span class="w-4 shrink-0 text-vc-accent">
33
+ {#if checked}<Icon name="check" size={14} />{/if}
34
+ </span>
35
+ {/if}
36
+ <span class="min-w-0 flex-1 truncate">{@render children()}</span>
37
+ {#if hint}<span class="shrink-0 opacity-50">{hint}</span>{/if}
38
+ </button>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ onselect: () => void;
4
+ /** Shows a check mark column when not undefined. */
5
+ checked?: boolean;
6
+ children: Snippet;
7
+ hint?: string;
8
+ tone?: 'light' | 'dark';
9
+ };
10
+ declare const MenuItem: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type MenuItem = ReturnType<typeof MenuItem>;
12
+ export default MenuItem;