@tldraw/editor 3.13.0-canary.c3ce2eeb1729 → 3.13.0-canary.c4135f4903f5

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.
@@ -5747,7 +5747,6 @@ export declare class TextManager {
5747
5747
  fontWeight: string;
5748
5748
  lineHeight: number;
5749
5749
  minWidth?: null | number;
5750
- otherStyles?: Record<string, string>;
5751
5750
  padding: string;
5752
5751
  }): BoxModel & {
5753
5752
  scrollWidth: number;
@@ -6786,7 +6785,6 @@ export declare interface TLMeasureTextSpanOpts {
6786
6785
  fontStyle: string;
6787
6786
  lineHeight: number;
6788
6787
  textAlign: TLDefaultHorizontalAlignStyle;
6789
- otherStyles?: Record<string, string>;
6790
6788
  }
6791
6789
 
6792
6790
  /** @public */
package/dist-cjs/index.js CHANGED
@@ -376,7 +376,7 @@ function debugEnableLicensing() {
376
376
  }
377
377
  (0, import_utils.registerTldrawLibraryVersion)(
378
378
  "@tldraw/editor",
379
- "3.13.0-canary.c3ce2eeb1729",
379
+ "3.13.0-canary.c4135f4903f5",
380
380
  "cjs"
381
381
  );
382
382
  //# sourceMappingURL=index.js.map
@@ -67,11 +67,6 @@ class TextManager {
67
67
  "overflow-wrap",
68
68
  opts.disableOverflowWrapBreaking ? "normal" : "break-word"
69
69
  );
70
- if (opts.otherStyles) {
71
- for (const [key, value] of Object.entries(opts.otherStyles)) {
72
- wrapperElm.style.setProperty(key, value);
73
- }
74
- }
75
70
  const scrollWidth = wrapperElm.scrollWidth;
76
71
  const rect = wrapperElm.getBoundingClientRect();
77
72
  wrapperElm.remove();
@@ -173,11 +168,6 @@ class TextManager {
173
168
  elm.style.setProperty("line-height", `${opts.lineHeight * opts.fontSize}px`);
174
169
  elm.style.setProperty("text-align", textAlignmentsForLtr[opts.textAlign]);
175
170
  elm.style.setProperty("font-style", opts.fontStyle);
176
- if (opts.otherStyles) {
177
- for (const [key, value] of Object.entries(opts.otherStyles)) {
178
- elm.style.setProperty(key, value);
179
- }
180
- }
181
171
  const shouldTruncateToFirstLine = opts.overflow === "truncate-ellipsis" || opts.overflow === "truncate-clip";
182
172
  if (shouldTruncateToFirstLine) {
183
173
  elm.style.setProperty("overflow-wrap", "anywhere");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/editor/managers/TextManager.ts"],
4
- "sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n}\n\nconst spaceCharacterRegex = /\\s/\n\n/** @public */\nexport class TextManager {\n\tprivate baseElem: HTMLDivElement\n\n\tconstructor(public editor: Editor) {\n\t\tthis.baseElem = document.createElement('div')\n\t\tthis.baseElem.classList.add('tl-text')\n\t\tthis.baseElem.classList.add('tl-text-measure')\n\t\tthis.baseElem.tabIndex = -1\n\t}\n\n\tmeasureText(\n\t\ttextToMeasure: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\tconst div = document.createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(\n\t\thtml: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\totherStyles?: Record<string, string>\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\t// Duplicate our base element; we don't need to clone deep\n\t\tconst wrapperElm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(wrapperElm)\n\t\twrapperElm.innerHTML = html\n\t\tthis.baseElem.insertAdjacentElement('afterend', wrapperElm)\n\n\t\twrapperElm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\twrapperElm.style.setProperty('unicode-bidi', 'plaintext')\n\t\twrapperElm.style.setProperty('font-family', opts.fontFamily)\n\t\twrapperElm.style.setProperty('font-style', opts.fontStyle)\n\t\twrapperElm.style.setProperty('font-weight', opts.fontWeight)\n\t\twrapperElm.style.setProperty('font-size', opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('line-height', opts.lineHeight * opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('max-width', opts.maxWidth === null ? null : opts.maxWidth + 'px')\n\t\twrapperElm.style.setProperty('min-width', opts.minWidth === null ? null : opts.minWidth + 'px')\n\t\twrapperElm.style.setProperty('padding', opts.padding)\n\t\twrapperElm.style.setProperty(\n\t\t\t'overflow-wrap',\n\t\t\topts.disableOverflowWrapBreaking ? 'normal' : 'break-word'\n\t\t)\n\t\tif (opts.otherStyles) {\n\t\t\tfor (const [key, value] of Object.entries(opts.otherStyles)) {\n\t\t\t\twrapperElm.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\n\t\tconst scrollWidth = wrapperElm.scrollWidth\n\t\tconst rect = wrapperElm.getBoundingClientRect()\n\t\twrapperElm.remove()\n\n\t\treturn {\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\tw: rect.width,\n\t\t\th: rect.height,\n\t\t\tscrollWidth,\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst elm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(elm)\n\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\telm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\telm.style.setProperty('unicode-bidi', 'plaintext')\n\t\telm.style.setProperty('width', `${elementWidth}px`)\n\t\telm.style.setProperty('height', 'min-content')\n\t\telm.style.setProperty('font-size', `${opts.fontSize}px`)\n\t\telm.style.setProperty('font-family', opts.fontFamily)\n\t\telm.style.setProperty('font-weight', opts.fontWeight)\n\t\telm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)\n\t\telm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])\n\t\telm.style.setProperty('font-style', opts.fontStyle)\n\t\tif (opts.otherStyles) {\n\t\t\tfor (const [key, value] of Object.entries(opts.otherStyles)) {\n\t\t\t\telm.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\n\t\tif (shouldTruncateToFirstLine) {\n\t\t\telm.style.setProperty('overflow-wrap', 'anywhere')\n\t\t\telm.style.setProperty('word-break', 'break-all')\n\t\t}\n\n\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t// Render the text into the measurement element:\n\t\telm.textContent = normalizedText\n\n\t\t// actually measure the text:\n\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\tshouldTruncateToFirstLine,\n\t\t})\n\n\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\telm.textContent = '\u2026'\n\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\telm.textContent = normalizedText\n\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t}).spans\n\n\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t// browser.\n\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\ttruncatedSpans.push({\n\t\t\t\ttext: '\u2026',\n\t\t\t\tbox: {\n\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn truncatedSpans\n\t\t}\n\n\t\telm.remove()\n\n\t\treturn spans\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n}\n\nconst spaceCharacterRegex = /\\s/\n\n/** @public */\nexport class TextManager {\n\tprivate baseElem: HTMLDivElement\n\n\tconstructor(public editor: Editor) {\n\t\tthis.baseElem = document.createElement('div')\n\t\tthis.baseElem.classList.add('tl-text')\n\t\tthis.baseElem.classList.add('tl-text-measure')\n\t\tthis.baseElem.tabIndex = -1\n\t}\n\n\tmeasureText(\n\t\ttextToMeasure: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\tconst div = document.createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(\n\t\thtml: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\t// Duplicate our base element; we don't need to clone deep\n\t\tconst wrapperElm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(wrapperElm)\n\t\twrapperElm.innerHTML = html\n\t\tthis.baseElem.insertAdjacentElement('afterend', wrapperElm)\n\n\t\twrapperElm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\twrapperElm.style.setProperty('unicode-bidi', 'plaintext')\n\t\twrapperElm.style.setProperty('font-family', opts.fontFamily)\n\t\twrapperElm.style.setProperty('font-style', opts.fontStyle)\n\t\twrapperElm.style.setProperty('font-weight', opts.fontWeight)\n\t\twrapperElm.style.setProperty('font-size', opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('line-height', opts.lineHeight * opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('max-width', opts.maxWidth === null ? null : opts.maxWidth + 'px')\n\t\twrapperElm.style.setProperty('min-width', opts.minWidth === null ? null : opts.minWidth + 'px')\n\t\twrapperElm.style.setProperty('padding', opts.padding)\n\t\twrapperElm.style.setProperty(\n\t\t\t'overflow-wrap',\n\t\t\topts.disableOverflowWrapBreaking ? 'normal' : 'break-word'\n\t\t)\n\n\t\tconst scrollWidth = wrapperElm.scrollWidth\n\t\tconst rect = wrapperElm.getBoundingClientRect()\n\t\twrapperElm.remove()\n\n\t\treturn {\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\tw: rect.width,\n\t\t\th: rect.height,\n\t\t\tscrollWidth,\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst elm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(elm)\n\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\telm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\telm.style.setProperty('unicode-bidi', 'plaintext')\n\t\telm.style.setProperty('width', `${elementWidth}px`)\n\t\telm.style.setProperty('height', 'min-content')\n\t\telm.style.setProperty('font-size', `${opts.fontSize}px`)\n\t\telm.style.setProperty('font-family', opts.fontFamily)\n\t\telm.style.setProperty('font-weight', opts.fontWeight)\n\t\telm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)\n\t\telm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])\n\t\telm.style.setProperty('font-style', opts.fontStyle)\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\n\t\tif (shouldTruncateToFirstLine) {\n\t\t\telm.style.setProperty('overflow-wrap', 'anywhere')\n\t\t\telm.style.setProperty('word-break', 'break-all')\n\t\t}\n\n\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t// Render the text into the measurement element:\n\t\telm.textContent = normalizedText\n\n\t\t// actually measure the text:\n\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\tshouldTruncateToFirstLine,\n\t\t})\n\n\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\telm.textContent = '\u2026'\n\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\telm.textContent = normalizedText\n\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t}).spans\n\n\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t// browser.\n\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\ttruncatedSpans.push({\n\t\t\t\ttext: '\u2026',\n\t\t\t\tbox: {\n\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn truncatedSpans\n\t\t}\n\n\t\telm.remove()\n\n\t\treturn spans\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
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 = "3.13.0-canary.c3ce2eeb1729";
25
+ const version = "3.13.0-canary.c4135f4903f5";
26
26
  const publishDates = {
27
27
  major: "2024-09-13T14:36:29.063Z",
28
- minor: "2025-05-07T09:23:48.217Z",
29
- patch: "2025-05-07T09:23:48.217Z"
28
+ minor: "2025-05-06T16:30:40.094Z",
29
+ patch: "2025-05-06T16:30:40.094Z"
30
30
  };
31
31
  //# sourceMappingURL=version.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/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 = '3.13.0-canary.c3ce2eeb1729'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-07T09:23:48.217Z',\n\tpatch: '2025-05-07T09:23:48.217Z',\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 = '3.13.0-canary.c4135f4903f5'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-06T16:30:40.094Z',\n\tpatch: '2025-05-06T16:30:40.094Z',\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
  }
@@ -5747,7 +5747,6 @@ export declare class TextManager {
5747
5747
  fontWeight: string;
5748
5748
  lineHeight: number;
5749
5749
  minWidth?: null | number;
5750
- otherStyles?: Record<string, string>;
5751
5750
  padding: string;
5752
5751
  }): BoxModel & {
5753
5752
  scrollWidth: number;
@@ -6786,7 +6785,6 @@ export declare interface TLMeasureTextSpanOpts {
6786
6785
  fontStyle: string;
6787
6786
  lineHeight: number;
6788
6787
  textAlign: TLDefaultHorizontalAlignStyle;
6789
- otherStyles?: Record<string, string>;
6790
6788
  }
6791
6789
 
6792
6790
  /** @public */
@@ -303,7 +303,7 @@ function debugEnableLicensing() {
303
303
  }
304
304
  registerTldrawLibraryVersion(
305
305
  "@tldraw/editor",
306
- "3.13.0-canary.c3ce2eeb1729",
306
+ "3.13.0-canary.c4135f4903f5",
307
307
  "esm"
308
308
  );
309
309
  export {
@@ -44,11 +44,6 @@ class TextManager {
44
44
  "overflow-wrap",
45
45
  opts.disableOverflowWrapBreaking ? "normal" : "break-word"
46
46
  );
47
- if (opts.otherStyles) {
48
- for (const [key, value] of Object.entries(opts.otherStyles)) {
49
- wrapperElm.style.setProperty(key, value);
50
- }
51
- }
52
47
  const scrollWidth = wrapperElm.scrollWidth;
53
48
  const rect = wrapperElm.getBoundingClientRect();
54
49
  wrapperElm.remove();
@@ -150,11 +145,6 @@ class TextManager {
150
145
  elm.style.setProperty("line-height", `${opts.lineHeight * opts.fontSize}px`);
151
146
  elm.style.setProperty("text-align", textAlignmentsForLtr[opts.textAlign]);
152
147
  elm.style.setProperty("font-style", opts.fontStyle);
153
- if (opts.otherStyles) {
154
- for (const [key, value] of Object.entries(opts.otherStyles)) {
155
- elm.style.setProperty(key, value);
156
- }
157
- }
158
148
  const shouldTruncateToFirstLine = opts.overflow === "truncate-ellipsis" || opts.overflow === "truncate-clip";
159
149
  if (shouldTruncateToFirstLine) {
160
150
  elm.style.setProperty("overflow-wrap", "anywhere");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/editor/managers/TextManager.ts"],
4
- "sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n}\n\nconst spaceCharacterRegex = /\\s/\n\n/** @public */\nexport class TextManager {\n\tprivate baseElem: HTMLDivElement\n\n\tconstructor(public editor: Editor) {\n\t\tthis.baseElem = document.createElement('div')\n\t\tthis.baseElem.classList.add('tl-text')\n\t\tthis.baseElem.classList.add('tl-text-measure')\n\t\tthis.baseElem.tabIndex = -1\n\t}\n\n\tmeasureText(\n\t\ttextToMeasure: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\tconst div = document.createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(\n\t\thtml: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\totherStyles?: Record<string, string>\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\t// Duplicate our base element; we don't need to clone deep\n\t\tconst wrapperElm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(wrapperElm)\n\t\twrapperElm.innerHTML = html\n\t\tthis.baseElem.insertAdjacentElement('afterend', wrapperElm)\n\n\t\twrapperElm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\twrapperElm.style.setProperty('unicode-bidi', 'plaintext')\n\t\twrapperElm.style.setProperty('font-family', opts.fontFamily)\n\t\twrapperElm.style.setProperty('font-style', opts.fontStyle)\n\t\twrapperElm.style.setProperty('font-weight', opts.fontWeight)\n\t\twrapperElm.style.setProperty('font-size', opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('line-height', opts.lineHeight * opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('max-width', opts.maxWidth === null ? null : opts.maxWidth + 'px')\n\t\twrapperElm.style.setProperty('min-width', opts.minWidth === null ? null : opts.minWidth + 'px')\n\t\twrapperElm.style.setProperty('padding', opts.padding)\n\t\twrapperElm.style.setProperty(\n\t\t\t'overflow-wrap',\n\t\t\topts.disableOverflowWrapBreaking ? 'normal' : 'break-word'\n\t\t)\n\t\tif (opts.otherStyles) {\n\t\t\tfor (const [key, value] of Object.entries(opts.otherStyles)) {\n\t\t\t\twrapperElm.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\n\t\tconst scrollWidth = wrapperElm.scrollWidth\n\t\tconst rect = wrapperElm.getBoundingClientRect()\n\t\twrapperElm.remove()\n\n\t\treturn {\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\tw: rect.width,\n\t\t\th: rect.height,\n\t\t\tscrollWidth,\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst elm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(elm)\n\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\telm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\telm.style.setProperty('unicode-bidi', 'plaintext')\n\t\telm.style.setProperty('width', `${elementWidth}px`)\n\t\telm.style.setProperty('height', 'min-content')\n\t\telm.style.setProperty('font-size', `${opts.fontSize}px`)\n\t\telm.style.setProperty('font-family', opts.fontFamily)\n\t\telm.style.setProperty('font-weight', opts.fontWeight)\n\t\telm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)\n\t\telm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])\n\t\telm.style.setProperty('font-style', opts.fontStyle)\n\t\tif (opts.otherStyles) {\n\t\t\tfor (const [key, value] of Object.entries(opts.otherStyles)) {\n\t\t\t\telm.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\n\t\tif (shouldTruncateToFirstLine) {\n\t\t\telm.style.setProperty('overflow-wrap', 'anywhere')\n\t\t\telm.style.setProperty('word-break', 'break-all')\n\t\t}\n\n\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t// Render the text into the measurement element:\n\t\telm.textContent = normalizedText\n\n\t\t// actually measure the text:\n\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\tshouldTruncateToFirstLine,\n\t\t})\n\n\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\telm.textContent = '\u2026'\n\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\telm.textContent = normalizedText\n\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t}).spans\n\n\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t// browser.\n\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\ttruncatedSpans.push({\n\t\t\t\ttext: '\u2026',\n\t\t\t\tbox: {\n\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn truncatedSpans\n\t\t}\n\n\t\telm.remove()\n\n\t\treturn spans\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n}\n\nconst spaceCharacterRegex = /\\s/\n\n/** @public */\nexport class TextManager {\n\tprivate baseElem: HTMLDivElement\n\n\tconstructor(public editor: Editor) {\n\t\tthis.baseElem = document.createElement('div')\n\t\tthis.baseElem.classList.add('tl-text')\n\t\tthis.baseElem.classList.add('tl-text-measure')\n\t\tthis.baseElem.tabIndex = -1\n\t}\n\n\tmeasureText(\n\t\ttextToMeasure: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\tconst div = document.createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(\n\t\thtml: string,\n\t\topts: {\n\t\t\tfontStyle: string\n\t\t\tfontWeight: string\n\t\t\tfontFamily: string\n\t\t\tfontSize: number\n\t\t\tlineHeight: number\n\t\t\t/**\n\t\t\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t\t\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t\t\t * space are preserved.\n\t\t\t */\n\t\t\tmaxWidth: null | number\n\t\t\tminWidth?: null | number\n\t\t\tpadding: string\n\t\t\tdisableOverflowWrapBreaking?: boolean\n\t\t}\n\t): BoxModel & { scrollWidth: number } {\n\t\t// Duplicate our base element; we don't need to clone deep\n\t\tconst wrapperElm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(wrapperElm)\n\t\twrapperElm.innerHTML = html\n\t\tthis.baseElem.insertAdjacentElement('afterend', wrapperElm)\n\n\t\twrapperElm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\twrapperElm.style.setProperty('unicode-bidi', 'plaintext')\n\t\twrapperElm.style.setProperty('font-family', opts.fontFamily)\n\t\twrapperElm.style.setProperty('font-style', opts.fontStyle)\n\t\twrapperElm.style.setProperty('font-weight', opts.fontWeight)\n\t\twrapperElm.style.setProperty('font-size', opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('line-height', opts.lineHeight * opts.fontSize + 'px')\n\t\twrapperElm.style.setProperty('max-width', opts.maxWidth === null ? null : opts.maxWidth + 'px')\n\t\twrapperElm.style.setProperty('min-width', opts.minWidth === null ? null : opts.minWidth + 'px')\n\t\twrapperElm.style.setProperty('padding', opts.padding)\n\t\twrapperElm.style.setProperty(\n\t\t\t'overflow-wrap',\n\t\t\topts.disableOverflowWrapBreaking ? 'normal' : 'break-word'\n\t\t)\n\n\t\tconst scrollWidth = wrapperElm.scrollWidth\n\t\tconst rect = wrapperElm.getBoundingClientRect()\n\t\twrapperElm.remove()\n\n\t\treturn {\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\tw: rect.width,\n\t\t\th: rect.height,\n\t\t\tscrollWidth,\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst elm = this.baseElem.cloneNode() as HTMLDivElement\n\t\tthis.editor.getContainer().appendChild(elm)\n\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\telm.setAttribute('dir', 'auto')\n\t\t// N.B. This property, while discouraged (\"intended for Document Type Definition (DTD) designers\")\n\t\t// is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs.\n\t\telm.style.setProperty('unicode-bidi', 'plaintext')\n\t\telm.style.setProperty('width', `${elementWidth}px`)\n\t\telm.style.setProperty('height', 'min-content')\n\t\telm.style.setProperty('font-size', `${opts.fontSize}px`)\n\t\telm.style.setProperty('font-family', opts.fontFamily)\n\t\telm.style.setProperty('font-weight', opts.fontWeight)\n\t\telm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)\n\t\telm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])\n\t\telm.style.setProperty('font-style', opts.fontStyle)\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\n\t\tif (shouldTruncateToFirstLine) {\n\t\t\telm.style.setProperty('overflow-wrap', 'anywhere')\n\t\t\telm.style.setProperty('word-break', 'break-all')\n\t\t}\n\n\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t// Render the text into the measurement element:\n\t\telm.textContent = normalizedText\n\n\t\t// actually measure the text:\n\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\tshouldTruncateToFirstLine,\n\t\t})\n\n\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\telm.textContent = '\u2026'\n\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\telm.textContent = normalizedText\n\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t}).spans\n\n\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t// browser.\n\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\ttruncatedSpans.push({\n\t\t\t\ttext: '\u2026',\n\t\t\t\tbox: {\n\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn truncatedSpans\n\t\t}\n\n\t\telm.remove()\n\n\t\treturn spans\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "3.13.0-canary.c3ce2eeb1729";
1
+ const version = "3.13.0-canary.c4135f4903f5";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-05-07T09:23:48.217Z",
5
- patch: "2025-05-07T09:23:48.217Z"
4
+ minor: "2025-05-06T16:30:40.094Z",
5
+ patch: "2025-05-06T16:30:40.094Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/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 = '3.13.0-canary.c3ce2eeb1729'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-07T09:23:48.217Z',\n\tpatch: '2025-05-07T09:23:48.217Z',\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 = '3.13.0-canary.c4135f4903f5'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-06T16:30:40.094Z',\n\tpatch: '2025-05-06T16:30:40.094Z',\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
  }
package/editor.css CHANGED
@@ -969,8 +969,6 @@ input,
969
969
 
970
970
  .tl-rich-text p {
971
971
  margin: 0;
972
- /* Depending on the extensions, <p> tags can be empty, without a <br />. */
973
- min-height: 1lh;
974
972
  }
975
973
 
976
974
  .tl-rich-text ul,
@@ -978,8 +976,6 @@ input,
978
976
  text-align: left;
979
977
  margin: 0;
980
978
  padding-left: 3.25ch;
981
- /* Some resets, like Tailwind, nix the list styling. */
982
- list-style: revert;
983
979
  }
984
980
 
985
981
  .tl-rich-text ol:has(> li:nth-child(10)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.13.0-canary.c3ce2eeb1729",
4
+ "version": "3.13.0-canary.c4135f4903f5",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -48,12 +48,12 @@
48
48
  "@tiptap/core": "^2.9.1",
49
49
  "@tiptap/pm": "^2.9.1",
50
50
  "@tiptap/react": "^2.9.1",
51
- "@tldraw/state": "3.13.0-canary.c3ce2eeb1729",
52
- "@tldraw/state-react": "3.13.0-canary.c3ce2eeb1729",
53
- "@tldraw/store": "3.13.0-canary.c3ce2eeb1729",
54
- "@tldraw/tlschema": "3.13.0-canary.c3ce2eeb1729",
55
- "@tldraw/utils": "3.13.0-canary.c3ce2eeb1729",
56
- "@tldraw/validate": "3.13.0-canary.c3ce2eeb1729",
51
+ "@tldraw/state": "3.13.0-canary.c4135f4903f5",
52
+ "@tldraw/state-react": "3.13.0-canary.c4135f4903f5",
53
+ "@tldraw/store": "3.13.0-canary.c4135f4903f5",
54
+ "@tldraw/tlschema": "3.13.0-canary.c4135f4903f5",
55
+ "@tldraw/utils": "3.13.0-canary.c4135f4903f5",
56
+ "@tldraw/validate": "3.13.0-canary.c4135f4903f5",
57
57
  "@types/core-js": "^2.5.8",
58
58
  "@use-gesture/react": "^10.3.1",
59
59
  "classnames": "^2.5.1",
@@ -32,7 +32,6 @@ export interface TLMeasureTextSpanOpts {
32
32
  fontStyle: string
33
33
  lineHeight: number
34
34
  textAlign: TLDefaultHorizontalAlignStyle
35
- otherStyles?: Record<string, string>
36
35
  }
37
36
 
38
37
  const spaceCharacterRegex = /\s/
@@ -87,7 +86,6 @@ export class TextManager {
87
86
  */
88
87
  maxWidth: null | number
89
88
  minWidth?: null | number
90
- otherStyles?: Record<string, string>
91
89
  padding: string
92
90
  disableOverflowWrapBreaking?: boolean
93
91
  }
@@ -114,11 +112,6 @@ export class TextManager {
114
112
  'overflow-wrap',
115
113
  opts.disableOverflowWrapBreaking ? 'normal' : 'break-word'
116
114
  )
117
- if (opts.otherStyles) {
118
- for (const [key, value] of Object.entries(opts.otherStyles)) {
119
- wrapperElm.style.setProperty(key, value)
120
- }
121
- }
122
115
 
123
116
  const scrollWidth = wrapperElm.scrollWidth
124
117
  const rect = wrapperElm.getBoundingClientRect()
@@ -263,11 +256,6 @@ export class TextManager {
263
256
  elm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)
264
257
  elm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])
265
258
  elm.style.setProperty('font-style', opts.fontStyle)
266
- if (opts.otherStyles) {
267
- for (const [key, value] of Object.entries(opts.otherStyles)) {
268
- elm.style.setProperty(key, value)
269
- }
270
- }
271
259
 
272
260
  const shouldTruncateToFirstLine =
273
261
  opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'
package/src/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 = '3.13.0-canary.c3ce2eeb1729'
4
+ export const version = '3.13.0-canary.c4135f4903f5'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-05-07T09:23:48.217Z',
8
- patch: '2025-05-07T09:23:48.217Z',
7
+ minor: '2025-05-06T16:30:40.094Z',
8
+ patch: '2025-05-06T16:30:40.094Z',
9
9
  }