{
  "version": 3,
  "sources": ["../../src/canvas/options.ts"],
  "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 { type CommentListItemRenderProps } from '../ui/comments-list'\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 * A commenting write that belongs to someone in particular, and the record it targets \u2014 the\n * argument to {@link CommentingOptions.canModifyComment}.\n *\n * Resolving, reopening, reacting, and moving a pin aren't here: none of them is anyone's in\n * particular, so {@link CommentingOptions.canComment} is the only gate on them.\n *\n * @public\n */\nexport type CommentModification =\n\t| { readonly action: 'edit-comment'; readonly comment: TLComment }\n\t| { readonly action: 'delete-comment'; readonly comment: TLComment }\n\t| { readonly action: 'delete-thread'; readonly thread: TLCommentThread }\n\n/**\n * The argument to {@link CommentingOptions.canModifyComment}: which write, against which record,\n * and by whom.\n *\n * @public\n */\nexport type CommentModificationContext = {\n\treadonly editor: Editor\n\treadonly currentUserId: string | null\n} & CommentModification\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 whole sidebar row. Replaces the default `<CommentListItem>`, which is exported \u2014 so a row\n\t * that only adds an unread dot or a status chip can spread these props into it. Use\n\t * `ThreadPreview` instead when only the preview text is changing.\n\t */\n\tThreadRow?: ComponentType<CommentListItemRenderProps & { thread: TLCommentThread }>\n\t/**\n\t * Extra controls in an open thread's header, added ahead of the built-in resolve and dismiss\n\t * buttons rather than replacing them. \"Copy link\" is already built in whenever the host supplies\n\t * `getThreadHref`.\n\t */\n\tThreadActions?: ComponentType<{ thread: TLCommentThread; comments: 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. Override to draw a custom palette \u2014 an `<img>`, an SVG, anything. The token is what gets\n\t * stored and 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. Replaces the default `<EmojiPicker>` grid. Pairs with\n\t * `ReactionContent` (which draws the tokens this emits) and `isAllowedReaction` (which must\n\t * accept them).\n\t */\n\tReactionPalette?: ComponentType<EmojiPickerProps>\n\t/**\n\t * The hover affordance naming who reacted with an emoji. Receives the reactors and the pill (as\n\t * `children`) and owns the whole thing \u2014 box, size, shape, position. For a wording change,\n\t * translate the `comments.reacted-*` strings instead.\n\t */\n\tReactionTooltip?: ComponentType<ReactionTooltipProps>\n\t/**\n\t * Shown where a composer would sit when the viewer can't compose (see\n\t * {@link CommentingOptions.canComment}). `context` is the surface rendering it: an open thread\n\t * popover (`'thread'`) or the comment tool's placement popover (`'pending'`). Unset, those\n\t * surfaces render nothing.\n\t */\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// History / undo\n\t/**\n\t * How comment mutations interact with the editor undo stack. Defaults to `'ignore'` \u2014 comments\n\t * are deliberately not undoable (see `TLComment`). `'record'` is a multiplayer footgun: undoing\n\t * a delete resurrects a thread a collaborator already removed. Safe only single-player.\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// Feature toggles\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}, which\n\t * keeps a scripted client from writing junk values the picker would never offer. Override\n\t * alongside a custom `ReactionPalette`. Removals aren't checked \u2014 an off-palette reaction must\n\t * 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, drawn as a dashed box with the pin on the corner the drag released on. Off\n\t * by default, where comments attach to points and shapes only and a drag trails the composer.\n\t */\n\treadonly enableRegions: boolean\n\n\t// Permissions\n\t/**\n\t * Whether the viewer may participate in commenting: composing, editing, deleting, resolving, and\n\t * moving pins. When false, {@link CommentingComponents.ComposerFallback} renders in the\n\t * composer's place and action affordances are hidden. Unset, participation is allowed exactly\n\t * when `currentUserId` is set.\n\t *\n\t * Called during render via {@link useCanComment}, so signal reads are tracked. Posting still\n\t * needs a `currentUserId`, so returning true for a signed-out viewer yields a composer whose\n\t * send button stays disabled. A callback that throws is logged and read as false, rather than\n\t * taking the comments layer down with it.\n\t */\n\treadonly canComment:\n\t\t| ((ctx: { editor: Editor; currentUserId: string | null }) => boolean)\n\t\t| undefined\n\t/**\n\t * Whether the viewer may make a particular write against a particular record: editing or\n\t * deleting a comment, or deleting a thread. Unset, each is its record's owner's to make\n\t * ({@link defaultCanModifyComment}) \u2014 you edit and delete your own comments, and delete threads\n\t * you started. Override it to widen that (a workspace admin or moderator who may remove\n\t * anyone's comment) or to narrow it (no edits after an hour). Where it returns false the\n\t * affordance isn't rendered.\n\t *\n\t * Checked after {@link CommentingOptions.canComment}, which gates commenting as a whole: a\n\t * viewer who may not participate gets no action affordances at all, whatever this returns.\n\t *\n\t * Called during render via {@link useCanModifyComment}, so reactive reads (signals) are tracked.\n\t * A callback that throws is logged and read as false: an affordance is withheld rather than the\n\t * comments layer lost, and a denial is what a server enforcing the same rule would have said.\n\t *\n\t * @example\n\t * ```tsx\n\t * CommentTool.configure({\n\t * \tcanModifyComment: (ctx) =>\n\t * \t\t// Moderators may delete anything; everything else stays the owner's to do.\n\t * \t\t(ctx.action !== 'edit-comment' && isModerator(ctx.currentUserId)) ||\n\t * \t\tdefaultCanModifyComment(ctx),\n\t * })\n\t * ```\n\t */\n\treadonly canModifyComment: ((ctx: CommentModificationContext) => boolean) | undefined\n\n\t// Anchoring\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 pins to the exact clicked spot, or to the shape as a\n\t * whole (rendered at `impreciseShapeAnchor`). Always precise by default; return `false`, or\n\t * decide from the context. Governs new placements only \u2014 existing anchors render as stored.\n\t */\n\tshouldBePrecise(editor: Editor, context: ShapeCommentPrecisionContext): boolean\n\n\t// Components\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\tcanModifyComment: 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 * Ask a host's permission callback, denying the write if it throws.\n *\n * These are called during render, so an exception in one would take the comments layer down with\n * the answer. Denying costs an affordance, which is what a `false` would have cost anyway, and it\n * can't offer a write a server enforcing the same rule would then reject.\n */\nfunction permits(option: string, check: () => boolean): boolean {\n\ttry {\n\t\treturn check()\n\t} catch (error) {\n\t\tconsole.error(`[tldraw] \\`${option}\\` threw, so the write is denied:`, error)\n\t\treturn false\n\t}\n}\n\n/**\n * Whether the viewer may participate in commenting, per {@link CommentingOptions.canComment}\n * (defaulting to `currentUserId != null` when unset). A callback that throws denies.\n *\n * This is a plain, untracked read \u2014 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\tif (!canComment) return currentUserId != null\n\treturn permits('canComment', () => canComment({ editor, currentUserId: 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\n/**\n * The default {@link CommentingOptions.canModifyComment}: a write is its record's owner's to make \u2014\n * a comment's author edits and deletes it, a thread's creator deletes the thread \u2014 and a viewer\n * with no identity may make none of them.\n *\n * Exported so a callback can widen the default rather than restate it:\n * `(ctx) => isModerator(ctx.currentUserId) || defaultCanModifyComment(ctx)`.\n *\n * @public\n */\nexport function defaultCanModifyComment(ctx: CommentModificationContext): boolean {\n\tconst { currentUserId } = ctx\n\tif (!currentUserId) return false\n\tconst owner = ctx.action === 'delete-thread' ? ctx.thread.createdBy : ctx.comment.authorId\n\treturn owner === currentUserId\n}\n\n/**\n * Whether the viewer may make a given write against a given record, per\n * {@link CommentingOptions.canModifyComment} (defaulting to {@link defaultCanModifyComment} when\n * unset). Where this is false the affordance isn't rendered.\n *\n * This is the per-record rule alone: the built-in UI additionally requires\n * {@link CommentingOptions.canComment}, since a viewer who may not participate gets no action\n * affordances at all.\n *\n * A plain, untracked read \u2014 a `canModifyComment` callback that reads signals is not observed. In\n * React, use {@link useCanModifyComment} instead.\n *\n * @public\n */\nexport function getCanModifyComment(\n\teditor: Editor,\n\tcurrentUserId: string | null | undefined,\n\tmodification: CommentModification\n): boolean {\n\tconst { canModifyComment } = getCommentingOptions(editor)\n\tconst ctx: CommentModificationContext = {\n\t\teditor,\n\t\tcurrentUserId: currentUserId ?? null,\n\t\t...modification,\n\t}\n\tif (!canModifyComment) return defaultCanModifyComment(ctx)\n\treturn permits('canModifyComment', () => canModifyComment(ctx))\n}\n\n/**\n * Reactive React hook for {@link getCanModifyComment}: a `canModifyComment` callback that reads\n * signals re-evaluates when they change.\n *\n * @public\n */\nexport function useCanModifyComment(\n\tcurrentUserId: string | null | undefined,\n\tmodification: CommentModification\n): boolean {\n\tconst editor = useEditor()\n\t// Comment records are immutable, so the record itself is what changes when the thing being\n\t// checked changes \u2014 `modification` is a fresh object on every render and can't be a dep.\n\tconst record =\n\t\tmodification.action === 'delete-thread' ? modification.thread : modification.comment\n\treturn useValue(\n\t\t'can modify comment',\n\t\t() => getCanModifyComment(editor, currentUserId, modification),\n\t\t[editor, currentUserId, modification.action, record]\n\t)\n}\n"],
  "mappings": "AAAA,SAAS,eAAmC;AAC5C;AAAA,EAOC;AAAA,EACA;AAAA,OACM;AAEP,SAAS,8BAAqD;AAgNvD,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,kBAAkB;AAAA,EAClB,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,SAAS,UAAU;AACzB,SAAO,QAAQ,MAAM,qBAAqB,MAAM,GAAG,CAAC,MAAM,CAAC;AAC5D;AASA,SAAS,QAAQ,QAAgB,OAA+B;AAC/D,MAAI;AACH,WAAO,MAAM;AAAA,EACd,SAAS,OAAO;AACf,YAAQ,MAAM,cAAc,MAAM,qCAAqC,KAAK;AAC5E,WAAO;AAAA,EACR;AACD;AAUO,SAAS,cAAc,QAAgB,eAAmD;AAChG,QAAM,EAAE,WAAW,IAAI,qBAAqB,MAAM;AAClD,MAAI,CAAC,WAAY,QAAO,iBAAiB;AACzC,SAAO,QAAQ,cAAc,MAAM,WAAW,EAAE,QAAQ,eAAe,iBAAiB,KAAK,CAAC,CAAC;AAChG;AAQO,SAAS,cAAc,eAAmD;AAChF,QAAM,SAAS,UAAU;AACzB,SAAO,SAAS,eAAe,MAAM,cAAc,QAAQ,aAAa,GAAG;AAAA,IAC1E;AAAA,IACA;AAAA,EACD,CAAC;AACF;AAYO,SAAS,wBAAwB,KAA0C;AACjF,QAAM,EAAE,cAAc,IAAI;AAC1B,MAAI,CAAC,cAAe,QAAO;AAC3B,QAAM,QAAQ,IAAI,WAAW,kBAAkB,IAAI,OAAO,YAAY,IAAI,QAAQ;AAClF,SAAO,UAAU;AAClB;AAgBO,SAAS,oBACf,QACA,eACA,cACU;AACV,QAAM,EAAE,iBAAiB,IAAI,qBAAqB,MAAM;AACxD,QAAM,MAAkC;AAAA,IACvC;AAAA,IACA,eAAe,iBAAiB;AAAA,IAChC,GAAG;AAAA,EACJ;AACA,MAAI,CAAC,iBAAkB,QAAO,wBAAwB,GAAG;AACzD,SAAO,QAAQ,oBAAoB,MAAM,iBAAiB,GAAG,CAAC;AAC/D;AAQO,SAAS,oBACf,eACA,cACU;AACV,QAAM,SAAS,UAAU;AAGzB,QAAM,SACL,aAAa,WAAW,kBAAkB,aAAa,SAAS,aAAa;AAC9E,SAAO;AAAA,IACN;AAAA,IACA,MAAM,oBAAoB,QAAQ,eAAe,YAAY;AAAA,IAC7D,CAAC,QAAQ,eAAe,aAAa,QAAQ,MAAM;AAAA,EACpD;AACD;",
  "names": []
}
