tldraw 4.2.0-canary.ff738678e7e0 → 4.2.0-internal.d43dfb4f2e76

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.
Files changed (38) hide show
  1. package/dist-cjs/index.d.ts +20 -3
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +10 -7
  5. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +2 -2
  6. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js +1 -1
  7. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js.map +2 -2
  8. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +6 -2
  9. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
  10. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +1 -1
  11. package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
  12. package/dist-cjs/lib/ui/version.js +3 -3
  13. package/dist-cjs/lib/ui/version.js.map +1 -1
  14. package/dist-cjs/lib/utils/text/richText.js +5 -6
  15. package/dist-cjs/lib/utils/text/richText.js.map +3 -3
  16. package/dist-esm/index.d.mts +20 -3
  17. package/dist-esm/index.mjs +1 -1
  18. package/dist-esm/index.mjs.map +2 -2
  19. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +10 -7
  20. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +2 -2
  21. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs +1 -1
  22. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs.map +2 -2
  23. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +6 -2
  24. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
  25. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +1 -1
  26. package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
  27. package/dist-esm/lib/ui/version.mjs +3 -3
  28. package/dist-esm/lib/ui/version.mjs.map +1 -1
  29. package/dist-esm/lib/utils/text/richText.mjs +5 -6
  30. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  31. package/package.json +10 -10
  32. package/src/index.ts +3 -0
  33. package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +27 -7
  34. package/src/lib/ui/components/DefaultDebugPanel.tsx +1 -1
  35. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +6 -2
  36. package/src/lib/ui/components/Toolbar/LinkEditor.tsx +1 -1
  37. package/src/lib/ui/version.ts +3 -3
  38. package/src/lib/utils/text/richText.ts +5 -5
@@ -1,8 +1,8 @@
1
- const version = "4.2.0-canary.ff738678e7e0";
1
+ const version = "4.2.0-internal.d43dfb4f2e76";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-10-15T14:16:26.985Z",
5
- patch: "2025-10-15T14:16:26.985Z"
4
+ minor: "2025-10-22T08:01:49.348Z",
5
+ patch: "2025-10-22T08:01:49.348Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ui/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.2.0-canary.ff738678e7e0'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-15T14:16:26.985Z',\n\tpatch: '2025-10-15T14:16:26.985Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.2.0-internal.d43dfb4f2e76'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-22T08:01:49.348Z',\n\tpatch: '2025-10-22T08:01:49.348Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -6,7 +6,6 @@ import {
6
6
  } from "@tiptap/core";
7
7
  import Code from "@tiptap/extension-code";
8
8
  import Highlight from "@tiptap/extension-highlight";
9
- import Link from "@tiptap/extension-link";
10
9
  import StarterKit from "@tiptap/starter-kit";
11
10
  import {
12
11
  getOwnProperty,
@@ -29,11 +28,11 @@ const tipTapDefaultExtensions = [
29
28
  StarterKit.configure({
30
29
  blockquote: false,
31
30
  codeBlock: false,
32
- horizontalRule: false
33
- }),
34
- Link.configure({
35
- openOnClick: false,
36
- autolink: true
31
+ horizontalRule: false,
32
+ link: {
33
+ openOnClick: false,
34
+ autolink: true
35
+ }
37
36
  }),
38
37
  Highlight,
39
38
  KeyboardShiftEnterTweakExtension,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/utils/text/richText.ts"],
4
- "sourcesContent": ["import {\n\tExtension,\n\tExtensions,\n\tgenerateHTML,\n\tgenerateJSON,\n\tgenerateText,\n\tJSONContent,\n} from '@tiptap/core'\nimport Code from '@tiptap/extension-code'\nimport Highlight from '@tiptap/extension-highlight'\nimport Link from '@tiptap/extension-link'\nimport { Node } from '@tiptap/pm/model'\nimport StarterKit from '@tiptap/starter-kit'\nimport {\n\tEditor,\n\tgetOwnProperty,\n\tRichTextFontVisitorState,\n\tTLFontFace,\n\tTLRichText,\n\tWeakCache,\n} from '@tldraw/editor'\nimport { DefaultFontFaces } from '../../shapes/shared/defaultFonts'\nimport { TextDirection } from './textDirection'\n\n/** @public */\nexport const KeyboardShiftEnterTweakExtension = Extension.create({\n\tname: 'keyboardShiftEnterHandler',\n\taddKeyboardShortcuts() {\n\t\treturn {\n\t\t\t// We don't support soft breaks, so we just use the default enter command.\n\t\t\t'Shift-Enter': ({ editor }) => editor.commands.enter(),\n\t\t}\n\t},\n})\n\n// We change the default Code to override what's in the StarterKit.\n// It allows for other attributes/extensions.\nCode.config.excludes = undefined\n\n// We want the highlighting to take precedence over bolding/italics/links\n// as far as rendering is concerned. Otherwise, the highlighting\n// looks broken up.\nHighlight.config.priority = 1100\n\n/**\n * Default extensions for the TipTap editor.\n *\n * @public\n */\nexport const tipTapDefaultExtensions: Extensions = [\n\tStarterKit.configure({\n\t\tblockquote: false,\n\t\tcodeBlock: false,\n\t\thorizontalRule: false,\n\t}),\n\tLink.configure({\n\t\topenOnClick: false,\n\t\tautolink: true,\n\t}),\n\tHighlight,\n\tKeyboardShiftEnterTweakExtension,\n\tTextDirection,\n]\n\n// todo: bust this if the editor changes, too\nconst htmlCache = new WeakCache<TLRichText, string>()\n\n/**\n * Renders HTML from a rich text string.\n *\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n * @public\n */\nexport function renderHtmlFromRichText(editor: Editor, richText: TLRichText) {\n\treturn htmlCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\tconst html = generateHTML(richText as JSONContent, tipTapExtensions)\n\t\t// We replace empty paragraphs with a single line break to prevent the browser from collapsing them.\n\t\treturn html.replaceAll('<p dir=\"auto\"></p>', '<p><br /></p>') ?? ''\n\t})\n}\n\n/**\n * Renders HTML from a rich text string for measurement.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderHtmlFromRichTextForMeasurement(editor: Editor, richText: TLRichText) {\n\tconst html = renderHtmlFromRichText(editor, richText)\n\treturn `<div class=\"tl-rich-text\">${html}</div>`\n}\n\n// A weak cache used to store plaintext that's been extracted from rich text.\nconst plainTextFromRichTextCache = new WeakCache<TLRichText, string>()\n\nexport function isEmptyRichText(richText: TLRichText) {\n\tif (richText.content.length === 1) {\n\t\tif (!(richText.content[0] as any).content) return true\n\t}\n\treturn false\n}\n\n/**\n * Renders plaintext from a rich text string.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderPlaintextFromRichText(editor: Editor, richText: TLRichText) {\n\tif (isEmptyRichText(richText)) return ''\n\n\treturn plainTextFromRichTextCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\treturn generateText(richText as JSONContent, tipTapExtensions, {\n\t\t\tblockSeparator: '\\n',\n\t\t})\n\t})\n}\n\n/**\n * Renders JSONContent from html.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderRichTextFromHTML(editor: Editor, html: string): TLRichText {\n\tconst tipTapExtensions =\n\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\treturn generateJSON(html, tipTapExtensions) as TLRichText\n}\n\n/** @public */\nexport function defaultAddFontsFromNode(\n\tnode: Node,\n\tstate: RichTextFontVisitorState,\n\taddFont: (font: TLFontFace) => void\n) {\n\tfor (const mark of node.marks) {\n\t\tif (mark.type.name === 'bold' && state.weight !== 'bold') {\n\t\t\tstate = { ...state, weight: 'bold' }\n\t\t}\n\t\tif (mark.type.name === 'italic' && state.style !== 'italic') {\n\t\t\tstate = { ...state, style: 'italic' }\n\t\t}\n\t\tif (mark.type.name === 'code' && state.family !== 'tldraw_mono') {\n\t\t\tstate = { ...state, family: 'tldraw_mono' }\n\t\t}\n\t}\n\n\tconst fontsForFamily = getOwnProperty(DefaultFontFaces, state.family)\n\tif (!fontsForFamily) return state\n\n\tconst fontsForStyle = getOwnProperty(fontsForFamily, state.style)\n\tif (!fontsForStyle) return state\n\n\tconst fontsForWeight = getOwnProperty(fontsForStyle, state.weight)\n\tif (!fontsForWeight) return state\n\n\taddFont(fontsForWeight)\n\n\treturn state\n}\n"],
5
- "mappings": "AAAA;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,OAAO,UAAU;AACjB,OAAO,eAAe;AACtB,OAAO,UAAU;AAEjB,OAAO,gBAAgB;AACvB;AAAA,EAEC;AAAA,EAIA;AAAA,OACM;AACP,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAGvB,MAAM,mCAAmC,UAAU,OAAO;AAAA,EAChE,MAAM;AAAA,EACN,uBAAuB;AACtB,WAAO;AAAA;AAAA,MAEN,eAAe,CAAC,EAAE,OAAO,MAAM,OAAO,SAAS,MAAM;AAAA,IACtD;AAAA,EACD;AACD,CAAC;AAID,KAAK,OAAO,WAAW;AAKvB,UAAU,OAAO,WAAW;AAOrB,MAAM,0BAAsC;AAAA,EAClD,WAAW,UAAU;AAAA,IACpB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,gBAAgB;AAAA,EACjB,CAAC;AAAA,EACD,KAAK,UAAU;AAAA,IACd,aAAa;AAAA,IACb,UAAU;AAAA,EACX,CAAC;AAAA,EACD;AAAA,EACA;AAAA,EACA;AACD;AAGA,MAAM,YAAY,IAAI,UAA8B;AAU7C,SAAS,uBAAuB,QAAgB,UAAsB;AAC5E,SAAO,UAAU,IAAI,UAAU,MAAM;AACpC,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,UAAM,OAAO,aAAa,UAAyB,gBAAgB;AAEnE,WAAO,KAAK,WAAW,sBAAsB,eAAe,KAAK;AAAA,EAClE,CAAC;AACF;AAUO,SAAS,qCAAqC,QAAgB,UAAsB;AAC1F,QAAM,OAAO,uBAAuB,QAAQ,QAAQ;AACpD,SAAO,6BAA6B,IAAI;AACzC;AAGA,MAAM,6BAA6B,IAAI,UAA8B;AAE9D,SAAS,gBAAgB,UAAsB;AACrD,MAAI,SAAS,QAAQ,WAAW,GAAG;AAClC,QAAI,CAAE,SAAS,QAAQ,CAAC,EAAU,QAAS,QAAO;AAAA,EACnD;AACA,SAAO;AACR;AAUO,SAAS,4BAA4B,QAAgB,UAAsB;AACjF,MAAI,gBAAgB,QAAQ,EAAG,QAAO;AAEtC,SAAO,2BAA2B,IAAI,UAAU,MAAM;AACrD,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,WAAO,aAAa,UAAyB,kBAAkB;AAAA,MAC9D,gBAAgB;AAAA,IACjB,CAAC;AAAA,EACF,CAAC;AACF;AAUO,SAAS,uBAAuB,QAAgB,MAA0B;AAChF,QAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,SAAO,aAAa,MAAM,gBAAgB;AAC3C;AAGO,SAAS,wBACf,MACA,OACA,SACC;AACD,aAAW,QAAQ,KAAK,OAAO;AAC9B,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,QAAQ;AACzD,cAAQ,EAAE,GAAG,OAAO,QAAQ,OAAO;AAAA,IACpC;AACA,QAAI,KAAK,KAAK,SAAS,YAAY,MAAM,UAAU,UAAU;AAC5D,cAAQ,EAAE,GAAG,OAAO,OAAO,SAAS;AAAA,IACrC;AACA,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,eAAe;AAChE,cAAQ,EAAE,GAAG,OAAO,QAAQ,cAAc;AAAA,IAC3C;AAAA,EACD;AAEA,QAAM,iBAAiB,eAAe,kBAAkB,MAAM,MAAM;AACpE,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,gBAAgB,eAAe,gBAAgB,MAAM,KAAK;AAChE,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,iBAAiB,eAAe,eAAe,MAAM,MAAM;AACjE,MAAI,CAAC,eAAgB,QAAO;AAE5B,UAAQ,cAAc;AAEtB,SAAO;AACR;",
4
+ "sourcesContent": ["import {\n\tExtension,\n\tExtensions,\n\tgenerateHTML,\n\tgenerateJSON,\n\tgenerateText,\n\tJSONContent,\n} from '@tiptap/core'\nimport Code from '@tiptap/extension-code'\nimport Highlight from '@tiptap/extension-highlight'\nimport { Node } from '@tiptap/pm/model'\nimport StarterKit from '@tiptap/starter-kit'\nimport {\n\tEditor,\n\tgetOwnProperty,\n\tRichTextFontVisitorState,\n\tTLFontFace,\n\tTLRichText,\n\tWeakCache,\n} from '@tldraw/editor'\nimport { DefaultFontFaces } from '../../shapes/shared/defaultFonts'\nimport { TextDirection } from './textDirection'\n\n/** @public */\nexport const KeyboardShiftEnterTweakExtension = Extension.create({\n\tname: 'keyboardShiftEnterHandler',\n\taddKeyboardShortcuts() {\n\t\treturn {\n\t\t\t// We don't support soft breaks, so we just use the default enter command.\n\t\t\t'Shift-Enter': ({ editor }) => editor.commands.enter(),\n\t\t}\n\t},\n})\n\n// We change the default Code to override what's in the StarterKit.\n// It allows for other attributes/extensions.\n// @ts-ignore this is fine.\nCode.config.excludes = undefined\n\n// We want the highlighting to take precedence over bolding/italics/links\n// as far as rendering is concerned. Otherwise, the highlighting\n// looks broken up.\nHighlight.config.priority = 1100\n\n/**\n * Default extensions for the TipTap editor.\n *\n * @public\n */\nexport const tipTapDefaultExtensions: Extensions = [\n\tStarterKit.configure({\n\t\tblockquote: false,\n\t\tcodeBlock: false,\n\t\thorizontalRule: false,\n\t\tlink: {\n\t\t\topenOnClick: false,\n\t\t\tautolink: true,\n\t\t},\n\t}),\n\tHighlight,\n\tKeyboardShiftEnterTweakExtension,\n\tTextDirection,\n]\n\n// todo: bust this if the editor changes, too\nconst htmlCache = new WeakCache<TLRichText, string>()\n\n/**\n * Renders HTML from a rich text string.\n *\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n * @public\n */\nexport function renderHtmlFromRichText(editor: Editor, richText: TLRichText) {\n\treturn htmlCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\tconst html = generateHTML(richText as JSONContent, tipTapExtensions)\n\t\t// We replace empty paragraphs with a single line break to prevent the browser from collapsing them.\n\t\treturn html.replaceAll('<p dir=\"auto\"></p>', '<p><br /></p>') ?? ''\n\t})\n}\n\n/**\n * Renders HTML from a rich text string for measurement.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderHtmlFromRichTextForMeasurement(editor: Editor, richText: TLRichText) {\n\tconst html = renderHtmlFromRichText(editor, richText)\n\treturn `<div class=\"tl-rich-text\">${html}</div>`\n}\n\n// A weak cache used to store plaintext that's been extracted from rich text.\nconst plainTextFromRichTextCache = new WeakCache<TLRichText, string>()\n\nexport function isEmptyRichText(richText: TLRichText) {\n\tif (richText.content.length === 1) {\n\t\tif (!(richText.content[0] as any).content) return true\n\t}\n\treturn false\n}\n\n/**\n * Renders plaintext from a rich text string.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderPlaintextFromRichText(editor: Editor, richText: TLRichText) {\n\tif (isEmptyRichText(richText)) return ''\n\n\treturn plainTextFromRichTextCache.get(richText, () => {\n\t\tconst tipTapExtensions =\n\t\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\t\treturn generateText(richText as JSONContent, tipTapExtensions, {\n\t\t\tblockSeparator: '\\n',\n\t\t})\n\t})\n}\n\n/**\n * Renders JSONContent from html.\n * @param editor - The editor instance.\n * @param richText - The rich text content.\n *\n *\n * @public\n */\nexport function renderRichTextFromHTML(editor: Editor, html: string): TLRichText {\n\tconst tipTapExtensions =\n\t\teditor.getTextOptions().tipTapConfig?.extensions ?? tipTapDefaultExtensions\n\treturn generateJSON(html, tipTapExtensions) as TLRichText\n}\n\n/** @public */\nexport function defaultAddFontsFromNode(\n\tnode: Node,\n\tstate: RichTextFontVisitorState,\n\taddFont: (font: TLFontFace) => void\n) {\n\tfor (const mark of node.marks) {\n\t\tif (mark.type.name === 'bold' && state.weight !== 'bold') {\n\t\t\tstate = { ...state, weight: 'bold' }\n\t\t}\n\t\tif (mark.type.name === 'italic' && state.style !== 'italic') {\n\t\t\tstate = { ...state, style: 'italic' }\n\t\t}\n\t\tif (mark.type.name === 'code' && state.family !== 'tldraw_mono') {\n\t\t\tstate = { ...state, family: 'tldraw_mono' }\n\t\t}\n\t}\n\n\tconst fontsForFamily = getOwnProperty(DefaultFontFaces, state.family)\n\tif (!fontsForFamily) return state\n\n\tconst fontsForStyle = getOwnProperty(fontsForFamily, state.style)\n\tif (!fontsForStyle) return state\n\n\tconst fontsForWeight = getOwnProperty(fontsForStyle, state.weight)\n\tif (!fontsForWeight) return state\n\n\taddFont(fontsForWeight)\n\n\treturn state\n}\n"],
5
+ "mappings": "AAAA;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,OAAO,UAAU;AACjB,OAAO,eAAe;AAEtB,OAAO,gBAAgB;AACvB;AAAA,EAEC;AAAA,EAIA;AAAA,OACM;AACP,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAGvB,MAAM,mCAAmC,UAAU,OAAO;AAAA,EAChE,MAAM;AAAA,EACN,uBAAuB;AACtB,WAAO;AAAA;AAAA,MAEN,eAAe,CAAC,EAAE,OAAO,MAAM,OAAO,SAAS,MAAM;AAAA,IACtD;AAAA,EACD;AACD,CAAC;AAKD,KAAK,OAAO,WAAW;AAKvB,UAAU,OAAO,WAAW;AAOrB,MAAM,0BAAsC;AAAA,EAClD,WAAW,UAAU;AAAA,IACpB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,MAAM;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,IACX;AAAA,EACD,CAAC;AAAA,EACD;AAAA,EACA;AAAA,EACA;AACD;AAGA,MAAM,YAAY,IAAI,UAA8B;AAU7C,SAAS,uBAAuB,QAAgB,UAAsB;AAC5E,SAAO,UAAU,IAAI,UAAU,MAAM;AACpC,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,UAAM,OAAO,aAAa,UAAyB,gBAAgB;AAEnE,WAAO,KAAK,WAAW,sBAAsB,eAAe,KAAK;AAAA,EAClE,CAAC;AACF;AAUO,SAAS,qCAAqC,QAAgB,UAAsB;AAC1F,QAAM,OAAO,uBAAuB,QAAQ,QAAQ;AACpD,SAAO,6BAA6B,IAAI;AACzC;AAGA,MAAM,6BAA6B,IAAI,UAA8B;AAE9D,SAAS,gBAAgB,UAAsB;AACrD,MAAI,SAAS,QAAQ,WAAW,GAAG;AAClC,QAAI,CAAE,SAAS,QAAQ,CAAC,EAAU,QAAS,QAAO;AAAA,EACnD;AACA,SAAO;AACR;AAUO,SAAS,4BAA4B,QAAgB,UAAsB;AACjF,MAAI,gBAAgB,QAAQ,EAAG,QAAO;AAEtC,SAAO,2BAA2B,IAAI,UAAU,MAAM;AACrD,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,WAAO,aAAa,UAAyB,kBAAkB;AAAA,MAC9D,gBAAgB;AAAA,IACjB,CAAC;AAAA,EACF,CAAC;AACF;AAUO,SAAS,uBAAuB,QAAgB,MAA0B;AAChF,QAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,SAAO,aAAa,MAAM,gBAAgB;AAC3C;AAGO,SAAS,wBACf,MACA,OACA,SACC;AACD,aAAW,QAAQ,KAAK,OAAO;AAC9B,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,QAAQ;AACzD,cAAQ,EAAE,GAAG,OAAO,QAAQ,OAAO;AAAA,IACpC;AACA,QAAI,KAAK,KAAK,SAAS,YAAY,MAAM,UAAU,UAAU;AAC5D,cAAQ,EAAE,GAAG,OAAO,OAAO,SAAS;AAAA,IACrC;AACA,QAAI,KAAK,KAAK,SAAS,UAAU,MAAM,WAAW,eAAe;AAChE,cAAQ,EAAE,GAAG,OAAO,QAAQ,cAAc;AAAA,IAC3C;AAAA,EACD;AAEA,QAAM,iBAAiB,eAAe,kBAAkB,MAAM,MAAM;AACpE,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,gBAAgB,eAAe,gBAAgB,MAAM,KAAK;AAChE,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,iBAAiB,eAAe,eAAe,MAAM,MAAM;AACjE,MAAI,CAAC,eAAgB,QAAO;AAE5B,UAAQ,cAAc;AAEtB,SAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tldraw",
3
3
  "description": "A tiny little drawing editor.",
4
- "version": "4.2.0-canary.ff738678e7e0",
4
+ "version": "4.2.0-internal.d43dfb4f2e76",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -55,15 +55,15 @@
55
55
  "src"
56
56
  ],
57
57
  "dependencies": {
58
- "@tiptap/core": "^2.9.1",
59
- "@tiptap/extension-code": "^2.9.1",
60
- "@tiptap/extension-highlight": "^2.9.1",
61
- "@tiptap/extension-link": "^2.9.1",
62
- "@tiptap/pm": "^2.9.1",
63
- "@tiptap/react": "^2.9.1",
64
- "@tiptap/starter-kit": "^2.9.1",
65
- "@tldraw/editor": "4.2.0-canary.ff738678e7e0",
66
- "@tldraw/store": "4.2.0-canary.ff738678e7e0",
58
+ "@tiptap/core": "3.6.2",
59
+ "@tiptap/extension-code": "3.6.2",
60
+ "@tiptap/extension-highlight": "3.6.2",
61
+ "@tiptap/extension-list": "3.6.2",
62
+ "@tiptap/pm": "3.6.2",
63
+ "@tiptap/react": "3.6.2",
64
+ "@tiptap/starter-kit": "3.6.2",
65
+ "@tldraw/editor": "4.2.0-internal.d43dfb4f2e76",
66
+ "@tldraw/store": "4.2.0-internal.d43dfb4f2e76",
67
67
  "classnames": "^2.5.1",
68
68
  "hotkeys-js": "^3.13.9",
69
69
  "idb": "^7.1.1",
package/src/index.ts CHANGED
@@ -241,7 +241,10 @@ export {
241
241
  DefaultDebugMenuContent,
242
242
  ExampleDialog,
243
243
  FeatureFlags,
244
+ type CustomDebugFlags,
245
+ type DebugFlagsProps,
244
246
  type ExampleDialogProps,
247
+ type FeatureFlagsProps,
245
248
  } from './lib/ui/components/DebugMenu/DefaultDebugMenuContent'
246
249
  export { DefaultMenuPanel } from './lib/ui/components/DefaultMenuPanel'
247
250
  export {
@@ -29,8 +29,17 @@ import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
29
29
  import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
30
30
  import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
31
31
 
32
+ /** @public */
33
+ export interface CustomDebugFlags {
34
+ customDebugFlags?: Record<string, DebugFlag<boolean>>
35
+ customFeatureFlags?: Record<string, DebugFlag<boolean>>
36
+ }
37
+
32
38
  /** @public @react */
33
- export function DefaultDebugMenuContent() {
39
+ export function DefaultDebugMenuContent({
40
+ customDebugFlags,
41
+ customFeatureFlags,
42
+ }: CustomDebugFlags) {
34
43
  const editor = useEditor()
35
44
  const { addToast } = useToasts()
36
45
  const { addDialog } = useDialogs()
@@ -161,15 +170,21 @@ export function DefaultDebugMenuContent() {
161
170
  <TldrawUiMenuItem id="throw-error" onSelect={() => setError(true)} label={'Throw error'} />
162
171
  </TldrawUiMenuGroup>
163
172
  <TldrawUiMenuGroup id="flags">
164
- <DebugFlags />
165
- <FeatureFlags />
173
+ <DebugFlags customDebugFlags={customDebugFlags} />
174
+ <FeatureFlags customFeatureFlags={customFeatureFlags} />
166
175
  </TldrawUiMenuGroup>
167
176
  </>
168
177
  )
169
178
  }
179
+
180
+ /** @public */
181
+ export interface DebugFlagsProps {
182
+ customDebugFlags?: Record<string, DebugFlag<boolean>> | undefined
183
+ }
184
+
170
185
  /** @public @react */
171
- export function DebugFlags() {
172
- const items = Object.values(debugFlags)
186
+ export function DebugFlags(props: DebugFlagsProps) {
187
+ const items = Object.values(props.customDebugFlags ?? debugFlags)
173
188
  if (!items.length) return null
174
189
  return (
175
190
  <TldrawUiMenuSubmenu id="debug flags" label="Debug flags">
@@ -181,9 +196,14 @@ export function DebugFlags() {
181
196
  </TldrawUiMenuSubmenu>
182
197
  )
183
198
  }
199
+ /** @public */
200
+ export interface FeatureFlagsProps {
201
+ customFeatureFlags?: Record<string, DebugFlag<boolean>> | undefined
202
+ }
203
+
184
204
  /** @public @react */
185
- export function FeatureFlags() {
186
- const items = Object.values(featureFlags)
205
+ export function FeatureFlags(props: FeatureFlagsProps) {
206
+ const items = Object.values(props.customFeatureFlags ?? featureFlags)
187
207
  if (!items.length) return null
188
208
  return (
189
209
  <TldrawUiMenuSubmenu id="feature flags" label="Feature flags">
@@ -110,7 +110,7 @@ function FPS() {
110
110
  isSlow = !isSlow
111
111
  }
112
112
 
113
- fpsRef.current!.innerHTML = `FPS ${fps.toString()}`
113
+ fpsRef.current!.innerHTML = `FPS ${fps.toString()} (max: ${maxKnownFps})`
114
114
  fpsRef.current!.className =
115
115
  `tlui-debug-panel__fps` + (isSlow ? ` tlui-debug-panel__fps__slow` : ``)
116
116
 
@@ -117,7 +117,9 @@ function useEditingLinkBehavior(textEditor?: TiptapEditor) {
117
117
 
118
118
  textEditor.view.dom.addEventListener('click', handleClick)
119
119
  return () => {
120
- textEditor.view.dom.removeEventListener('click', handleClick)
120
+ if (textEditor.isInitialized) {
121
+ textEditor.view.dom.removeEventListener('click', handleClick)
122
+ }
121
123
  }
122
124
  }, [textEditor, isEditingLink])
123
125
 
@@ -193,7 +195,9 @@ function useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {
193
195
  })
194
196
  return () => {
195
197
  touchDownEvents.forEach((eventName: string) => {
196
- textEditor.view.dom.removeEventListener(eventName, handlePointingDown)
198
+ if (textEditor.isInitialized) {
199
+ textEditor.view.dom.removeEventListener(eventName, handlePointingDown)
200
+ }
197
201
  })
198
202
  touchUpEvents.forEach((eventName: string) => {
199
203
  document.body.removeEventListener(eventName, handlePointingUp)
@@ -31,7 +31,7 @@ export function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEdi
31
31
  link = `https://${link}`
32
32
  }
33
33
 
34
- textEditor.commands.setLink({ href: link })
34
+ textEditor.chain().setLink({ href: link }).run()
35
35
  // N.B. We shouldn't focus() on mobile because it causes the
36
36
  // Return key to replace the link with a newline :facepalm:
37
37
  if (editor.getInstanceState().isCoarsePointer) {
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '4.2.0-canary.ff738678e7e0'
4
+ export const version = '4.2.0-internal.d43dfb4f2e76'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2025-10-15T14:16:26.985Z',
8
- patch: '2025-10-15T14:16:26.985Z',
7
+ minor: '2025-10-22T08:01:49.348Z',
8
+ patch: '2025-10-22T08:01:49.348Z',
9
9
  }
@@ -8,7 +8,6 @@ import {
8
8
  } from '@tiptap/core'
9
9
  import Code from '@tiptap/extension-code'
10
10
  import Highlight from '@tiptap/extension-highlight'
11
- import Link from '@tiptap/extension-link'
12
11
  import { Node } from '@tiptap/pm/model'
13
12
  import StarterKit from '@tiptap/starter-kit'
14
13
  import {
@@ -35,6 +34,7 @@ export const KeyboardShiftEnterTweakExtension = Extension.create({
35
34
 
36
35
  // We change the default Code to override what's in the StarterKit.
37
36
  // It allows for other attributes/extensions.
37
+ // @ts-ignore this is fine.
38
38
  Code.config.excludes = undefined
39
39
 
40
40
  // We want the highlighting to take precedence over bolding/italics/links
@@ -52,10 +52,10 @@ export const tipTapDefaultExtensions: Extensions = [
52
52
  blockquote: false,
53
53
  codeBlock: false,
54
54
  horizontalRule: false,
55
- }),
56
- Link.configure({
57
- openOnClick: false,
58
- autolink: true,
55
+ link: {
56
+ openOnClick: false,
57
+ autolink: true,
58
+ },
59
59
  }),
60
60
  Highlight,
61
61
  KeyboardShiftEnterTweakExtension,