{
  "version": 3,
  "sources": ["../../src/canvas/comment-render.ts"],
  "sourcesContent": ["import { generateText, JSONContent } from '@tiptap/core'\nimport { type CommentAuthor, createMentionExtension } from '@tldraw/mentions'\nimport { renderHtmlFromRichTextWithExtensions, TLRichText } from 'tldraw'\nimport { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'\n\n/**\n * The author name shown in a byline when no source can name an id (e.g. a deleted account, or a\n * member with no comment and no live presence). The toolkit's one generic default, applied where a\n * byline needs a name; hosts pre-empt it by resolving the id from `resolveAuthor`. Not a\n * translation key \u2014 a single English literal.\n */\nexport const UNKNOWN_AUTHOR = 'Someone'\n\n/** The `CommentAuthor` fallback for an unresolvable id \u2014 just the name, no color or image.\n *  Frozen: the one shared instance is handed to host render slots. */\nexport const UNKNOWN_COMMENT_AUTHOR: CommentAuthor = Object.freeze({ name: UNKNOWN_AUTHOR })\n\n/**\n * The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a\n * record created programmatically or synced from a client with a fuller set) would make TipTap\n * throw on an unknown node type. Demote any heading to a paragraph first, so headings can never\n * render as headings and rendering never crashes.\n */\nfunction demoteHeadings(node: JSONContent): JSONContent {\n\tconst content = Array.isArray(node.content) ? node.content.map(demoteHeadings) : node.content\n\tif (node.type === 'heading') {\n\t\tconst { attrs: _attrs, ...rest } = node\n\t\treturn { ...rest, type: 'paragraph', content }\n\t}\n\treturn content === node.content ? node : { ...node, content }\n}\n\n/**\n * Whether a body contains a mention node. Mention text is resolved from a member id at render time,\n * so a body with one can't be cached by identity alone (the same body renders differently as names\n * change) \u2014 those bodies skip the cache and re-render each call.\n */\nfunction hasMention(node: JSONContent): boolean {\n\tif (node.type === 'mention') return true\n\treturn Array.isArray(node.content) ? node.content.some(hasMention) : false\n}\n\nconst htmlCache = new WeakMap<TLRichText, string>()\n\n/**\n * Render a comment body to HTML through the limited comment extension set (no headings), so a body\n * always renders with comment formatting regardless of the host editor's rich-text config. Renders\n * through tldraw's shared helper, so it gets the same empty-paragraph fix that keeps blank lines\n * from collapsing. `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentHtml(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = htmlCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst html = renderHtmlFromRichTextWithExtensions(\n\t\tdemoteHeadings(richText as JSONContent) as TLRichText,\n\t\textensions\n\t)\n\tif (!mentions) htmlCache.set(richText, html)\n\treturn html\n}\n\nconst textCache = new WeakMap<TLRichText, string>()\n\n/**\n * Flatten a comment body to plaintext through the limited comment extension set \u2014 paragraphs and\n * list items separated by newlines. Used for previews (e.g. the sidebar) where formatting is dropped.\n * `resolveName` maps a member id to its current name for any @mentions.\n */\nexport function renderCommentPlaintext(\n\trichText: TLRichText,\n\tresolveName?: (id: string) => string | undefined\n): string {\n\tif (isCommentEmpty(richText)) return ''\n\tconst mentions = hasMention(richText as JSONContent)\n\tif (!mentions) {\n\t\tconst cached = textCache.get(richText)\n\t\tif (cached !== undefined) return cached\n\t}\n\tconst extensions = mentions\n\t\t? [...commentTipTapExtensions, createMentionExtension({ resolveName })]\n\t\t: commentTipTapExtensions\n\tconst text = generateText(demoteHeadings(richText as JSONContent), extensions, {\n\t\tblockSeparator: '\\n',\n\t})\n\tif (!mentions) textCache.set(richText, text)\n\treturn text\n}\n"],
  "mappings": "AAAA,SAAS,oBAAiC;AAC1C,SAA6B,8BAA8B;AAC3D,SAAS,4CAAwD;AACjE,SAAS,yBAAyB,sBAAsB;AAQjD,MAAM,iBAAiB;AAIvB,MAAM,yBAAwC,OAAO,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ3F,SAAS,eAAe,MAAgC;AACvD,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,KAAK;AACtF,MAAI,KAAK,SAAS,WAAW;AAC5B,UAAM,EAAE,OAAO,QAAQ,GAAG,KAAK,IAAI;AACnC,WAAO,EAAE,GAAG,MAAM,MAAM,aAAa,QAAQ;AAAA,EAC9C;AACA,SAAO,YAAY,KAAK,UAAU,OAAO,EAAE,GAAG,MAAM,QAAQ;AAC7D;AAOA,SAAS,WAAW,MAA4B;AAC/C,MAAI,KAAK,SAAS,UAAW,QAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI;AACtE;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAQ3C,SAAS,kBACf,UACA,aACS;AACT,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,yBAAyB,uBAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,OAAO;AAAA,IACZ,eAAe,QAAuB;AAAA,IACtC;AAAA,EACD;AACA,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;AAEA,MAAM,YAAY,oBAAI,QAA4B;AAO3C,SAAS,uBACf,UACA,aACS;AACT,MAAI,eAAe,QAAQ,EAAG,QAAO;AACrC,QAAM,WAAW,WAAW,QAAuB;AACnD,MAAI,CAAC,UAAU;AACd,UAAM,SAAS,UAAU,IAAI,QAAQ;AACrC,QAAI,WAAW,OAAW,QAAO;AAAA,EAClC;AACA,QAAM,aAAa,WAChB,CAAC,GAAG,yBAAyB,uBAAuB,EAAE,YAAY,CAAC,CAAC,IACpE;AACH,QAAM,OAAO,aAAa,eAAe,QAAuB,GAAG,YAAY;AAAA,IAC9E,gBAAgB;AAAA,EACjB,CAAC;AACD,MAAI,CAAC,SAAU,WAAU,IAAI,UAAU,IAAI;AAC3C,SAAO;AACR;",
  "names": []
}
