@tldraw/commenting 0.0.0-bootstrap → 5.3.0-canary.31036673d3f8
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 +0 -2
- package/commenting.css +1139 -329
- package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
- package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
- package/dist-cjs/canvas/cluster-input.js +2 -2
- package/dist-cjs/canvas/cluster-input.js.map +2 -2
- package/dist-cjs/canvas/comment-body.js +1 -1
- package/dist-cjs/canvas/comment-body.js.map +2 -2
- package/dist-cjs/canvas/comment-drafts.js +54 -0
- package/dist-cjs/canvas/comment-drafts.js.map +7 -0
- package/dist-cjs/canvas/comment-mutations.js +123 -0
- package/dist-cjs/canvas/comment-mutations.js.map +7 -0
- package/dist-cjs/canvas/comment-reactions.js +163 -0
- package/dist-cjs/canvas/comment-reactions.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +5 -3
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-store.js +19 -9
- package/dist-cjs/canvas/comment-store.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +64 -14
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +20 -38
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +22 -27
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +429 -474
- package/dist-cjs/canvas/comments-overlay.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +35 -32
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +65 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/context.js +17 -0
- package/dist-cjs/canvas/context.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +22 -3
- package/dist-cjs/canvas/hooks.js.map +2 -2
- package/dist-cjs/canvas/options.js +19 -2
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/pin-stacking.js +56 -0
- package/dist-cjs/canvas/pin-stacking.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +2 -2
- package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
- package/dist-cjs/canvas/state.js +28 -14
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-preview.js +185 -0
- package/dist-cjs/canvas/thread-preview.js.map +7 -0
- package/dist-cjs/canvas/thread-stack.js +199 -0
- package/dist-cjs/canvas/thread-stack.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +67 -21
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +397 -0
- package/dist-cjs/canvas/thread-view.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +4 -4
- package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
- package/dist-cjs/clustering/runtime.js +119 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/clustering/types.js.map +1 -1
- package/dist-cjs/index.d.ts +763 -333
- package/dist-cjs/index.js +46 -28
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/byline.js +6 -4
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-card.js +15 -8
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +137 -36
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +24 -2
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +9 -7
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +30 -18
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +3 -2
- package/dist-cjs/ui/count-badge.js.map +2 -2
- package/dist-cjs/ui/emoji-picker.js +54 -0
- package/dist-cjs/ui/emoji-picker.js.map +7 -0
- package/dist-cjs/ui/empty-state.js +3 -3
- package/dist-cjs/ui/empty-state.js.map +2 -2
- package/dist-cjs/ui/format-time.js +4 -1
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/reaction-picker.js +73 -0
- package/dist-cjs/ui/reaction-picker.js.map +7 -0
- package/dist-cjs/ui/reaction.js +59 -6
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +20 -7
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
- package/dist-cjs/ui/reply-count.js.map +7 -0
- package/dist-cjs/ui/send-button.js +20 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/{comment-text.js → tooltip-button.js} +12 -9
- package/dist-cjs/ui/tooltip-button.js.map +7 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +2 -2
- package/dist-esm/canvas/cluster-input.mjs.map +2 -2
- package/dist-esm/canvas/comment-body.mjs +1 -1
- package/dist-esm/canvas/comment-body.mjs.map +2 -2
- package/dist-esm/canvas/comment-drafts.mjs +34 -0
- package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
- package/dist-esm/canvas/comment-mutations.mjs +103 -0
- package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
- package/dist-esm/canvas/comment-reactions.mjs +152 -0
- package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +5 -3
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-store.mjs +19 -9
- package/dist-esm/canvas/comment-store.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +67 -17
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +20 -38
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +23 -27
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +450 -479
- package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
- package/dist-esm/canvas/comments-sidebar.mjs +38 -35
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +45 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/context.mjs +1 -0
- package/dist-esm/canvas/context.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +28 -5
- package/dist-esm/canvas/hooks.mjs.map +2 -2
- package/dist-esm/canvas/options.mjs +21 -3
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/pin-stacking.mjs +36 -0
- package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
- package/dist-esm/canvas/state.mjs +29 -19
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-preview.mjs +170 -0
- package/dist-esm/canvas/thread-preview.mjs.map +7 -0
- package/dist-esm/canvas/thread-stack.mjs +190 -0
- package/dist-esm/canvas/thread-stack.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +71 -22
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +402 -0
- package/dist-esm/canvas/thread-view.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +4 -4
- package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
- package/dist-esm/clustering/runtime.mjs +119 -7
- package/dist-esm/clustering/runtime.mjs.map +2 -2
- package/dist-esm/index.d.mts +763 -333
- package/dist-esm/index.mjs +87 -41
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +6 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-card.mjs +15 -8
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +148 -36
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +24 -2
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +9 -7
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +43 -29
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +3 -2
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- package/dist-esm/ui/emoji-picker.mjs +34 -0
- package/dist-esm/ui/emoji-picker.mjs.map +7 -0
- package/dist-esm/ui/empty-state.mjs +3 -3
- package/dist-esm/ui/empty-state.mjs.map +2 -2
- package/dist-esm/ui/format-time.mjs +4 -1
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/reaction-picker.mjs +60 -0
- package/dist-esm/ui/reaction-picker.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +60 -7
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +21 -8
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/reply-count.mjs +9 -0
- package/dist-esm/ui/reply-count.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +20 -1
- package/dist-esm/ui/send-button.mjs.map +2 -2
- package/dist-esm/ui/tooltip-button.mjs +12 -0
- package/dist-esm/ui/tooltip-button.mjs.map +7 -0
- package/package.json +6 -6
- package/src/canvas/anchor-lifecycle.test.ts +290 -0
- package/src/canvas/anchor-lifecycle.ts +205 -0
- package/src/canvas/canvas.css +461 -46
- package/src/canvas/cluster-input.test.ts +31 -23
- package/src/canvas/cluster-input.ts +3 -4
- package/src/canvas/comment-body.tsx +1 -1
- package/src/canvas/comment-drafts.test.ts +55 -0
- package/src/canvas/comment-drafts.ts +46 -0
- package/src/canvas/comment-mutations.test.ts +410 -0
- package/src/canvas/comment-mutations.ts +288 -0
- package/src/canvas/comment-reactions.test.ts +132 -0
- package/src/canvas/comment-reactions.tsx +245 -0
- package/src/canvas/comment-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +9 -5
- package/src/canvas/comment-store.ts +67 -27
- package/src/canvas/comment-tool.test.ts +162 -0
- package/src/canvas/comment-tool.tsx +105 -23
- package/src/canvas/comments-filter-menu.tsx +23 -32
- package/src/canvas/comments-overflow-menu.tsx +14 -21
- package/src/canvas/comments-overlay.tsx +695 -622
- package/src/canvas/comments-sidebar.test.ts +45 -0
- package/src/canvas/comments-sidebar.tsx +76 -58
- package/src/canvas/comments-visibility-toggle.tsx +49 -0
- package/src/canvas/context.ts +47 -0
- package/src/canvas/hooks.ts +52 -11
- package/src/canvas/options.test.ts +52 -2
- package/src/canvas/options.ts +133 -9
- package/src/canvas/pin-stacking.test.ts +114 -0
- package/src/canvas/pin-stacking.ts +57 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +92 -35
- package/src/canvas/thread-preview.test.ts +91 -0
- package/src/canvas/thread-preview.tsx +304 -0
- package/src/canvas/thread-stack.tsx +252 -0
- package/src/canvas/thread-state.test.ts +209 -0
- package/src/canvas/thread-state.ts +137 -36
- package/src/canvas/thread-view.tsx +516 -0
- package/src/clustering/computeClusterTable.test.ts +24 -23
- package/src/clustering/computeClusterTable.ts +5 -5
- package/src/clustering/runtime.test.ts +217 -7
- package/src/clustering/runtime.ts +171 -17
- package/src/clustering/types.ts +9 -9
- package/src/index.ts +76 -40
- package/src/ui/byline.tsx +7 -5
- package/src/ui/comment-card.tsx +20 -10
- package/src/ui/comment-composer.tsx +206 -40
- package/src/ui/comment-pin.tsx +26 -2
- package/src/ui/comment-thread.tsx +13 -6
- package/src/ui/comments-list.test.ts +31 -0
- package/src/ui/comments-list.tsx +45 -21
- package/src/ui/comments.css +566 -281
- package/src/ui/count-badge.tsx +9 -2
- package/src/ui/emoji-picker.test.ts +25 -0
- package/src/ui/emoji-picker.tsx +76 -0
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.ts +7 -2
- package/src/ui/reaction-picker.tsx +109 -0
- package/src/ui/reaction.tsx +121 -6
- package/src/ui/reactions.tsx +69 -10
- package/src/ui/reply-count.ts +16 -0
- package/src/ui/send-button.tsx +18 -3
- package/src/ui/tooltip-button.tsx +20 -0
- package/dist-cjs/canvas/region-options.js +0 -49
- package/dist-cjs/canvas/region-options.js.map +0 -7
- package/dist-cjs/ui/avatar.js +0 -43
- package/dist-cjs/ui/avatar.js.map +0 -7
- package/dist-cjs/ui/comment-mention.js +0 -42
- package/dist-cjs/ui/comment-mention.js.map +0 -7
- package/dist-cjs/ui/comment-text.js.map +0 -7
- package/dist-cjs/ui/mention-list.js +0 -70
- package/dist-cjs/ui/mention-list.js.map +0 -7
- package/dist-cjs/ui/mention-suggestion.js +0 -206
- package/dist-cjs/ui/mention-suggestion.js.map +0 -7
- package/dist-cjs/ui/mention.js.map +0 -7
- package/dist-cjs/ui/render-markdown.js +0 -63
- package/dist-cjs/ui/render-markdown.js.map +0 -7
- package/dist-esm/canvas/region-options.mjs +0 -29
- package/dist-esm/canvas/region-options.mjs.map +0 -7
- package/dist-esm/ui/avatar.mjs +0 -23
- package/dist-esm/ui/avatar.mjs.map +0 -7
- package/dist-esm/ui/comment-mention.mjs +0 -22
- package/dist-esm/ui/comment-mention.mjs.map +0 -7
- package/dist-esm/ui/comment-text.mjs +0 -9
- package/dist-esm/ui/comment-text.mjs.map +0 -7
- package/dist-esm/ui/mention-list.mjs +0 -50
- package/dist-esm/ui/mention-list.mjs.map +0 -7
- package/dist-esm/ui/mention-suggestion.mjs +0 -186
- package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
- package/dist-esm/ui/mention.mjs +0 -11
- package/dist-esm/ui/mention.mjs.map +0 -7
- package/dist-esm/ui/render-markdown.mjs +0 -45
- package/dist-esm/ui/render-markdown.mjs.map +0 -7
- package/src/canvas/region-options.ts +0 -57
- package/src/ui/avatar.tsx +0 -31
- package/src/ui/comment-mention.ts +0 -47
- package/src/ui/comment-text.tsx +0 -12
- package/src/ui/mention-list.tsx +0 -106
- package/src/ui/mention-suggestion.test.ts +0 -18
- package/src/ui/mention-suggestion.tsx +0 -285
- package/src/ui/mention.tsx +0 -9
- package/src/ui/render-markdown.tsx +0 -72
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-sidebar.tsx"],
|
|
4
|
-
"sourcesContent": ["import { ReactNode, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport {\n\tTLComment,\n\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import { ReactNode, useCallback, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport {\n\tTLComment,\n\tuseContainer,\n\tuseEditor,\n\tusePassThroughMouseOverEvents,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { CommentListItemProps, CommentsList } from '../ui/comments-list'\nimport { UNKNOWN_COMMENT_AUTHOR } from './comment-render'\nimport { CommentsFilterMenu } from './comments-filter-menu'\nimport { CommentsVisibilityToggle } from './comments-visibility-toggle'\nimport { type CommentingContext } from './context'\nimport { useComments, useCommentThreads } from './hooks'\nimport { useCommentingEnabled } from './license'\nimport { useCommentingOptions } from './options'\nimport { richTextToPlaintext } from './rich-text'\nimport { commentsSidebarOpen, openThreadId, sidebarFilters } from './state'\nimport { focusThread } from './thread-state'\n\n/**\n * The host wiring for {@link CanvasCommentsSidebar}: the {@link CommentingContext} fields it reads,\n * plus the panel's own slots. A non-null `currentUserId` enables the \"only your threads\" filter, and\n * an `isCommentUnread` the \"only unread\" one. `CanvasComments` takes the same fields, so a host\n * mounting both can spread one object into each.\n *\n * @public\n */\nexport interface CanvasCommentsSidebarProps extends Pick<\n\tCommentingContext,\n\t'currentUserId' | 'resolveAuthor' | 'isCommentUnread' | 'getThreadHref'\n> {\n\t/** Header above the list. */\n\theader?: ReactNode\n\t/** Shown when the page has no threads. */\n\tempty?: ReactNode\n}\n\n/**\n * A comments list panel for the current page, shown while {@link commentsSidebarOpen} is set (e.g.\n * toggled by a button). Clicking a thread brings its pin into view and opens it. Batteries-included\n * over the store (a sibling to `CanvasComments`); `CommentsList` is exported for a differently-placed\n * or always-on list.\n * @public @react\n */\nexport function CanvasCommentsSidebar(props: CanvasCommentsSidebarProps) {\n\tconst { resolveAuthor, currentUserId, isCommentUnread, header, empty, getThreadHref } = props\n\t// Name-only view of the resolver, for the plaintext previews (which resolve @-mentions).\n\tconst resolveName = useCallback((id: string) => resolveAuthor(id)?.name, [resolveAuthor])\n\tconst editor = useEditor()\n\tconst options = useCommentingOptions()\n\tconst container = useContainer()\n\tconst commentingEnabled = useCommentingEnabled()\n\tconst msg = useTranslation()\n\tconst threads = useCommentThreads(editor)\n\tconst comments = useComments(editor)\n\tconst currentPageId = useValue('page id', () => editor.getCurrentPageId(), [editor])\n\tconst open = useValue('sidebar open', () => commentsSidebarOpen.get(editor), [editor])\n\tconst openId = useValue('open thread', () => openThreadId.get(editor), [editor])\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst pageNames = useValue(\n\t\t'page names',\n\t\t() => new Map(editor.getPages().map((page) => [page.id, page.name])),\n\t\t[editor]\n\t)\n\n\tif (!commentingEnabled || !open) return null\n\n\t// Group comments by thread (they arrive oldest-first, so [0] is each thread's first comment).\n\tconst byThread = new Map<string, TLComment[]>()\n\tfor (const comment of comments) {\n\t\tconst list = byThread.get(comment.threadId) ?? []\n\t\tlist.push(comment)\n\t\tbyThread.set(comment.threadId, list)\n\t}\n\n\t// Page scoping is treated as scoping, not a filter: an empty page reads \"no comments yet\",\n\t// while a list emptied by the toggles below reads \"nothing matches your filters\".\n\tconst pageThreads = threads.filter(\n\t\t(thread) => !filters.onlyCurrentPage || thread.pageId === currentPageId\n\t)\n\n\tconst rows: SidebarRow[] = pageThreads\n\t\t.filter((thread) => filters.showResolved || thread.resolved == null)\n\t\t// \"Only mine\" is ignored without a known user \u2014 otherwise a persisted onlyMine=true would\n\t\t// empty the list for a signed-out viewer, with the (hidden) toggle giving no way to clear it.\n\t\t.filter(\n\t\t\t(thread) => !filters.onlyMine || currentUserId === null || thread.createdBy === currentUserId\n\t\t)\n\t\t// \"Only unread\" is likewise ignored without a read-status source.\n\t\t.filter(\n\t\t\t(thread) =>\n\t\t\t\t!filters.onlyUnread ||\n\t\t\t\tisCommentUnread === undefined ||\n\t\t\t\t(byThread.get(thread.id) ?? []).some((c) => isCommentUnread(c.id))\n\t\t)\n\t\t.map((thread) => {\n\t\t\tconst threadComments = byThread.get(thread.id) ?? []\n\t\t\tconst first = threadComments[0]\n\t\t\t// Comments arrive oldest-first, so the last one is the thread's most recent activity.\n\t\t\tconst last = threadComments[threadComments.length - 1]\n\t\t\tlet preview: ReactNode = ''\n\t\t\t// The `ThreadPreview` component slot overrides the built-in plaintext default.\n\t\t\tconst ThreadPreview = options.components.ThreadPreview\n\t\t\tif (first) {\n\t\t\t\tpreview = ThreadPreview ? (\n\t\t\t\t\t<ThreadPreview comment={first} />\n\t\t\t\t) : (\n\t\t\t\t\trichTextToPlaintext(first.body, resolveName)\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\titem: {\n\t\t\t\t\tid: thread.id,\n\t\t\t\t\tauthor: resolveAuthor(thread.createdBy) ?? UNKNOWN_COMMENT_AUTHOR,\n\t\t\t\t\tpreview,\n\t\t\t\t\tdate: new Date((first ?? thread).createdAt).toISOString(),\n\t\t\t\t\tresolved: thread.resolved != null,\n\t\t\t\t\t// The page label only earns its place when it adds information: multiple pages\n\t\t\t\t\t// exist, and the thread is somewhere other than where you already are.\n\t\t\t\t\tpage:\n\t\t\t\t\t\tpageNames.size > 1 && thread.pageId !== currentPageId\n\t\t\t\t\t\t\t? pageNames.get(thread.pageId)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tcount: threadComments.length,\n\t\t\t\t\tselected: openId === thread.id,\n\t\t\t\t\thref: getThreadHref?.(thread.id),\n\t\t\t\t},\n\t\t\t\tlastActivity: (last ?? thread).createdAt,\n\t\t\t}\n\t\t})\n\n\tconst items = sortSidebarRows(rows).map((row) => row.item)\n\n\tconst focus = (id: string) => {\n\t\tconst thread = threads.find((t) => t.id === id)\n\t\tif (thread) focusThread(editor, thread)\n\t}\n\n\treturn (\n\t\t<SidebarPanel container={container}>\n\t\t\t<CommentsList\n\t\t\t\titems={items}\n\t\t\t\theader={header ?? msg('comments.title')}\n\t\t\t\theaderAction={\n\t\t\t\t\t<div className=\"tlui-cmt-list__header-actions\">\n\t\t\t\t\t\t<CommentsFilterMenu\n\t\t\t\t\t\t\tcanFilterByAuthor={currentUserId !== null}\n\t\t\t\t\t\t\tcanFilterByUnread={isCommentUnread !== undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<CommentsVisibilityToggle />\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\tempty={\n\t\t\t\t\titems.length === 0 && pageThreads.length > 0\n\t\t\t\t\t\t? msg('comments.empty-filtered')\n\t\t\t\t\t\t: (empty ?? msg('comments.empty'))\n\t\t\t\t}\n\t\t\t\tresolvedLabel={msg('comments.resolved')}\n\t\t\t\tonSelect={focus}\n\t\t\t/>\n\t\t</SidebarPanel>\n\t)\n}\n\n/** A list row paired with the sort key that isn't part of what the row displays. */\ninterface SidebarRow {\n\titem: CommentListItemProps\n\t/** When the thread's most recent comment was posted \u2014 what the list orders by. */\n\tlastActivity: number\n}\n\n/**\n * Order the list: unresolved threads first, then by most recent activity, id as a stable tiebreak.\n * Recency is the thread's *latest* comment, not its first, so a thread someone just replied to rises\n * to the top instead of staying wherever it was started. (The row still shows the thread's opening\n * comment and its date \u2014 that's what identifies the thread; only the ordering follows the replies.)\n */\nexport function sortSidebarRows(rows: readonly SidebarRow[]): readonly SidebarRow[] {\n\treturn [...rows].sort(\n\t\t(a, b) =>\n\t\t\tNumber(!!a.item.resolved) - Number(!!b.item.resolved) ||\n\t\t\tb.lastActivity - a.lastActivity ||\n\t\t\t(a.item.id < b.item.id ? -1 : 1)\n\t)\n}\n\n/** The sidebar surface, portaled into the container. It scrolls its own list, so \u2014 unlike tldraw's\n * wheel-transparent panels \u2014 a wheel over it doesn't pan the canvas. Hover still passes through so\n * shapes beneath it stay interactive. */\nfunction SidebarPanel({ container, children }: { container: HTMLElement; children: ReactNode }) {\n\tconst ref = useRef<HTMLDivElement>(null)\n\tusePassThroughMouseOverEvents(ref)\n\treturn createPortal(\n\t\t<div ref={ref} className=\"tlui-cmt-canvas-sidebar\" onContextMenu={(e) => e.stopPropagation()}>\n\t\t\t{children}\n\t\t</div>,\n\t\tcontainer\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4GK;AA5GL,mBAA+C;AAC/C,uBAA6B;AAC7B,oBAOO;AACP,2BAAmD;AACnD,4BAAuC;AACvC,kCAAmC;AACnC,wCAAyC;AAEzC,mBAA+C;AAC/C,qBAAqC;AACrC,qBAAqC;AACrC,uBAAoC;AACpC,mBAAkE;AAClE,0BAA4B;AA2BrB,SAAS,sBAAsB,OAAmC;AACxE,QAAM,EAAE,eAAe,eAAe,iBAAiB,QAAQ,OAAO,cAAc,IAAI;AAExF,QAAM,kBAAc,0BAAY,CAAC,OAAe,cAAc,EAAE,GAAG,MAAM,CAAC,aAAa,CAAC;AACxF,QAAM,aAAS,yBAAU;AACzB,QAAM,cAAU,qCAAqB;AACrC,QAAM,gBAAY,4BAAa;AAC/B,QAAM,wBAAoB,qCAAqB;AAC/C,QAAM,UAAM,8BAAe;AAC3B,QAAM,cAAU,gCAAkB,MAAM;AACxC,QAAM,eAAW,0BAAY,MAAM;AACnC,QAAM,oBAAgB,wBAAS,WAAW,MAAM,OAAO,iBAAiB,GAAG,CAAC,MAAM,CAAC;AACnF,QAAM,WAAO,wBAAS,gBAAgB,MAAM,iCAAoB,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACrF,QAAM,aAAS,wBAAS,eAAe,MAAM,0BAAa,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAC/E,QAAM,cAAU,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,gBAAY;AAAA,IACjB;AAAA,IACA,MAAM,IAAI,IAAI,OAAO,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,IACnE,CAAC,MAAM;AAAA,EACR;AAEA,MAAI,CAAC,qBAAqB,CAAC,KAAM,QAAO;AAGxC,QAAM,WAAW,oBAAI,IAAyB;AAC9C,aAAW,WAAW,UAAU;AAC/B,UAAM,OAAO,SAAS,IAAI,QAAQ,QAAQ,KAAK,CAAC;AAChD,SAAK,KAAK,OAAO;AACjB,aAAS,IAAI,QAAQ,UAAU,IAAI;AAAA,EACpC;AAIA,QAAM,cAAc,QAAQ;AAAA,IAC3B,CAAC,WAAW,CAAC,QAAQ,mBAAmB,OAAO,WAAW;AAAA,EAC3D;AAEA,QAAM,OAAqB,YACzB,OAAO,CAAC,WAAW,QAAQ,gBAAgB,OAAO,YAAY,IAAI,EAGlE;AAAA,IACA,CAAC,WAAW,CAAC,QAAQ,YAAY,kBAAkB,QAAQ,OAAO,cAAc;AAAA,EACjF,EAEC;AAAA,IACA,CAAC,WACA,CAAC,QAAQ,cACT,oBAAoB,WACnB,SAAS,IAAI,OAAO,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,gBAAgB,EAAE,EAAE,CAAC;AAAA,EACnE,EACC,IAAI,CAAC,WAAW;AAChB,UAAM,iBAAiB,SAAS,IAAI,OAAO,EAAE,KAAK,CAAC;AACnD,UAAM,QAAQ,eAAe,CAAC;AAE9B,UAAM,OAAO,eAAe,eAAe,SAAS,CAAC;AACrD,QAAI,UAAqB;AAEzB,UAAM,gBAAgB,QAAQ,WAAW;AACzC,QAAI,OAAO;AACV,gBAAU,gBACT,4CAAC,iBAAc,SAAS,OAAO,QAE/B,sCAAoB,MAAM,MAAM,WAAW;AAAA,IAE7C;AACA,WAAO;AAAA,MACN,MAAM;AAAA,QACL,IAAI,OAAO;AAAA,QACX,QAAQ,cAAc,OAAO,SAAS,KAAK;AAAA,QAC3C;AAAA,QACA,MAAM,IAAI,MAAM,SAAS,QAAQ,SAAS,EAAE,YAAY;AAAA,QACxD,UAAU,OAAO,YAAY;AAAA;AAAA;AAAA,QAG7B,MACC,UAAU,OAAO,KAAK,OAAO,WAAW,gBACrC,UAAU,IAAI,OAAO,MAAM,IAC3B;AAAA,QACJ,OAAO,eAAe;AAAA,QACtB,UAAU,WAAW,OAAO;AAAA,QAC5B,MAAM,gBAAgB,OAAO,EAAE;AAAA,MAChC;AAAA,MACA,eAAe,QAAQ,QAAQ;AAAA,IAChC;AAAA,EACD,CAAC;AAEF,QAAM,QAAQ,gBAAgB,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;AAEzD,QAAM,QAAQ,CAAC,OAAe;AAC7B,UAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC9C,QAAI,OAAQ,sCAAY,QAAQ,MAAM;AAAA,EACvC;AAEA,SACC,4CAAC,gBAAa,WACb;AAAA,IAAC;AAAA;AAAA,MACA;AAAA,MACA,QAAQ,UAAU,IAAI,gBAAgB;AAAA,MACtC,cACC,6CAAC,SAAI,WAAU,iCACd;AAAA;AAAA,UAAC;AAAA;AAAA,YACA,mBAAmB,kBAAkB;AAAA,YACrC,mBAAmB,oBAAoB;AAAA;AAAA,QACxC;AAAA,QACA,4CAAC,8DAAyB;AAAA,SAC3B;AAAA,MAED,OACC,MAAM,WAAW,KAAK,YAAY,SAAS,IACxC,IAAI,yBAAyB,IAC5B,SAAS,IAAI,gBAAgB;AAAA,MAElC,eAAe,IAAI,mBAAmB;AAAA,MACtC,UAAU;AAAA;AAAA,EACX,GACD;AAEF;AAeO,SAAS,gBAAgB,MAAoD;AACnF,SAAO,CAAC,GAAG,IAAI,EAAE;AAAA,IAChB,CAAC,GAAG,MACH,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,KACpD,EAAE,eAAe,EAAE,iBAClB,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,KAAK;AAAA,EAChC;AACD;AAKA,SAAS,aAAa,EAAE,WAAW,SAAS,GAAoD;AAC/F,QAAM,UAAM,qBAAuB,IAAI;AACvC,mDAA8B,GAAG;AACjC,aAAO;AAAA,IACN,4CAAC,SAAI,KAAU,WAAU,2BAA0B,eAAe,CAAC,MAAM,EAAE,gBAAgB,GACzF,UACF;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 comments_visibility_toggle_exports = {};
|
|
20
|
+
__export(comments_visibility_toggle_exports, {
|
|
21
|
+
CommentsVisibilityToggle: () => CommentsVisibilityToggle
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(comments_visibility_toggle_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
var import_tldraw = require("tldraw");
|
|
26
|
+
var import_tooltip_button = require("../ui/tooltip-button");
|
|
27
|
+
var import_state = require("./state");
|
|
28
|
+
function CommentsVisibilityToggle() {
|
|
29
|
+
const editor = (0, import_tldraw.useEditor)();
|
|
30
|
+
const msg = (0, import_tldraw.useTranslation)();
|
|
31
|
+
const hidden = (0, import_tldraw.useValue)("comments hidden", () => import_state.commentsHidden.get(editor), [editor]);
|
|
32
|
+
const label = hidden ? msg("comments.show") : msg("comments.hide");
|
|
33
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
34
|
+
import_tooltip_button.TooltipButton,
|
|
35
|
+
{
|
|
36
|
+
tooltip: label,
|
|
37
|
+
className: "tlui-cmt-header-btn",
|
|
38
|
+
onClick: () => (0, import_state.toggleCommentsHidden)(editor),
|
|
39
|
+
children: hidden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EyeClosedIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EyeOpenIcon, {})
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function EyeOpenIcon() {
|
|
44
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
45
|
+
"path",
|
|
46
|
+
{
|
|
47
|
+
d: "M7.5 11C4.80285 11 2.52952 9.62184 1.09622 7.50001C2.52952 5.37816 4.80285 4 7.5 4C10.1971 4 12.4705 5.37816 13.9038 7.5C12.4705 9.62183 10.1971 11 7.5 11ZM7.5 3C4.30786 3 1.65639 4.70638 0.0760002 7.23501C-0.0253338 7.39715 -0.0253334 7.60288 0.0760014 7.76501C1.65639 10.2936 4.30786 12 7.5 12C10.6921 12 13.3436 10.2936 14.924 7.76501C15.0253 7.60288 15.0253 7.39715 14.924 7.23501C13.3436 4.70638 10.6921 3 7.5 3ZM7.5 9.5C8.60457 9.5 9.5 8.60457 9.5 7.5C9.5 6.39543 8.60457 5.5 7.5 5.5C6.39543 5.5 5.5 6.39543 5.5 7.5C5.5 8.60457 6.39543 9.5 7.5 9.5Z",
|
|
48
|
+
fill: "currentColor",
|
|
49
|
+
fillRule: "evenodd",
|
|
50
|
+
clipRule: "evenodd"
|
|
51
|
+
}
|
|
52
|
+
) });
|
|
53
|
+
}
|
|
54
|
+
function EyeClosedIcon() {
|
|
55
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
56
|
+
"path",
|
|
57
|
+
{
|
|
58
|
+
d: "M14.7649 6.07596C14.9991 6.22231 15.0703 6.53079 14.9239 6.76495C14.4849 7.46743 13.9632 8.10645 13.3702 8.66305L14.5712 9.86406C14.7664 10.0593 14.7664 10.3759 14.5712 10.5712C14.3759 10.7664 14.0593 10.7664 13.8641 10.5712L12.6011 9.30817C11.805 9.90283 10.9089 10.3621 9.93375 10.651L10.383 12.3277C10.4544 12.5944 10.2961 12.8685 10.0294 12.94C9.76267 13.0115 9.4885 12.8532 9.41704 12.5865L8.95917 10.8775C8.48743 10.958 8.00036 11 7.50001 11C6.99965 11 6.51257 10.958 6.04082 10.8775L5.58299 12.5864C5.51153 12.8532 5.23737 13.0115 4.97064 12.94C4.7039 12.8686 4.5456 12.5944 4.61706 12.3277L5.06625 10.651C4.09111 10.3621 3.19503 9.90282 2.39889 9.30815L1.1359 10.5712C0.940638 10.7664 0.624058 10.7664 0.428798 10.5712C0.233537 10.3759 0.233537 10.0593 0.428798 9.86405L1.62982 8.66303C1.03682 8.10643 0.515113 7.46742 0.0760677 6.76495C-0.0702867 6.53079 0.000898544 6.22231 0.235065 6.07596C0.469231 5.9296 0.777703 6.00079 0.924058 6.23496C1.40354 7.00213 1.989 7.68057 2.66233 8.2427C2.67315 8.25096 2.6837 8.25972 2.69397 8.26898C4.00897 9.35527 5.65537 10 7.50001 10C10.3078 10 12.6564 8.5063 14.076 6.23495C14.2223 6.00079 14.5308 5.9296 14.7649 6.07596Z",
|
|
59
|
+
fill: "currentColor",
|
|
60
|
+
fillRule: "evenodd",
|
|
61
|
+
clipRule: "evenodd"
|
|
62
|
+
}
|
|
63
|
+
) });
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=comments-visibility-toggle.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/comments-visibility-toggle.tsx"],
|
|
4
|
+
"sourcesContent": ["import { useEditor, useTranslation, useValue } from 'tldraw'\nimport { TooltipButton } from '../ui/tooltip-button'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n/** The sidebar header's show/hide toggle for comment pins \u2014 an eye that closes while comments\n * are hidden. The same state as the Shift+C shortcut.\n * @public @react */\nexport function CommentsVisibilityToggle() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\tconst label = hidden ? msg('comments.show') : msg('comments.hide')\n\n\treturn (\n\t\t<TooltipButton\n\t\t\ttooltip={label}\n\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t>\n\t\t\t{hidden ? <EyeClosedIcon /> : <EyeOpenIcon />}\n\t\t</TooltipButton>\n\t)\n}\n\nfunction EyeOpenIcon() {\n\treturn (\n\t\t<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M7.5 11C4.80285 11 2.52952 9.62184 1.09622 7.50001C2.52952 5.37816 4.80285 4 7.5 4C10.1971 4 12.4705 5.37816 13.9038 7.5C12.4705 9.62183 10.1971 11 7.5 11ZM7.5 3C4.30786 3 1.65639 4.70638 0.0760002 7.23501C-0.0253338 7.39715 -0.0253334 7.60288 0.0760014 7.76501C1.65639 10.2936 4.30786 12 7.5 12C10.6921 12 13.3436 10.2936 14.924 7.76501C15.0253 7.60288 15.0253 7.39715 14.924 7.23501C13.3436 4.70638 10.6921 3 7.5 3ZM7.5 9.5C8.60457 9.5 9.5 8.60457 9.5 7.5C9.5 6.39543 8.60457 5.5 7.5 5.5C6.39543 5.5 5.5 6.39543 5.5 7.5C5.5 8.60457 6.39543 9.5 7.5 9.5Z\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n\nfunction EyeClosedIcon() {\n\treturn (\n\t\t<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" aria-hidden=\"true\">\n\t\t\t<path\n\t\t\t\td=\"M14.7649 6.07596C14.9991 6.22231 15.0703 6.53079 14.9239 6.76495C14.4849 7.46743 13.9632 8.10645 13.3702 8.66305L14.5712 9.86406C14.7664 10.0593 14.7664 10.3759 14.5712 10.5712C14.3759 10.7664 14.0593 10.7664 13.8641 10.5712L12.6011 9.30817C11.805 9.90283 10.9089 10.3621 9.93375 10.651L10.383 12.3277C10.4544 12.5944 10.2961 12.8685 10.0294 12.94C9.76267 13.0115 9.4885 12.8532 9.41704 12.5865L8.95917 10.8775C8.48743 10.958 8.00036 11 7.50001 11C6.99965 11 6.51257 10.958 6.04082 10.8775L5.58299 12.5864C5.51153 12.8532 5.23737 13.0115 4.97064 12.94C4.7039 12.8686 4.5456 12.5944 4.61706 12.3277L5.06625 10.651C4.09111 10.3621 3.19503 9.90282 2.39889 9.30815L1.1359 10.5712C0.940638 10.7664 0.624058 10.7664 0.428798 10.5712C0.233537 10.3759 0.233537 10.0593 0.428798 9.86405L1.62982 8.66303C1.03682 8.10643 0.515113 7.46742 0.0760677 6.76495C-0.0702867 6.53079 0.000898544 6.22231 0.235065 6.07596C0.469231 5.9296 0.777703 6.00079 0.924058 6.23496C1.40354 7.00213 1.989 7.68057 2.66233 8.2427C2.67315 8.25096 2.6837 8.25972 2.69397 8.26898C4.00897 9.35527 5.65537 10 7.50001 10C10.3078 10 12.6564 8.5063 14.076 6.23495C14.2223 6.00079 14.5308 5.9296 14.7649 6.07596Z\"\n\t\t\t\tfill=\"currentColor\"\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t/>\n\t\t</svg>\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBa;AAnBb,oBAAoD;AACpD,4BAA8B;AAC9B,mBAAqD;AAK9C,SAAS,2BAA2B;AAC1C,QAAM,aAAS,yBAAU;AACzB,QAAM,UAAM,8BAAe;AAC3B,QAAM,aAAS,wBAAS,mBAAmB,MAAM,4BAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACrF,QAAM,QAAQ,SAAS,IAAI,eAAe,IAAI,IAAI,eAAe;AAEjE,SACC;AAAA,IAAC;AAAA;AAAA,MACA,SAAS;AAAA,MACT,WAAU;AAAA,MACV,SAAS,UAAM,mCAAqB,MAAM;AAAA,MAEzC,mBAAS,4CAAC,iBAAc,IAAK,4CAAC,eAAY;AAAA;AAAA,EAC5C;AAEF;AAEA,SAAS,cAAc;AACtB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,MAAK;AAAA,MACL,UAAS;AAAA,MACT,UAAS;AAAA;AAAA,EACV,GACD;AAEF;AAEA,SAAS,gBAAgB;AACxB,SACC,4CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,eAAY,QACvE;AAAA,IAAC;AAAA;AAAA,MACA,GAAE;AAAA,MACF,MAAK;AAAA,MACL,UAAS;AAAA,MACT,UAAS;AAAA;AAAA,EACV,GACD;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var context_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(context_exports);
|
|
17
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/context.ts"],
|
|
4
|
+
"sourcesContent": ["import { type CommentAuthor, type MentionMember } from '@tldraw/mentions'\nimport { type ReactNode } from 'react'\nimport { type TLComment, type TLCommentId, type TLCommentThreadId } from 'tldraw'\n\n/**\n * The live, host-supplied half of commenting: who the viewer is, how author ids become names, read\n * status, and the mention roster. (The static half is {@link CommentingOptions}, configured once on\n * the tool.)\n *\n * Every commenting surface reads the same set, so a host mounting more than one \u2014 `CanvasComments`\n * and `CanvasCommentsSidebar`, say \u2014 can build it once and spread it into each rather than\n * repeating the props:\n *\n * ```tsx\n * const commenting: CommentingContext = { currentUserId, resolveAuthor, isCommentUnread }\n *\n * <CanvasComments {...commenting} />\n * <CanvasCommentsSidebar {...commenting} />\n * ```\n *\n * @public\n */\nexport interface CommentingContext {\n\t/** The signed-in user's id, or null for a read-only viewer. Only a signed-in user composes. */\n\tcurrentUserId: string | null\n\t/** Map an author id to their display info, or `undefined` when the id can't be resolved. */\n\tresolveAuthor(id: string): CommentAuthor | undefined\n\t/** Called after any comment (a new thread's first comment, or a reply) is posted. */\n\tonPostComment?(comment: TLComment): void\n\t/** Whether a comment is unread for the current user (return true for unread). */\n\tisCommentUnread?(commentId: TLCommentId): boolean\n\t/**\n\t * Called for each unread comment shown to the user in an open thread popover, so hosts can\n\t * record a read receipt. Needs `isCommentUnread` to know what's unread.\n\t */\n\tonCommentRead?(commentId: TLCommentId): void\n\t/** Resolve the members matching an `@`-query in the composers (sync or async). */\n\tgetMentionSuggestions?(query: string): MentionMember[] | Promise<MentionMember[]>\n\t/** Override a mention-picker row's content. */\n\trenderMentionSuggestion?(member: MentionMember): ReactNode\n\t/**\n\t * The host's URL for a thread, so surfaces can link to it \u2014 e.g. the sidebar's rows render as\n\t * anchors when this is present, letting ctrl/cmd-click and middle-click open the thread in a\n\t * new tab (a plain click still selects the thread in place; the href isn't followed).\n\t */\n\tgetThreadHref?(threadId: TLCommentThreadId): string | undefined\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist-cjs/canvas/hooks.js
CHANGED
|
@@ -26,19 +26,38 @@ module.exports = __toCommonJS(hooks_exports);
|
|
|
26
26
|
var import_tldraw = require("tldraw");
|
|
27
27
|
var import_comment_store = require("./comment-store");
|
|
28
28
|
function useCommentThreads(editor) {
|
|
29
|
-
return (0, import_tldraw.useValue)("comment threads", () => (0, import_comment_store.
|
|
29
|
+
return (0, import_tldraw.useValue)("comment threads", () => (0, import_comment_store.getLiveCommentThreads)(editor), [editor]);
|
|
30
|
+
}
|
|
31
|
+
const commentsByThread = new import_tldraw.WeakCache();
|
|
32
|
+
function getCommentsByThread(editor) {
|
|
33
|
+
return commentsByThread.get(
|
|
34
|
+
editor,
|
|
35
|
+
() => (0, import_tldraw.computed)("comments by thread", () => {
|
|
36
|
+
const byThread = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const comment of (0, import_comment_store.getLiveComments)(editor)) {
|
|
38
|
+
const existing = byThread.get(comment.threadId);
|
|
39
|
+
if (existing) existing.push(comment);
|
|
40
|
+
else byThread.set(comment.threadId, [comment]);
|
|
41
|
+
}
|
|
42
|
+
for (const comments of byThread.values()) {
|
|
43
|
+
comments.sort((a, b) => a.createdAt - b.createdAt);
|
|
44
|
+
}
|
|
45
|
+
return byThread;
|
|
46
|
+
})
|
|
47
|
+
);
|
|
30
48
|
}
|
|
31
49
|
function useThreadComments(editor, threadId) {
|
|
32
50
|
return (0, import_tldraw.useValue)(
|
|
33
51
|
"thread comments",
|
|
34
|
-
() => (
|
|
52
|
+
() => getCommentsByThread(editor).get().get(threadId) ?? EMPTY_COMMENTS,
|
|
35
53
|
[editor, threadId]
|
|
36
54
|
);
|
|
37
55
|
}
|
|
56
|
+
const EMPTY_COMMENTS = [];
|
|
38
57
|
function useComments(editor) {
|
|
39
58
|
return (0, import_tldraw.useValue)(
|
|
40
59
|
"all comments",
|
|
41
|
-
() => (0, import_comment_store.
|
|
60
|
+
() => (0, import_comment_store.getLiveComments)(editor).sort((a, b) => a.createdAt - b.createdAt),
|
|
42
61
|
[editor]
|
|
43
62
|
);
|
|
44
63
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/hooks.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n\tcomputed,\n\tComputed,\n\tEditor,\n\tTLComment,\n\tTLCommentThread,\n\tTLCommentThreadId,\n\tuseValue,\n\tWeakCache,\n} from 'tldraw'\nimport { getLiveComments, getLiveCommentThreads } from './comment-store'\n\n/**\n * The comment threads that should render (pins, sidebar), reactively \u2014 the live set described by\n * {@link getLiveCommentThreads}. Use `getCommentThreads` for the unfiltered set, including\n * soft-deleted threads awaiting the server's prune.\n *\n * @public\n */\nexport function useCommentThreads(editor: Editor): TLCommentThread[] {\n\treturn useValue('comment threads', () => getLiveCommentThreads(editor), [editor])\n}\n\n/**\n * Every live comment grouped by thread, oldest first within each thread. One computed shared by\n * every mounted thread: each `useThreadComments` would otherwise scan and sort the whole comment\n * set, so N open threads did N full passes on every comment mutation. Cached per editor so the\n * grouping survives re-renders and is rebuilt only when the comment set actually changes.\n */\nconst commentsByThread = new WeakCache<Editor, Computed<Map<TLCommentThreadId, TLComment[]>>>()\n\nfunction getCommentsByThread(editor: Editor) {\n\treturn commentsByThread.get(editor, () =>\n\t\tcomputed('comments by thread', () => {\n\t\t\tconst byThread = new Map<TLCommentThreadId, TLComment[]>()\n\t\t\tfor (const comment of getLiveComments(editor)) {\n\t\t\t\tconst existing = byThread.get(comment.threadId)\n\t\t\t\tif (existing) existing.push(comment)\n\t\t\t\telse byThread.set(comment.threadId, [comment])\n\t\t\t}\n\t\t\tfor (const comments of byThread.values()) {\n\t\t\t\tcomments.sort((a, b) => a.createdAt - b.createdAt)\n\t\t\t}\n\t\t\treturn byThread\n\t\t})\n\t)\n}\n\n/** A thread's live comments, oldest first, reactively. @public */\nexport function useThreadComments(editor: Editor, threadId: TLCommentThreadId): TLComment[] {\n\treturn useValue(\n\t\t'thread comments',\n\t\t() => getCommentsByThread(editor).get().get(threadId) ?? EMPTY_COMMENTS,\n\t\t[editor, threadId]\n\t)\n}\n\n/** Shared empty result so a thread with no comments doesn't churn referential equality. */\nconst EMPTY_COMMENTS: TLComment[] = []\n\n/** Every live comment in the store ({@link getLiveComments}), oldest first, reactively. Group by\n * `threadId` for per-thread lists. @public */\nexport function useComments(editor: Editor): TLComment[] {\n\treturn useValue(\n\t\t'all comments',\n\t\t() => getLiveComments(editor).sort((a, b) => a.createdAt - b.createdAt),\n\t\t[editor]\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBASO;AACP,2BAAuD;AAShD,SAAS,kBAAkB,QAAmC;AACpE,aAAO,wBAAS,mBAAmB,UAAM,4CAAsB,MAAM,GAAG,CAAC,MAAM,CAAC;AACjF;AAQA,MAAM,mBAAmB,IAAI,wBAAiE;AAE9F,SAAS,oBAAoB,QAAgB;AAC5C,SAAO,iBAAiB;AAAA,IAAI;AAAA,IAAQ,UACnC,wBAAS,sBAAsB,MAAM;AACpC,YAAM,WAAW,oBAAI,IAAoC;AACzD,iBAAW,eAAW,sCAAgB,MAAM,GAAG;AAC9C,cAAM,WAAW,SAAS,IAAI,QAAQ,QAAQ;AAC9C,YAAI,SAAU,UAAS,KAAK,OAAO;AAAA,YAC9B,UAAS,IAAI,QAAQ,UAAU,CAAC,OAAO,CAAC;AAAA,MAC9C;AACA,iBAAW,YAAY,SAAS,OAAO,GAAG;AACzC,iBAAS,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,MAClD;AACA,aAAO;AAAA,IACR,CAAC;AAAA,EACF;AACD;AAGO,SAAS,kBAAkB,QAAgB,UAA0C;AAC3F,aAAO;AAAA,IACN;AAAA,IACA,MAAM,oBAAoB,MAAM,EAAE,IAAI,EAAE,IAAI,QAAQ,KAAK;AAAA,IACzD,CAAC,QAAQ,QAAQ;AAAA,EAClB;AACD;AAGA,MAAM,iBAA8B,CAAC;AAI9B,SAAS,YAAY,QAA6B;AACxD,aAAO;AAAA,IACN;AAAA,IACA,UAAM,sCAAgB,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,IACtE,CAAC,MAAM;AAAA,EACR;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,19 +19,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var options_exports = {};
|
|
20
20
|
__export(options_exports, {
|
|
21
21
|
defaultCommentingOptions: () => defaultCommentingOptions,
|
|
22
|
+
getCanComment: () => getCanComment,
|
|
22
23
|
getCommentingOptions: () => getCommentingOptions,
|
|
24
|
+
useCanComment: () => useCanComment,
|
|
23
25
|
useCommentingOptions: () => useCommentingOptions
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(options_exports);
|
|
26
28
|
var import_react = require("react");
|
|
27
29
|
var import_tldraw = require("tldraw");
|
|
30
|
+
var import_emoji_picker = require("../ui/emoji-picker");
|
|
28
31
|
const defaultCommentingOptions = {
|
|
29
32
|
history: "ignore",
|
|
30
33
|
dragHistory: void 0,
|
|
31
34
|
enableClustering: true,
|
|
35
|
+
allowMultipleReactions: true,
|
|
36
|
+
isAllowedReaction: import_emoji_picker.isAllowedReactionEmoji,
|
|
37
|
+
enableRegions: false,
|
|
38
|
+
canComment: void 0,
|
|
32
39
|
impreciseShapeAnchor: { x: 1, y: 0 },
|
|
33
|
-
|
|
34
|
-
clusterSplitZoomFactor: 1.05,
|
|
40
|
+
shouldBePrecise: () => true,
|
|
35
41
|
components: {}
|
|
36
42
|
};
|
|
37
43
|
function getCommentingOptions(editor) {
|
|
@@ -42,4 +48,15 @@ function useCommentingOptions() {
|
|
|
42
48
|
const editor = (0, import_tldraw.useEditor)();
|
|
43
49
|
return (0, import_react.useMemo)(() => getCommentingOptions(editor), [editor]);
|
|
44
50
|
}
|
|
51
|
+
function getCanComment(editor, currentUserId) {
|
|
52
|
+
const { canComment } = getCommentingOptions(editor);
|
|
53
|
+
return canComment ? canComment({ editor, currentUserId: currentUserId ?? null }) : currentUserId != null;
|
|
54
|
+
}
|
|
55
|
+
function useCanComment(currentUserId) {
|
|
56
|
+
const editor = (0, import_tldraw.useEditor)();
|
|
57
|
+
return (0, import_tldraw.useValue)("can comment", () => getCanComment(editor, currentUserId), [
|
|
58
|
+
editor,
|
|
59
|
+
currentUserId
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
45
62
|
//# sourceMappingURL=options.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/options.ts"],
|
|
4
|
-
"sourcesContent": ["import { useMemo, type ComponentType } from 'react'\nimport {\n\ttype Editor,\n\ttype TLComment,\n\ttype TLCommentThread,\n\ttype TLHistoryBatchOptions,\n\tuseEditor,\n} from 'tldraw'\n\n/**\n * Component overrides for the batteries-included comments layer. Each slot replaces a built-in\n * piece; leave a slot unset to keep its default.\n *\n * @public\n */\nexport interface CommentingComponents {\n\t/** A comment's body. Replaces the default rich-text `<CommentBody>`. */\n\tCommentBody?: ComponentType<{ comment: TLComment }>\n\t/** A pin's inner content. Replaces the author-initial default. */\n\tPinContent?: ComponentType<{ thread: TLCommentThread; comments: TLComment[] }>\n\t/** A sidebar row's preview. Replaces the plaintext default. */\n\tThreadPreview?: ComponentType<{ comment: TLComment }>\n}\n\n/**\n * Configuration for the commenting layer. Static config only \u2014 pass it once via\n * `CommentTool.configure({ ... })`, mirroring `ShapeUtil.configure`. Live, reactive values\n * (`currentUserId`, `resolveName`, read-status callbacks) stay as props on `<CanvasComments>`.\n *\n * For defaults, see {@link defaultCommentingOptions}.\n *\n * @example\n * ```tsx\n * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n * ```\n *\n * @public\n */\nexport interface CommentingOptions {\n\t// \u2500\u2500 History / undo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/**\n\t * How comment mutations (post, reply, edit, resolve, delete) interact with the editor undo\n\t * stack. Defaults to `'ignore'` \u2014 comments are deliberately not undoable (see `TLComment`).\n\t * `'record'` is a multiplayer footgun: undoing a delete resurrects a thread a collaborator\n\t * already removed, and undoing a resolve/edit reverts their newer state. Safe only single-player\n\t * or on a non-synced local comment store.\n\t */\n\treadonly history: TLHistoryBatchOptions['history']\n\t/**\n\t * History mode for the pin drag-to-move re-anchor specifically. Unlike posts/edits this is a\n\t * spatial edit that may reasonably be undoable alongside a shape move. Defaults to `history`.\n\t */\n\treadonly dragHistory: TLHistoryBatchOptions['history'] | undefined\n\n\t// \u2500\u2500 Feature toggles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Fold nearby pins into count badges as the camera zooms out. */\n\treadonly enableClustering: boolean\n\n\t// \u2500\u2500 Anchoring \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Normalized (0\u20131) spot within a shape where imprecise shape pins sit. Default top-right. */\n\treadonly impreciseShapeAnchor: { readonly x: number; readonly y: number }\n\n\t// \u2500\u2500 Clustering tuning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Screen-pixel margin by which the viewport is inflated when culling cluster badges. */\n\treadonly clusterCullMargin: number\n\t/** How far past a cluster's split zoom to land when expanding it (1.05 = 5% overshoot). */\n\treadonly clusterSplitZoomFactor: number\n\n\t// \u2500\u2500 Components \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Component overrides. See {@link CommentingComponents}. */\n\treadonly components: CommentingComponents\n}\n\n/**\n * The default {@link CommentingOptions}. Override via `CommentTool.configure({ ... })`.\n *\n * @public\n */\nexport const defaultCommentingOptions = {\n\thistory: 'ignore',\n\tdragHistory: undefined,\n\tenableClustering: true,\n\timpreciseShapeAnchor: { x: 1, y: 0 },\n\tclusterCullMargin: 120,\n\tclusterSplitZoomFactor: 1.05,\n\tcomponents: {},\n} as const satisfies CommentingOptions\n\n/**\n * The merged {@link CommentingOptions} for an editor, read off the registered comment tool (which\n * carries them via `CommentTool.configure`). Falls back to {@link defaultCommentingOptions} when\n * the comment tool isn't registered. Usable from anywhere with an `Editor` \u2014 including the tool's\n * own state, which has no React context.\n *\n * @public\n */\nexport function getCommentingOptions(editor: Editor): CommentingOptions {\n\tconst tool = editor.getStateDescendant('comment') as { options?: CommentingOptions } | undefined\n\treturn tool?.options ?? defaultCommentingOptions\n}\n\n/**\n * React hook for {@link getCommentingOptions}. Options are fixed per editor (set at tool\n * registration), so this doesn't need to be reactive.\n *\n * @public\n */\nexport function useCommentingOptions(): CommentingOptions {\n\tconst editor = useEditor()\n\treturn useMemo(() => getCommentingOptions(editor), [editor])\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4C;AAC5C,
|
|
4
|
+
"sourcesContent": ["import { useMemo, type ComponentType } from 'react'\nimport {\n\ttype Editor,\n\ttype TLComment,\n\ttype TLCommentThread,\n\ttype TLHistoryBatchOptions,\n\ttype TLShapeId,\n\ttype VecLike,\n\tuseEditor,\n\tuseValue,\n} from 'tldraw'\nimport { isAllowedReactionEmoji, type EmojiPickerProps } from '../ui/emoji-picker'\nimport { type ReactionTooltipProps } from '../ui/reaction'\n\n/**\n * The gesture that's creating a shape anchor, passed to\n * {@link CommentingOptions.shouldBePrecise}: the target shape, the page point of the release, and\n * whether Alt was held.\n *\n * @public\n */\nexport interface ShapeCommentPrecisionContext {\n\treadonly shapeId: TLShapeId\n\treadonly point: VecLike\n\treadonly altKey: boolean\n}\n\n/**\n * Component overrides for the batteries-included comments layer. Each slot replaces a built-in\n * piece; leave a slot unset to keep its default.\n *\n * @public\n */\nexport interface CommentingComponents {\n\t/** A comment's body. Replaces the default rich-text `<CommentBody>`. */\n\tCommentBody?: ComponentType<{ comment: TLComment }>\n\t/** A pin's inner content. Replaces the author-initial default. */\n\tPinContent?: ComponentType<{ thread: TLCommentThread; comments: TLComment[] }>\n\t/** A sidebar row's preview. Replaces the plaintext default. */\n\tThreadPreview?: ComponentType<{ comment: TLComment }>\n\t/**\n\t * A reaction's visual, given its token. The default renders the token string for the OS emoji\n\t * font to draw (so the token is the emoji glyph). Override this to render your own palette \u2014\n\t * return an `<img>` for custom emoji, an SVG, or anything. The token is whatever your picker\n\t * emits and is what gets stored/synced; this only controls how it's drawn.\n\t */\n\tReactionContent?: ComponentType<{ token: string }>\n\t/**\n\t * What the add-reaction button opens: the thing that produces a reaction token. Replaces the\n\t * default `<EmojiPicker>` grid. Pairs with `ReactionContent` (which draws whatever tokens this\n\t * emits) and with `isAllowedReaction` (which has to accept them).\n\t */\n\tReactionPalette?: ComponentType<EmojiPickerProps>\n\t/**\n\t * The hover affordance naming who reacted with an emoji. It receives the reactors and the pill\n\t * (as `children`) and returns the whole thing \u2014 so it owns the tooltip, its box, size, shape, and\n\t * position. Replaces the default (`DefaultReactionTooltip`). For a simple wording change, translate\n\t * the `comments.reacted-*` strings instead; reach for this to change the structure \u2014 a different\n\t * box, avatars, a banner anywhere on screen.\n\t */\n\tReactionTooltip?: ComponentType<ReactionTooltipProps>\n\t/** Shown where a composer would sit when the viewer can't compose (see\n\t * {@link CommentingOptions.canComment} \u2014 a signed-out viewer, a viewer role, a host that\n\t * turns commenting off). `context` says which surface is rendering it: the bottom of an open\n\t * thread popover (`'thread'`) or the placement popover the comment tool opens (`'pending'`).\n\t * Unset, those surfaces render nothing. */\n\tComposerFallback?: ComponentType<{ context: 'pending' | 'thread' }>\n}\n\n/**\n * Configuration for the commenting layer. Static config only \u2014 pass it once via\n * `CommentTool.configure({ ... })`, mirroring `ShapeUtil.configure`. Live, reactive values\n * (`currentUserId`, author resolution, read-status callbacks) are the `CommentingContext`, passed as\n * props to each commenting surface.\n *\n * For defaults, see {@link defaultCommentingOptions}.\n *\n * @example\n * ```tsx\n * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n * ```\n *\n * @public\n */\nexport interface CommentingOptions {\n\t// \u2500\u2500 History / undo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/**\n\t * How comment mutations (post, reply, edit, resolve, delete) interact with the editor undo\n\t * stack. Defaults to `'ignore'` \u2014 comments are deliberately not undoable (see `TLComment`).\n\t * `'record'` is a multiplayer footgun: undoing a delete resurrects a thread a collaborator\n\t * already removed, and undoing a resolve/edit reverts their newer state. Safe only single-player\n\t * or on a non-synced local comment store.\n\t */\n\treadonly history: TLHistoryBatchOptions['history']\n\t/**\n\t * History mode for the pin drag-to-move re-anchor specifically. Unlike posts/edits this is a\n\t * spatial edit that may reasonably be undoable alongside a shape move. Defaults to `history`.\n\t */\n\treadonly dragHistory: TLHistoryBatchOptions['history'] | undefined\n\n\t// \u2500\u2500 Feature toggles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Fold nearby pins into count badges as the camera zooms out. */\n\treadonly enableClustering: boolean\n\t/**\n\t * Whether a user may hold several emoji reactions on one comment. `true` (the default) is the\n\t * Slack model: each emoji toggles independently. `false` is single-select: picking a new emoji\n\t * replaces the user's existing reaction. Note this is enforced client-side; the server accepts\n\t * per-emoji records either way.\n\t */\n\treadonly allowMultipleReactions: boolean\n\t/**\n\t * Whether a token may be added as a reaction. Defaults to {@link isAllowedReactionEmoji} against\n\t * the built-in emoji palette, which is what keeps a scripted client from writing junk `emoji`\n\t * values the picker would never offer. Override it alongside a custom `ReactionPalette` so the\n\t * tokens that palette emits get through. Removals aren't checked \u2014 a reaction carrying an\n\t * off-palette token must still be clearable.\n\t */\n\tisAllowedReaction(token: string): boolean\n\t/**\n\t * Whether dragging the comment tool out creates a region anchor \u2014 a comment attached to a\n\t * rectangular area of the page, drawn as a dashed box with the thread's pin on the corner the\n\t * drag released on. Off by default: comments attach to points and shapes only, and a drag just\n\t * trails the composer. A region reveals its box while the pointer is inside it, moves by its\n\t * pin, and resizes from its corners.\n\t */\n\treadonly enableRegions: boolean\n\n\t// \u2500\u2500 Permissions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/**\n\t * Whether the viewer may participate in commenting: composing new threads and replies, editing\n\t * and deleting comments, resolving threads, and moving pins or regions. Composers render when\n\t * it returns true; when it returns false, the {@link CommentingComponents.ComposerFallback}\n\t * slot renders in their place (or nothing, if that slot is unset) and the action affordances\n\t * are hidden. Unset, participation is allowed exactly when `currentUserId` is set.\n\t *\n\t * Called during render via {@link useCanComment}, so reactive reads (signals) are tracked.\n\t * The comment tool itself stays registered and selectable \u2014 hosts that want its toolbar button\n\t * to do something else (e.g. open a sign-in dialog) can override the tool item's `onSelect`.\n\t * Note posting still requires a `currentUserId` to author the records, so a callback that\n\t * returns true for a signed-out viewer yields a composer whose send button stays disabled.\n\t */\n\treadonly canComment:\n\t\t| ((ctx: { editor: Editor; currentUserId: string | null }) => boolean)\n\t\t| undefined\n\n\t// \u2500\u2500 Anchoring \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Normalized (0\u20131) spot within a shape where imprecise shape pins sit. Default top-right. */\n\treadonly impreciseShapeAnchor: { readonly x: number; readonly y: number }\n\t/**\n\t * Whether a comment landing on a shape anchors precisely \u2014 pinned to the exact clicked spot\n\t * within the shape \u2014 or imprecisely \u2014 pinned to the shape as a whole, rendered at\n\t * `impreciseShapeAnchor`. Called wherever a shape anchor is created (placing with the comment\n\t * tool, dropping a dragged pin onto a shape). Always precise by default. Return `false` for\n\t * shape-level anchoring, or decide from the context \u2014 the Alt key's state, or the shape itself,\n\t * e.g. precise only on notes. Governs new placements only; existing anchors render as stored.\n\t */\n\tshouldBePrecise(editor: Editor, context: ShapeCommentPrecisionContext): boolean\n\n\t// \u2500\u2500 Components \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\t/** Component overrides. See {@link CommentingComponents}. */\n\treadonly components: CommentingComponents\n}\n\n/**\n * The default {@link CommentingOptions}. Override via `CommentTool.configure({ ... })`.\n *\n * @public\n */\nexport const defaultCommentingOptions = {\n\thistory: 'ignore',\n\tdragHistory: undefined,\n\tenableClustering: true,\n\tallowMultipleReactions: true,\n\tisAllowedReaction: isAllowedReactionEmoji,\n\tenableRegions: false,\n\tcanComment: undefined,\n\timpreciseShapeAnchor: { x: 1, y: 0 },\n\tshouldBePrecise: () => true,\n\tcomponents: {},\n} as const satisfies CommentingOptions\n\n/**\n * The merged {@link CommentingOptions} for an editor, read off the registered comment tool (which\n * carries them via `CommentTool.configure`). Falls back to {@link defaultCommentingOptions} when\n * the comment tool isn't registered. Usable from anywhere with an `Editor` \u2014 including the tool's\n * own state, which has no React context.\n *\n * @public\n */\nexport function getCommentingOptions(editor: Editor): CommentingOptions {\n\tconst tool = editor.getStateDescendant('comment') as { options?: CommentingOptions } | undefined\n\treturn tool?.options ?? defaultCommentingOptions\n}\n\n/**\n * React hook for {@link getCommentingOptions}. Options are fixed per editor (set at tool\n * registration), so this doesn't need to be reactive.\n *\n * @public\n */\nexport function useCommentingOptions(): CommentingOptions {\n\tconst editor = useEditor()\n\treturn useMemo(() => getCommentingOptions(editor), [editor])\n}\n\n/**\n * Whether the viewer may participate in commenting, per {@link CommentingOptions.canComment}\n * (defaulting to `currentUserId != null` when unset). Where this is false, composers give way to\n * the {@link CommentingComponents.ComposerFallback} slot and action affordances are hidden.\n *\n * This is a plain, untracked read \u2014 a `canComment` callback that reads signals is not observed.\n * In React, use {@link useCanComment} instead.\n *\n * @public\n */\nexport function getCanComment(editor: Editor, currentUserId: string | null | undefined): boolean {\n\tconst { canComment } = getCommentingOptions(editor)\n\treturn canComment\n\t\t? canComment({ editor, currentUserId: currentUserId ?? null })\n\t\t: currentUserId != null\n}\n\n/**\n * Reactive React hook for {@link getCanComment}: a `canComment` callback that reads signals\n * re-evaluates when they change.\n *\n * @public\n */\nexport function useCanComment(currentUserId: string | null | undefined): boolean {\n\tconst editor = useEditor()\n\treturn useValue('can comment', () => getCanComment(editor, currentUserId), [\n\t\teditor,\n\t\tcurrentUserId,\n\t])\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4C;AAC5C,oBASO;AACP,0BAA8D;AA6JvD,MAAM,2BAA2B;AAAA,EACvC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,sBAAsB,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,EACnC,iBAAiB,MAAM;AAAA,EACvB,YAAY,CAAC;AACd;AAUO,SAAS,qBAAqB,QAAmC;AACvE,QAAM,OAAO,OAAO,mBAAmB,SAAS;AAChD,SAAO,MAAM,WAAW;AACzB;AAQO,SAAS,uBAA0C;AACzD,QAAM,aAAS,yBAAU;AACzB,aAAO,sBAAQ,MAAM,qBAAqB,MAAM,GAAG,CAAC,MAAM,CAAC;AAC5D;AAYO,SAAS,cAAc,QAAgB,eAAmD;AAChG,QAAM,EAAE,WAAW,IAAI,qBAAqB,MAAM;AAClD,SAAO,aACJ,WAAW,EAAE,QAAQ,eAAe,iBAAiB,KAAK,CAAC,IAC3D,iBAAiB;AACrB;AAQO,SAAS,cAAc,eAAmD;AAChF,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,eAAe,MAAM,cAAc,QAAQ,aAAa,GAAG;AAAA,IAC1E;AAAA,IACA;AAAA,EACD,CAAC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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 pin_stacking_exports = {};
|
|
20
|
+
__export(pin_stacking_exports, {
|
|
21
|
+
computePinStacks: () => computePinStacks,
|
|
22
|
+
pinStackKey: () => pinStackKey
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(pin_stacking_exports);
|
|
25
|
+
var import_thread_state = require("./thread-state");
|
|
26
|
+
const PIN_STACK_QUANTUM = 0.1;
|
|
27
|
+
function pinStackKey(point) {
|
|
28
|
+
return `${Math.round(point.x / PIN_STACK_QUANTUM)}:${Math.round(point.y / PIN_STACK_QUANTUM)}`;
|
|
29
|
+
}
|
|
30
|
+
function computePinStacks(editor, threads) {
|
|
31
|
+
const pageId = editor.getCurrentPageId();
|
|
32
|
+
const groups = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const thread of threads) {
|
|
34
|
+
if (thread.pageId !== pageId) continue;
|
|
35
|
+
const point = (0, import_thread_state.anchorPagePoint)(editor, thread.anchor);
|
|
36
|
+
if (!point) continue;
|
|
37
|
+
const key = pinStackKey(point);
|
|
38
|
+
const group = groups.get(key);
|
|
39
|
+
if (group) {
|
|
40
|
+
group.push(thread);
|
|
41
|
+
} else {
|
|
42
|
+
groups.set(key, [thread]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const group of groups.values()) {
|
|
47
|
+
if (group.length < 2) continue;
|
|
48
|
+
group.sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : 1));
|
|
49
|
+
const ids = group.map((thread) => thread.id);
|
|
50
|
+
for (const id of ids) {
|
|
51
|
+
stacks.set(id, ids);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return stacks;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=pin-stacking.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/canvas/pin-stacking.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Editor, TLCommentThread } from 'tldraw'\nimport { anchorPagePoint } from './thread-state'\n\n/** Two anchors within this page-space distance (per axis) share a stack. Identical imprecise\n * anchors on one shape resolve to the same point exactly; the tolerance only absorbs float noise. */\nconst PIN_STACK_QUANTUM = 0.1\n\n/**\n * The key a page point stacks under. Threads sharing a key are coincident. Stable identity for a\n * stack itself: it survives losing any member (the survivors keep the same key), unlike keying by\n * a particular thread id, so open-stack state stays put when the stack's oldest thread is deleted.\n */\nexport function pinStackKey(point: { x: number; y: number }): string {\n\treturn `${Math.round(point.x / PIN_STACK_QUANTUM)}:${Math.round(point.y / PIN_STACK_QUANTUM)}`\n}\n\n/**\n * Group threads whose pins land on the same page point \u2014 coincident pins (typically several\n * imprecise comments on one shape) that zooming can never separate. The overlay renders each\n * group as a single count-badge pin that opens the threads as a list. Every member id maps to\n * its group's ordered member ids (oldest first); threads without an entry pin individually.\n *\n * Keyed by page-space anchor point, not screen position, so the result only changes when threads\n * or their anchors move \u2014 never on camera moves.\n * @internal\n */\nexport function computePinStacks(\n\teditor: Editor,\n\tthreads: readonly TLCommentThread[]\n): Map<string, readonly string[]> {\n\tconst pageId = editor.getCurrentPageId()\n\tconst groups = new Map<string, TLCommentThread[]>()\n\n\tfor (const thread of threads) {\n\t\tif (thread.pageId !== pageId) continue\n\t\tconst point = anchorPagePoint(editor, thread.anchor)\n\t\tif (!point) continue\n\t\tconst key = pinStackKey(point)\n\t\tconst group = groups.get(key)\n\t\tif (group) {\n\t\t\tgroup.push(thread)\n\t\t} else {\n\t\t\tgroups.set(key, [thread])\n\t\t}\n\t}\n\n\tconst stacks = new Map<string, readonly string[]>()\n\tfor (const group of groups.values()) {\n\t\tif (group.length < 2) continue\n\t\tgroup.sort((a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : 1))\n\t\tconst ids = group.map((thread) => thread.id)\n\t\tfor (const id of ids) {\n\t\t\tstacks.set(id, ids)\n\t\t}\n\t}\n\treturn stacks\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,0BAAgC;AAIhC,MAAM,oBAAoB;AAOnB,SAAS,YAAY,OAAyC;AACpE,SAAO,GAAG,KAAK,MAAM,MAAM,IAAI,iBAAiB,CAAC,IAAI,KAAK,MAAM,MAAM,IAAI,iBAAiB,CAAC;AAC7F;AAYO,SAAS,iBACf,QACA,SACiC;AACjC,QAAM,SAAS,OAAO,iBAAiB;AACvC,QAAM,SAAS,oBAAI,IAA+B;AAElD,aAAW,UAAU,SAAS;AAC7B,QAAI,OAAO,WAAW,OAAQ;AAC9B,UAAM,YAAQ,qCAAgB,QAAQ,OAAO,MAAM;AACnD,QAAI,CAAC,MAAO;AACZ,UAAM,MAAM,YAAY,KAAK;AAC7B,UAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,QAAI,OAAO;AACV,YAAM,KAAK,MAAM;AAAA,IAClB,OAAO;AACN,aAAO,IAAI,KAAK,CAAC,MAAM,CAAC;AAAA,IACzB;AAAA,EACD;AAEA,QAAM,SAAS,oBAAI,IAA+B;AAClD,aAAW,SAAS,OAAO,OAAO,GAAG;AACpC,QAAI,MAAM,SAAS,EAAG;AACtB,UAAM,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE;AACxE,UAAM,MAAM,MAAM,IAAI,CAAC,WAAW,OAAO,EAAE;AAC3C,eAAW,MAAM,KAAK;AACrB,aAAO,IAAI,IAAI,GAAG;AAAA,IACnB;AAAA,EACD;AACA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -22,9 +22,9 @@ __export(sidebar_filters_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(sidebar_filters_exports);
|
|
24
24
|
const DEFAULT_SIDEBAR_FILTERS = {
|
|
25
|
-
showResolved:
|
|
25
|
+
showResolved: false,
|
|
26
26
|
onlyMine: false,
|
|
27
27
|
onlyUnread: false,
|
|
28
|
-
onlyCurrentPage:
|
|
28
|
+
onlyCurrentPage: false
|
|
29
29
|
};
|
|
30
30
|
//# sourceMappingURL=sidebar-filters.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/sidebar-filters.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Which threads the comments sidebar shows. Held as an editor-scoped signal (the `sidebarFilters`\n * `EditorAtom` in `./state`) rather than component state so it survives the sidebar\n * unmounting when the comment tool deactivates \u2014 a user's \"hide resolved\" choice shouldn't reset\n * every time they leave the tool.\n * @public\n */\nexport interface SidebarFilters {\n\t/** Include resolved threads. */\n\tshowResolved: boolean\n\t/** Only threads the current user started. Ignored when there's no current user. */\n\tonlyMine: boolean\n\t/** Only threads with unread comments. Ignored when the host provides no read status. */\n\tonlyUnread: boolean\n\t/** Only threads on the current page. Off = every page's threads, each labelled. */\n\tonlyCurrentPage: boolean\n}\n\n/** @public */\nexport const DEFAULT_SIDEBAR_FILTERS: SidebarFilters = {\n\tshowResolved:
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/**\n * Which threads the comments sidebar shows. Held as an editor-scoped signal (the `sidebarFilters`\n * `EditorAtom` in `./state`) rather than component state so it survives the sidebar\n * unmounting when the comment tool deactivates \u2014 a user's \"hide resolved\" choice shouldn't reset\n * every time they leave the tool.\n * @public\n */\nexport interface SidebarFilters {\n\t/** Include resolved threads. */\n\tshowResolved: boolean\n\t/** Only threads the current user started. Ignored when there's no current user. */\n\tonlyMine: boolean\n\t/** Only threads with unread comments. Ignored when the host provides no read status. */\n\tonlyUnread: boolean\n\t/** Only threads on the current page. Off = every page's threads, each labelled. */\n\tonlyCurrentPage: boolean\n}\n\n/** The out-of-the-box view: every page's threads, resolved ones hidden until asked for.\n * @public */\nexport const DEFAULT_SIDEBAR_FILTERS: SidebarFilters = {\n\tshowResolved: false,\n\tonlyMine: false,\n\tonlyUnread: false,\n\tonlyCurrentPage: false,\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBO,MAAM,0BAA0C;AAAA,EACtD,cAAc;AAAA,EACd,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,iBAAiB;AAClB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/canvas/state.js
CHANGED
|
@@ -19,25 +19,44 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var state_exports = {};
|
|
20
20
|
__export(state_exports, {
|
|
21
21
|
commentsHidden: () => commentsHidden,
|
|
22
|
-
|
|
22
|
+
commentsSidebarOpen: () => commentsSidebarOpen,
|
|
23
|
+
getRevealThreadPending: () => getRevealThreadPending,
|
|
24
|
+
openStackId: () => openStackId,
|
|
23
25
|
openThreadId: () => openThreadId,
|
|
24
26
|
pendingComment: () => pendingComment,
|
|
25
27
|
regionDraft: () => regionDraft,
|
|
28
|
+
revealThread: () => revealThread,
|
|
29
|
+
revealThreadRequest: () => revealThreadRequest,
|
|
26
30
|
sidebarFilters: () => sidebarFilters,
|
|
27
31
|
toggleCommentsHidden: () => toggleCommentsHidden,
|
|
32
|
+
toggleCommentsSidebar: () => toggleCommentsSidebar,
|
|
28
33
|
useCommentsHidden: () => useCommentsHidden,
|
|
34
|
+
useCommentsSidebarOpen: () => useCommentsSidebarOpen,
|
|
29
35
|
useOpenThreadId: () => useOpenThreadId,
|
|
30
36
|
usePendingComment: () => usePendingComment,
|
|
37
|
+
useRevealThreadPending: () => useRevealThreadPending,
|
|
31
38
|
useSidebarFilters: () => useSidebarFilters
|
|
32
39
|
});
|
|
33
40
|
module.exports = __toCommonJS(state_exports);
|
|
34
41
|
var import_tldraw = require("tldraw");
|
|
35
|
-
var import_options = require("./options");
|
|
36
42
|
var import_sidebar_filters = require("./sidebar-filters");
|
|
37
43
|
const openThreadId = new import_tldraw.EditorAtom("openThreadId", () => null);
|
|
44
|
+
const openStackId = new import_tldraw.EditorAtom("openStackId", () => null);
|
|
38
45
|
const pendingComment = new import_tldraw.EditorAtom("pendingComment", () => null);
|
|
46
|
+
const revealThreadRequest = new import_tldraw.EditorAtom("revealThreadRequest", () => null);
|
|
47
|
+
function revealThread(editor, threadOrCommentId) {
|
|
48
|
+
revealThreadRequest.set(editor, threadOrCommentId);
|
|
49
|
+
}
|
|
50
|
+
function getRevealThreadPending(editor) {
|
|
51
|
+
return revealThreadRequest.get(editor);
|
|
52
|
+
}
|
|
53
|
+
function useRevealThreadPending() {
|
|
54
|
+
const editor = (0, import_tldraw.useEditor)();
|
|
55
|
+
return (0, import_tldraw.useValue)("pending reveal thread", () => getRevealThreadPending(editor), [editor]);
|
|
56
|
+
}
|
|
39
57
|
const regionDraft = new import_tldraw.EditorAtom("regionDraft", () => null);
|
|
40
58
|
const commentsHidden = new import_tldraw.EditorAtom("commentsHidden", () => false);
|
|
59
|
+
const commentsSidebarOpen = new import_tldraw.EditorAtom("commentsSidebarOpen", () => false);
|
|
41
60
|
const sidebarFilters = new import_tldraw.EditorAtom(
|
|
42
61
|
"sidebarFilters",
|
|
43
62
|
() => import_sidebar_filters.DEFAULT_SIDEBAR_FILTERS
|
|
@@ -45,6 +64,9 @@ const sidebarFilters = new import_tldraw.EditorAtom(
|
|
|
45
64
|
function toggleCommentsHidden(editor) {
|
|
46
65
|
commentsHidden.update(editor, (hidden) => !hidden);
|
|
47
66
|
}
|
|
67
|
+
function toggleCommentsSidebar(editor) {
|
|
68
|
+
commentsSidebarOpen.update(editor, (open) => !open);
|
|
69
|
+
}
|
|
48
70
|
function useOpenThreadId() {
|
|
49
71
|
const editor = (0, import_tldraw.useEditor)();
|
|
50
72
|
return (0, import_tldraw.useValue)("open thread id", () => openThreadId.get(editor), [editor]);
|
|
@@ -57,20 +79,12 @@ function useCommentsHidden() {
|
|
|
57
79
|
const editor = (0, import_tldraw.useEditor)();
|
|
58
80
|
return (0, import_tldraw.useValue)("comments hidden", () => commentsHidden.get(editor), [editor]);
|
|
59
81
|
}
|
|
82
|
+
function useCommentsSidebarOpen() {
|
|
83
|
+
const editor = (0, import_tldraw.useEditor)();
|
|
84
|
+
return (0, import_tldraw.useValue)("comments sidebar open", () => commentsSidebarOpen.get(editor), [editor]);
|
|
85
|
+
}
|
|
60
86
|
function useSidebarFilters() {
|
|
61
87
|
const editor = (0, import_tldraw.useEditor)();
|
|
62
88
|
return (0, import_tldraw.useValue)("sidebar filters", () => sidebarFilters.get(editor), [editor]);
|
|
63
89
|
}
|
|
64
|
-
function commitCommentMutation(editor, fn, kind = "mutation") {
|
|
65
|
-
const options = (0, import_options.getCommentingOptions)(editor);
|
|
66
|
-
const history = kind === "drag" ? options.dragHistory ?? options.history : options.history;
|
|
67
|
-
let result;
|
|
68
|
-
editor.run(
|
|
69
|
-
() => {
|
|
70
|
-
result = fn();
|
|
71
|
-
},
|
|
72
|
-
{ history }
|
|
73
|
-
);
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
90
|
//# sourceMappingURL=state.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/state.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA
|
|
4
|
+
"sourcesContent": ["import { EditorAtom, type BoxModel, type Editor, useEditor, useValue } from 'tldraw'\nimport type { PendingComment } from './comment-tool'\nimport { DEFAULT_SIDEBAR_FILTERS, type SidebarFilters } from './sidebar-filters'\n\n/**\n * Transient commenting UI state, scoped per editor via {@link EditorAtom}. Editor-scoping (rather\n * than module-global atoms) keeps two editors on one page \u2014 or an editor and a second instance \u2014\n * from sharing open-thread, visibility, and filter state. Reachable from both React (`useEditor()`)\n * and the comment tool (`this.editor`).\n */\n\n/** The id of the one open thread (only one popover is open at a time), or null when all closed.\n * @public */\nexport const openThreadId = new EditorAtom<string | null>('openThreadId', () => null)\n\n/** The coincident-pin stack whose thread list is showing (keyed by its oldest member's thread\n * id), or null. Editor state rather than component state: the stack pin remounts when its owning\n * render path changes (e.g. a member thread opens), and the open list must survive that.\n * @internal */\nexport const openStackId = new EditorAtom<string | null>('openStackId', () => null)\n\n/** The comment currently being placed (composer open, not yet posted), or null.\n * @internal */\nexport const pendingComment = new EditorAtom<PendingComment | null>('pendingComment', () => null)\n\n/**\n * A pending request to reveal a thread: a thread or comment id to open and bring into view, or\n * null when none is pending. Written by {@link revealThread}; served and cleared by\n * `CanvasComments`, which owns the wait for the records to sync in and the cluster-aware reveal.\n * @internal\n */\nexport const revealThreadRequest = new EditorAtom<string | null>('revealThreadRequest', () => null)\n\n/**\n * Open a thread and bring it into view, given a thread id or the id of any comment in it. Use it\n * to jump to a thread from outside the canvas \u2014 a notification, a deep link, your own list.\n *\n * The request is served by `CanvasComments`, so it works before the records have arrived: the\n * layer waits for them to sync in, switches pages if it needs to, unhides pins, zooms in far\n * enough to split the thread out of any cluster it's folded into, and then opens it. That also\n * means nothing happens if `CanvasComments` isn't mounted.\n *\n * To open a thread you already hold and skip the wait, see {@link focusThread}.\n *\n * @example\n * ```ts\n * revealThread(editor, new URLSearchParams(location.search).get('comment')!)\n * ```\n *\n * @public\n */\nexport function revealThread(editor: Editor, threadOrCommentId: string): void {\n\trevealThreadRequest.set(editor, threadOrCommentId)\n}\n\n/**\n * The id passed to the most recent {@link revealThread} call that `CanvasComments` hasn't served\n * yet, or null when there's nothing outstanding. A request also clears when `CanvasComments`\n * unmounts, since nothing is left to serve it.\n *\n * This is a plain, untracked read. In React, use {@link useRevealThreadPending} \u2014 but reach for\n * this one inside a timer or callback that needs the value as of *now* rather than as of the\n * render it closed over.\n *\n * @public\n */\nexport function getRevealThreadPending(editor: Editor): string | null {\n\treturn revealThreadRequest.get(editor)\n}\n\n/**\n * Reactive React hook for {@link getRevealThreadPending}.\n *\n * Use it to notice a reveal that never lands \u2014 most often a deep link to a comment that has since\n * been deleted. Give it a grace period before you act: a request also sits here while its records\n * are still syncing in, which is the normal case on a cold load. Re-check with\n * {@link getRevealThreadPending} when the grace period elapses, since the request can clear inside\n * it without this hook's value having caught up yet.\n *\n * @public\n */\nexport function useRevealThreadPending(): string | null {\n\tconst editor = useEditor()\n\treturn useValue('pending reveal thread', () => getRevealThreadPending(editor), [editor])\n}\n\n/** The region rectangle being dragged out right now (page coords), or null when not dragging. The\n * comment tool writes it on each move; the overlay reads it to draw the live dashed box. */\nexport const regionDraft = new EditorAtom<BoxModel | null>('regionDraft', () => null)\n\n/**\n * Whether comment pins are hidden on the canvas. Governs the on-canvas layer (pins + open popover)\n * only \u2014 the sidebar is unaffected.\n * @public\n */\nexport const commentsHidden = new EditorAtom<boolean>('commentsHidden', () => false)\n\n/**\n * Whether the comments sidebar (the thread list) is open. Driven by an explicit control \u2014 a button\n * next to Share on dotcom \u2014 rather than by which tool is active, so browsing threads is separate\n * from placing them. The comment tool additionally closes it on enter, keeping placement\n * canvas-focused.\n * @public\n */\nexport const commentsSidebarOpen = new EditorAtom<boolean>('commentsSidebarOpen', () => false)\n\n/** Which threads the comments sidebar shows.\n * @public */\nexport const sidebarFilters = new EditorAtom<SidebarFilters>(\n\t'sidebarFilters',\n\t() => DEFAULT_SIDEBAR_FILTERS\n)\n\n/** Toggle comment-pin visibility for an editor.\n * @public */\nexport function toggleCommentsHidden(editor: Editor): void {\n\tcommentsHidden.update(editor, (hidden) => !hidden)\n}\n\n/** Open or close the comments sidebar for an editor.\n * @public */\nexport function toggleCommentsSidebar(editor: Editor): void {\n\tcommentsSidebarOpen.update(editor, (open) => !open)\n}\n\n/** React hook for the open thread id.\n * @public */\nexport function useOpenThreadId(): string | null {\n\tconst editor = useEditor()\n\treturn useValue('open thread id', () => openThreadId.get(editor), [editor])\n}\n\n/** React hook for the pending (being-placed) comment.\n * @internal */\nexport function usePendingComment(): PendingComment | null {\n\tconst editor = useEditor()\n\treturn useValue('pending comment', () => pendingComment.get(editor), [editor])\n}\n\n/** React hook for whether comment pins are hidden.\n * @public */\nexport function useCommentsHidden(): boolean {\n\tconst editor = useEditor()\n\treturn useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n}\n\n/** React hook for whether the comments sidebar is open.\n * @public */\nexport function useCommentsSidebarOpen(): boolean {\n\tconst editor = useEditor()\n\treturn useValue('comments sidebar open', () => commentsSidebarOpen.get(editor), [editor])\n}\n\n/** React hook for the current sidebar filters.\n * @public */\nexport function useSidebarFilters(): SidebarFilters {\n\tconst editor = useEditor()\n\treturn useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA4E;AAE5E,6BAA6D;AAWtD,MAAM,eAAe,IAAI,yBAA0B,gBAAgB,MAAM,IAAI;AAM7E,MAAM,cAAc,IAAI,yBAA0B,eAAe,MAAM,IAAI;AAI3E,MAAM,iBAAiB,IAAI,yBAAkC,kBAAkB,MAAM,IAAI;AAQzF,MAAM,sBAAsB,IAAI,yBAA0B,uBAAuB,MAAM,IAAI;AAoB3F,SAAS,aAAa,QAAgB,mBAAiC;AAC7E,sBAAoB,IAAI,QAAQ,iBAAiB;AAClD;AAaO,SAAS,uBAAuB,QAA+B;AACrE,SAAO,oBAAoB,IAAI,MAAM;AACtC;AAaO,SAAS,yBAAwC;AACvD,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,yBAAyB,MAAM,uBAAuB,MAAM,GAAG,CAAC,MAAM,CAAC;AACxF;AAIO,MAAM,cAAc,IAAI,yBAA4B,eAAe,MAAM,IAAI;AAO7E,MAAM,iBAAiB,IAAI,yBAAoB,kBAAkB,MAAM,KAAK;AAS5E,MAAM,sBAAsB,IAAI,yBAAoB,uBAAuB,MAAM,KAAK;AAItF,MAAM,iBAAiB,IAAI;AAAA,EACjC;AAAA,EACA,MAAM;AACP;AAIO,SAAS,qBAAqB,QAAsB;AAC1D,iBAAe,OAAO,QAAQ,CAAC,WAAW,CAAC,MAAM;AAClD;AAIO,SAAS,sBAAsB,QAAsB;AAC3D,sBAAoB,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI;AACnD;AAIO,SAAS,kBAAiC;AAChD,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,kBAAkB,MAAM,aAAa,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAC3E;AAIO,SAAS,oBAA2C;AAC1D,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAC9E;AAIO,SAAS,oBAA6B;AAC5C,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAC9E;AAIO,SAAS,yBAAkC;AACjD,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,yBAAyB,MAAM,oBAAoB,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACzF;AAIO,SAAS,oBAAoC;AACnD,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAC9E;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|