@tldraw/commenting 0.0.0-bootstrap
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/README.md +11 -0
- package/commenting.css +832 -0
- package/dist-cjs/canvas/cluster-input.js +40 -0
- package/dist-cjs/canvas/cluster-input.js.map +7 -0
- package/dist-cjs/canvas/comment-body.js +31 -0
- package/dist-cjs/canvas/comment-body.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +72 -0
- package/dist-cjs/canvas/comment-render.js.map +7 -0
- package/dist-cjs/canvas/comment-store.js +48 -0
- package/dist-cjs/canvas/comment-store.js.map +7 -0
- package/dist-cjs/canvas/comment-tool.js +160 -0
- package/dist-cjs/canvas/comment-tool.js.map +7 -0
- package/dist-cjs/canvas/comments-filter-menu.js +109 -0
- package/dist-cjs/canvas/comments-filter-menu.js.map +7 -0
- package/dist-cjs/canvas/comments-menu-item.js +42 -0
- package/dist-cjs/canvas/comments-menu-item.js.map +7 -0
- package/dist-cjs/canvas/comments-overflow-menu.js +64 -0
- package/dist-cjs/canvas/comments-overflow-menu.js.map +7 -0
- package/dist-cjs/canvas/comments-overlay.js +1081 -0
- package/dist-cjs/canvas/comments-overlay.js.map +7 -0
- package/dist-cjs/canvas/comments-sidebar.js +134 -0
- package/dist-cjs/canvas/comments-sidebar.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +45 -0
- package/dist-cjs/canvas/hooks.js.map +7 -0
- package/dist-cjs/canvas/license.js +28 -0
- package/dist-cjs/canvas/license.js.map +7 -0
- package/dist-cjs/canvas/options.js +45 -0
- package/dist-cjs/canvas/options.js.map +7 -0
- package/dist-cjs/canvas/region-options.js +49 -0
- package/dist-cjs/canvas/region-options.js.map +7 -0
- package/dist-cjs/canvas/rich-text.js +28 -0
- package/dist-cjs/canvas/rich-text.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +30 -0
- package/dist-cjs/canvas/sidebar-filters.js.map +7 -0
- package/dist-cjs/canvas/state.js +76 -0
- package/dist-cjs/canvas/state.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +81 -0
- package/dist-cjs/canvas/thread-state.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +83 -0
- package/dist-cjs/clustering/computeClusterTable.js.map +7 -0
- package/dist-cjs/clustering/mst.js +117 -0
- package/dist-cjs/clustering/mst.js.map +7 -0
- package/dist-cjs/clustering/replay.js +275 -0
- package/dist-cjs/clustering/replay.js.map +7 -0
- package/dist-cjs/clustering/runtime.js +134 -0
- package/dist-cjs/clustering/runtime.js.map +7 -0
- package/dist-cjs/clustering/schedule.js +170 -0
- package/dist-cjs/clustering/schedule.js.map +7 -0
- package/dist-cjs/clustering/types.js +17 -0
- package/dist-cjs/clustering/types.js.map +7 -0
- package/dist-cjs/index.d.ts +868 -0
- package/dist-cjs/index.js +126 -0
- package/dist-cjs/index.js.map +7 -0
- package/dist-cjs/ui/avatar.js +43 -0
- package/dist-cjs/ui/avatar.js.map +7 -0
- package/dist-cjs/ui/byline.js +35 -0
- package/dist-cjs/ui/byline.js.map +7 -0
- package/dist-cjs/ui/comment-card.js +37 -0
- package/dist-cjs/ui/comment-card.js.map +7 -0
- package/dist-cjs/ui/comment-composer.js +136 -0
- package/dist-cjs/ui/comment-composer.js.map +7 -0
- package/dist-cjs/ui/comment-extensions.js +37 -0
- package/dist-cjs/ui/comment-extensions.js.map +7 -0
- package/dist-cjs/ui/comment-mention.js +42 -0
- package/dist-cjs/ui/comment-mention.js.map +7 -0
- package/dist-cjs/ui/comment-pin.js +29 -0
- package/dist-cjs/ui/comment-pin.js.map +7 -0
- package/dist-cjs/ui/comment-text.js +29 -0
- package/dist-cjs/ui/comment-text.js.map +7 -0
- package/dist-cjs/ui/comment-thread.js +45 -0
- package/dist-cjs/ui/comment-thread.js.map +7 -0
- package/dist-cjs/ui/comments-list.js +106 -0
- package/dist-cjs/ui/comments-list.js.map +7 -0
- package/dist-cjs/ui/count-badge.js +28 -0
- package/dist-cjs/ui/count-badge.js.map +7 -0
- package/dist-cjs/ui/empty-state.js +31 -0
- package/dist-cjs/ui/empty-state.js.map +7 -0
- package/dist-cjs/ui/format-time.js +46 -0
- package/dist-cjs/ui/format-time.js.map +7 -0
- package/dist-cjs/ui/mention-list.js +70 -0
- package/dist-cjs/ui/mention-list.js.map +7 -0
- package/dist-cjs/ui/mention-suggestion.js +206 -0
- package/dist-cjs/ui/mention-suggestion.js.map +7 -0
- package/dist-cjs/ui/mention.js +31 -0
- package/dist-cjs/ui/mention.js.map +7 -0
- package/dist-cjs/ui/reaction.js +31 -0
- package/dist-cjs/ui/reaction.js.map +7 -0
- package/dist-cjs/ui/reactions.js +34 -0
- package/dist-cjs/ui/reactions.js.map +7 -0
- package/dist-cjs/ui/render-markdown.js +63 -0
- package/dist-cjs/ui/render-markdown.js.map +7 -0
- package/dist-cjs/ui/send-button.js +28 -0
- package/dist-cjs/ui/send-button.js.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +20 -0
- package/dist-esm/canvas/cluster-input.mjs.map +7 -0
- package/dist-esm/canvas/comment-body.mjs +11 -0
- package/dist-esm/canvas/comment-body.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +52 -0
- package/dist-esm/canvas/comment-render.mjs.map +7 -0
- package/dist-esm/canvas/comment-store.mjs +28 -0
- package/dist-esm/canvas/comment-store.mjs.map +7 -0
- package/dist-esm/canvas/comment-tool.mjs +142 -0
- package/dist-esm/canvas/comment-tool.mjs.map +7 -0
- package/dist-esm/canvas/comments-filter-menu.mjs +99 -0
- package/dist-esm/canvas/comments-filter-menu.mjs.map +7 -0
- package/dist-esm/canvas/comments-menu-item.mjs +22 -0
- package/dist-esm/canvas/comments-menu-item.mjs.map +7 -0
- package/dist-esm/canvas/comments-overflow-menu.mjs +53 -0
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +7 -0
- package/dist-esm/canvas/comments-overlay.mjs +1094 -0
- package/dist-esm/canvas/comments-overlay.mjs.map +7 -0
- package/dist-esm/canvas/comments-sidebar.mjs +120 -0
- package/dist-esm/canvas/comments-sidebar.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +25 -0
- package/dist-esm/canvas/hooks.mjs.map +7 -0
- package/dist-esm/canvas/license.mjs +8 -0
- package/dist-esm/canvas/license.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +27 -0
- package/dist-esm/canvas/options.mjs.map +7 -0
- package/dist-esm/canvas/region-options.mjs +29 -0
- package/dist-esm/canvas/region-options.mjs.map +7 -0
- package/dist-esm/canvas/rich-text.mjs +8 -0
- package/dist-esm/canvas/rich-text.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +10 -0
- package/dist-esm/canvas/sidebar-filters.mjs.map +7 -0
- package/dist-esm/canvas/state.mjs +60 -0
- package/dist-esm/canvas/state.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +61 -0
- package/dist-esm/canvas/thread-state.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +63 -0
- package/dist-esm/clustering/computeClusterTable.mjs.map +7 -0
- package/dist-esm/clustering/mst.mjs +97 -0
- package/dist-esm/clustering/mst.mjs.map +7 -0
- package/dist-esm/clustering/replay.mjs +255 -0
- package/dist-esm/clustering/replay.mjs.map +7 -0
- package/dist-esm/clustering/runtime.mjs +114 -0
- package/dist-esm/clustering/runtime.mjs.map +7 -0
- package/dist-esm/clustering/schedule.mjs +150 -0
- package/dist-esm/clustering/schedule.mjs.map +7 -0
- package/dist-esm/clustering/types.mjs +1 -0
- package/dist-esm/clustering/types.mjs.map +7 -0
- package/dist-esm/index.d.mts +868 -0
- package/dist-esm/index.mjs +139 -0
- package/dist-esm/index.mjs.map +7 -0
- package/dist-esm/ui/avatar.mjs +23 -0
- package/dist-esm/ui/avatar.mjs.map +7 -0
- package/dist-esm/ui/byline.mjs +15 -0
- package/dist-esm/ui/byline.mjs.map +7 -0
- package/dist-esm/ui/comment-card.mjs +17 -0
- package/dist-esm/ui/comment-card.mjs.map +7 -0
- package/dist-esm/ui/comment-composer.mjs +116 -0
- package/dist-esm/ui/comment-composer.mjs.map +7 -0
- package/dist-esm/ui/comment-extensions.mjs +17 -0
- package/dist-esm/ui/comment-extensions.mjs.map +7 -0
- package/dist-esm/ui/comment-mention.mjs +22 -0
- package/dist-esm/ui/comment-mention.mjs.map +7 -0
- package/dist-esm/ui/comment-pin.mjs +9 -0
- package/dist-esm/ui/comment-pin.mjs.map +7 -0
- package/dist-esm/ui/comment-text.mjs +9 -0
- package/dist-esm/ui/comment-text.mjs.map +7 -0
- package/dist-esm/ui/comment-thread.mjs +25 -0
- package/dist-esm/ui/comment-thread.mjs.map +7 -0
- package/dist-esm/ui/comments-list.mjs +86 -0
- package/dist-esm/ui/comments-list.mjs.map +7 -0
- package/dist-esm/ui/count-badge.mjs +8 -0
- package/dist-esm/ui/count-badge.mjs.map +7 -0
- package/dist-esm/ui/empty-state.mjs +11 -0
- package/dist-esm/ui/empty-state.mjs.map +7 -0
- package/dist-esm/ui/format-time.mjs +26 -0
- package/dist-esm/ui/format-time.mjs.map +7 -0
- package/dist-esm/ui/mention-list.mjs +50 -0
- package/dist-esm/ui/mention-list.mjs.map +7 -0
- package/dist-esm/ui/mention-suggestion.mjs +186 -0
- package/dist-esm/ui/mention-suggestion.mjs.map +7 -0
- package/dist-esm/ui/mention.mjs +11 -0
- package/dist-esm/ui/mention.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +11 -0
- package/dist-esm/ui/reaction.mjs.map +7 -0
- package/dist-esm/ui/reactions.mjs +14 -0
- package/dist-esm/ui/reactions.mjs.map +7 -0
- package/dist-esm/ui/render-markdown.mjs +45 -0
- package/dist-esm/ui/render-markdown.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +8 -0
- package/dist-esm/ui/send-button.mjs.map +7 -0
- package/package.json +71 -0
- package/src/canvas/canvas.css +148 -0
- package/src/canvas/cluster-input.test.ts +223 -0
- package/src/canvas/cluster-input.ts +29 -0
- package/src/canvas/comment-body.tsx +21 -0
- package/src/canvas/comment-render.test.ts +90 -0
- package/src/canvas/comment-render.ts +92 -0
- package/src/canvas/comment-store.ts +55 -0
- package/src/canvas/comment-tool.tsx +200 -0
- package/src/canvas/comments-filter-menu.tsx +104 -0
- package/src/canvas/comments-menu-item.tsx +24 -0
- package/src/canvas/comments-overflow-menu.tsx +62 -0
- package/src/canvas/comments-overlay.tsx +1460 -0
- package/src/canvas/comments-sidebar.tsx +184 -0
- package/src/canvas/hooks.ts +28 -0
- package/src/canvas/license.ts +15 -0
- package/src/canvas/options.test.ts +127 -0
- package/src/canvas/options.ts +111 -0
- package/src/canvas/region-options.ts +57 -0
- package/src/canvas/rich-text.ts +15 -0
- package/src/canvas/sidebar-filters.ts +25 -0
- package/src/canvas/state.ts +102 -0
- package/src/canvas/thread-state.ts +91 -0
- package/src/clustering/computeClusterTable.test.ts +299 -0
- package/src/clustering/computeClusterTable.ts +82 -0
- package/src/clustering/locality.test.ts +189 -0
- package/src/clustering/mst.test.ts +383 -0
- package/src/clustering/mst.ts +134 -0
- package/src/clustering/replay.test.ts +547 -0
- package/src/clustering/replay.ts +326 -0
- package/src/clustering/runtime.test.ts +596 -0
- package/src/clustering/runtime.ts +153 -0
- package/src/clustering/schedule.test.ts +514 -0
- package/src/clustering/schedule.ts +184 -0
- package/src/clustering/types.ts +102 -0
- package/src/index.ts +96 -0
- package/src/ui/avatar.tsx +31 -0
- package/src/ui/byline.tsx +23 -0
- package/src/ui/comment-card.tsx +32 -0
- package/src/ui/comment-composer.tsx +179 -0
- package/src/ui/comment-extensions.ts +25 -0
- package/src/ui/comment-mention.ts +47 -0
- package/src/ui/comment-pin.tsx +21 -0
- package/src/ui/comment-text.tsx +12 -0
- package/src/ui/comment-thread.tsx +56 -0
- package/src/ui/comments-list.tsx +139 -0
- package/src/ui/comments.css +678 -0
- package/src/ui/count-badge.tsx +9 -0
- package/src/ui/empty-state.tsx +16 -0
- package/src/ui/format-time.ts +29 -0
- package/src/ui/mention-list.tsx +106 -0
- package/src/ui/mention-suggestion.test.ts +18 -0
- package/src/ui/mention-suggestion.tsx +285 -0
- package/src/ui/mention.tsx +9 -0
- package/src/ui/reaction.tsx +16 -0
- package/src/ui/reactions.tsx +15 -0
- package/src/ui/render-markdown.tsx +72 -0
- package/src/ui/send-button.tsx +15 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/mention-suggestion.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { MentionNodeAttrs } from '@tiptap/extension-mention'\nimport { ReactRenderer } from '@tiptap/react'\nimport type { SuggestionKeyDownProps, SuggestionOptions } from '@tiptap/suggestion'\nimport { type ReactNode, forwardRef, useImperativeHandle, useState } from 'react'\nimport { type Editor as TldrawEditor, atom, react } from 'tldraw'\nimport { MentionList, MentionMember } from './mention-list'\n\n/** The handle the suggestion plugin drives \u2014 it forwards navigation keys into the popup. */\nexport interface MentionPopupHandle {\n\tonKeyDown(props: SuggestionKeyDownProps): boolean\n}\n\ninterface MentionPopupProps {\n\titems: MentionMember[]\n\tcommand(attrs: MentionNodeAttrs): void\n\trenderMember?(member: MentionMember): ReactNode\n}\n\n/** The live @-picker popup: owns the highlighted index and keyboard, renders the presentational list. */\nconst MentionPopup = forwardRef<MentionPopupHandle, MentionPopupProps>(function MentionPopup(\n\t{ items, command, renderMember },\n\tref\n) {\n\tconst [activeIndex, setActiveIndex] = useState(0)\n\t// A new query yields new items; reset the highlight to the top during render \u2014 not in an effect,\n\t// which would leave a frame where `activeIndex` still points past a shrunk list and Enter selects\n\t// its (now out-of-range, undefined) item, swallowing the key without inserting a mention.\n\tconst [prevItems, setPrevItems] = useState(items)\n\tif (items !== prevItems) {\n\t\tsetPrevItems(items)\n\t\tsetActiveIndex(0)\n\t}\n\n\tconst select = (member: MentionMember | undefined) => {\n\t\tif (member) command({ id: member.id, label: member.name })\n\t}\n\n\tuseImperativeHandle(ref, () => ({\n\t\tonKeyDown: ({ event }) => {\n\t\t\tif (items.length === 0) return false\n\t\t\tif (event.key === 'ArrowUp') {\n\t\t\t\tsetActiveIndex((i) => (i + items.length - 1) % items.length)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (event.key === 'ArrowDown') {\n\t\t\t\tsetActiveIndex((i) => (i + 1) % items.length)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Enter and Tab both complete the highlighted member (falling back to the top match so a\n\t\t\t// stale index never selects `undefined`). The empty-roster case is handled a level up, in\n\t\t\t// the suggestion's onKeyDown, which cancels the picker.\n\t\t\tif (event.key === 'Enter' || event.key === 'Tab') {\n\t\t\t\tselect(items[activeIndex] ?? items[0])\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t}))\n\n\treturn (\n\t\t<MentionList\n\t\t\tmembers={items}\n\t\t\tactiveIndex={activeIndex}\n\t\t\tonSelect={select}\n\t\t\trenderMember={renderMember}\n\t\t/>\n\t)\n})\n\nconst MAX_SUGGESTIONS = 8\n\n/** Members whose name contains the query (case-insensitive), capped to the popup's length.\n * @public */\nexport function filterMentionMembers(members: MentionMember[], query: string): MentionMember[] {\n\tconst q = query.toLowerCase()\n\treturn members.filter((m) => m.name.toLowerCase().includes(q)).slice(0, MAX_SUGGESTIONS)\n}\n\n// A reactive flag for whether the @-picker is showing. Deliberately NOT tldraw's open-menu registry:\n// registering there mounts MenuClickCapture, which covers the canvas with a click-capture overlay to\n// make it inert while a menu is open. But the picker is an inline autocomplete, not a modal \u2014 the\n// canvas must stay pannable/zoomable beneath it \u2014 so we track \"open\" ourselves instead.\nconst mentionPickerOpen = atom('isMentionPickerOpen', false)\n\n/**\n * Whether the @-mention picker is currently showing. Host dismissal (Escape, outside-click) checks\n * this so it can defer to the picker instead of tearing down the composer or thread beneath it.\n */\nexport function isMentionPickerOpen(): boolean {\n\treturn mentionPickerOpen.get()\n}\n\n/** @public */\nexport interface MentionSuggestionOptions {\n\t/** Override a member row's content in the picker. Defaults to avatar + name (+ secondary). */\n\trenderMember?(member: MentionMember): ReactNode\n\t/**\n\t * The tldraw editor the composer lives in. When provided, the popup re-anchors reactively as the\n\t * canvas camera moves (the composer rides it) instead of polling every frame. Omit off-canvas.\n\t */\n\teditor?: TldrawEditor | null\n}\n\n/**\n * Build the TipTap `suggestion` config for the comment \\@-picker. `getSuggestions(query)` is the\n * host's resolver \u2014 it returns the members matching the query (sync or async); the SDK owns neither\n * the roster nor the filtering. The plugin runs outside React, so `render` mounts `MentionPopup` via\n * a `ReactRenderer`, forwards navigation keys through the popup's imperative handle, and lets it call\n * `command` to insert.\n * @public\n */\nexport function createMentionSuggestion(\n\tgetSuggestions: (query: string) => MentionMember[] | Promise<MentionMember[]>,\n\toptions: MentionSuggestionOptions = {}\n): Omit<SuggestionOptions<MentionMember, MentionNodeAttrs>, 'editor'> {\n\treturn {\n\t\tchar: '@',\n\t\titems: ({ query }) => getSuggestions(query),\n\t\trender: () => {\n\t\t\tlet renderer: ReactRenderer<MentionPopupHandle, MentionPopupProps> | null = null\n\t\t\tlet container: HTMLElement | null = null\n\t\t\tlet editorEl: HTMLElement | null = null\n\t\t\tlet canvasEl: Element | null = null\n\t\t\tlet stopCameraReaction: (() => void) | null = null\n\t\t\t// The composer field's top-left in page space, plus the popup's screen width. Captured on a\n\t\t\t// fresh read so camera moves can re-derive the popup's screen position from the page anchor\n\t\t\t// (see reposition) rather than the field's DOM rect.\n\t\t\tlet anchorPage: { x: number; y: number } | null = null\n\t\t\tlet popupWidth = 0\n\n\t\t\tconst applyScreen = (left: number, top: number, width: number) => {\n\t\t\t\tif (!container) return\n\t\t\t\tcontainer.style.left = `${left}px`\n\t\t\t\tcontainer.style.top = `${top + 4}px`\n\t\t\t\tcontainer.style.width = `${width}px`\n\t\t\t}\n\n\t\t\t// Fresh placement: read the field's real screen rect, position the popup flush under it (not\n\t\t\t// the caret, matching its width), and remember the field's page-space anchor for reposition.\n\t\t\tconst place = () => {\n\t\t\t\tif (!container || !editorEl || container.style.display === 'none') return\n\t\t\t\tconst field = editorEl.closest('.cmt-composer__field') ?? editorEl\n\t\t\t\tconst rect = field.getBoundingClientRect()\n\t\t\t\tpopupWidth = rect.width\n\t\t\t\tanchorPage = options.editor?.screenToPage({ x: rect.left, y: rect.bottom }) ?? null\n\t\t\t\tapplyScreen(rect.left, rect.bottom, rect.width)\n\t\t\t}\n\n\t\t\t// Re-derive the popup's screen position from the remembered page anchor \u2014 pure camera math,\n\t\t\t// always current. Reading the field's DOM rect here instead would lag by a frame: the canvas\n\t\t\t// composer re-positions itself on a React commit (a `useValue(pageToViewport(...))`), which\n\t\t\t// lands after a camera reaction runs, so the rect is still last frame's during a pan.\n\t\t\tconst reposition = () => {\n\t\t\t\tif (!anchorPage || !options.editor) return\n\t\t\t\tconst s = options.editor.pageToScreen(anchorPage)\n\t\t\t\tapplyScreen(s.x, s.y, popupWidth)\n\t\t\t}\n\n\t\t\t// The popup is `position: fixed`, but its anchor \u2014 a canvas composer \u2014 rides the camera:\n\t\t\t// panning or zooming moves the composer with no scroll/resize event to hook. Re-anchor when\n\t\t\t// the camera actually changes (via a tldraw reaction) rather than polling every frame, plus\n\t\t\t// on window scroll/resize for the off-canvas case (which re-read the field directly).\n\t\t\tconst startFollowing = () => {\n\t\t\t\twindow.addEventListener('scroll', place, true)\n\t\t\t\twindow.addEventListener('resize', place)\n\t\t\t\tif (options.editor) {\n\t\t\t\t\tstopCameraReaction = react('anchor mention popup to camera', () => {\n\t\t\t\t\t\toptions.editor!.getCamera() // track the camera so this re-runs as it moves\n\t\t\t\t\t\treposition()\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst stopFollowing = () => {\n\t\t\t\twindow.removeEventListener('scroll', place, true)\n\t\t\t\twindow.removeEventListener('resize', place)\n\t\t\t\tstopCameraReaction?.()\n\t\t\t\tstopCameraReaction = null\n\t\t\t\tanchorPage = null\n\t\t\t}\n\n\t\t\t// Dismiss the roster \u2014 on Escape, or when the composer loses focus \u2014 by hiding it and\n\t\t\t// clearing the open flag, so isMentionPickerOpen() stays accurate and the thread's own\n\t\t\t// Escape/outside-click dismissal can take over. The TipTap suggestion stays active, so typing\n\t\t\t// re-shows the roster via onUpdate.\n\t\t\tconst hide = () => {\n\t\t\t\tif (container) container.style.display = 'none'\n\t\t\t\tmentionPickerOpen.set(false)\n\t\t\t}\n\n\t\t\t// Wheel/panning over the popup drives the canvas beneath it, so scrolling to pan or zoom\n\t\t\t// isn't swallowed by the roster \u2014 the same passthrough the rest of the comments UI gets from\n\t\t\t// `usePassThroughWheelEvents`. The list still scrolls itself when the roster overflows (we\n\t\t\t// only redispatch when it can't). Done imperatively because the popup lives outside React.\n\t\t\tconst onWheel = (e: WheelEvent) => {\n\t\t\t\tif ((e as any).isSpecialRedispatchedEvent || !canvasEl) return\n\t\t\t\tconst list = container?.querySelector('.cmt-mention-list')\n\t\t\t\tif (list && list.scrollHeight > list.clientHeight) return\n\t\t\t\te.preventDefault()\n\t\t\t\tconst redispatched = new WheelEvent('wheel', e)\n\t\t\t\t;(redispatched as any).isSpecialRedispatchedEvent = true\n\t\t\t\tcanvasEl.dispatchEvent(redispatched)\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tonStart: (props) => {\n\t\t\t\t\trenderer = new ReactRenderer(MentionPopup, {\n\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\titems: props.items,\n\t\t\t\t\t\t\tcommand: props.command,\n\t\t\t\t\t\t\trenderMember: options.renderMember,\n\t\t\t\t\t\t},\n\t\t\t\t\t\teditor: props.editor,\n\t\t\t\t\t})\n\t\t\t\t\teditorEl = props.editor.view.dom as HTMLElement\n\t\t\t\t\t// The TipTap suggestion has no blur handling, so without this the picker would stay\n\t\t\t\t\t// \"open\" (and the thread would defer its Escape to it) after focus moved away from the\n\t\t\t\t\t// composer \u2014 leaving Escape a no-op and the roster stuck on screen.\n\t\t\t\t\teditorEl.addEventListener('blur', hide)\n\t\t\t\t\tcontainer = document.createElement('div')\n\t\t\t\t\tcontainer.className = 'cmt-mention-popup'\n\t\t\t\t\tcontainer.appendChild(renderer.element)\n\t\t\t\t\t// Mount inside the tldraw container so the popup inherits the theme variables\n\t\t\t\t\t// (--tl-color-*); portaling to document.body would strip them and lose the panel.\n\t\t\t\t\tconst themed = editorEl.closest('.tl-container')\n\t\t\t\t\t;(themed ?? document.body).appendChild(container)\n\t\t\t\t\tcanvasEl = themed?.querySelector('.tl-canvas') ?? null\n\t\t\t\t\tcontainer.addEventListener('wheel', onWheel, { passive: false })\n\t\t\t\t\tplace()\n\t\t\t\t\tstartFollowing()\n\t\t\t\t\tmentionPickerOpen.set(true)\n\t\t\t\t},\n\t\t\t\tonUpdate: (props) => {\n\t\t\t\t\tif (renderer)\n\t\t\t\t\t\trenderer.updateProps({\n\t\t\t\t\t\t\titems: props.items,\n\t\t\t\t\t\t\tcommand: props.command,\n\t\t\t\t\t\t\trenderMember: options.renderMember,\n\t\t\t\t\t\t})\n\t\t\t\t\t// Typing after an Escape re-shows the roster.\n\t\t\t\t\tif (container) container.style.display = ''\n\t\t\t\t\tmentionPickerOpen.set(true)\n\t\t\t\t\tplace()\n\t\t\t\t},\n\t\t\t\tonKeyDown: (props) => {\n\t\t\t\t\t// Once the roster is hidden (a prior Escape), the TipTap suggestion is still active but\n\t\t\t\t\t// this handler goes inert \u2014 every key passes through to the composer/thread (so a second\n\t\t\t\t\t// Escape closes them, and Arrow/Enter don't select from an invisible list). Typing\n\t\t\t\t\t// re-shows the roster via onUpdate.\n\t\t\t\t\tif (!isMentionPickerOpen()) return false\n\t\t\t\t\tif (props.event.key === 'Escape') {\n\t\t\t\t\t\t// Dismiss only the roster: hide it and stop the key so the composer/thread beneath\n\t\t\t\t\t\t// doesn't also treat Escape as \"abandon\".\n\t\t\t\t\t\thide()\n\t\t\t\t\t\tprops.event.stopPropagation()\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tif (props.event.key === 'Enter' || props.event.key === 'Tab') {\n\t\t\t\t\t\t// Complete the highlighted member if there is one to complete; if the roster is empty\n\t\t\t\t\t\t// there's nothing to pick, so cancel the picker and swallow the key \u2014 the composer\n\t\t\t\t\t\t// beneath neither submits (Enter) nor moves focus / indents (Tab).\n\t\t\t\t\t\tconst completed = renderer?.ref?.onKeyDown(props) ?? false\n\t\t\t\t\t\tif (!completed) {\n\t\t\t\t\t\t\thide()\n\t\t\t\t\t\t\tprops.event.stopPropagation()\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\tif (renderer && renderer.ref) return renderer.ref.onKeyDown(props)\n\t\t\t\t\treturn false\n\t\t\t\t},\n\t\t\t\tonExit: () => {\n\t\t\t\t\tstopFollowing()\n\t\t\t\t\tif (editorEl) editorEl.removeEventListener('blur', hide)\n\t\t\t\t\tif (container) container.removeEventListener('wheel', onWheel)\n\t\t\t\t\tif (container) container.remove()\n\t\t\t\t\tif (renderer) renderer.destroy()\n\t\t\t\t\trenderer = null\n\t\t\t\t\tcontainer = null\n\t\t\t\t\tcanvasEl = null\n\t\t\t\t\tmentionPickerOpen.set(false)\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_react", "MentionPopup"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var mention_exports = {};
|
|
20
|
+
__export(mention_exports, {
|
|
21
|
+
Mention: () => Mention
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(mention_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
function Mention({ name }) {
|
|
26
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "cmt-mention", children: [
|
|
27
|
+
"@",
|
|
28
|
+
name
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=mention.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/mention.tsx"],
|
|
4
|
+
"sourcesContent": ["/** @public */\nexport interface MentionProps {\n\tname: string\n}\n\n/** An \\@-mention pill for referencing a person in a comment. @public @react */\nexport function Mention({ name }: MentionProps) {\n\treturn <span className=\"cmt-mention\">@{name}</span>\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOQ;AADD,SAAS,QAAQ,EAAE,KAAK,GAAiB;AAC/C,SAAO,6CAAC,UAAK,WAAU,eAAc;AAAA;AAAA,IAAE;AAAA,KAAK;AAC7C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var reaction_exports = {};
|
|
20
|
+
__export(reaction_exports, {
|
|
21
|
+
Reaction: () => Reaction
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(reaction_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
function Reaction({ emoji, count, active }) {
|
|
26
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: active ? "cmt-reaction cmt-reaction--active" : "cmt-reaction", type: "button", children: [
|
|
27
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-reaction__emoji", children: emoji }),
|
|
28
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cmt-reaction__count", children: count })
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=reaction.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/reaction.tsx"],
|
|
4
|
+
"sourcesContent": ["/** @public */\nexport interface ReactionProps {\n\temoji: string\n\tcount: number\n\tactive: boolean\n}\n\n/** A single emoji reaction pill with a count, highlighted when the user reacted. @public @react */\nexport function Reaction({ emoji, count, active }: ReactionProps) {\n\treturn (\n\t\t<button className={active ? 'cmt-reaction cmt-reaction--active' : 'cmt-reaction'} type=\"button\">\n\t\t\t<span className=\"cmt-reaction__emoji\">{emoji}</span>\n\t\t\t<span className=\"cmt-reaction__count\">{count}</span>\n\t\t</button>\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUE;AAFK,SAAS,SAAS,EAAE,OAAO,OAAO,OAAO,GAAkB;AACjE,SACC,6CAAC,YAAO,WAAW,SAAS,sCAAsC,gBAAgB,MAAK,UACtF;AAAA,gDAAC,UAAK,WAAU,uBAAuB,iBAAM;AAAA,IAC7C,4CAAC,UAAK,WAAU,uBAAuB,iBAAM;AAAA,KAC9C;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var reactions_exports = {};
|
|
20
|
+
__export(reactions_exports, {
|
|
21
|
+
Reactions: () => Reactions
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(reactions_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
var import_reaction = require("./reaction");
|
|
26
|
+
function Reactions() {
|
|
27
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "cmt-reactions", children: [
|
|
28
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_reaction.Reaction, { emoji: "\u{1F44D}", count: 3, active: true }),
|
|
29
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_reaction.Reaction, { emoji: "\u{1F389}", count: 1, active: false }),
|
|
30
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_reaction.Reaction, { emoji: "\u{1F440}", count: 2, active: false }),
|
|
31
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "cmt-reaction cmt-reaction--add", type: "button", "aria-label": "Add reaction", children: "\uFF0B" })
|
|
32
|
+
] });
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=reactions.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/reactions.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Reaction } from './reaction'\n\n/** The row of reactions under a comment, plus an add-reaction button. @public @react */\nexport function Reactions() {\n\treturn (\n\t\t<div className=\"cmt-reactions\">\n\t\t\t<Reaction emoji=\"\uD83D\uDC4D\" count={3} active={true} />\n\t\t\t<Reaction emoji=\"\uD83C\uDF89\" count={1} active={false} />\n\t\t\t<Reaction emoji=\"\uD83D\uDC40\" count={2} active={false} />\n\t\t\t<button className=\"cmt-reaction cmt-reaction--add\" type=\"button\" aria-label=\"Add reaction\">\n\t\t\t\t\uFF0B\n\t\t\t</button>\n\t\t</div>\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAKE;AALF,sBAAyB;AAGlB,SAAS,YAAY;AAC3B,SACC,6CAAC,SAAI,WAAU,iBACd;AAAA,gDAAC,4BAAS,OAAM,aAAK,OAAO,GAAG,QAAQ,MAAM;AAAA,IAC7C,4CAAC,4BAAS,OAAM,aAAK,OAAO,GAAG,QAAQ,OAAO;AAAA,IAC9C,4CAAC,4BAAS,OAAM,aAAK,OAAO,GAAG,QAAQ,OAAO;AAAA,IAC9C,4CAAC,YAAO,WAAU,kCAAiC,MAAK,UAAS,cAAW,gBAAe,oBAE3F;AAAA,KACD;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var render_markdown_exports = {};
|
|
20
|
+
__export(render_markdown_exports, {
|
|
21
|
+
renderMarkdown: () => renderMarkdown
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(render_markdown_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
var import_react = require("react");
|
|
26
|
+
function renderMarkdown(text) {
|
|
27
|
+
return text.trim().split(/\n{2,}/).map((block, i) => {
|
|
28
|
+
const lines = block.split("\n");
|
|
29
|
+
if (lines.every((line) => /^\s*[-*]\s+/.test(line))) {
|
|
30
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "md-list", children: lines.map((line, j) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: renderInline(line.replace(/^\s*[-*]\s+/, "")) }, j)) }, i);
|
|
31
|
+
}
|
|
32
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "md-p", children: lines.map((line, j) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [
|
|
33
|
+
j > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
|
|
34
|
+
renderInline(line)
|
|
35
|
+
] }, j)) }, i);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const INLINE = /(\*\*[^*]+\*\*|\*[^*]+\*|_[^_]+_|`[^`]+`|\[[^\]]+\]\([^)]+\))/g;
|
|
39
|
+
function renderInline(text) {
|
|
40
|
+
const out = [];
|
|
41
|
+
let last = 0;
|
|
42
|
+
let key = 0;
|
|
43
|
+
for (const match of text.matchAll(INLINE)) {
|
|
44
|
+
const index = match.index ?? 0;
|
|
45
|
+
if (index > last) out.push(text.slice(last, index));
|
|
46
|
+
out.push(renderToken(match[0], key++));
|
|
47
|
+
last = index + match[0].length;
|
|
48
|
+
}
|
|
49
|
+
if (last < text.length) out.push(text.slice(last));
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function renderToken(token, key) {
|
|
53
|
+
if (token.startsWith("**")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: token.slice(2, -2) }, key);
|
|
54
|
+
if (token.startsWith("`"))
|
|
55
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "md-code", children: token.slice(1, -1) }, key);
|
|
56
|
+
if (token.startsWith("[")) {
|
|
57
|
+
const link = /\[([^\]]+)\]\(([^)]+)\)/.exec(token);
|
|
58
|
+
if (!link) return token;
|
|
59
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: link[2], target: "_blank", rel: "noreferrer", children: link[1] }, key);
|
|
60
|
+
}
|
|
61
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { children: token.slice(1, -1) }, key);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=render-markdown.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/render-markdown.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Fragment, ReactNode } from 'react'\n\n/**\n * Minimal markdown for comment bodies: paragraphs, line breaks, bullet lists, and\n * inline **bold**, *italic*, `code`, and [links](url). Not a full CommonMark parser \u2014\n * just the markdown people actually write in comments. Renders React elements only \u2014\n * never raw HTML \u2014 so there's no HTML-injection surface.\n * @public\n */\nexport function renderMarkdown(text: string): ReactNode {\n\treturn text\n\t\t.trim()\n\t\t.split(/\\n{2,}/)\n\t\t.map((block, i) => {\n\t\t\tconst lines = block.split('\\n')\n\t\t\tif (lines.every((line) => /^\\s*[-*]\\s+/.test(line))) {\n\t\t\t\treturn (\n\t\t\t\t\t<ul key={i} className=\"md-list\">\n\t\t\t\t\t\t{lines.map((line, j) => (\n\t\t\t\t\t\t\t<li key={j}>{renderInline(line.replace(/^\\s*[-*]\\s+/, ''))}</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<p key={i} className=\"md-p\">\n\t\t\t\t\t{lines.map((line, j) => (\n\t\t\t\t\t\t<Fragment key={j}>\n\t\t\t\t\t\t\t{j > 0 && <br />}\n\t\t\t\t\t\t\t{renderInline(line)}\n\t\t\t\t\t\t</Fragment>\n\t\t\t\t\t))}\n\t\t\t\t</p>\n\t\t\t)\n\t\t})\n}\n\nconst INLINE = /(\\*\\*[^*]+\\*\\*|\\*[^*]+\\*|_[^_]+_|`[^`]+`|\\[[^\\]]+\\]\\([^)]+\\))/g\n\nfunction renderInline(text: string): ReactNode {\n\tconst out: ReactNode[] = []\n\tlet last = 0\n\tlet key = 0\n\tfor (const match of text.matchAll(INLINE)) {\n\t\tconst index = match.index ?? 0\n\t\tif (index > last) out.push(text.slice(last, index))\n\t\tout.push(renderToken(match[0], key++))\n\t\tlast = index + match[0].length\n\t}\n\tif (last < text.length) out.push(text.slice(last))\n\treturn out\n}\n\nfunction renderToken(token: string, key: number): ReactNode {\n\tif (token.startsWith('**')) return <strong key={key}>{token.slice(2, -2)}</strong>\n\tif (token.startsWith('`'))\n\t\treturn (\n\t\t\t<code key={key} className=\"md-code\">\n\t\t\t\t{token.slice(1, -1)}\n\t\t\t</code>\n\t\t)\n\tif (token.startsWith('[')) {\n\t\tconst link = /\\[([^\\]]+)\\]\\(([^)]+)\\)/.exec(token)\n\t\tif (!link) return token\n\t\treturn (\n\t\t\t<a key={key} href={link[2]} target=\"_blank\" rel=\"noreferrer\">\n\t\t\t\t{link[1]}\n\t\t\t</a>\n\t\t)\n\t}\n\treturn <em key={key}>{token.slice(1, -1)}</em>\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO;AAnBP,mBAAoC;AAS7B,SAAS,eAAe,MAAyB;AACvD,SAAO,KACL,KAAK,EACL,MAAM,QAAQ,EACd,IAAI,CAAC,OAAO,MAAM;AAClB,UAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,QAAI,MAAM,MAAM,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC,GAAG;AACpD,aACC,4CAAC,QAAW,WAAU,WACpB,gBAAM,IAAI,CAAC,MAAM,MACjB,4CAAC,QAAY,uBAAa,KAAK,QAAQ,eAAe,EAAE,CAAC,KAAhD,CAAkD,CAC3D,KAHO,CAIT;AAAA,IAEF;AACA,WACC,4CAAC,OAAU,WAAU,QACnB,gBAAM,IAAI,CAAC,MAAM,MACjB,6CAAC,yBACC;AAAA,UAAI,KAAK,4CAAC,QAAG;AAAA,MACb,aAAa,IAAI;AAAA,SAFJ,CAGf,CACA,KANM,CAOR;AAAA,EAEF,CAAC;AACH;AAEA,MAAM,SAAS;AAEf,SAAS,aAAa,MAAyB;AAC9C,QAAM,MAAmB,CAAC;AAC1B,MAAI,OAAO;AACX,MAAI,MAAM;AACV,aAAW,SAAS,KAAK,SAAS,MAAM,GAAG;AAC1C,UAAM,QAAQ,MAAM,SAAS;AAC7B,QAAI,QAAQ,KAAM,KAAI,KAAK,KAAK,MAAM,MAAM,KAAK,CAAC;AAClD,QAAI,KAAK,YAAY,MAAM,CAAC,GAAG,KAAK,CAAC;AACrC,WAAO,QAAQ,MAAM,CAAC,EAAE;AAAA,EACzB;AACA,MAAI,OAAO,KAAK,OAAQ,KAAI,KAAK,KAAK,MAAM,IAAI,CAAC;AACjD,SAAO;AACR;AAEA,SAAS,YAAY,OAAe,KAAwB;AAC3D,MAAI,MAAM,WAAW,IAAI,EAAG,QAAO,4CAAC,YAAkB,gBAAM,MAAM,GAAG,EAAE,KAAvB,GAAyB;AACzE,MAAI,MAAM,WAAW,GAAG;AACvB,WACC,4CAAC,UAAe,WAAU,WACxB,gBAAM,MAAM,GAAG,EAAE,KADR,GAEX;AAEF,MAAI,MAAM,WAAW,GAAG,GAAG;AAC1B,UAAM,OAAO,0BAA0B,KAAK,KAAK;AACjD,QAAI,CAAC,KAAM,QAAO;AAClB,WACC,4CAAC,OAAY,MAAM,KAAK,CAAC,GAAG,QAAO,UAAS,KAAI,cAC9C,eAAK,CAAC,KADA,GAER;AAAA,EAEF;AACA,SAAO,4CAAC,QAAc,gBAAM,MAAM,GAAG,EAAE,KAAvB,GAAyB;AAC1C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var send_button_exports = {};
|
|
20
|
+
__export(send_button_exports, {
|
|
21
|
+
SendButton: () => SendButton
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(send_button_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
function SendButton({ label, disabled, onClick }) {
|
|
26
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "cmt-send", type: "button", disabled, onClick, children: label });
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=send-button.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/send-button.tsx"],
|
|
4
|
+
"sourcesContent": ["/** @public */\nexport interface SendButtonProps {\n\tlabel: string\n\tdisabled?: boolean\n\tonClick?(): void\n}\n\n/** The button that posts a comment. @public @react */\nexport function SendButton({ label, disabled, onClick }: SendButtonProps) {\n\treturn (\n\t\t<button className=\"cmt-send\" type=\"button\" disabled={disabled} onClick={onClick}>\n\t\t\t{label}\n\t\t</button>\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUE;AAFK,SAAS,WAAW,EAAE,OAAO,UAAU,QAAQ,GAAoB;AACzE,SACC,4CAAC,YAAO,WAAU,YAAW,MAAK,UAAS,UAAoB,SAC7D,iBACF;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { anchorPagePoint } from "./thread-state.mjs";
|
|
2
|
+
function collectClusterLeaves(editor, threads, openThreadId, impreciseShapeAnchor) {
|
|
3
|
+
const pageId = editor.getCurrentPageId();
|
|
4
|
+
const leaves = [];
|
|
5
|
+
for (const thread of threads) {
|
|
6
|
+
if (thread.id === openThreadId) continue;
|
|
7
|
+
if (thread.pageId !== pageId) continue;
|
|
8
|
+
const point = anchorPagePoint(editor, thread.anchor, impreciseShapeAnchor);
|
|
9
|
+
if (!point) continue;
|
|
10
|
+
leaves.push({
|
|
11
|
+
id: thread.id,
|
|
12
|
+
point: { x: point.x, y: point.y }
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return leaves;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
collectClusterLeaves
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=cluster-input.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/cluster-input.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Editor, TLCommentThread } from 'tldraw'\nimport type { LeafInput } from '../clustering/types'\nimport { anchorPagePoint } from './thread-state'\n\n/** @public */\nexport function collectClusterLeaves(\n\teditor: Editor,\n\tthreads: readonly TLCommentThread[],\n\topenThreadId: string | null,\n\timpreciseShapeAnchor?: { x: number; y: number }\n): LeafInput[] {\n\tconst pageId = editor.getCurrentPageId()\n\tconst leaves: LeafInput[] = []\n\n\tfor (const thread of threads) {\n\t\tif (thread.id === openThreadId) continue\n\t\tif (thread.pageId !== pageId) continue\n\n\t\tconst point = anchorPagePoint(editor, thread.anchor, impreciseShapeAnchor)\n\t\tif (!point) continue\n\n\t\tleaves.push({\n\t\t\tid: thread.id,\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\treturn leaves\n}\n"],
|
|
5
|
+
"mappings": "AAEA,SAAS,uBAAuB;AAGzB,SAAS,qBACf,QACA,SACA,cACA,sBACc;AACd,QAAM,SAAS,OAAO,iBAAiB;AACvC,QAAM,SAAsB,CAAC;AAE7B,aAAW,UAAU,SAAS;AAC7B,QAAI,OAAO,OAAO,aAAc;AAChC,QAAI,OAAO,WAAW,OAAQ;AAE9B,UAAM,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,oBAAoB;AACzE,QAAI,CAAC,MAAO;AAEZ,WAAO,KAAK;AAAA,MACX,IAAI,OAAO;AAAA,MACX,OAAO,EAAE,GAAG,MAAM,GAAG,GAAG,MAAM,EAAE;AAAA,IACjC,CAAC;AAAA,EACF;AAEA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { renderCommentHtml } from "./comment-render.mjs";
|
|
4
|
+
function CommentBody({ richText, resolveName }) {
|
|
5
|
+
const html = useMemo(() => renderCommentHtml(richText, resolveName), [richText, resolveName]);
|
|
6
|
+
return /* @__PURE__ */ jsx("div", { className: "cmt-text", dangerouslySetInnerHTML: { __html: html } });
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
CommentBody
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=comment-body.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/comment-body.tsx"],
|
|
4
|
+
"sourcesContent": ["import { useMemo } from 'react'\nimport { TLRichText } from 'tldraw'\nimport { renderCommentHtml } from './comment-render'\n\n/** @public */\nexport interface CommentBodyProps {\n\trichText: TLRichText\n\t/** Maps a member id to its current display name, so \\@mentions show the live name. */\n\tresolveName?(id: string): string | undefined\n}\n\n/**\n * Renders a comment's rich-text body read-only through the limited comment extension set (no\n * headings), so formatting (bold, links, lists, highlight) is preserved rather than flattened, and\n * headings can never render. Use this as the `body` of a `CommentCard` on a canvas.\n * @public @react\n */\nexport function CommentBody({ richText, resolveName }: CommentBodyProps) {\n\tconst html = useMemo(() => renderCommentHtml(richText, resolveName), [richText, resolveName])\n\treturn <div className=\"cmt-text\" dangerouslySetInnerHTML={{ __html: html }} />\n}\n"],
|
|
5
|
+
"mappings": "AAmBQ;AAnBR,SAAS,eAAe;AAExB,SAAS,yBAAyB;AAe3B,SAAS,YAAY,EAAE,UAAU,YAAY,GAAqB;AACxE,QAAM,OAAO,QAAQ,MAAM,kBAAkB,UAAU,WAAW,GAAG,CAAC,UAAU,WAAW,CAAC;AAC5F,SAAO,oBAAC,SAAI,WAAU,YAAW,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AAC7E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { generateHTML, generateText } from "@tiptap/core";
|
|
2
|
+
import { commentTipTapExtensions, isCommentEmpty } from "../ui/comment-extensions.mjs";
|
|
3
|
+
import { commentMention } from "../ui/comment-mention.mjs";
|
|
4
|
+
const UNKNOWN_AUTHOR = "Someone";
|
|
5
|
+
function demoteHeadings(node) {
|
|
6
|
+
const content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content;
|
|
7
|
+
if (node.type === "heading") {
|
|
8
|
+
const { attrs: _attrs, ...rest } = node;
|
|
9
|
+
return { ...rest, type: "paragraph", content };
|
|
10
|
+
}
|
|
11
|
+
return content === node.content ? node : { ...node, content };
|
|
12
|
+
}
|
|
13
|
+
function hasMention(node) {
|
|
14
|
+
if (node.type === "mention") return true;
|
|
15
|
+
return Array.isArray(node.content) ? node.content.some(hasMention) : false;
|
|
16
|
+
}
|
|
17
|
+
const htmlCache = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
function renderCommentHtml(richText, resolveName) {
|
|
19
|
+
const mentions = hasMention(richText);
|
|
20
|
+
if (!mentions) {
|
|
21
|
+
const cached = htmlCache.get(richText);
|
|
22
|
+
if (cached !== void 0) return cached;
|
|
23
|
+
}
|
|
24
|
+
const extensions = mentions ? [...commentTipTapExtensions, commentMention({ resolveName })] : commentTipTapExtensions;
|
|
25
|
+
const html = generateHTML(demoteHeadings(richText), extensions).replaceAll(
|
|
26
|
+
'<p dir="auto"></p>',
|
|
27
|
+
"<p><br /></p>"
|
|
28
|
+
);
|
|
29
|
+
if (!mentions) htmlCache.set(richText, html);
|
|
30
|
+
return html;
|
|
31
|
+
}
|
|
32
|
+
const textCache = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
function renderCommentPlaintext(richText, resolveName) {
|
|
34
|
+
if (isCommentEmpty(richText)) return "";
|
|
35
|
+
const mentions = hasMention(richText);
|
|
36
|
+
if (!mentions) {
|
|
37
|
+
const cached = textCache.get(richText);
|
|
38
|
+
if (cached !== void 0) return cached;
|
|
39
|
+
}
|
|
40
|
+
const extensions = mentions ? [...commentTipTapExtensions, commentMention({ resolveName })] : commentTipTapExtensions;
|
|
41
|
+
const text = generateText(demoteHeadings(richText), extensions, {
|
|
42
|
+
blockSeparator: "\n"
|
|
43
|
+
});
|
|
44
|
+
if (!mentions) textCache.set(richText, text);
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
UNKNOWN_AUTHOR,
|
|
49
|
+
renderCommentHtml,
|
|
50
|
+
renderCommentPlaintext
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=comment-render.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/comment-render.ts"],
|
|
4
|
+
"sourcesContent": ["import { generateHTML, generateText, JSONContent } from '@tiptap/core'\nimport { TLRichText } from 'tldraw'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\nimport { commentMention } from '../ui/comment-mention'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by returning a name from `resolveName`. Not a translation\n * key \u2014 a single English literal, matching the prior host-side default.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Mirrors\n * tldraw's `renderHtmlFromRichText`, including the empty-paragraph fix that keeps blank lines from\n * collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = generateHTML(demoteHeadings(richText as JSONContent), extensions).replaceAll(\n\t\t'<p dir=\"auto\"></p>',\n\t\t'<p><br /></p>'\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, commentMention({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc,oBAAiC;AAExD,SAAS,yBAAyB,sBAAsB;AACxD,SAAS,sBAAsB;AAQxB,MAAM,iBAAiB;AAQ9B,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,yBAAyB,eAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,OAAO,aAAa,eAAe,QAAuB,GAAG,UAAU,EAAE;AAAA,IAC9E;AAAA,IACA;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,MAAI,eAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,yBAAyB,eAAe,EAAE,YAAY,CAAC,CAAC,IAC5D;AACH,QAAM,OAAO,aAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function putCommentRecords(editor, records) {
|
|
2
|
+
editor.store.put(records);
|
|
3
|
+
}
|
|
4
|
+
function removeCommentRecords(editor, ids) {
|
|
5
|
+
editor.store.remove(ids);
|
|
6
|
+
}
|
|
7
|
+
function getCommentRecord(editor, id) {
|
|
8
|
+
const record = editor.store.get(id);
|
|
9
|
+
if (!record) return void 0;
|
|
10
|
+
if (record.typeName === "comment" || record.typeName === "comment-thread") return record;
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
function getCommentThreads(editor) {
|
|
14
|
+
const typeName = "comment-thread";
|
|
15
|
+
return editor.store.query.records(typeName).get();
|
|
16
|
+
}
|
|
17
|
+
function getComments(editor) {
|
|
18
|
+
const typeName = "comment";
|
|
19
|
+
return editor.store.query.records(typeName).get();
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
getCommentRecord,
|
|
23
|
+
getCommentThreads,
|
|
24
|
+
getComments,
|
|
25
|
+
putCommentRecords,
|
|
26
|
+
removeCommentRecords
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=comment-store.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/comment-store.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n\tEditor,\n\tTLComment,\n\tTLCommentId,\n\tTLCommentThread,\n\tTLCommentThreadId,\n\tTLRecord,\n} from 'tldraw'\n\n/**\n * Typed access to comment records on the editor store.\n *\n * Comment threads and comments live on the editor's local store so the canvas can render them\n * reactively, but they are opt-in records that aren't part of the `TLRecord` union (they ride the\n * sync server's object-store lane on the wire \u2014 see `TLCommentThread`). `editor.store` is therefore\n * statically typed `Store<TLRecord>` and doesn't know about them, so every access has to reinterpret\n * the type. These helpers own that reinterpretation \u2014 an `unknown` hop to exactly the type the store\n * expects, so the rest of each call stays checked \u2014 behind one boundary, and keep call sites typed.\n */\n\n/** A record that lives in a comment thread: the thread itself or one of its messages. @public */\nexport type TLCommentRecord = TLComment | TLCommentThread\n\n/** Write comment records to the store. @public */\nexport function putCommentRecords(editor: Editor, records: TLCommentRecord[]): void {\n\teditor.store.put(records as unknown as TLRecord[])\n}\n\n/** Remove comment records from the store by id. @public */\nexport function removeCommentRecords(\n\teditor: Editor,\n\tids: (TLCommentId | TLCommentThreadId)[]\n): void {\n\teditor.store.remove(ids as unknown as TLRecord['id'][])\n}\n\n/** Read one comment record by id, or `undefined` if the id isn't a present comment record. @public */\nexport function getCommentRecord(editor: Editor, id: string): TLCommentRecord | undefined {\n\tconst record = editor.store.get(id as TLRecord['id']) as unknown as TLCommentRecord | undefined\n\tif (!record) return undefined\n\tif (record.typeName === 'comment' || record.typeName === 'comment-thread') return record\n\treturn undefined\n}\n\n/** All comment threads currently in the store (non-reactive; wrap in `useValue` to react). @public */\nexport function getCommentThreads(editor: Editor): TLCommentThread[] {\n\tconst typeName = 'comment-thread' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLCommentThread[]\n}\n\n/** All comments currently in the store (non-reactive; wrap in `useValue` to react). @public */\nexport function getComments(editor: Editor): TLComment[] {\n\tconst typeName = 'comment' as TLRecord['typeName']\n\treturn editor.store.query.records(typeName).get() as unknown as TLComment[]\n}\n"],
|
|
5
|
+
"mappings": "AAwBO,SAAS,kBAAkB,QAAgB,SAAkC;AACnF,SAAO,MAAM,IAAI,OAAgC;AAClD;AAGO,SAAS,qBACf,QACA,KACO;AACP,SAAO,MAAM,OAAO,GAAkC;AACvD;AAGO,SAAS,iBAAiB,QAAgB,IAAyC;AACzF,QAAM,SAAS,OAAO,MAAM,IAAI,EAAoB;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,aAAa,aAAa,OAAO,aAAa,iBAAkB,QAAO;AAClF,SAAO;AACR;AAGO,SAAS,kBAAkB,QAAmC;AACpE,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;AAGO,SAAS,YAAY,QAA6B;AACxD,QAAM,WAAW;AACjB,SAAO,OAAO,MAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI;AACjD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StateNode
|
|
3
|
+
} from "tldraw";
|
|
4
|
+
import { defaultCommentingOptions } from "./options.mjs";
|
|
5
|
+
import { getRegionCommentOptions } from "./region-options.mjs";
|
|
6
|
+
import { pendingComment, regionDraft } from "./state.mjs";
|
|
7
|
+
import { regionPinPoint, shapeAnchorAt } from "./thread-state.mjs";
|
|
8
|
+
function mergeCommentingOptions(base, overrides) {
|
|
9
|
+
return {
|
|
10
|
+
...base,
|
|
11
|
+
...overrides,
|
|
12
|
+
components: { ...base.components, ...overrides.components }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function regionBetween(a, b) {
|
|
16
|
+
return {
|
|
17
|
+
x: Math.min(a.x, b.x),
|
|
18
|
+
y: Math.min(a.y, b.y),
|
|
19
|
+
w: Math.abs(a.x - b.x),
|
|
20
|
+
h: Math.abs(a.y - b.y)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
class CommentTool extends StateNode {
|
|
24
|
+
static id = "comment";
|
|
25
|
+
static initial = "idle";
|
|
26
|
+
static children() {
|
|
27
|
+
return [CommentIdle, CommentPointing, CommentDragging];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass
|
|
31
|
+
* to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so
|
|
32
|
+
* calls can be chained.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
static configure(options) {
|
|
40
|
+
return class extends this {
|
|
41
|
+
// @ts-expect-error
|
|
42
|
+
options = mergeCommentingOptions(this.options, options);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The merged commenting options for this editor. Read from anywhere via
|
|
47
|
+
* {@link getCommentingOptions}. Override with {@link CommentTool.configure}.
|
|
48
|
+
*/
|
|
49
|
+
options = defaultCommentingOptions;
|
|
50
|
+
onEnter() {
|
|
51
|
+
this.editor.setCursor({ type: "cross", rotation: 0 });
|
|
52
|
+
}
|
|
53
|
+
// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).
|
|
54
|
+
onCancel() {
|
|
55
|
+
this.editor.setCurrentTool("select");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
class CommentIdle extends StateNode {
|
|
59
|
+
static id = "idle";
|
|
60
|
+
onPointerDown() {
|
|
61
|
+
this.parent.transition("pointing");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class CommentPointing extends StateNode {
|
|
65
|
+
static id = "pointing";
|
|
66
|
+
// Once the pointer passes the editor's drag threshold this is a region, not a click — but only
|
|
67
|
+
// when region comments are enabled; otherwise a drag is treated as a click (point/shape).
|
|
68
|
+
onPointerMove() {
|
|
69
|
+
if (getRegionCommentOptions(this.editor).enabled && this.editor.inputs.getIsDragging()) {
|
|
70
|
+
this.parent.transition("dragging");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// A pointer up with no drag is a click: anchor to the shape under it, or drop a point.
|
|
74
|
+
onPointerUp() {
|
|
75
|
+
const { editor } = this;
|
|
76
|
+
const point = editor.inputs.getCurrentPagePoint();
|
|
77
|
+
const hit = editor.getShapeAtPoint(point, { hitInside: true });
|
|
78
|
+
const anchor = hit ? shapeAnchorAt(editor, hit.id, point, editor.inputs.getAltKey()) : { type: "point", x: point.x, y: point.y };
|
|
79
|
+
pendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } });
|
|
80
|
+
editor.setCurrentTool("select");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class CommentDragging extends StateNode {
|
|
84
|
+
static id = "dragging";
|
|
85
|
+
onEnter() {
|
|
86
|
+
this.updateDraft();
|
|
87
|
+
}
|
|
88
|
+
onPointerMove() {
|
|
89
|
+
this.updateDraft();
|
|
90
|
+
}
|
|
91
|
+
// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.
|
|
92
|
+
onPointerUp() {
|
|
93
|
+
const { editor } = this;
|
|
94
|
+
const region = regionBetween(
|
|
95
|
+
editor.inputs.getOriginPagePoint(),
|
|
96
|
+
editor.inputs.getCurrentPagePoint()
|
|
97
|
+
);
|
|
98
|
+
regionDraft.set(editor, null);
|
|
99
|
+
pendingComment.set(editor, {
|
|
100
|
+
anchor: { type: "region", ...region },
|
|
101
|
+
point: regionPinPoint(region, getRegionCommentOptions(editor).pinCorner)
|
|
102
|
+
});
|
|
103
|
+
editor.setCurrentTool("select");
|
|
104
|
+
}
|
|
105
|
+
onCancel() {
|
|
106
|
+
this.cancel();
|
|
107
|
+
}
|
|
108
|
+
onInterrupt() {
|
|
109
|
+
this.cancel();
|
|
110
|
+
}
|
|
111
|
+
updateDraft() {
|
|
112
|
+
const { editor } = this;
|
|
113
|
+
regionDraft.set(
|
|
114
|
+
editor,
|
|
115
|
+
regionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
cancel() {
|
|
119
|
+
regionDraft.set(this.editor, null);
|
|
120
|
+
this.editor.setCurrentTool("select");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const commentTools = [CommentTool];
|
|
124
|
+
const commentToolOverrides = {
|
|
125
|
+
tools(editor, tools) {
|
|
126
|
+
tools.comment = {
|
|
127
|
+
id: "comment",
|
|
128
|
+
icon: "comment",
|
|
129
|
+
label: "Comment",
|
|
130
|
+
kbd: "c",
|
|
131
|
+
onSelect: () => editor.setCurrentTool("comment")
|
|
132
|
+
};
|
|
133
|
+
return tools;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
CommentTool,
|
|
138
|
+
commentToolOverrides,
|
|
139
|
+
commentTools,
|
|
140
|
+
regionBetween
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=comment-tool.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/comment-tool.tsx"],
|
|
4
|
+
"sourcesContent": ["import {\n\tBoxModel,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions } from './options'\nimport { getRegionCommentOptions } from './region-options'\nimport { pendingComment, regionDraft } from './state'\nimport { regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer). Null when nothing is being placed. The atom itself lives in `./state`\n * ({@link pendingComment}), scoped per editor.\n * @public */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. A click starts a point/shape thread (the pin tracks the shape it lands on);\n * dragging out a rectangle starts a region thread anchored to that area. Placement only opens a\n * composer (via `pendingComment`); the records are created when the comment is posted.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\t// Once the pointer passes the editor's drag threshold this is a region, not a click \u2014 but only\n\t// when region comments are enabled; otherwise a drag is treated as a click (point/shape).\n\toverride onPointerMove() {\n\t\tif (getRegionCommentOptions(this.editor).enabled && this.editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t}\n\t}\n\n\t// A pointer up with no drag is a click: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = editor.getShapeAtPoint(point, { hitInside: true })\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(editor, hit.id, point, editor.inputs.getAltKey())\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\t// Hand back to select; the open composer is now the focus.\n\t\teditor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst region = regionBetween(\n\t\t\teditor.inputs.getOriginPagePoint(),\n\t\t\teditor.inputs.getCurrentPagePoint()\n\t\t)\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region },\n\t\t\tpoint: regionPinPoint(region, getRegionCommentOptions(editor).pinCorner),\n\t\t})\n\t\teditor.setCurrentTool('select')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\tprivate cancel() {\n\t\tregionDraft.set(this.editor, null)\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|