@inkandswitch/patchwork-pkg-base 0.0.53 → 0.0.55

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 (26) hide show
  1. package/package.json +1 -1
  2. package/packages/account/dist/{AccountPicker-HMEN6MEN.js → AccountPicker-HB6MDLKN.js} +390 -123
  3. package/packages/account/dist/AccountPicker-HB6MDLKN.js.map +7 -0
  4. package/packages/account/dist/{ContactAvatar-KCZOCABR.js → ContactAvatar-KM4BONY5.js} +7 -7
  5. package/packages/account/dist/ContactAvatar-KM4BONY5.js.map +7 -0
  6. package/packages/account/dist/{ContactCursor-RLXRAX65.js → ContactCursor-4FYCATG5.js} +6 -6
  7. package/packages/account/dist/ContactCursor-4FYCATG5.js.map +7 -0
  8. package/packages/account/dist/index.css +96 -15
  9. package/packages/account/dist/index.css.map +2 -2
  10. package/packages/account/dist/index.js +3 -3
  11. package/packages/codemirror-base/dist/{chunk-H5POLO74.js → chunk-AKNT5U37.js} +43 -28
  12. package/packages/codemirror-base/dist/chunk-AKNT5U37.js.map +7 -0
  13. package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
  14. package/packages/codemirror-base/dist/tool.js +4 -1
  15. package/packages/codemirror-base/dist/tool.js.map +2 -2
  16. package/packages/drafts/dist/assets/{main-CCEftK7X.js → main-EEciTwbZ.js} +6 -8
  17. package/packages/drafts/dist/assets/{main-CCEftK7X.js.map → main-EEciTwbZ.js.map} +1 -1
  18. package/packages/drafts/dist/index.js +3 -3
  19. package/packages/tldraw4/dist/tool.js +3 -23
  20. package/packages/tldraw4/dist/tool.js.map +2 -2
  21. package/packages/account/dist/AccountPicker-HMEN6MEN.js.map +0 -7
  22. package/packages/account/dist/ContactAvatar-KCZOCABR.js.map +0 -7
  23. package/packages/account/dist/ContactCursor-RLXRAX65.js.map +0 -7
  24. package/packages/account/dist/chunk-3YUNEZX6.js +0 -38
  25. package/packages/account/dist/chunk-3YUNEZX6.js.map +0 -7
  26. package/packages/codemirror-base/dist/chunk-H5POLO74.js.map +0 -7
@@ -4,9 +4,6 @@ import {
4
4
  setAvatarFallback,
5
5
  setAvatarImage
6
6
  } from "./chunk-PLRZBPU4.js";
7
- import {
8
- generateColorFromString
9
- } from "./chunk-3YUNEZX6.js";
10
7
  import "./chunk-5WRI5ZAA.js";
11
8
 
12
9
  // src/contact/ContactAvatar.ts
@@ -58,16 +55,19 @@ function renderContactAvatar(handle, element) {
58
55
  updatePresence();
59
56
  }
60
57
  function updatePresence() {
61
- const docUrl = handle.url;
62
58
  const now = Date.now();
63
59
  const timeout = 3e4;
64
60
  const isPresent = Object.entries(heartbeats).some(
65
61
  ([id, ts]) => id !== selfName && now - ts < timeout
66
62
  );
67
- const userColor = generateColorFromString(docUrl);
63
+ const userColor = handle.doc()?.color;
68
64
  if (isPresent) {
69
65
  avatar.classList.add("contact-avatar--present");
70
- avatar.style.setProperty("--contact-presence-color", userColor);
66
+ if (userColor) {
67
+ avatar.style.setProperty("--contact-presence-color", userColor);
68
+ } else {
69
+ avatar.style.removeProperty("--contact-presence-color");
70
+ }
71
71
  } else {
72
72
  avatar.classList.remove("contact-avatar--present");
73
73
  avatar.style.removeProperty("--contact-presence-color");
@@ -138,4 +138,4 @@ function renderContactAvatar(handle, element) {
138
138
  export {
139
139
  renderContactAvatar
140
140
  };
141
- //# sourceMappingURL=ContactAvatar-KCZOCABR.js.map
141
+ //# sourceMappingURL=ContactAvatar-KM4BONY5.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/contact/ContactAvatar.ts", "../node_modules/.pnpm/@inkandswitch+patchwork-providers@0.2.2_@automerge+automerge-repo@2.6.0-subduction.12/node_modules/@inkandswitch/patchwork-providers/dist/index.js"],
4
+ "sourcesContent": ["import type { AutomergeUrl, DocHandle } from \"@automerge/automerge-repo/slim\";\nimport type { ToolElement } from \"@inkandswitch/patchwork-plugins\";\nimport type { ContactDoc } from \"../types\";\nimport { createAvatar, setAvatarImage, setAvatarFallback, getInitials } from \"./Avatar\";\nimport { getImportableUrlFromAutomergeUrl } from \"@inkandswitch/patchwork-filesystem\";\nimport { subscribe } from \"@inkandswitch/patchwork-providers\";\n\ndeclare global {\n interface Window {\n accountDocHandle?: { url: AutomergeUrl };\n }\n}\n\ntype TinyPatchworkLayoutDoc = {\n contactUrl: AutomergeUrl;\n};\n\nexport function renderContactAvatar(\n handle: DocHandle<ContactDoc>,\n element: ToolElement\n) {\n const avatar = createAvatar(\"default\");\n element.appendChild(avatar);\n\n let selfName = \"me\";\n const cleanups: (() => void)[] = [];\n\n // Track presence via ephemeral messages\n const heartbeats: Record<string, number> = {};\n let presenceTimer: ReturnType<typeof setInterval>;\n\n function onEphemeralMessage(message: { senderId: string; message: unknown }) {\n // useRemoteAwareness tracks heartbeats from ephemeral messages\n if (message.senderId) {\n heartbeats[message.senderId] = Date.now();\n }\n updatePresence();\n }\n\n function updatePresence() {\n const now = Date.now();\n const timeout = 30000;\n const isPresent = Object.entries(heartbeats).some(\n ([id, ts]) => id !== selfName && now - ts < timeout\n );\n\n // The contact's chosen color; without one, the CSS fallback (neutral\n // gray) applies.\n const userColor = handle.doc()?.color;\n\n if (isPresent) {\n avatar.classList.add(\"contact-avatar--present\");\n if (userColor) {\n avatar.style.setProperty(\"--contact-presence-color\", userColor);\n } else {\n avatar.style.removeProperty(\"--contact-presence-color\");\n }\n } else {\n avatar.classList.remove(\"contact-avatar--present\");\n avatar.style.removeProperty(\"--contact-presence-color\");\n }\n }\n\n handle.on(\"ephemeral-message\", onEphemeralMessage);\n cleanups.push(() => handle.off(\"ephemeral-message\", onEphemeralMessage));\n\n // Periodically clean up stale heartbeats\n presenceTimer = setInterval(updatePresence, 5000);\n cleanups.push(() => clearInterval(presenceTimer));\n\n // Get own contact via provider subscription\n const unsubscribe = subscribe<string>(\n element,\n { type: \"patchwork:contact\" },\n (contactUrl) => {\n element.repo\n .find<ContactDoc>(contactUrl as AutomergeUrl)\n .then((selfHandle) => {\n const selfDoc = selfHandle.doc();\n if (selfDoc?.type === \"registered\") {\n selfName = selfDoc.name;\n }\n });\n }\n );\n cleanups.push(unsubscribe);\n\n // Fallback: use window.accountDocHandle\n if (window.accountDocHandle) {\n element.repo\n .find<TinyPatchworkLayoutDoc>(window.accountDocHandle.url)\n .then((accountHandle) => {\n const accountDoc = accountHandle.doc();\n if (accountDoc?.contactUrl) {\n element.repo\n .find<ContactDoc>(accountDoc.contactUrl)\n .then((selfHandle) => {\n const selfDoc = selfHandle.doc();\n if (selfDoc?.type === \"registered\") {\n selfName = selfDoc.name;\n }\n });\n }\n });\n }\n\n async function update() {\n const contact = handle.doc();\n if (!contact) {\n avatar.style.display = \"none\";\n return;\n }\n avatar.style.display = \"\";\n\n const isRegistered = contact.type === \"registered\";\n const name = isRegistered ? contact.name : \"Anonymous\";\n\n // avatar image\n let avatarImgUrl: string | undefined;\n if (isRegistered && contact.avatarUrl) {\n try {\n const avatarHandle = await element.repo.find(contact.avatarUrl);\n avatarImgUrl = getImportableUrlFromAutomergeUrl(avatarHandle.url);\n } catch {\n // ignore failed avatar lookup\n }\n }\n setAvatarImage(avatar, avatarImgUrl, name);\n\n // fallback\n if (isRegistered && name) {\n setAvatarFallback(avatar, { initials: getInitials(name) });\n } else {\n setAvatarFallback(avatar, { iconSize: 20 });\n }\n\n updatePresence();\n }\n\n handle.on(\"change\", update);\n cleanups.push(() => handle.off(\"change\", update));\n update();\n\n return () => {\n for (const cleanup of cleanups) cleanup();\n };\n}\n", "/**\n * Open a streaming subscription. Dispatches a `patchwork:subscribe` for the\n * given `selector` carrying a fresh `MessageChannel` port; the answering\n * provider pushes values back over that channel for as long as the\n * subscription is live. `listener` is invoked once per emission (the first\n * delivery is always async because `MessagePort.postMessage` queues a task).\n *\n * The event is dispatched from the nearest enclosing `<patchwork-view>` so\n * callers can pass any node inside a provider subtree. An unclaimed\n * subscription is never settled: if no provider answers, `listener` simply\n * never fires.\n *\n * Returns an unsubscribe function. Calling it tells the provider to tear down\n * (via an `unsubscribe` message) and closes the consumer's port; any values\n * the provider emits after that are dropped.\n */\nexport function subscribe(element, selector, listener) {\n const view = element.closest(\"patchwork-view\");\n const dispatchEl = view ?? element;\n const channel = new MessageChannel();\n const port = channel.port2;\n const controller = new AbortController();\n const { signal } = controller;\n port.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"change\")\n listener(event.data.value);\n }, { signal });\n // addEventListener (unlike assigning onmessage) does not implicitly start\n // the port, so message delivery has to be kicked off explicitly.\n port.start();\n const detail = {\n selector,\n port: channel.port1,\n };\n dispatchEl.dispatchEvent(new CustomEvent(\"patchwork:subscribe\", {\n detail,\n bubbles: true,\n composed: true,\n }));\n return () => {\n if (signal.aborted)\n return;\n controller.abort();\n port.postMessage({ type: \"unsubscribe\" });\n port.close();\n };\n}\n/**\n * Answer a `patchwork:subscribe`. The `producer` receives a `respond`\n * callback it can call any number of times to push values to the consumer,\n * and may return a teardown that runs when the consumer unsubscribes. Stops\n * propagation so ancestor providers don't double-answer. Values emitted after\n * the consumer unsubscribes are dropped.\n */\nexport function accept(event, producer) {\n event.stopPropagation();\n const port = event.detail.port;\n let alive = true;\n const respond = (value) => {\n if (!alive)\n return;\n port.postMessage({ type: \"change\", value });\n };\n let teardown;\n try {\n teardown = producer(respond);\n }\n catch (err) {\n console.error(\"[patchwork-providers] subscribe producer threw:\", err);\n }\n const stop = () => {\n if (!alive)\n return;\n alive = false;\n try {\n teardown?.();\n }\n catch (err) {\n console.error(\"[patchwork-providers] subscribe teardown threw:\", err);\n }\n port.close();\n };\n port.onmessage = (event) => {\n if (event.data?.type === \"unsubscribe\")\n stop();\n };\n}\n/**\n * One-shot convenience wrapper over {@link subscribe}: opens a subscription\n * for `selector`, resolves with the first value a provider emits, then\n * immediately unsubscribes. Because there is no fallback provider, an\n * unclaimed selector never resolves \u2014 use `subscribe` directly if you need to\n * handle the \"no provider\" case.\n */\nexport function request(element, selector) {\n return new Promise((resolve) => {\n const unsubscribe = subscribe(element, selector, (value) => {\n // The first emission is always async (postMessage queues a task), so\n // `unsubscribe` is assigned by the time this fires.\n unsubscribe();\n resolve(value);\n });\n });\n}\nexport { registerRepoProviderElement, } from \"./repo-provider.js\";\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,6 +1,3 @@
1
- import {
2
- generateColorFromString
3
- } from "./chunk-3YUNEZX6.js";
4
1
  import "./chunk-5WRI5ZAA.js";
5
2
 
6
3
  // src/contact/ContactCursor.ts
@@ -16,8 +13,11 @@ function renderContactCursor(handle, element) {
16
13
  }
17
14
  token.style.display = "";
18
15
  const name = contact.type === "registered" ? contact.name : "Anonymous";
19
- const color = generateColorFromString(handle.url);
20
- token.style.setProperty("--contact-cursor-color", color);
16
+ if (contact.color) {
17
+ token.style.setProperty("--contact-cursor-color", contact.color);
18
+ } else {
19
+ token.style.removeProperty("--contact-cursor-color");
20
+ }
21
21
  token.textContent = name;
22
22
  token.title = name;
23
23
  }
@@ -31,4 +31,4 @@ function renderContactCursor(handle, element) {
31
31
  export {
32
32
  renderContactCursor
33
33
  };
34
- //# sourceMappingURL=ContactCursor-RLXRAX65.js.map
34
+ //# sourceMappingURL=ContactCursor-4FYCATG5.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/contact/ContactCursor.ts"],
4
+ "sourcesContent": ["import type { DocHandle } from \"@automerge/automerge-repo/slim\";\nimport type { ToolElement } from \"@inkandswitch/patchwork-plugins\";\nimport type { ContactDoc } from \"../types\";\n\n/**\n * A compact cursor token: the contact's name on a pill in their presence\n * color. Hosts that render remote cursors (text editors, canvases) mount it\n * next to their own cursor primitive (caret line, pointer arrow) via\n * `<patchwork-view doc-url={contactUrl} tool-id=\"contact-cursor\">`.\n */\nexport function renderContactCursor(\n handle: DocHandle<ContactDoc>,\n element: ToolElement\n) {\n const token = document.createElement(\"div\");\n token.className = \"contact-cursor-token\";\n element.appendChild(token);\n\n function update() {\n const contact = handle.doc();\n if (!contact) {\n token.style.display = \"none\";\n return;\n }\n token.style.display = \"\";\n const name = contact.type === \"registered\" ? contact.name : \"Anonymous\";\n // The contact's chosen color; without one, the CSS fallback (neutral\n // gray) applies.\n if (contact.color) {\n token.style.setProperty(\"--contact-cursor-color\", contact.color);\n } else {\n token.style.removeProperty(\"--contact-cursor-color\");\n }\n token.textContent = name;\n token.title = name;\n }\n\n handle.on(\"change\", update);\n update();\n\n return () => {\n handle.off(\"change\", update);\n token.remove();\n };\n}\n"],
5
+ "mappings": ";;;AAUO,SAAS,oBACd,QACA,SACA;AACA,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,YAAY;AAClB,UAAQ,YAAY,KAAK;AAEzB,WAAS,SAAS;AAChB,UAAM,UAAU,OAAO,IAAI;AAC3B,QAAI,CAAC,SAAS;AACZ,YAAM,MAAM,UAAU;AACtB;AAAA,IACF;AACA,UAAM,MAAM,UAAU;AACtB,UAAM,OAAO,QAAQ,SAAS,eAAe,QAAQ,OAAO;AAG5D,QAAI,QAAQ,OAAO;AACjB,YAAM,MAAM,YAAY,0BAA0B,QAAQ,KAAK;AAAA,IACjE,OAAO;AACL,YAAM,MAAM,eAAe,wBAAwB;AAAA,IACrD;AACA,UAAM,cAAc;AACpB,UAAM,QAAQ;AAAA,EAChB;AAEA,SAAO,GAAG,UAAU,MAAM;AAC1B,SAAO;AAEP,SAAO,MAAM;AACX,WAAO,IAAI,UAAU,MAAM;AAC3B,UAAM,OAAO;AAAA,EACf;AACF;",
6
+ "names": []
7
+ }
@@ -42,7 +42,7 @@
42
42
  height: 64px;
43
43
  }
44
44
  .contact-avatar--present {
45
- box-shadow: 0 0 0 2px var(--contact-presence-color);
45
+ box-shadow: 0 0 0 2px var(--contact-presence-color, #888);
46
46
  }
47
47
  .contact-avatar-image {
48
48
  position: absolute;
@@ -118,26 +118,50 @@
118
118
  padding: 1.5rem 2rem;
119
119
  color: var(--account-picker-fg);
120
120
  & .profile-header {
121
+ position: relative;
121
122
  display: flex;
122
- align-items: center;
123
+ align-items: flex-start;
123
124
  gap: 0.75rem;
124
125
  margin-bottom: 1.5rem;
125
126
  }
126
- & .avatar-area {
127
+ & .identity {
127
128
  position: relative;
128
129
  flex-shrink: 0;
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: flex-start;
133
+ gap: 0.125rem;
134
+ }
135
+ & .presence-slot {
136
+ width: 0;
137
+ overflow: visible;
129
138
  }
130
139
  & .avatar-button {
140
+ position: relative;
131
141
  background: none;
132
142
  border: none;
133
143
  padding: 0;
134
- cursor: pointer;
144
+ margin-inline-start: 5px;
135
145
  border-radius: 50%;
136
146
  flex-shrink: 0;
137
147
  overflow: hidden;
138
148
  width: 56px;
139
149
  height: 56px;
140
150
  display: block;
151
+ cursor: pointer;
152
+ &::after {
153
+ content: "";
154
+ position: absolute;
155
+ inset: 0;
156
+ border-radius: 50%;
157
+ background: rgb(0 0 0 / 30%);
158
+ opacity: 0;
159
+ transition: opacity 100ms;
160
+ }
161
+ &:hover::after,
162
+ &:focus-visible::after {
163
+ opacity: 1;
164
+ }
141
165
  & patchwork-view {
142
166
  display: block;
143
167
  width: 56px;
@@ -154,6 +178,7 @@
154
178
  }
155
179
  }
156
180
  & .input.profile-name-input {
181
+ margin-top: 12px;
157
182
  font-size: 1.125rem;
158
183
  font-weight: 600;
159
184
  border: 1px solid transparent;
@@ -308,17 +333,36 @@
308
333
  font-size: 0.75rem;
309
334
  color: var(--account-picker-tooltip-fg);
310
335
  }
311
- & .color-trigger {
312
- position: absolute;
313
- bottom: -1px;
314
- right: -1px;
315
- width: 18px;
316
- height: 18px;
317
- border-radius: 50%;
318
- border: 2px solid var(--account-picker-bg);
336
+ & .presence-cursor {
337
+ display: inline-flex;
338
+ align-items: flex-start;
339
+ background: none;
340
+ border: none;
341
+ padding: 0;
319
342
  cursor: pointer;
320
- z-index: 1;
321
- box-shadow: 0 1px 3px rgb(0 0 0 / 15%);
343
+ &:hover .presence-nametag {
344
+ opacity: 0.85;
345
+ }
346
+ }
347
+ & .presence-cursor-arrow {
348
+ width: 16px;
349
+ height: 24px;
350
+ flex-shrink: 0;
351
+ filter: drop-shadow(0 1px 1px rgb(0 0 0 / 25%));
352
+ }
353
+ & .presence-nametag {
354
+ margin-top: 12px;
355
+ margin-inline-start: -3px;
356
+ padding: 2px 6px;
357
+ border-radius: 4px;
358
+ font-size: 0.75rem;
359
+ font-weight: 500;
360
+ line-height: 1.3;
361
+ color: white;
362
+ max-width: 12rem;
363
+ overflow: hidden;
364
+ text-overflow: ellipsis;
365
+ white-space: nowrap;
322
366
  }
323
367
  & .color-popup {
324
368
  position: absolute;
@@ -329,7 +373,7 @@
329
373
  border: 1px solid var(--account-picker-border);
330
374
  border-radius: 8px;
331
375
  box-shadow: 0 4px 16px rgb(0 0 0 / 12%);
332
- z-index: 10;
376
+ z-index: 30;
333
377
  }
334
378
  & .color-grid {
335
379
  display: grid;
@@ -455,6 +499,43 @@
455
499
  max-width: 90vw;
456
500
  box-shadow: 0 8px 30px rgb(0 0 0 / 10%);
457
501
  }
502
+ & .cropper-modal {
503
+ width: auto;
504
+ align-items: center;
505
+ }
506
+ & .cropper-viewport {
507
+ position: relative;
508
+ width: 260px;
509
+ height: 260px;
510
+ overflow: hidden;
511
+ border-radius: 6px;
512
+ background: var(--account-picker-border);
513
+ cursor: grab;
514
+ touch-action: none;
515
+ &:active {
516
+ cursor: grabbing;
517
+ }
518
+ }
519
+ & .cropper-image {
520
+ position: absolute;
521
+ top: 0;
522
+ left: 0;
523
+ transform-origin: 0 0;
524
+ max-width: none;
525
+ user-select: none;
526
+ -webkit-user-drag: none;
527
+ }
528
+ & .cropper-mask {
529
+ position: absolute;
530
+ inset: 0;
531
+ border-radius: 50%;
532
+ box-shadow: 0 0 0 9999px rgb(0 0 0 / 45%);
533
+ pointer-events: none;
534
+ }
535
+ & .cropper-zoom {
536
+ width: 260px;
537
+ accent-color: var(--account-picker-fg);
538
+ }
458
539
  & .modal-title {
459
540
  font-size: 0.875rem;
460
541
  font-weight: 600;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/contact/styles.css", "../src/picker/styles.css"],
4
- "sourcesContent": ["@layer package {\n:root,\n:host,\n[theme] {\n --contact-fg: var(--editor-line, black);\n --contact-muted-fg: var(--editor-line-offset-50, #999);\n --contact-border: var(--editor-fill-offset-10, #e5e5e5);\n --contact-fallback-bg: var(--editor-fill-offset-20, #ccc);\n --contact-fallback-fg: var(--editor-line-offset-30, #4d4d4d);\n --contact-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Avatar */\n\n.contact-avatar {\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n overflow: hidden;\n border-radius: 9999px;\n border: 1px solid var(--contact-border);\n min-width: 2rem;\n min-height: 2rem;\n max-width: 2rem;\n max-height: 2rem;\n height: 100%;\n width: 100%;\n}\n\n.contact-avatar--sm {\n min-width: initial;\n min-height: initial;\n max-width: initial;\n max-height: initial;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.contact-avatar--lg {\n min-width: initial;\n min-height: initial;\n max-width: initial;\n max-height: initial;\n width: 64px;\n height: 64px;\n}\n\n.contact-avatar--present {\n box-shadow: 0 0 0 2px var(--contact-presence-color);\n}\n\n.contact-avatar-image {\n position: absolute;\n inset: 0;\n z-index: 1;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.contact-avatar-fallback {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 9999px;\n background: var(--contact-fallback-bg);\n color: var(--contact-fallback-fg);\n}\n\n/* Contact Viewer */\n\n.contact-viewer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n color: var(--contact-fg);\n}\n\n.contact-viewer-name {\n margin: 0;\n font-size: 1.25rem;\n font-weight: 600;\n}\n\n.contact-viewer-label {\n margin: 0;\n font-size: 0.875rem;\n color: var(--contact-muted-fg);\n}\n\n/* Cursor token (tool id \"contact-cursor\") \u2014 the contact's name on a pill in\n their presence color. Hosts overlay it next to their own cursor primitive,\n so it must not intercept pointer events. The pill background is the user's\n presence color (not a theme surface), so the label uses white ink, which is\n legible across the whole USER_COLOR_PALETTE. */\n\n.contact-cursor-token {\n display: inline-block;\n max-width: 160px;\n padding: 2px 6px;\n border-radius: 4px;\n background: var(--contact-cursor-color, #888);\n color: white;\n font-family: var(--contact-family, system-ui, sans-serif);\n font-size: 11px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n user-select: none;\n pointer-events: none;\n}\n", "@layer package {\n:root,\n:host,\n[theme] {\n --account-picker-fg: var(--studio-line, black);\n --account-picker-muted: var(--studio-line-offset-50, #999);\n --account-picker-bg: var(--studio-fill, white);\n --account-picker-border: var(--studio-fill-offset-10, #e5e5e5);\n --account-picker-hover: var(--studio-fill-offset-10, #e5e5e5);\n --account-picker-tooltip-bg: var(--studio-line, black);\n --account-picker-tooltip-fg: var(--studio-fill, white);\n}\n}\n\n.account-picker {\n width: 100%;\n height: 100%;\n overflow: auto;\n padding: 1.5rem 2rem;\n color: var(--account-picker-fg);\n\n & .profile-header {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n }\n\n & .avatar-area {\n position: relative;\n flex-shrink: 0;\n }\n\n & .avatar-button {\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n border-radius: 50%;\n flex-shrink: 0;\n overflow: hidden;\n width: 56px;\n height: 56px;\n display: block;\n\n & patchwork-view {\n display: block;\n width: 56px;\n height: 56px;\n overflow: hidden;\n }\n\n & img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n border: none;\n outline: none;\n }\n }\n\n & .input.profile-name-input {\n font-size: 1.125rem;\n font-weight: 600;\n border: 1px solid transparent;\n background: transparent;\n padding: 0.25rem 0.375rem;\n border-radius: 4px;\n height: auto;\n\n &:focus-visible {\n outline: 2px solid var(--account-picker-fg);\n outline-offset: 1px;\n }\n }\n\n & .actions {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 1rem;\n }\n\n & .section {\n margin-bottom: 0.25rem;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0.25rem;\n }\n\n & .field-group {\n display: grid;\n width: 100%;\n gap: 0.5rem;\n padding-block: 0.5rem;\n }\n\n & .input-row {\n display: flex;\n gap: 0.25rem;\n }\n\n & .tabs-list {\n display: grid;\n grid-template-columns: 1fr 1fr;\n height: 2rem;\n align-items: center;\n border-radius: 6px;\n background: var(--account-picker-border);\n padding: 2px;\n color: var(--account-picker-muted);\n }\n\n & .tabs-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0.25rem 0.5rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n border: none;\n background: transparent;\n color: inherit;\n transition: all 100ms;\n\n &[data-selected] {\n background: var(--account-picker-bg);\n color: var(--account-picker-fg);\n box-shadow: 0 1px 2px rgb(0 0 0 / 6%);\n }\n }\n\n & .tabs-content {\n margin-top: 0.5rem;\n }\n\n & .btn {\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n border-radius: 4px;\n font-size: 0.8125rem;\n font-weight: 500;\n border: none;\n height: 2rem;\n padding: 0 0.75rem;\n transition: all 100ms;\n color: var(--account-picker-fg);\n\n &:disabled {\n pointer-events: none;\n opacity: 0.3;\n }\n\n &.default {\n background: var(--account-picker-fg);\n color: var(--account-picker-bg);\n\n &:hover {\n opacity: 0.85;\n }\n }\n\n &.outline,\n &.secondary,\n &.ghost {\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n box-shadow: 0 1px 2px rgb(0 0 0 / 5%);\n\n &:hover {\n background: var(--account-picker-hover);\n box-shadow: none;\n }\n }\n }\n\n & .input {\n display: flex;\n height: 2rem;\n width: 100%;\n border-radius: 4px;\n border: 1px solid var(--account-picker-border);\n background: var(--account-picker-bg);\n padding: 0.375rem 0.5rem;\n font-size: 0.8125rem;\n color: var(--account-picker-fg);\n\n &::placeholder {\n color: var(--account-picker-muted);\n }\n\n &:focus-visible {\n outline: 2px solid var(--account-picker-fg);\n outline-offset: 1px;\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &.valid {\n border-color: hsl(140 40% 60%);\n }\n }\n\n & .label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--account-picker-muted);\n }\n\n & .tooltip-trigger {\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n height: 2rem;\n width: 2rem;\n border: none;\n background: transparent;\n color: inherit;\n\n &:hover {\n background: var(--account-picker-hover);\n }\n }\n\n & .tooltip-content {\n z-index: 50;\n border-radius: 4px;\n background: var(--account-picker-tooltip-bg);\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n color: var(--account-picker-tooltip-fg);\n }\n\n /* ---- Color picker ---- */\n\n & .color-trigger {\n position: absolute;\n bottom: -1px;\n right: -1px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2px solid var(--account-picker-bg);\n cursor: pointer;\n z-index: 1;\n box-shadow: 0 1px 3px rgb(0 0 0 / 15%);\n }\n\n & .color-popup {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n padding: 6px;\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgb(0 0 0 / 12%);\n z-index: 10;\n }\n\n & .color-grid {\n display: grid;\n grid-template-columns: repeat(7, 24px);\n gap: 2px;\n }\n\n & .color-swatch {\n width: 24px;\n height: 24px;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 3px;\n\n &:hover {\n opacity: 0.75;\n }\n\n &.selected {\n box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.5);\n }\n }\n\n & .color-swatch-custom {\n position: relative;\n overflow: hidden;\n cursor: pointer;\n background: conic-gradient(\n hsl(0 70% 55%),\n hsl(60 70% 55%),\n hsl(120 70% 55%),\n hsl(180 70% 55%),\n hsl(240 70% 55%),\n hsl(300 70% 55%),\n hsl(360 70% 55%)\n );\n\n & input {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n width: 100%;\n height: 100%;\n border: none;\n padding: 0;\n }\n }\n\n & .icon {\n width: 14px;\n height: 14px;\n }\n\n & .icon-inline {\n width: 14px;\n height: 14px;\n margin-inline-end: 0.25rem;\n }\n\n & .icon-check {\n width: 14px;\n height: 14px;\n margin: auto;\n color: white;\n filter: drop-shadow(0 0.5px 1px rgb(0 0 0 / 25%));\n }\n\n & .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n & .hint {\n font-size: 0.75rem;\n color: var(--account-picker-muted);\n line-height: 1.4;\n }\n\n & .error-text {\n font-size: 0.75rem;\n color: hsl(0 70% 50%);\n min-height: 1rem;\n }\n\n & .loading-text {\n color: var(--account-picker-muted);\n }\n\n & .btn.sign-out {\n color: var(--account-picker-muted);\n\n &:hover {\n color: white;\n background: hsl(0 65% 52%);\n }\n }\n\n & .btn.sign-out-confirm {\n background: hsl(0 65% 52%);\n color: white;\n\n &:hover {\n opacity: 0.9;\n }\n }\n\n & .modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgb(0 0 0 / 20%);\n backdrop-filter: blur(2px);\n z-index: 100;\n }\n\n & .modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 101;\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n border-radius: 8px;\n padding: 1.25rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n width: 20rem;\n max-width: 90vw;\n box-shadow: 0 8px 30px rgb(0 0 0 / 10%);\n }\n\n & .modal-title {\n font-size: 0.875rem;\n font-weight: 600;\n margin: 0;\n }\n\n & .modal-actions {\n display: flex;\n gap: 0.375rem;\n justify-content: flex-end;\n margin-top: 0.25rem;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["@layer package {\n:root,\n:host,\n[theme] {\n --contact-fg: var(--editor-line, black);\n --contact-muted-fg: var(--editor-line-offset-50, #999);\n --contact-border: var(--editor-fill-offset-10, #e5e5e5);\n --contact-fallback-bg: var(--editor-fill-offset-20, #ccc);\n --contact-fallback-fg: var(--editor-line-offset-30, #4d4d4d);\n --contact-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Avatar */\n\n.contact-avatar {\n position: relative;\n display: inline-flex;\n flex-shrink: 0;\n overflow: hidden;\n border-radius: 9999px;\n border: 1px solid var(--contact-border);\n min-width: 2rem;\n min-height: 2rem;\n max-width: 2rem;\n max-height: 2rem;\n height: 100%;\n width: 100%;\n}\n\n.contact-avatar--sm {\n min-width: initial;\n min-height: initial;\n max-width: initial;\n max-height: initial;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.contact-avatar--lg {\n min-width: initial;\n min-height: initial;\n max-width: initial;\n max-height: initial;\n width: 64px;\n height: 64px;\n}\n\n.contact-avatar--present {\n box-shadow: 0 0 0 2px var(--contact-presence-color, #888);\n}\n\n.contact-avatar-image {\n position: absolute;\n inset: 0;\n z-index: 1;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.contact-avatar-fallback {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 9999px;\n background: var(--contact-fallback-bg);\n color: var(--contact-fallback-fg);\n}\n\n/* Contact Viewer */\n\n.contact-viewer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n color: var(--contact-fg);\n}\n\n.contact-viewer-name {\n margin: 0;\n font-size: 1.25rem;\n font-weight: 600;\n}\n\n.contact-viewer-label {\n margin: 0;\n font-size: 0.875rem;\n color: var(--contact-muted-fg);\n}\n\n/* Cursor token (tool id \"contact-cursor\") \u2014 the contact's name on a pill in\n their presence color. Hosts overlay it next to their own cursor primitive,\n so it must not intercept pointer events. The pill background is the user's\n presence color (not a theme surface), so the label uses white ink, which is\n legible across the whole USER_COLOR_PALETTE. */\n\n.contact-cursor-token {\n display: inline-block;\n max-width: 160px;\n padding: 2px 6px;\n border-radius: 4px;\n background: var(--contact-cursor-color, #888);\n color: white;\n font-family: var(--contact-family, system-ui, sans-serif);\n font-size: 11px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n user-select: none;\n pointer-events: none;\n}\n", "@layer package {\n:root,\n:host,\n[theme] {\n --account-picker-fg: var(--studio-line, black);\n --account-picker-muted: var(--studio-line-offset-50, #999);\n --account-picker-bg: var(--studio-fill, white);\n --account-picker-border: var(--studio-fill-offset-10, #e5e5e5);\n --account-picker-hover: var(--studio-fill-offset-10, #e5e5e5);\n --account-picker-tooltip-bg: var(--studio-line, black);\n --account-picker-tooltip-fg: var(--studio-fill, white);\n}\n}\n\n.account-picker {\n width: 100%;\n height: 100%;\n overflow: auto;\n padding: 1.5rem 2rem;\n color: var(--account-picker-fg);\n\n & .profile-header {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n }\n\n & .identity {\n position: relative;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0.125rem;\n }\n\n & .presence-slot {\n width: 0;\n overflow: visible;\n }\n\n\n & .avatar-button {\n position: relative;\n background: none;\n border: none;\n padding: 0;\n margin-inline-start: 5px;\n border-radius: 50%;\n flex-shrink: 0;\n overflow: hidden;\n width: 56px;\n height: 56px;\n display: block;\n cursor: pointer;\n\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: 50%;\n background: rgb(0 0 0 / 30%);\n opacity: 0;\n transition: opacity 100ms;\n }\n\n &:hover::after,\n &:focus-visible::after {\n opacity: 1;\n }\n\n & patchwork-view {\n display: block;\n width: 56px;\n height: 56px;\n overflow: hidden;\n }\n\n & img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n border: none;\n outline: none;\n }\n }\n\n & .input.profile-name-input {\n margin-top: 12px;\n font-size: 1.125rem;\n font-weight: 600;\n border: 1px solid transparent;\n background: transparent;\n padding: 0.25rem 0.375rem;\n border-radius: 4px;\n height: auto;\n\n &:focus-visible {\n outline: 2px solid var(--account-picker-fg);\n outline-offset: 1px;\n }\n }\n\n & .actions {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 1rem;\n }\n\n & .section {\n margin-bottom: 0.25rem;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0.25rem;\n }\n\n & .field-group {\n display: grid;\n width: 100%;\n gap: 0.5rem;\n padding-block: 0.5rem;\n }\n\n & .input-row {\n display: flex;\n gap: 0.25rem;\n }\n\n & .tabs-list {\n display: grid;\n grid-template-columns: 1fr 1fr;\n height: 2rem;\n align-items: center;\n border-radius: 6px;\n background: var(--account-picker-border);\n padding: 2px;\n color: var(--account-picker-muted);\n }\n\n & .tabs-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0.25rem 0.5rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n border: none;\n background: transparent;\n color: inherit;\n transition: all 100ms;\n\n &[data-selected] {\n background: var(--account-picker-bg);\n color: var(--account-picker-fg);\n box-shadow: 0 1px 2px rgb(0 0 0 / 6%);\n }\n }\n\n & .tabs-content {\n margin-top: 0.5rem;\n }\n\n & .btn {\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n border-radius: 4px;\n font-size: 0.8125rem;\n font-weight: 500;\n border: none;\n height: 2rem;\n padding: 0 0.75rem;\n transition: all 100ms;\n color: var(--account-picker-fg);\n\n &:disabled {\n pointer-events: none;\n opacity: 0.3;\n }\n\n &.default {\n background: var(--account-picker-fg);\n color: var(--account-picker-bg);\n\n &:hover {\n opacity: 0.85;\n }\n }\n\n &.outline,\n &.secondary,\n &.ghost {\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n box-shadow: 0 1px 2px rgb(0 0 0 / 5%);\n\n &:hover {\n background: var(--account-picker-hover);\n box-shadow: none;\n }\n }\n }\n\n & .input {\n display: flex;\n height: 2rem;\n width: 100%;\n border-radius: 4px;\n border: 1px solid var(--account-picker-border);\n background: var(--account-picker-bg);\n padding: 0.375rem 0.5rem;\n font-size: 0.8125rem;\n color: var(--account-picker-fg);\n\n &::placeholder {\n color: var(--account-picker-muted);\n }\n\n &:focus-visible {\n outline: 2px solid var(--account-picker-fg);\n outline-offset: 1px;\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &.valid {\n border-color: hsl(140 40% 60%);\n }\n }\n\n & .label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--account-picker-muted);\n }\n\n & .tooltip-trigger {\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n height: 2rem;\n width: 2rem;\n border: none;\n background: transparent;\n color: inherit;\n\n &:hover {\n background: var(--account-picker-hover);\n }\n }\n\n & .tooltip-content {\n z-index: 50;\n border-radius: 4px;\n background: var(--account-picker-tooltip-bg);\n padding: 0.25rem 0.5rem;\n font-size: 0.75rem;\n color: var(--account-picker-tooltip-fg);\n }\n\n /* ---- Color picker ---- */\n\n & .presence-cursor {\n display: inline-flex;\n align-items: flex-start;\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n\n &:hover .presence-nametag {\n opacity: 0.85;\n }\n }\n\n & .presence-cursor-arrow {\n width: 16px;\n height: 24px;\n flex-shrink: 0;\n filter: drop-shadow(0 1px 1px rgb(0 0 0 / 25%));\n }\n\n & .presence-nametag {\n margin-top: 12px;\n margin-inline-start: -3px;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 0.75rem;\n font-weight: 500;\n line-height: 1.3;\n color: white;\n max-width: 12rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n & .color-popup {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n padding: 6px;\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgb(0 0 0 / 12%);\n z-index: 30;\n }\n\n & .color-grid {\n display: grid;\n grid-template-columns: repeat(7, 24px);\n gap: 2px;\n }\n\n & .color-swatch {\n width: 24px;\n height: 24px;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 3px;\n\n &:hover {\n opacity: 0.75;\n }\n\n &.selected {\n box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.5);\n }\n }\n\n & .color-swatch-custom {\n position: relative;\n overflow: hidden;\n cursor: pointer;\n background: conic-gradient(\n hsl(0 70% 55%),\n hsl(60 70% 55%),\n hsl(120 70% 55%),\n hsl(180 70% 55%),\n hsl(240 70% 55%),\n hsl(300 70% 55%),\n hsl(360 70% 55%)\n );\n\n & input {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n width: 100%;\n height: 100%;\n border: none;\n padding: 0;\n }\n }\n\n & .icon {\n width: 14px;\n height: 14px;\n }\n\n & .icon-inline {\n width: 14px;\n height: 14px;\n margin-inline-end: 0.25rem;\n }\n\n & .icon-check {\n width: 14px;\n height: 14px;\n margin: auto;\n color: white;\n filter: drop-shadow(0 0.5px 1px rgb(0 0 0 / 25%));\n }\n\n & .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n & .hint {\n font-size: 0.75rem;\n color: var(--account-picker-muted);\n line-height: 1.4;\n }\n\n & .error-text {\n font-size: 0.75rem;\n color: hsl(0 70% 50%);\n min-height: 1rem;\n }\n\n & .loading-text {\n color: var(--account-picker-muted);\n }\n\n & .btn.sign-out {\n color: var(--account-picker-muted);\n\n &:hover {\n color: white;\n background: hsl(0 65% 52%);\n }\n }\n\n & .btn.sign-out-confirm {\n background: hsl(0 65% 52%);\n color: white;\n\n &:hover {\n opacity: 0.9;\n }\n }\n\n & .modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgb(0 0 0 / 20%);\n backdrop-filter: blur(2px);\n z-index: 100;\n }\n\n & .modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 101;\n background: var(--account-picker-bg);\n border: 1px solid var(--account-picker-border);\n border-radius: 8px;\n padding: 1.25rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n width: 20rem;\n max-width: 90vw;\n box-shadow: 0 8px 30px rgb(0 0 0 / 10%);\n }\n\n & .cropper-modal {\n width: auto;\n align-items: center;\n }\n\n & .cropper-viewport {\n position: relative;\n width: 260px;\n height: 260px;\n overflow: hidden;\n border-radius: 6px;\n background: var(--account-picker-border);\n cursor: grab;\n touch-action: none;\n\n &:active {\n cursor: grabbing;\n }\n }\n\n & .cropper-image {\n position: absolute;\n top: 0;\n left: 0;\n transform-origin: 0 0;\n max-width: none;\n user-select: none;\n -webkit-user-drag: none;\n }\n\n & .cropper-mask {\n position: absolute;\n inset: 0;\n border-radius: 50%;\n box-shadow: 0 0 0 9999px rgb(0 0 0 / 45%);\n pointer-events: none;\n }\n\n & .cropper-zoom {\n width: 260px;\n accent-color: var(--account-picker-fg);\n }\n\n & .modal-title {\n font-size: 0.875rem;\n font-weight: 600;\n margin: 0;\n }\n\n & .modal-actions {\n display: flex;\n gap: 0.375rem;\n justify-content: flex-end;\n margin-top: 0.25rem;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -30,7 +30,7 @@ var plugins = [
30
30
  name: "Contact Avatar",
31
31
  supportedDatatypes: ["contact"],
32
32
  async load() {
33
- const { renderContactAvatar } = await import("./ContactAvatar-KCZOCABR.js");
33
+ const { renderContactAvatar } = await import("./ContactAvatar-KM4BONY5.js");
34
34
  const { styled } = await import("./styles-KCFZAQMN.js");
35
35
  return styled(renderContactAvatar);
36
36
  }
@@ -52,7 +52,7 @@ var plugins = [
52
52
  name: "Contact Cursor",
53
53
  supportedDatatypes: ["contact"],
54
54
  async load() {
55
- const { renderContactCursor } = await import("./ContactCursor-RLXRAX65.js");
55
+ const { renderContactCursor } = await import("./ContactCursor-4FYCATG5.js");
56
56
  const { styled } = await import("./styles-KCFZAQMN.js");
57
57
  return styled(renderContactCursor);
58
58
  }
@@ -63,7 +63,7 @@ var plugins = [
63
63
  name: "Account Picker",
64
64
  supportedDatatypes: ["account"],
65
65
  async load() {
66
- const { AccountPickerTool } = await import("./AccountPicker-HMEN6MEN.js");
66
+ const { AccountPickerTool } = await import("./AccountPicker-HB6MDLKN.js");
67
67
  const { styled } = await import("./styles-KCFZAQMN.js");
68
68
  return styled(AccountPickerTool);
69
69
  }
@@ -2311,18 +2311,18 @@ import {
2311
2311
  import {
2312
2312
  Presence
2313
2313
  } from "@automerge/automerge-repo/slim";
2314
- function createPresenceExtension(handle, path, contactUrl) {
2314
+ function createPresenceExtension(handle, path, contactUrl, repo) {
2315
2315
  const compartment = new Compartment5();
2316
2316
  const extension = () => {
2317
2317
  const url = contactUrl();
2318
- return url ? presence(handle(), path(), url) : [];
2318
+ return url ? presence(handle(), path(), url, repo) : [];
2319
2319
  };
2320
2320
  const createReconfigureEffect = (view) => createEffect6(() => {
2321
2321
  view.dispatch({ effects: compartment.reconfigure(extension()) });
2322
2322
  });
2323
2323
  return [compartment.of(extension()), createReconfigureEffect];
2324
2324
  }
2325
- function presence(handle, path, contactUrl) {
2325
+ function presence(handle, path, contactUrl, repo) {
2326
2326
  const setRemoteSelections = StateEffect4.define();
2327
2327
  const remoteSelectionField = StateField3.define({
2328
2328
  create() {
@@ -2377,6 +2377,11 @@ function presence(handle, path, contactUrl) {
2377
2377
  presence;
2378
2378
  destroyed = false;
2379
2379
  broadcastQueued = false;
2380
+ // Peers' contact docs, followed live so a color picked in the account
2381
+ // picker recolors that peer's caret and tint immediately. Keyed by
2382
+ // contact url; the stored function stops the subscription.
2383
+ contactWatchers = /* @__PURE__ */ new Map();
2384
+ contactColors = /* @__PURE__ */ new Map();
2380
2385
  constructor(view) {
2381
2386
  this.view = view;
2382
2387
  this.presence = new Presence({
@@ -2409,6 +2414,33 @@ function presence(handle, path, contactUrl) {
2409
2414
  this.destroyed = true;
2410
2415
  this.stopPresence();
2411
2416
  window.removeEventListener("pagehide", this.stopPresence);
2417
+ for (const stop of this.contactWatchers.values()) stop();
2418
+ this.contactWatchers.clear();
2419
+ }
2420
+ // A peer's color is whatever their account says (contact doc `color`),
2421
+ // never derived locally. The doc is fetched once and followed; until it
2422
+ // loads (or if the contact never picked a color) the caret stays on the
2423
+ // neutral fallback.
2424
+ trackContactColor(url) {
2425
+ if (this.contactWatchers.has(url)) return;
2426
+ this.contactWatchers.set(url, () => {
2427
+ });
2428
+ repo.find(url).then((contactHandle) => {
2429
+ if (this.destroyed) return;
2430
+ const read = () => {
2431
+ const color = contactHandle.doc()?.color;
2432
+ if (!color || this.contactColors.get(url) === color) return;
2433
+ this.contactColors.set(url, color);
2434
+ this.refresh();
2435
+ };
2436
+ contactHandle.on("change", read);
2437
+ this.contactWatchers.set(
2438
+ url,
2439
+ () => contactHandle.off("change", read)
2440
+ );
2441
+ read();
2442
+ }).catch(() => {
2443
+ });
2412
2444
  }
2413
2445
  stopPresence = () => {
2414
2446
  if (this.presence.running) this.presence.stop();
@@ -2447,12 +2479,14 @@ function presence(handle, path, contactUrl) {
2447
2479
  const from = A3.getCursorPosition(doc, path, selection.start);
2448
2480
  const to = A3.getCursorPosition(doc, path, selection.end);
2449
2481
  const peerContactUrl = peer.userId ?? null;
2482
+ if (peerContactUrl) this.trackContactColor(peerContactUrl);
2450
2483
  resolved.push({
2451
2484
  peerId,
2452
2485
  contactUrl: peerContactUrl,
2453
- // Same rule as the contact-cursor token (palette hash of the
2454
- // contact url), so the tint and the token can't drift apart.
2455
- color: generateColorFromString(peerContactUrl ?? peerId),
2486
+ // The color the peer chose in their account -- the same contact
2487
+ // doc the contact-cursor token renders from, so the tint and
2488
+ // the token can't drift apart.
2489
+ color: peerContactUrl && this.contactColors.get(peerContactUrl) || FALLBACK_PRESENCE_COLOR,
2456
2490
  from,
2457
2491
  to,
2458
2492
  head: selection.headAtStart ? from : to,
@@ -2570,26 +2604,7 @@ var presenceTheme = EditorView9.baseTheme({
2570
2604
  opacity: "0"
2571
2605
  }
2572
2606
  });
2573
- var USER_COLOR_PALETTE = [
2574
- "hsl(200, 70%, 50%)",
2575
- "hsl(10, 75%, 58%)",
2576
- "hsl(145, 70%, 45%)",
2577
- "hsl(270, 70%, 55%)",
2578
- "hsl(38, 85%, 50%)",
2579
- "hsl(350, 70%, 55%)",
2580
- "hsl(178, 70%, 45%)",
2581
- "hsl(235, 70%, 58%)",
2582
- "hsl(85, 70%, 45%)",
2583
- "hsl(310, 70%, 55%)",
2584
- "hsl(25, 80%, 52%)",
2585
- "hsl(188, 75%, 48%)"
2586
- ];
2587
- function generateColorFromString(str) {
2588
- const hash = Math.abs(
2589
- str.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0)
2590
- );
2591
- return USER_COLOR_PALETTE[hash % USER_COLOR_PALETTE.length];
2592
- }
2607
+ var FALLBACK_PRESENCE_COLOR = "#888";
2593
2608
 
2594
2609
  // src/lib/codemirror.tsx
2595
2610
  var lookup = (doc, path) => {
@@ -2609,7 +2624,7 @@ function CodeMirror(props) {
2609
2624
  const [decorationsExtension, createEffectReconfigureDecorations] = createDecorationsExtension(() => props.decorations?.());
2610
2625
  const [diffExtension, createEffectReconfigureDiff] = createDiffExtension(() => props.handle, () => props.path, () => props.baseline?.() ?? null);
2611
2626
  const [scrollHighlightIntoViewExtension, createEffectScrollHighlightIntoView] = createScrollHighlightIntoViewExtension(() => props.scrollTarget?.() ?? null);
2612
- const [presenceExtension, createEffectReconfigurePresence] = createPresenceExtension(() => props.handle, () => props.path, () => props.readOnly || props.handle.isReadOnly() ? null : props.contactUrl?.() ?? null);
2627
+ const [presenceExtension, createEffectReconfigurePresence] = createPresenceExtension(() => props.handle, () => props.path, () => props.readOnly || props.handle.isReadOnly() ? null : props.contactUrl?.() ?? null, props.repo);
2613
2628
  const userExtensionsCompartment = new Compartment6();
2614
2629
  const selectionExtension = EditorView10.updateListener.of((update) => {
2615
2630
  if (!props.onChangeSelection) return;
@@ -2655,4 +2670,4 @@ function CodeMirror(props) {
2655
2670
  export {
2656
2671
  CodeMirror
2657
2672
  };
2658
- //# sourceMappingURL=chunk-H5POLO74.js.map
2673
+ //# sourceMappingURL=chunk-AKNT5U37.js.map