@inkandswitch/patchwork-pkg-base 0.0.54 → 0.0.56
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 +1 -1
- package/packages/account/dist/{AccountPicker-HMEN6MEN.js → AccountPicker-HB6MDLKN.js} +390 -123
- package/packages/account/dist/AccountPicker-HB6MDLKN.js.map +7 -0
- package/packages/account/dist/{ContactAvatar-KCZOCABR.js → ContactAvatar-KM4BONY5.js} +7 -7
- package/packages/account/dist/ContactAvatar-KM4BONY5.js.map +7 -0
- package/packages/account/dist/{ContactCursor-RLXRAX65.js → ContactCursor-4FYCATG5.js} +6 -6
- package/packages/account/dist/ContactCursor-4FYCATG5.js.map +7 -0
- package/packages/account/dist/index.css +96 -15
- package/packages/account/dist/index.css.map +2 -2
- package/packages/account/dist/index.js +3 -3
- package/packages/codemirror-base/dist/{chunk-H5POLO74.js → chunk-AKNT5U37.js} +43 -28
- package/packages/codemirror-base/dist/chunk-AKNT5U37.js.map +7 -0
- package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
- package/packages/codemirror-base/dist/tool.js +21 -3
- package/packages/codemirror-base/dist/tool.js.map +2 -2
- package/packages/comments-view/dist/assets/main-BPvgT7fH.js +1 -1
- package/packages/tldraw4/dist/tool.js +3 -23
- package/packages/tldraw4/dist/tool.js.map +2 -2
- package/packages/account/dist/AccountPicker-HMEN6MEN.js.map +0 -7
- package/packages/account/dist/ContactAvatar-KCZOCABR.js.map +0 -7
- package/packages/account/dist/ContactCursor-RLXRAX65.js.map +0 -7
- package/packages/account/dist/chunk-3YUNEZX6.js +0 -38
- package/packages/account/dist/chunk-3YUNEZX6.js.map +0 -7
- package/packages/codemirror-base/dist/chunk-H5POLO74.js.map +0 -7
|
@@ -1,7 +0,0 @@
|
|
|
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 { generateColorFromString } from \"../user-colors\";\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 docUrl = handle.url;\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 const userColor = generateColorFromString(docUrl);\n\n if (isPresent) {\n avatar.classList.add(\"contact-avatar--present\");\n avatar.style.setProperty(\"--contact-presence-color\", userColor);\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,7 +0,0 @@
|
|
|
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\";\nimport { generateColorFromString } from \"../user-colors\";\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 const color = generateColorFromString(handle.url);\n token.style.setProperty(\"--contact-cursor-color\", color);\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": ";;;;;;AAWO,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;AAC5D,UAAM,QAAQ,wBAAwB,OAAO,GAAG;AAChD,UAAM,MAAM,YAAY,0BAA0B,KAAK;AACvD,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
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// src/user-colors.ts
|
|
2
|
-
var USER_COLOR_PALETTE = [
|
|
3
|
-
{ name: "Sky Blue", value: "hsl(200, 70%, 50%)" },
|
|
4
|
-
{ name: "Coral", value: "hsl(10, 75%, 58%)" },
|
|
5
|
-
{ name: "Emerald", value: "hsl(145, 70%, 45%)" },
|
|
6
|
-
{ name: "Violet", value: "hsl(270, 70%, 55%)" },
|
|
7
|
-
{ name: "Amber", value: "hsl(38, 85%, 50%)" },
|
|
8
|
-
{ name: "Rose", value: "hsl(350, 70%, 55%)" },
|
|
9
|
-
{ name: "Teal", value: "hsl(178, 70%, 45%)" },
|
|
10
|
-
{ name: "Indigo", value: "hsl(235, 70%, 58%)" },
|
|
11
|
-
{ name: "Lime", value: "hsl(85, 70%, 45%)" },
|
|
12
|
-
{ name: "Fuchsia", value: "hsl(310, 70%, 55%)" },
|
|
13
|
-
{ name: "Orange", value: "hsl(25, 80%, 52%)" },
|
|
14
|
-
{ name: "Cyan", value: "hsl(188, 75%, 48%)" }
|
|
15
|
-
];
|
|
16
|
-
function generateColorFromString(str) {
|
|
17
|
-
const hash = Math.abs(
|
|
18
|
-
str.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0)
|
|
19
|
-
);
|
|
20
|
-
const colorIndex = hash % USER_COLOR_PALETTE.length;
|
|
21
|
-
return USER_COLOR_PALETTE[colorIndex].value;
|
|
22
|
-
}
|
|
23
|
-
function parseHslColor(color) {
|
|
24
|
-
const match = color.match(/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$/);
|
|
25
|
-
if (!match) return null;
|
|
26
|
-
return {
|
|
27
|
-
h: parseInt(match[1], 10),
|
|
28
|
-
s: parseInt(match[2], 10),
|
|
29
|
-
l: parseInt(match[3], 10)
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export {
|
|
34
|
-
USER_COLOR_PALETTE,
|
|
35
|
-
generateColorFromString,
|
|
36
|
-
parseHslColor
|
|
37
|
-
};
|
|
38
|
-
//# sourceMappingURL=chunk-3YUNEZX6.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/user-colors.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * User color palette for presence indicators (cursors, avatars, etc.)\n *\n * Colors are selected to:\n * - Be easily distinguishable from each other\n * - Work well on both light and dark backgrounds\n * - Be accessible and readable\n * - Look professional and modern\n */\n\nexport const USER_COLOR_PALETTE = [\n { name: \"Sky Blue\", value: \"hsl(200, 70%, 50%)\" },\n { name: \"Coral\", value: \"hsl(10, 75%, 58%)\" },\n { name: \"Emerald\", value: \"hsl(145, 70%, 45%)\" },\n { name: \"Violet\", value: \"hsl(270, 70%, 55%)\" },\n { name: \"Amber\", value: \"hsl(38, 85%, 50%)\" },\n { name: \"Rose\", value: \"hsl(350, 70%, 55%)\" },\n { name: \"Teal\", value: \"hsl(178, 70%, 45%)\" },\n { name: \"Indigo\", value: \"hsl(235, 70%, 58%)\" },\n { name: \"Lime\", value: \"hsl(85, 70%, 45%)\" },\n { name: \"Fuchsia\", value: \"hsl(310, 70%, 55%)\" },\n { name: \"Orange\", value: \"hsl(25, 80%, 52%)\" },\n { name: \"Cyan\", value: \"hsl(188, 75%, 48%)\" },\n] as const;\n\n/**\n * Generate a deterministic color from a string (like userId)\n * Useful for fallback when user hasn't chosen a color\n */\nexport function generateColorFromString(str: string): string {\n const hash = Math.abs(\n str.split(\"\").reduce((acc, char) => acc + char.charCodeAt(0), 0)\n );\n const colorIndex = hash % USER_COLOR_PALETTE.length;\n return USER_COLOR_PALETTE[colorIndex].value;\n}\n\n/**\n * Get a default color for a user (picks from palette based on index)\n */\nexport function getDefaultUserColor(index: number): string {\n return USER_COLOR_PALETTE[index % USER_COLOR_PALETTE.length].value;\n}\n\n/**\n * Validate if a string is a valid HSL color\n */\nexport function isValidHslColor(color: string): boolean {\n return /^hsl\\(\\s*\\d+\\s*,\\s*\\d+%\\s*,\\s*\\d+%\\s*\\)$/.test(color);\n}\n\n/**\n * Parse HSL values from a color string\n */\nexport function parseHslColor(\n color: string\n): { h: number; s: number; l: number } | null {\n const match = color.match(/^hsl\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*\\)$/);\n if (!match) return null;\n return {\n h: parseInt(match[1], 10),\n s: parseInt(match[2], 10),\n l: parseInt(match[3], 10),\n };\n}\n\n/**\n * Create an HSL color string from values\n */\nexport function createHslColor(h: number, s: number, l: number): string {\n return `hsl(${h}, ${s}%, ${l}%)`;\n}\n"],
|
|
5
|
-
"mappings": ";AAUO,IAAM,qBAAqB;AAAA,EAChC,EAAE,MAAM,YAAY,OAAO,qBAAqB;AAAA,EAChD,EAAE,MAAM,SAAS,OAAO,oBAAoB;AAAA,EAC5C,EAAE,MAAM,WAAW,OAAO,qBAAqB;AAAA,EAC/C,EAAE,MAAM,UAAU,OAAO,qBAAqB;AAAA,EAC9C,EAAE,MAAM,SAAS,OAAO,oBAAoB;AAAA,EAC5C,EAAE,MAAM,QAAQ,OAAO,qBAAqB;AAAA,EAC5C,EAAE,MAAM,QAAQ,OAAO,qBAAqB;AAAA,EAC5C,EAAE,MAAM,UAAU,OAAO,qBAAqB;AAAA,EAC9C,EAAE,MAAM,QAAQ,OAAO,oBAAoB;AAAA,EAC3C,EAAE,MAAM,WAAW,OAAO,qBAAqB;AAAA,EAC/C,EAAE,MAAM,UAAU,OAAO,oBAAoB;AAAA,EAC7C,EAAE,MAAM,QAAQ,OAAO,qBAAqB;AAC9C;AAMO,SAAS,wBAAwB,KAAqB;AAC3D,QAAM,OAAO,KAAK;AAAA,IAChB,IAAI,MAAM,EAAE,EAAE,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,WAAW,CAAC,GAAG,CAAC;AAAA,EACjE;AACA,QAAM,aAAa,OAAO,mBAAmB;AAC7C,SAAO,mBAAmB,UAAU,EAAE;AACxC;AAmBO,SAAS,cACd,OAC4C;AAC5C,QAAM,QAAQ,MAAM,MAAM,gDAAgD;AAC1E,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO;AAAA,IACL,GAAG,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IACxB,GAAG,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IACxB,GAAG,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,EAC1B;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|