tldraw 4.2.0-canary.43016e91a60a → 4.2.0-canary.5887eb336025
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/index.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +6 -2
- package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js +1 -1
- package/dist-cjs/lib/ui/components/Toolbar/LinkEditor.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/text/richText.js +5 -6
- package/dist-cjs/lib/utils/text/richText.js.map +3 -3
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +6 -2
- package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs +1 -1
- package/dist-esm/lib/ui/components/Toolbar/LinkEditor.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/text/richText.mjs +5 -6
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/package.json +10 -10
- package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +6 -2
- package/src/lib/ui/components/Toolbar/LinkEditor.tsx +1 -1
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/text/richText.ts +5 -5
package/dist-cjs/index.js
CHANGED
|
@@ -584,7 +584,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
584
584
|
var import_file = require("./lib/utils/tldr/file");
|
|
585
585
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
586
586
|
"tldraw",
|
|
587
|
-
"4.2.0-canary.
|
|
587
|
+
"4.2.0-canary.5887eb336025",
|
|
588
588
|
"cjs"
|
|
589
589
|
);
|
|
590
590
|
//# sourceMappingURL=index.js.map
|
|
@@ -102,7 +102,9 @@ function useEditingLinkBehavior(textEditor) {
|
|
|
102
102
|
};
|
|
103
103
|
textEditor.view.dom.addEventListener("click", handleClick);
|
|
104
104
|
return () => {
|
|
105
|
-
textEditor.
|
|
105
|
+
if (textEditor.isInitialized) {
|
|
106
|
+
textEditor.view.dom.removeEventListener("click", handleClick);
|
|
107
|
+
}
|
|
106
108
|
};
|
|
107
109
|
}, [textEditor, isEditingLink]);
|
|
108
110
|
(0, import_react.useEffect)(() => {
|
|
@@ -155,7 +157,9 @@ function useIsMousingDownOnTextEditor(textEditor) {
|
|
|
155
157
|
});
|
|
156
158
|
return () => {
|
|
157
159
|
touchDownEvents.forEach((eventName) => {
|
|
158
|
-
textEditor.
|
|
160
|
+
if (textEditor.isInitialized) {
|
|
161
|
+
textEditor.view.dom.removeEventListener(eventName, handlePointingDown);
|
|
162
|
+
}
|
|
159
163
|
});
|
|
160
164
|
touchUpEvents.forEach((eventName) => {
|
|
161
165
|
document.body.removeEventListener(eventName, handlePointingUp);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx"],
|
|
4
|
-
"sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BQ;AA5BR,kBAAsE;AAEtE,oBAAwE;AACxE,mBAAgE;AAChE,4BAA+B;AAC/B,uCAAqD;AACrD,2CAA8C;AAC9C,wBAA2B;AAYpB,MAAM,6BAAyB,qBAAM,SAASA,wBAAuB;AAAA,EAC3E;AACD,GAA6B;AAC5B,QAAM,aAAS,yBAAU;AAEzB,QAAM,iBAAa,wBAAS,cAAc,MAAM,OAAO,kBAAkB,GAAG,CAAC,MAAM,CAAC;AAEpF,MAAI,OAAO,iBAAiB,EAAE,mBAAmB,CAAC,WAAY,QAAO;AAErE,SAAO,4CAAC,0BAAuB,YAAyB,UAAS;AAClE,CAAC;AAED,SAAS,uBAAuB;AAAA,EAC/B;AAAA,EACA;AACD,GAGG;AACF,QAAM,EAAE,eAAe,iBAAiB,gBAAgB,IAAI,uBAAuB,UAAU;AAC7F,QAAM,CAAC,kBAAkB,mBAAmB,QAAI,uBAAuB,IAAI;AAC3E,QAAM,8BAA0B,qBAAwB;AACxD,QAAM,gBAAgB,6BAA6B,UAAU;AAC7D,QAAM,UAAM,sCAAe;AAE3B,QAAM,yBAAqB,0BAAY,MAAM;AAC5C,QAAI,eAAe;AAElB,aAAO,wBAAwB;AAAA,IAChC;AAEA,UAAM,YAAY,OAAO,aAAa;AAGtC,QAAI,CAAC,oBAAoB,CAAC,aAAa,UAAU,eAAe,KAAK,UAAU;AAC9E;AAGD,UAAM,aAAoB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,UAAU,YAAY,KAAK;AAC9C,YAAM,QAAQ,UAAU,WAAW,CAAC;AACpC,iBAAW,SAAK,4CAAU,MAAM,sBAAsB,CAAC,CAAC;AAAA,IACzD;AAEA,UAAM,SAAS,kBAAI,OAAO,UAAU;AACpC,4BAAwB,UAAU;AAClC,WAAO;AAAA,EACR,GAAG,CAAC,kBAAkB,aAAa,CAAC;AAEpC,8BAAU,MAAM;AACf,UAAM,wBAAwB,CAAC,EAAE,QAAQC,YAAW,MACnD,oBAAoBA,YAAW,MAAM,SAAS;AAC/C,eAAW,GAAG,mBAAmB,qBAAqB;AAEtD,0BAAsB,EAAE,QAAQ,WAAW,CAAwC;AACnF,WAAO,MAAM;AACZ,iBAAW,IAAI,mBAAmB,qBAAqB;AAAA,IACxD;AAAA,EACD,GAAG,CAAC,UAAU,CAAC;AAEf,SACC;AAAA,IAAC;AAAA;AAAA,MACA,WAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,wBAAwB;AAAA,MACxB,OAAO,IAAI,8BAA8B;AAAA,MAExC,qBACA,WACG,gBACH;AAAA,QAAC;AAAA;AAAA,UACA;AAAA,UACA,OAAO,WAAW,SAAS,MAAM,IAAI,WAAW,cAAc,MAAM,EAAE,OAAO;AAAA,UAC7E,SAAS;AAAA;AAAA,MACV,IAEA,4CAAC,sEAA8B,YAAwB,iBAAkC;AAAA;AAAA,EAE3F;AAEF;AAEA,SAAS,uBAAuB,YAA2B;AAC1D,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAS,KAAK;AAGxD,8BAAU,MAAM;AACf,QAAI,CAAC,YAAY;AAChB,uBAAiB,KAAK;AACtB;AAAA,IACD;AAEA,UAAM,cAAc,MAAM;AACzB,YAAM,eAAe,WAAW,SAAS,MAAM;AAC/C,uBAAiB,YAAY;AAAA,IAC9B;AAEA,eAAW,KAAK,IAAI,iBAAiB,SAAS,WAAW;AACzD,WAAO,MAAM;AACZ,
|
|
4
|
+
"sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\tif (textEditor.isInitialized) {\n\t\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\tif (textEditor.isInitialized) {\n\t\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t\t}\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DefaultRichTextToolbar", "textEditor"]
|
|
7
7
|
}
|
|
@@ -43,7 +43,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
43
43
|
if (!link.startsWith("http://") && !link.startsWith("https://")) {
|
|
44
44
|
link = `https://${link}`;
|
|
45
45
|
}
|
|
46
|
-
textEditor.
|
|
46
|
+
textEditor.chain().setLink({ href: link }).run();
|
|
47
47
|
if (editor.getInstanceState().isCoarsePointer) {
|
|
48
48
|
textEditor.commands.blur();
|
|
49
49
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,
|
|
4
|
+
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.chain().setLink({ href: link }).run()\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmEE;AAnEF,oBAAwD;AACxD,mBAA4C;AAC5C,oBAA4B;AAC5B,4BAA+B;AAC/B,4BAA+B;AAC/B,gCAAmC;AACnC,2BAA8B;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,aAAS,yBAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,YAAY;AAC/C,QAAM,UAAM,sCAAe;AAC3B,QAAM,UAAM,qBAAyB,IAAI;AACzC,QAAM,iBAAa,2BAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,EAAE,IAAI;AAG/C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,8BAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,8BAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,4EACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,sDAAC,gDAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,sDAAC,gDAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "4.2.0-canary.
|
|
25
|
+
const version = "4.2.0-canary.5887eb336025";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2025-09-18T14:39:22.803Z",
|
|
28
|
-
minor: "2025-10-
|
|
29
|
-
patch: "2025-10-
|
|
28
|
+
minor: "2025-10-16T09:29:09.234Z",
|
|
29
|
+
patch: "2025-10-16T09:29:09.234Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
|
@@ -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.
|
|
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.5887eb336025'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-16T09:29:09.234Z',\n\tpatch: '2025-10-16T09:29:09.234Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -41,7 +41,6 @@ module.exports = __toCommonJS(richText_exports);
|
|
|
41
41
|
var import_core = require("@tiptap/core");
|
|
42
42
|
var import_extension_code = __toESM(require("@tiptap/extension-code"), 1);
|
|
43
43
|
var import_extension_highlight = __toESM(require("@tiptap/extension-highlight"), 1);
|
|
44
|
-
var import_extension_link = __toESM(require("@tiptap/extension-link"), 1);
|
|
45
44
|
var import_starter_kit = __toESM(require("@tiptap/starter-kit"), 1);
|
|
46
45
|
var import_editor = require("@tldraw/editor");
|
|
47
46
|
var import_defaultFonts = require("../../shapes/shared/defaultFonts");
|
|
@@ -61,11 +60,11 @@ const tipTapDefaultExtensions = [
|
|
|
61
60
|
import_starter_kit.default.configure({
|
|
62
61
|
blockquote: false,
|
|
63
62
|
codeBlock: false,
|
|
64
|
-
horizontalRule: false
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
horizontalRule: false,
|
|
64
|
+
link: {
|
|
65
|
+
openOnClick: false,
|
|
66
|
+
autolink: true
|
|
67
|
+
}
|
|
69
68
|
}),
|
|
70
69
|
import_extension_highlight.default,
|
|
71
70
|
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
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAOO;AACP,4BAAiB;AACjB,iCAAsB;
|
|
6
|
-
"names": ["Code", "Highlight", "StarterKit"
|
|
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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAOO;AACP,4BAAiB;AACjB,iCAAsB;AAEtB,yBAAuB;AACvB,oBAOO;AACP,0BAAiC;AACjC,2BAA8B;AAGvB,MAAM,mCAAmC,sBAAU,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,sBAAAA,QAAK,OAAO,WAAW;AAKvB,2BAAAC,QAAU,OAAO,WAAW;AAOrB,MAAM,0BAAsC;AAAA,EAClD,mBAAAC,QAAW,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,2BAAAD;AAAA,EACA;AAAA,EACA;AACD;AAGA,MAAM,YAAY,IAAI,wBAA8B;AAU7C,SAAS,uBAAuB,QAAgB,UAAsB;AAC5E,SAAO,UAAU,IAAI,UAAU,MAAM;AACpC,UAAM,mBACL,OAAO,eAAe,EAAE,cAAc,cAAc;AACrD,UAAM,WAAO,0BAAa,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,wBAA8B;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,eAAO,0BAAa,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,aAAO,0BAAa,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,qBAAiB,8BAAe,sCAAkB,MAAM,MAAM;AACpE,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,oBAAgB,8BAAe,gBAAgB,MAAM,KAAK;AAChE,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,qBAAiB,8BAAe,eAAe,MAAM,MAAM;AACjE,MAAI,CAAC,eAAgB,QAAO;AAE5B,UAAQ,cAAc;AAEtB,SAAO;AACR;",
|
|
6
|
+
"names": ["Code", "Highlight", "StarterKit"]
|
|
7
7
|
}
|
package/dist-esm/index.mjs
CHANGED
|
@@ -79,7 +79,9 @@ function useEditingLinkBehavior(textEditor) {
|
|
|
79
79
|
};
|
|
80
80
|
textEditor.view.dom.addEventListener("click", handleClick);
|
|
81
81
|
return () => {
|
|
82
|
-
textEditor.
|
|
82
|
+
if (textEditor.isInitialized) {
|
|
83
|
+
textEditor.view.dom.removeEventListener("click", handleClick);
|
|
84
|
+
}
|
|
83
85
|
};
|
|
84
86
|
}, [textEditor, isEditingLink]);
|
|
85
87
|
useEffect(() => {
|
|
@@ -132,7 +134,9 @@ function useIsMousingDownOnTextEditor(textEditor) {
|
|
|
132
134
|
});
|
|
133
135
|
return () => {
|
|
134
136
|
touchDownEvents.forEach((eventName) => {
|
|
135
|
-
textEditor.
|
|
137
|
+
if (textEditor.isInitialized) {
|
|
138
|
+
textEditor.view.dom.removeEventListener(eventName, handlePointingDown);
|
|
139
|
+
}
|
|
136
140
|
});
|
|
137
141
|
touchUpEvents.forEach((eventName) => {
|
|
138
142
|
document.body.removeEventListener(eventName, handlePointingUp);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx"],
|
|
4
|
-
"sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
|
|
5
|
-
"mappings": "AA4BQ;AA5BR,SAAS,oBAA6D;AAEtE,SAAS,KAAK,UAAwB,OAAO,WAAW,gBAAgB;AACxE,SAAgB,aAAa,WAAW,QAAQ,gBAAgB;AAChE,SAAS,sBAAsB;AAC/B,SAAS,WAAW,iCAAiC;AACrD,SAAS,qCAAqC;AAC9C,SAAS,kBAAkB;AAYpB,MAAM,yBAAyB,MAAM,SAASA,wBAAuB;AAAA,EAC3E;AACD,GAA6B;AAC5B,QAAM,SAAS,UAAU;AAEzB,QAAM,aAAa,SAAS,cAAc,MAAM,OAAO,kBAAkB,GAAG,CAAC,MAAM,CAAC;AAEpF,MAAI,OAAO,iBAAiB,EAAE,mBAAmB,CAAC,WAAY,QAAO;AAErE,SAAO,oBAAC,0BAAuB,YAAyB,UAAS;AAClE,CAAC;AAED,SAAS,uBAAuB;AAAA,EAC/B;AAAA,EACA;AACD,GAGG;AACF,QAAM,EAAE,eAAe,iBAAiB,gBAAgB,IAAI,uBAAuB,UAAU;AAC7F,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAuB,IAAI;AAC3E,QAAM,0BAA0B,OAAwB;AACxD,QAAM,gBAAgB,6BAA6B,UAAU;AAC7D,QAAM,MAAM,eAAe;AAE3B,QAAM,qBAAqB,YAAY,MAAM;AAC5C,QAAI,eAAe;AAElB,aAAO,wBAAwB;AAAA,IAChC;AAEA,UAAM,YAAY,OAAO,aAAa;AAGtC,QAAI,CAAC,oBAAoB,CAAC,aAAa,UAAU,eAAe,KAAK,UAAU;AAC9E;AAGD,UAAM,aAAoB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,UAAU,YAAY,KAAK;AAC9C,YAAM,QAAQ,UAAU,WAAW,CAAC;AACpC,iBAAW,KAAK,UAAU,MAAM,sBAAsB,CAAC,CAAC;AAAA,IACzD;AAEA,UAAM,SAAS,IAAI,OAAO,UAAU;AACpC,4BAAwB,UAAU;AAClC,WAAO;AAAA,EACR,GAAG,CAAC,kBAAkB,aAAa,CAAC;AAEpC,YAAU,MAAM;AACf,UAAM,wBAAwB,CAAC,EAAE,QAAQC,YAAW,MACnD,oBAAoBA,YAAW,MAAM,SAAS;AAC/C,eAAW,GAAG,mBAAmB,qBAAqB;AAEtD,0BAAsB,EAAE,QAAQ,WAAW,CAAwC;AACnF,WAAO,MAAM;AACZ,iBAAW,IAAI,mBAAmB,qBAAqB;AAAA,IACxD;AAAA,EACD,GAAG,CAAC,UAAU,CAAC;AAEf,SACC;AAAA,IAAC;AAAA;AAAA,MACA,WAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,wBAAwB;AAAA,MACxB,OAAO,IAAI,8BAA8B;AAAA,MAExC,qBACA,WACG,gBACH;AAAA,QAAC;AAAA;AAAA,UACA;AAAA,UACA,OAAO,WAAW,SAAS,MAAM,IAAI,WAAW,cAAc,MAAM,EAAE,OAAO;AAAA,UAC7E,SAAS;AAAA;AAAA,MACV,IAEA,oBAAC,iCAA8B,YAAwB,iBAAkC;AAAA;AAAA,EAE3F;AAEF;AAEA,SAAS,uBAAuB,YAA2B;AAC1D,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AAGxD,YAAU,MAAM;AACf,QAAI,CAAC,YAAY;AAChB,uBAAiB,KAAK;AACtB;AAAA,IACD;AAEA,UAAM,cAAc,MAAM;AACzB,YAAM,eAAe,WAAW,SAAS,MAAM;AAC/C,uBAAiB,YAAY;AAAA,IAC9B;AAEA,eAAW,KAAK,IAAI,iBAAiB,SAAS,WAAW;AACzD,WAAO,MAAM;AACZ,
|
|
4
|
+
"sourcesContent": ["import { getMarkRange, Range, EditorEvents as TextEditorEvents } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Box, debounce, TiptapEditor, track, useEditor, useValue } from '@tldraw/editor'\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { rectToBox, TldrawUiContextualToolbar } from '../primitives/TldrawUiContextualToolbar'\nimport { DefaultRichTextToolbarContent } from './DefaultRichTextToolbarContent'\nimport { LinkEditor } from './LinkEditor'\n\n/** @public */\nexport interface TLUiRichTextToolbarProps {\n\tchildren?: React.ReactNode\n}\n\n/**\n * The default rich text toolbar.\n *\n * @public @react\n */\nexport const DefaultRichTextToolbar = track(function DefaultRichTextToolbar({\n\tchildren,\n}: TLUiRichTextToolbarProps) {\n\tconst editor = useEditor()\n\n\tconst textEditor = useValue('textEditor', () => editor.getRichTextEditor(), [editor])\n\n\tif (editor.getInstanceState().isCoarsePointer || !textEditor) return null\n\n\treturn <ContextualToolbarInner textEditor={textEditor}>{children}</ContextualToolbarInner>\n})\n\nfunction ContextualToolbarInner({\n\ttextEditor,\n\tchildren,\n}: {\n\tchildren?: React.ReactNode\n\ttextEditor: TiptapEditor\n}) {\n\tconst { isEditingLink, onEditLinkStart, onEditLinkClose } = useEditingLinkBehavior(textEditor)\n\tconst [currentSelection, setCurrentSelection] = useState<Range | null>(null)\n\tconst previousSelectionBounds = useRef<Box | undefined>()\n\tconst isMousingDown = useIsMousingDownOnTextEditor(textEditor)\n\tconst msg = useTranslation()\n\n\tconst getSelectionBounds = useCallback(() => {\n\t\tif (isEditingLink) {\n\t\t\t// If we're editing a link we don't have selection bounds temporarily.\n\t\t\treturn previousSelectionBounds.current\n\t\t}\n\t\t// Get the text selection rects as a box. This will be undefined if there are no selections.\n\t\tconst selection = window.getSelection()\n\n\t\t// If there are no selections, don't return a box\n\t\tif (!currentSelection || !selection || selection.rangeCount === 0 || selection.isCollapsed)\n\t\t\treturn\n\n\t\t// Get a common box from all of the ranges' screen rects\n\t\tconst rangeBoxes: Box[] = []\n\t\tfor (let i = 0; i < selection.rangeCount; i++) {\n\t\t\tconst range = selection.getRangeAt(i)\n\t\t\trangeBoxes.push(rectToBox(range.getBoundingClientRect()))\n\t\t}\n\n\t\tconst bounds = Box.Common(rangeBoxes)\n\t\tpreviousSelectionBounds.current = bounds\n\t\treturn bounds\n\t}, [currentSelection, isEditingLink])\n\n\tuseEffect(() => {\n\t\tconst handleSelectionUpdate = ({ editor: textEditor }: TextEditorEvents['selectionUpdate']) =>\n\t\t\tsetCurrentSelection(textEditor.state.selection)\n\t\ttextEditor.on('selectionUpdate', handleSelectionUpdate)\n\t\t// Need to kick off the selection update manually to get the initial selection, esp. if select-all.\n\t\thandleSelectionUpdate({ editor: textEditor } as TextEditorEvents['selectionUpdate'])\n\t\treturn () => {\n\t\t\ttextEditor.off('selectionUpdate', handleSelectionUpdate)\n\t\t}\n\t}, [textEditor])\n\n\treturn (\n\t\t<TldrawUiContextualToolbar\n\t\t\tclassName=\"tlui-rich-text__toolbar\"\n\t\t\tgetSelectionBounds={getSelectionBounds}\n\t\t\tisMousingDown={isMousingDown}\n\t\t\tchangeOnlyWhenYChanges={true}\n\t\t\tlabel={msg('tool.rich-text-toolbar-title')}\n\t\t>\n\t\t\t{children ? (\n\t\t\t\tchildren\n\t\t\t) : isEditingLink ? (\n\t\t\t\t<LinkEditor\n\t\t\t\t\ttextEditor={textEditor}\n\t\t\t\t\tvalue={textEditor.isActive('link') ? textEditor.getAttributes('link').href : ''}\n\t\t\t\t\tonClose={onEditLinkClose}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<DefaultRichTextToolbarContent textEditor={textEditor} onEditLinkStart={onEditLinkStart} />\n\t\t\t)}\n\t\t</TldrawUiContextualToolbar>\n\t)\n}\n\nfunction useEditingLinkBehavior(textEditor?: TiptapEditor) {\n\tconst [isEditingLink, setIsEditingLink] = useState(false)\n\n\t// Set up text editor event listeners.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\tsetIsEditingLink(false)\n\t\t\treturn\n\t\t}\n\n\t\tconst handleClick = () => {\n\t\t\tconst isLinkActive = textEditor.isActive('link')\n\t\t\tsetIsEditingLink(isLinkActive)\n\t\t}\n\n\t\ttextEditor.view.dom.addEventListener('click', handleClick)\n\t\treturn () => {\n\t\t\tif (textEditor.isInitialized) {\n\t\t\t\ttextEditor.view.dom.removeEventListener('click', handleClick)\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\t// If we're editing a link, select the entire link.\n\t// This can happen via a click or via keyboarding over to the link and then\n\t// clicking the toolbar button.\n\tuseEffect(() => {\n\t\tif (!textEditor) {\n\t\t\treturn\n\t\t}\n\n\t\t// N.B. This specifically isn't checking the isEditingLink state but\n\t\t// the current active state of the text editor. This is because there's\n\t\t// a subtelty where when going edit-to-edit, that is text editor-to-text editor\n\t\t// in different shapes, the isEditingLink state doesn't get reset quickly enough.\n\t\tif (textEditor.isActive('link')) {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = getMarkRange(\n\t\t\t\t\ttextEditor.state.doc.resolve(textEditor.state.selection.from),\n\t\t\t\t\ttextEditor.schema.marks.link as MarkType\n\t\t\t\t) as Range\n\t\t\t\t// Select the entire link if we just clicked on it while in edit mode, but not if there's\n\t\t\t\t// a specific selection.\n\t\t\t\tif (textEditor.state.selection.empty) {\n\t\t\t\t\ttextEditor.commands.setTextSelection({ from, to })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Sometimes getMarkRange throws an error when the selection is the entire document.\n\t\t\t\t// This is somewhat mysterious but it's harmless. We just need to ignore it.\n\t\t\t\t// Also, this seems to have recently broken with the React 19 preparation changes.\n\t\t\t}\n\t\t}\n\t}, [textEditor, isEditingLink])\n\n\tconst onEditLinkStart = useCallback(() => {\n\t\tsetIsEditingLink(true)\n\t}, [])\n\n\tconst onEditLinkCancel = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t}, [])\n\n\tconst onEditLinkClose = useCallback(() => {\n\t\tsetIsEditingLink(false)\n\t\tif (!textEditor) return\n\t\tconst from = textEditor.state.selection.from\n\t\ttextEditor.commands.setTextSelection({ from, to: from })\n\t}, [textEditor])\n\n\treturn { isEditingLink, onEditLinkStart, onEditLinkClose, onEditLinkCancel }\n}\n\nfunction useIsMousingDownOnTextEditor(textEditor: TiptapEditor) {\n\tconst [isMousingDown, setIsMousingDown] = useState(false)\n\n\t// Set up general event listeners for text selection.\n\tuseEffect(() => {\n\t\tif (!textEditor) return\n\n\t\tconst handlePointingStateChange = debounce(({ isPointing }: { isPointing: boolean }) => {\n\t\t\tsetIsMousingDown(isPointing)\n\t\t}, 16)\n\t\tconst handlePointingDown = () => handlePointingStateChange({ isPointing: true })\n\t\tconst handlePointingUp = () => handlePointingStateChange({ isPointing: false })\n\n\t\tconst touchDownEvents = ['touchstart', 'pointerdown', 'mousedown']\n\t\tconst touchUpEvents = ['touchend', 'pointerup', 'mouseup']\n\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\ttextEditor.view.dom.addEventListener(eventName, handlePointingDown)\n\t\t})\n\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\tdocument.body.addEventListener(eventName, handlePointingUp)\n\t\t})\n\t\treturn () => {\n\t\t\ttouchDownEvents.forEach((eventName: string) => {\n\t\t\t\tif (textEditor.isInitialized) {\n\t\t\t\t\ttextEditor.view.dom.removeEventListener(eventName, handlePointingDown)\n\t\t\t\t}\n\t\t\t})\n\t\t\ttouchUpEvents.forEach((eventName: string) => {\n\t\t\t\tdocument.body.removeEventListener(eventName, handlePointingUp)\n\t\t\t})\n\t\t}\n\t}, [textEditor])\n\n\treturn isMousingDown\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DefaultRichTextToolbar", "textEditor"]
|
|
7
7
|
}
|
|
@@ -20,7 +20,7 @@ function LinkEditor({ textEditor, value: initialValue, onClose }) {
|
|
|
20
20
|
if (!link.startsWith("http://") && !link.startsWith("https://")) {
|
|
21
21
|
link = `https://${link}`;
|
|
22
22
|
}
|
|
23
|
-
textEditor.
|
|
23
|
+
textEditor.chain().setLink({ href: link }).run();
|
|
24
24
|
if (editor.getInstanceState().isCoarsePointer) {
|
|
25
25
|
textEditor.commands.blur();
|
|
26
26
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/ui/components/Toolbar/LinkEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.
|
|
5
|
-
"mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,
|
|
4
|
+
"sourcesContent": ["import { preventDefault, TiptapEditor, useEditor } from '@tldraw/editor'\nimport { useEffect, useRef, useState } from 'react'\nimport { useUiEvents } from '../../context/events'\nimport { useTranslation } from '../../hooks/useTranslation/useTranslation'\nimport { TldrawUiButton } from '../primitives/Button/TldrawUiButton'\nimport { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'\nimport { TldrawUiInput } from '../primitives/TldrawUiInput'\n\n/** @public */\nexport interface LinkEditorProps {\n\ttextEditor: TiptapEditor\n\tvalue: string\n\tonClose(): void\n}\n\n/** @public @react */\nexport function LinkEditor({ textEditor, value: initialValue, onClose }: LinkEditorProps) {\n\tconst editor = useEditor()\n\tconst [value, setValue] = useState(initialValue)\n\tconst msg = useTranslation()\n\tconst ref = useRef<HTMLInputElement>(null)\n\tconst trackEvent = useUiEvents()\n\tconst source = 'rich-text-menu'\n\tconst linkifiedValue = value.startsWith('http') ? value : `https://${value}`\n\n\tconst handleValueChange = (value: string) => setValue(value)\n\n\tconst handleLinkComplete = (link: string) => {\n\t\ttrackEvent('rich-text', { operation: 'link-edit', source })\n\t\tif (!link.startsWith('http://') && !link.startsWith('https://')) {\n\t\t\tlink = `https://${link}`\n\t\t}\n\n\t\ttextEditor.chain().setLink({ href: link }).run()\n\t\t// N.B. We shouldn't focus() on mobile because it causes the\n\t\t// Return key to replace the link with a newline :facepalm:\n\t\tif (editor.getInstanceState().isCoarsePointer) {\n\t\t\ttextEditor.commands.blur()\n\t\t} else {\n\t\t\ttextEditor.commands.focus()\n\t\t}\n\t\tonClose()\n\t}\n\n\tconst handleVisitLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-visit', source })\n\t\twindow.open(linkifiedValue, '_blank', 'noopener, noreferrer')\n\t\tonClose()\n\t}\n\n\tconst handleRemoveLink = () => {\n\t\ttrackEvent('rich-text', { operation: 'link-remove', source })\n\t\ttextEditor.chain().unsetLink().focus().run()\n\t\tonClose()\n\t}\n\n\tconst handleLinkCancel = () => onClose()\n\n\tuseEffect(() => {\n\t\tref.current?.focus()\n\t}, [value])\n\n\tuseEffect(() => {\n\t\tsetValue(initialValue)\n\t}, [initialValue])\n\n\treturn (\n\t\t<>\n\t\t\t<TldrawUiInput\n\t\t\t\tref={ref}\n\t\t\t\tdata-testid=\"rich-text.link-input\"\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-input\"\n\t\t\t\tvalue={value}\n\t\t\t\tonValueChange={handleValueChange}\n\t\t\t\tonComplete={handleLinkComplete}\n\t\t\t\tonCancel={handleLinkCancel}\n\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\taria-label=\"example.com\"\n\t\t\t/>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-visit\"\n\t\t\t\ttitle={msg('tool.rich-text-link-visit')}\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleVisitLink}\n\t\t\t\tdisabled={!value}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"external-link\" />\n\t\t\t</TldrawUiButton>\n\t\t\t<TldrawUiButton\n\t\t\t\tclassName=\"tlui-rich-text__toolbar-link-remove\"\n\t\t\t\ttitle={msg('tool.rich-text-link-remove')}\n\t\t\t\tdata-testid=\"rich-text.link-remove\"\n\t\t\t\ttype=\"icon\"\n\t\t\t\tonPointerDown={preventDefault}\n\t\t\t\tonClick={handleRemoveLink}\n\t\t\t>\n\t\t\t\t<TldrawUiButtonIcon small icon=\"trash\" />\n\t\t\t</TldrawUiButton>\n\t\t</>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAmEE,mBACC,KADD;AAnEF,SAAS,gBAA8B,iBAAiB;AACxD,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAUvB,SAAS,WAAW,EAAE,YAAY,OAAO,cAAc,QAAQ,GAAoB;AACzF,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,YAAY;AAC/C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,OAAyB,IAAI;AACzC,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS;AACf,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAAI,QAAQ,WAAW,KAAK;AAE1E,QAAM,oBAAoB,CAACA,WAAkB,SAASA,MAAK;AAE3D,QAAM,qBAAqB,CAAC,SAAiB;AAC5C,eAAW,aAAa,EAAE,WAAW,aAAa,OAAO,CAAC;AAC1D,QAAI,CAAC,KAAK,WAAW,SAAS,KAAK,CAAC,KAAK,WAAW,UAAU,GAAG;AAChE,aAAO,WAAW,IAAI;AAAA,IACvB;AAEA,eAAW,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC,EAAE,IAAI;AAG/C,QAAI,OAAO,iBAAiB,EAAE,iBAAiB;AAC9C,iBAAW,SAAS,KAAK;AAAA,IAC1B,OAAO;AACN,iBAAW,SAAS,MAAM;AAAA,IAC3B;AACA,YAAQ;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM;AAC7B,eAAW,aAAa,EAAE,WAAW,cAAc,OAAO,CAAC;AAC3D,WAAO,KAAK,gBAAgB,UAAU,sBAAsB;AAC5D,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM;AAC9B,eAAW,aAAa,EAAE,WAAW,eAAe,OAAO,CAAC;AAC5D,eAAW,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI;AAC3C,YAAQ;AAAA,EACT;AAEA,QAAM,mBAAmB,MAAM,QAAQ;AAEvC,YAAU,MAAM;AACf,QAAI,SAAS,MAAM;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACf,aAAS,YAAY;AAAA,EACtB,GAAG,CAAC,YAAY,CAAC;AAEjB,SACC,iCACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACA;AAAA,QACA,eAAY;AAAA,QACZ,WAAU;AAAA,QACV;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,cAAW;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,2BAA2B;AAAA,QACtC,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QAEX,8BAAC,sBAAmB,OAAK,MAAC,MAAK,iBAAgB;AAAA;AAAA,IAChD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,OAAO,IAAI,4BAA4B;AAAA,QACvC,eAAY;AAAA,QACZ,MAAK;AAAA,QACL,eAAe;AAAA,QACf,SAAS;AAAA,QAET,8BAAC,sBAAmB,OAAK,MAAC,MAAK,SAAQ;AAAA;AAAA,IACxC;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "4.2.0-canary.
|
|
1
|
+
const version = "4.2.0-canary.5887eb336025";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2025-09-18T14:39:22.803Z",
|
|
4
|
-
minor: "2025-10-
|
|
5
|
-
patch: "2025-10-
|
|
4
|
+
minor: "2025-10-16T09:29:09.234Z",
|
|
5
|
+
patch: "2025-10-16T09:29:09.234Z"
|
|
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.
|
|
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.5887eb336025'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-10-16T09:29:09.234Z',\n\tpatch: '2025-10-16T09:29:09.234Z',\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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OAEM;AACP,OAAO,UAAU;AACjB,OAAO,eAAe;
|
|
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.
|
|
4
|
+
"version": "4.2.0-canary.5887eb336025",
|
|
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": "
|
|
59
|
-
"@tiptap/extension-code": "
|
|
60
|
-
"@tiptap/extension-highlight": "
|
|
61
|
-
"@tiptap/extension-
|
|
62
|
-
"@tiptap/pm": "
|
|
63
|
-
"@tiptap/react": "
|
|
64
|
-
"@tiptap/starter-kit": "
|
|
65
|
-
"@tldraw/editor": "4.2.0-canary.
|
|
66
|
-
"@tldraw/store": "4.2.0-canary.
|
|
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-canary.5887eb336025",
|
|
66
|
+
"@tldraw/store": "4.2.0-canary.5887eb336025",
|
|
67
67
|
"classnames": "^2.5.1",
|
|
68
68
|
"hotkeys-js": "^3.13.9",
|
|
69
69
|
"idb": "^7.1.1",
|
|
@@ -117,7 +117,9 @@ function useEditingLinkBehavior(textEditor?: TiptapEditor) {
|
|
|
117
117
|
|
|
118
118
|
textEditor.view.dom.addEventListener('click', handleClick)
|
|
119
119
|
return () => {
|
|
120
|
-
textEditor.
|
|
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.
|
|
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.
|
|
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) {
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -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.
|
|
4
|
+
export const version = '4.2.0-canary.5887eb336025'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
|
-
minor: '2025-10-
|
|
8
|
-
patch: '2025-10-
|
|
7
|
+
minor: '2025-10-16T09:29:09.234Z',
|
|
8
|
+
patch: '2025-10-16T09:29:09.234Z',
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
link: {
|
|
56
|
+
openOnClick: false,
|
|
57
|
+
autolink: true,
|
|
58
|
+
},
|
|
59
59
|
}),
|
|
60
60
|
Highlight,
|
|
61
61
|
KeyboardShiftEnterTweakExtension,
|