@tldraw/commenting 5.6.0-canary.ce8fde24c9aa → 5.6.0-canary.dd9b57126e5d
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/dist-cjs/canvas/comment-tool.js +4 -1
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/index.d.ts +3 -1
- package/dist-cjs/index.js +1 -1
- package/dist-esm/canvas/comment-tool.mjs +4 -1
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/index.d.mts +3 -1
- package/dist-esm/index.mjs +1 -1
- package/package.json +5 -5
- package/src/canvas/comment-tool.test.ts +34 -1
- package/src/canvas/comment-tool.tsx +8 -2
|
@@ -221,7 +221,10 @@ const commentToolOverrides = {
|
|
|
221
221
|
icon: "comment",
|
|
222
222
|
label: "Comment",
|
|
223
223
|
kbd: "c",
|
|
224
|
-
onSelect: () =>
|
|
224
|
+
onSelect: (source) => {
|
|
225
|
+
if (source === "context-menu") startCommentAt(editor, editor.inputs.getOriginPagePoint());
|
|
226
|
+
else editor.setCurrentTool("comment");
|
|
227
|
+
}
|
|
225
228
|
};
|
|
226
229
|
return tools;
|
|
227
230
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comment-tool.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, 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) through the internal `pendingComment` atom.\n * @internal */\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. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and so does clicking the\n * canvas elsewhere, which cancels the placement.\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: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\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\nfunction pendingCommentAt(editor: Editor, point: VecLike): PendingComment {\n\tconst hit = commentTargetShapeAt(editor, point)\n\tconst anchor: TLCommentAnchor = hit\n\t\t? shapeAnchorAt(\n\t\t\t\teditor,\n\t\t\t\thit.id,\n\t\t\t\tpoint,\n\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t})\n\t\t\t)\n\t\t: { type: 'point', x: point.x, y: point.y }\n\treturn { anchor, point: { x: point.x, y: point.y } }\n}\n\n/**\n * Open the comment composer at a page point, anchored as a comment tool click there would be. Use\n * it to start a comment from outside the tool, such as a context menu item. Enters the comment tool\n * (which must be registered), since the draft composer closes when the tool exits. It doesn't check\n * `canComment`: a viewer who can't comment sees the `ComposerFallback` slot instead.\n *\n * @example\n * ```tsx\n * <TldrawUiMenuItem id=\"comment\" label=\"Comment\" onSelect={() => startCommentAt(editor, point)} />\n * ```\n * @public\n */\nexport function startCommentAt(editor: Editor, point: VecLike) {\n\teditor.setCurrentTool('comment')\n\tconst pending = pendingCommentAt(editor, point)\n\tpendingComment.set(editor, pending)\n\t// Entering the tool hints the shape under the live pointer, which is wherever the caller's UI\n\t// was (a menu item), not `point`.\n\teditor.setHintingShapes(pending.anchor.type === 'shape' ? [pending.anchor.shapeId] : [])\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\t// A press away from an open composer cancels the placement rather than moving it. The\n\t\t// tool's onExit drops the draft composer; its text survives in the draft store.\n\t\tif (pendingComment.get(this.editor)) {\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\treturn\n\t\t}\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: 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\tpendingComment.set(editor, pendingCommentAt(editor, point))\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\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 origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\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\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\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 `DefaultQuickActionsContent` shows the comment button.\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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
|
|
4
|
+
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, 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) through the internal `pendingComment` atom.\n * @internal */\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. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and so does clicking the\n * canvas elsewhere, which cancels the placement.\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: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\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\nfunction pendingCommentAt(editor: Editor, point: VecLike): PendingComment {\n\tconst hit = commentTargetShapeAt(editor, point)\n\tconst anchor: TLCommentAnchor = hit\n\t\t? shapeAnchorAt(\n\t\t\t\teditor,\n\t\t\t\thit.id,\n\t\t\t\tpoint,\n\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t})\n\t\t\t)\n\t\t: { type: 'point', x: point.x, y: point.y }\n\treturn { anchor, point: { x: point.x, y: point.y } }\n}\n\n/**\n * Open the comment composer at a page point, anchored as a comment tool click there would be. Use\n * it to start a comment from outside the tool, such as a context menu item. Enters the comment tool\n * (which must be registered), since the draft composer closes when the tool exits. It doesn't check\n * `canComment`: a viewer who can't comment sees the `ComposerFallback` slot instead.\n *\n * @example\n * ```tsx\n * <TldrawUiMenuItem id=\"comment\" label=\"Comment\" onSelect={() => startCommentAt(editor, point)} />\n * ```\n * @public\n */\nexport function startCommentAt(editor: Editor, point: VecLike) {\n\teditor.setCurrentTool('comment')\n\tconst pending = pendingCommentAt(editor, point)\n\tpendingComment.set(editor, pending)\n\t// Entering the tool hints the shape under the live pointer, which is wherever the caller's UI\n\t// was (a menu item), not `point`.\n\teditor.setHintingShapes(pending.anchor.type === 'shape' ? [pending.anchor.shapeId] : [])\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\t// A press away from an open composer cancels the placement rather than moving it. The\n\t\t// tool's onExit drops the draft composer; its text survives in the draft store.\n\t\tif (pendingComment.get(this.editor)) {\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\treturn\n\t\t}\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: 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\tpendingComment.set(editor, pendingCommentAt(editor, point))\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\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 origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\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\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\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 `DefaultQuickActionsContent` shows the comment button, and its\n * `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was\n * opened.\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: (source) => {\n\t\t\t\t// The menu was opened by the last pointer down; the live pointer is over the menu now.\n\t\t\t\tif (source === 'context-menu') startCommentAt(editor, editor.inputs.getOriginPagePoint())\n\t\t\t\telse editor.setCurrentTool('comment')\n\t\t\t},\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/index.d.ts
CHANGED
|
@@ -657,7 +657,9 @@ export declare class CommentTool extends StateNode {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.
|
|
660
|
-
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button
|
|
660
|
+
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button, and its
|
|
661
|
+
* `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was
|
|
662
|
+
* opened.
|
|
661
663
|
* @public */
|
|
662
664
|
export declare const commentToolOverrides: TLUiOverrides;
|
|
663
665
|
|
package/dist-cjs/index.js
CHANGED
|
@@ -145,7 +145,7 @@ var import_state = require("./canvas/state");
|
|
|
145
145
|
var import_thread_state = require("./canvas/thread-state");
|
|
146
146
|
(0, import_utils.registerTldrawLibraryVersion)(
|
|
147
147
|
"@tldraw/commenting",
|
|
148
|
-
"5.6.0-canary.
|
|
148
|
+
"5.6.0-canary.dd9b57126e5d",
|
|
149
149
|
"cjs"
|
|
150
150
|
);
|
|
151
151
|
//# sourceMappingURL=index.js.map
|
|
@@ -196,7 +196,10 @@ const commentToolOverrides = {
|
|
|
196
196
|
icon: "comment",
|
|
197
197
|
label: "Comment",
|
|
198
198
|
kbd: "c",
|
|
199
|
-
onSelect: () =>
|
|
199
|
+
onSelect: (source) => {
|
|
200
|
+
if (source === "context-menu") startCommentAt(editor, editor.inputs.getOriginPagePoint());
|
|
201
|
+
else editor.setCurrentTool("comment");
|
|
202
|
+
}
|
|
200
203
|
};
|
|
201
204
|
return tools;
|
|
202
205
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comment-tool.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, 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) through the internal `pendingComment` atom.\n * @internal */\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. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and so does clicking the\n * canvas elsewhere, which cancels the placement.\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: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\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\nfunction pendingCommentAt(editor: Editor, point: VecLike): PendingComment {\n\tconst hit = commentTargetShapeAt(editor, point)\n\tconst anchor: TLCommentAnchor = hit\n\t\t? shapeAnchorAt(\n\t\t\t\teditor,\n\t\t\t\thit.id,\n\t\t\t\tpoint,\n\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t})\n\t\t\t)\n\t\t: { type: 'point', x: point.x, y: point.y }\n\treturn { anchor, point: { x: point.x, y: point.y } }\n}\n\n/**\n * Open the comment composer at a page point, anchored as a comment tool click there would be. Use\n * it to start a comment from outside the tool, such as a context menu item. Enters the comment tool\n * (which must be registered), since the draft composer closes when the tool exits. It doesn't check\n * `canComment`: a viewer who can't comment sees the `ComposerFallback` slot instead.\n *\n * @example\n * ```tsx\n * <TldrawUiMenuItem id=\"comment\" label=\"Comment\" onSelect={() => startCommentAt(editor, point)} />\n * ```\n * @public\n */\nexport function startCommentAt(editor: Editor, point: VecLike) {\n\teditor.setCurrentTool('comment')\n\tconst pending = pendingCommentAt(editor, point)\n\tpendingComment.set(editor, pending)\n\t// Entering the tool hints the shape under the live pointer, which is wherever the caller's UI\n\t// was (a menu item), not `point`.\n\teditor.setHintingShapes(pending.anchor.type === 'shape' ? [pending.anchor.shapeId] : [])\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\t// A press away from an open composer cancels the placement rather than moving it. The\n\t\t// tool's onExit drops the draft composer; its text survives in the draft store.\n\t\tif (pendingComment.get(this.editor)) {\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\treturn\n\t\t}\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: 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\tpendingComment.set(editor, pendingCommentAt(editor, point))\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\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 origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\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\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\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 `DefaultQuickActionsContent` shows the comment button.\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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
|
|
4
|
+
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, 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) through the internal `pendingComment` atom.\n * @internal */\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. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and so does clicking the\n * canvas elsewhere, which cancels the placement.\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: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\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\nfunction pendingCommentAt(editor: Editor, point: VecLike): PendingComment {\n\tconst hit = commentTargetShapeAt(editor, point)\n\tconst anchor: TLCommentAnchor = hit\n\t\t? shapeAnchorAt(\n\t\t\t\teditor,\n\t\t\t\thit.id,\n\t\t\t\tpoint,\n\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t})\n\t\t\t)\n\t\t: { type: 'point', x: point.x, y: point.y }\n\treturn { anchor, point: { x: point.x, y: point.y } }\n}\n\n/**\n * Open the comment composer at a page point, anchored as a comment tool click there would be. Use\n * it to start a comment from outside the tool, such as a context menu item. Enters the comment tool\n * (which must be registered), since the draft composer closes when the tool exits. It doesn't check\n * `canComment`: a viewer who can't comment sees the `ComposerFallback` slot instead.\n *\n * @example\n * ```tsx\n * <TldrawUiMenuItem id=\"comment\" label=\"Comment\" onSelect={() => startCommentAt(editor, point)} />\n * ```\n * @public\n */\nexport function startCommentAt(editor: Editor, point: VecLike) {\n\teditor.setCurrentTool('comment')\n\tconst pending = pendingCommentAt(editor, point)\n\tpendingComment.set(editor, pending)\n\t// Entering the tool hints the shape under the live pointer, which is wherever the caller's UI\n\t// was (a menu item), not `point`.\n\teditor.setHintingShapes(pending.anchor.type === 'shape' ? [pending.anchor.shapeId] : [])\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\t// A press away from an open composer cancels the placement rather than moving it. The\n\t\t// tool's onExit drops the draft composer; its text survives in the draft store.\n\t\tif (pendingComment.get(this.editor)) {\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\treturn\n\t\t}\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: 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\tpendingComment.set(editor, pendingCommentAt(editor, point))\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\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 origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\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\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\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 `DefaultQuickActionsContent` shows the comment button, and its\n * `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was\n * opened.\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: (source) => {\n\t\t\t\t// The menu was opened by the last pointer down; the live pointer is over the menu now.\n\t\t\t\tif (source === 'context-menu') startCommentAt(editor, editor.inputs.getOriginPagePoint())\n\t\t\t\telse editor.setCurrentTool('comment')\n\t\t\t},\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.d.mts
CHANGED
|
@@ -657,7 +657,9 @@ export declare class CommentTool extends StateNode {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.
|
|
660
|
-
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button
|
|
660
|
+
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button, and its
|
|
661
|
+
* `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was
|
|
662
|
+
* opened.
|
|
661
663
|
* @public */
|
|
662
664
|
export declare const commentToolOverrides: TLUiOverrides;
|
|
663
665
|
|
package/dist-esm/index.mjs
CHANGED
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
import { anchorPagePoint, focusThread, shapeAnchorAt } from "./canvas/thread-state.mjs";
|
|
109
109
|
registerTldrawLibraryVersion(
|
|
110
110
|
"@tldraw/commenting",
|
|
111
|
-
"5.6.0-canary.
|
|
111
|
+
"5.6.0-canary.dd9b57126e5d",
|
|
112
112
|
"esm"
|
|
113
113
|
);
|
|
114
114
|
export {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/commenting",
|
|
3
3
|
"description": "tldraw commenting UI components.",
|
|
4
|
-
"version": "5.6.0-canary.
|
|
4
|
+
"version": "5.6.0-canary.dd9b57126e5d",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"src"
|
|
33
33
|
],
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@tldraw/driver": "5.6.0-canary.
|
|
35
|
+
"@tldraw/driver": "5.6.0-canary.dd9b57126e5d",
|
|
36
36
|
"@types/react": "^19.2.7",
|
|
37
37
|
"@types/react-dom": "^19.2.3",
|
|
38
38
|
"react": "^19.2.1",
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"@tiptap/core": "^3.12.1",
|
|
51
51
|
"@tiptap/pm": "^3.12.1",
|
|
52
52
|
"@tiptap/react": "^3.12.1",
|
|
53
|
-
"@tldraw/mentions": "5.6.0-canary.
|
|
54
|
-
"@tldraw/utils": "5.6.0-canary.
|
|
55
|
-
"tldraw": "5.6.0-canary.
|
|
53
|
+
"@tldraw/mentions": "5.6.0-canary.dd9b57126e5d",
|
|
54
|
+
"@tldraw/utils": "5.6.0-canary.dd9b57126e5d",
|
|
55
|
+
"tldraw": "5.6.0-canary.dd9b57126e5d"
|
|
56
56
|
},
|
|
57
57
|
"module": "dist-esm/index.mjs",
|
|
58
58
|
"source": "src/index.ts",
|
|
@@ -10,9 +10,11 @@ import {
|
|
|
10
10
|
Editor,
|
|
11
11
|
TLShapeId,
|
|
12
12
|
TLStateNodeConstructor,
|
|
13
|
+
TLUiOverrideHelpers,
|
|
14
|
+
TLUiToolsContextType,
|
|
13
15
|
} from 'tldraw'
|
|
14
16
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
15
|
-
import { CommentTool, startCommentAt } from './comment-tool'
|
|
17
|
+
import { CommentTool, commentToolOverrides, startCommentAt } from './comment-tool'
|
|
16
18
|
import { commentsSidebarOpen, pendingComment, regionDraft } from './state'
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -197,6 +199,37 @@ describe('startCommentAt', () => {
|
|
|
197
199
|
})
|
|
198
200
|
})
|
|
199
201
|
|
|
202
|
+
describe('commentToolOverrides', () => {
|
|
203
|
+
let driver: Driver
|
|
204
|
+
|
|
205
|
+
function getCommentTool() {
|
|
206
|
+
const tools = commentToolOverrides.tools!(
|
|
207
|
+
editor,
|
|
208
|
+
{} as TLUiToolsContextType,
|
|
209
|
+
{} as TLUiOverrideHelpers
|
|
210
|
+
)
|
|
211
|
+
return tools.comment
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
beforeEach(() => {
|
|
215
|
+
driver = makeEditor()
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('opens the composer where the context menu was opened, not at the live pointer', () => {
|
|
219
|
+
driver.pointerDown(400, 300).pointerUp(400, 300)
|
|
220
|
+
driver.pointerMove(600, 500)
|
|
221
|
+
getCommentTool().onSelect('context-menu')
|
|
222
|
+
expect(editor.isIn('comment.idle')).toBe(true)
|
|
223
|
+
expect(pendingComment.get(editor)?.point).toEqual({ x: 400, y: 300 })
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('only enters the tool from other sources', () => {
|
|
227
|
+
getCommentTool().onSelect('quick-actions')
|
|
228
|
+
expect(editor.isIn('comment.idle')).toBe(true)
|
|
229
|
+
expect(pendingComment.get(editor)).toBeNull()
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
200
233
|
describe('comment tool placement hints with regions enabled', () => {
|
|
201
234
|
it('clears the hint when a drag becomes a region', () => {
|
|
202
235
|
const driver = makeEditor(CommentTool.configure({ enableRegions: true }))
|
|
@@ -309,7 +309,9 @@ class CommentDragging extends StateNode {
|
|
|
309
309
|
export const commentTools = [CommentTool]
|
|
310
310
|
|
|
311
311
|
/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.
|
|
312
|
-
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button
|
|
312
|
+
* Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button, and its
|
|
313
|
+
* `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was
|
|
314
|
+
* opened.
|
|
313
315
|
* @public */
|
|
314
316
|
export const commentToolOverrides: TLUiOverrides = {
|
|
315
317
|
tools(editor, tools) {
|
|
@@ -318,7 +320,11 @@ export const commentToolOverrides: TLUiOverrides = {
|
|
|
318
320
|
icon: 'comment',
|
|
319
321
|
label: 'Comment',
|
|
320
322
|
kbd: 'c',
|
|
321
|
-
onSelect: () =>
|
|
323
|
+
onSelect: (source) => {
|
|
324
|
+
// The menu was opened by the last pointer down; the live pointer is over the menu now.
|
|
325
|
+
if (source === 'context-menu') startCommentAt(editor, editor.inputs.getOriginPagePoint())
|
|
326
|
+
else editor.setCurrentTool('comment')
|
|
327
|
+
},
|
|
322
328
|
}
|
|
323
329
|
return tools
|
|
324
330
|
},
|