tldraw 4.3.0-canary.b2e9b1218a6b → 4.3.0-canary.b9cf1eed518f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist-cjs/index.d.ts +9 -0
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +4 -2
  4. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  5. package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
  6. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +7 -2
  7. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  8. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +1 -0
  9. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
  11. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
  12. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +11 -3
  13. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
  14. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
  15. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/ui/version.js +3 -3
  17. package/dist-cjs/lib/ui/version.js.map +1 -1
  18. package/dist-esm/index.d.mts +9 -0
  19. package/dist-esm/index.mjs +1 -1
  20. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +4 -2
  21. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  22. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +7 -2
  23. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  24. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +1 -0
  25. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  26. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
  27. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  28. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +11 -3
  29. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  30. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
  31. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  32. package/dist-esm/lib/ui/version.mjs +3 -3
  33. package/dist-esm/lib/ui/version.mjs.map +1 -1
  34. package/package.json +3 -3
  35. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +4 -1
  36. package/src/lib/shapes/arrow/arrow-types.ts +2 -0
  37. package/src/lib/shapes/geo/GeoShapeUtil.tsx +6 -0
  38. package/src/lib/shapes/note/NoteShapeUtil.tsx +1 -0
  39. package/src/lib/shapes/shared/PlainTextLabel.tsx +10 -1
  40. package/src/lib/shapes/shared/RichTextLabel.tsx +11 -2
  41. package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
  42. package/src/lib/ui/version.ts +3 -3
  43. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
  44. package/tldraw.css +8 -4
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/note/NoteShapeUtil.tsx"],
4
- "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEMPTY_ARRAY,\n\tEditor,\n\tGroup2d,\n\tIndexKey,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLHandle,\n\tTLNoteShape,\n\tTLNoteShapeProps,\n\tTLResizeInfo,\n\tTLShape,\n\tTLShapeId,\n\tVec,\n\tWeakCache,\n\texhaustiveSwitchError,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tlerp,\n\tnoteShapeMigrations,\n\tnoteShapeProps,\n\tresizeScaled,\n\trng,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { useCallback, useContext } from 'react'\nimport { startEditingShapeWithLabel } from '../../tools/SelectTool/selectHelpers'\nimport { TranslationsContext } from '../../ui/hooks/useTranslation/useTranslation'\nimport {\n\tisEmptyRichText,\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { isRightToLeftLanguage } from '../../utils/text/text'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport {\n\tFONT_FAMILIES,\n\tLABEL_FONT_SIZES,\n\tLABEL_PADDING,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\nimport { useIsReadyForEditing } from '../shared/useEditablePlainText'\nimport { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'\nimport {\n\tCLONE_HANDLE_MARGIN,\n\tNOTE_CENTER_OFFSET,\n\tNOTE_SIZE,\n\tgetNoteShapeForAdjacentPosition,\n} from './noteHelpers'\n\n/** @public */\nexport interface NoteShapeOptions {\n\t/**\n\t * How should the note shape resize? By default it does not resize (except automatically based on its text content),\n\t * but you can set it to be user-resizable using scale.\n\t */\n\tresizeMode: 'none' | 'scale'\n}\n\n/** @public */\nexport class NoteShapeUtil extends ShapeUtil<TLNoteShape> {\n\tstatic override type = 'note' as const\n\tstatic override props = noteShapeProps\n\tstatic override migrations = noteShapeMigrations\n\n\toverride options: NoteShapeOptions = {\n\t\tresizeMode: 'none',\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\toverride hideResizeHandles() {\n\t\tconst { resizeMode } = this.options\n\t\tswitch (resizeMode) {\n\t\t\tcase 'none': {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcase 'scale': {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthrow exhaustiveSwitchError(resizeMode)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn this.options.resizeMode === 'scale'\n\t}\n\n\toverride hideSelectionBoundsFg() {\n\t\treturn false\n\t}\n\n\tgetDefaultProps(): TLNoteShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\trichText: toRichText(''),\n\t\t\tsize: 'm',\n\t\t\tfont: 'draw',\n\t\t\talign: 'middle',\n\t\t\tverticalAlign: 'middle',\n\t\t\tlabelColor: 'black',\n\t\t\tgrowY: 0,\n\t\t\tfontSizeAdjustment: 0,\n\t\t\turl: '',\n\t\t\tscale: 1,\n\t\t}\n\t}\n\n\tgetGeometry(shape: TLNoteShape) {\n\t\tconst { labelHeight, labelWidth } = getLabelSize(this.editor, shape)\n\t\tconst { scale } = shape.props\n\n\t\tconst lh = labelHeight * scale\n\t\tconst lw = labelWidth * scale\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst nh = getNoteHeight(shape)\n\n\t\treturn new Group2d({\n\t\t\tchildren: [\n\t\t\t\tnew Rectangle2d({ width: nw, height: nh, isFilled: true }),\n\t\t\t\tnew Rectangle2d({\n\t\t\t\t\tx:\n\t\t\t\t\t\tshape.props.align === 'start'\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: shape.props.align === 'end'\n\t\t\t\t\t\t\t\t? nw - lw\n\t\t\t\t\t\t\t\t: (nw - lw) / 2,\n\t\t\t\t\ty:\n\t\t\t\t\t\tshape.props.verticalAlign === 'start'\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: shape.props.verticalAlign === 'end'\n\t\t\t\t\t\t\t\t? nh - lh\n\t\t\t\t\t\t\t\t: (nh - lh) / 2,\n\t\t\t\t\twidth: lw,\n\t\t\t\t\theight: lh,\n\t\t\t\t\tisFilled: true,\n\t\t\t\t\tisLabel: true,\n\t\t\t\t\texcludeFromShapeBounds: true,\n\t\t\t\t}),\n\t\t\t],\n\t\t})\n\t}\n\n\toverride getHandles(shape: TLNoteShape): TLHandle[] {\n\t\tconst { scale } = shape.props\n\t\tconst isCoarsePointer = this.editor.getInstanceState().isCoarsePointer\n\t\tif (isCoarsePointer) return []\n\n\t\tconst zoom = this.editor.getEfficientZoomLevel()\n\t\tif (zoom * scale < 0.25) return []\n\n\t\tconst nh = getNoteHeight(shape)\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst offset = (CLONE_HANDLE_MARGIN / zoom) * scale\n\n\t\tif (zoom * scale < 0.5) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tid: 'bottom',\n\t\t\t\t\tindex: 'a3' as IndexKey,\n\t\t\t\t\ttype: 'clone',\n\t\t\t\t\tx: nw / 2,\n\t\t\t\t\ty: nh + offset,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'top',\n\t\t\t\tindex: 'a1' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw / 2,\n\t\t\t\ty: -offset,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'right',\n\t\t\t\tindex: 'a2' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw + offset,\n\t\t\t\ty: nh / 2,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'bottom',\n\t\t\t\tindex: 'a3' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw / 2,\n\t\t\t\ty: nh + offset,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'left',\n\t\t\t\tindex: 'a4' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: -offset,\n\t\t\t\ty: nh / 2,\n\t\t\t},\n\t\t]\n\t}\n\n\toverride onResize(shape: any, info: TLResizeInfo<any>) {\n\t\tconst { resizeMode } = this.options\n\t\tswitch (resizeMode) {\n\t\t\tcase 'none': {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tcase 'scale': {\n\t\t\t\treturn resizeScaled(shape, info)\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthrow exhaustiveSwitchError(resizeMode)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride getText(shape: TLNoteShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride getFontFaces(shape: TLNoteShape) {\n\t\tif (isEmptyRichText(shape.props.richText)) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\tcomponent(shape: TLNoteShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\ttype,\n\t\t\tprops: {\n\t\t\t\tlabelColor,\n\t\t\t\tscale,\n\t\t\t\tcolor,\n\t\t\t\tfont,\n\t\t\t\tsize,\n\t\t\t\talign,\n\t\t\t\trichText,\n\t\t\t\tverticalAlign,\n\t\t\t\tfontSizeAdjustment,\n\t\t\t},\n\t\t} = shape\n\n\t\tconst handleKeyDown = useNoteKeydownHandler(id)\n\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst nh = getNoteHeight(shape)\n\n\t\tconst rotation = useValue(\n\t\t\t'shape rotation',\n\t\t\t() => this.editor.getShapePageTransform(id)?.rotation() ?? 0,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst isDarkMode = useValue('dark mode', () => this.editor.user.getIsDarkMode(), [this.editor])\n\n\t\t// Shadows are hidden when zoomed out far enough or in dark mode\n\t\tlet hideShadows = useEfficientZoomThreshold(scale * 0.25)\n\t\tif (isDarkMode) hideShadows = true\n\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\n\t\tconst isReadyForEditing = useIsReadyForEditing(this.editor, shape.id)\n\t\tconst isEmpty = isEmptyRichText(richText)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<div\n\t\t\t\t\tid={id}\n\t\t\t\t\tclassName=\"tl-note__container\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: nw,\n\t\t\t\t\t\theight: nh,\n\t\t\t\t\t\tbackgroundColor: getColorValue(theme, color, 'noteFill'),\n\t\t\t\t\t\tborderBottom: hideShadows\n\t\t\t\t\t\t\t? isDarkMode\n\t\t\t\t\t\t\t\t? `${2 * scale}px solid rgb(20, 20, 20)`\n\t\t\t\t\t\t\t\t: `${2 * scale}px solid rgb(144, 144, 144)`\n\t\t\t\t\t\t\t: 'none',\n\t\t\t\t\t\tboxShadow: hideShadows ? 'none' : getNoteShadow(shape.id, rotation, scale),\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{(isSelected || isReadyForEditing || !isEmpty) && (\n\t\t\t\t\t\t<RichTextLabel\n\t\t\t\t\t\t\tshapeId={id}\n\t\t\t\t\t\t\ttype={type}\n\t\t\t\t\t\t\tfont={font}\n\t\t\t\t\t\t\tfontSize={(fontSizeAdjustment || LABEL_FONT_SIZES[size]) * scale}\n\t\t\t\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\t\t\t\talign={align}\n\t\t\t\t\t\t\tverticalAlign={verticalAlign}\n\t\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t\t\tlabelColor={\n\t\t\t\t\t\t\t\tlabelColor === 'black'\n\t\t\t\t\t\t\t\t\t? getColorValue(theme, color, 'noteText')\n\t\t\t\t\t\t\t\t\t: getColorValue(theme, labelColor, 'fill')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twrap\n\t\t\t\t\t\t\tpadding={LABEL_PADDING * scale}\n\t\t\t\t\t\t\thasCustomTabBehavior\n\t\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{'url' in shape.props && shape.props.url && <HyperlinkButton url={shape.props.url} />}\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLNoteShape) {\n\t\tconst { scale } = shape.props\n\t\treturn (\n\t\t\t<rect\n\t\t\t\trx={scale}\n\t\t\t\twidth={toDomPrecision(NOTE_SIZE * scale)}\n\t\t\t\theight={toDomPrecision(getNoteHeight(shape))}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride toSvg(shape: TLNoteShape, ctx: SvgExportContext) {\n\t\tconst theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })\n\t\tconst bounds = getBoundsForSVG(shape)\n\n\t\tconst textLabel = (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={shape.props.fontSizeAdjustment || LABEL_FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.align}\n\t\t\t\tverticalAlign={shape.props.verticalAlign}\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'noteText')}\n\t\t\t\tbounds={bounds}\n\t\t\t\tpadding={LABEL_PADDING}\n\t\t\t\tshowTextOutline={false}\n\t\t\t/>\n\t\t)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<rect x={5} y={5} rx={1} width={NOTE_SIZE - 10} height={bounds.h} fill=\"rgba(0,0,0,.1)\" />\n\t\t\t\t<rect\n\t\t\t\t\trx={1}\n\t\t\t\t\twidth={NOTE_SIZE}\n\t\t\t\t\theight={bounds.h}\n\t\t\t\t\tfill={getColorValue(theme, shape.props.color, 'noteFill')}\n\t\t\t\t/>\n\t\t\t\t{textLabel}\n\t\t\t</>\n\t\t)\n\t}\n\n\toverride onBeforeCreate(next: TLNoteShape) {\n\t\treturn getNoteSizeAdjustments(this.editor, next)\n\t}\n\n\toverride onBeforeUpdate(prev: TLNoteShape, next: TLNoteShape) {\n\t\tif (\n\t\t\tisEqual(prev.props.richText, next.props.richText) &&\n\t\t\tprev.props.font === next.props.font &&\n\t\t\tprev.props.size === next.props.size\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\treturn getNoteSizeAdjustments(this.editor, next)\n\t}\n\n\toverride getInterpolatedProps(\n\t\tstartShape: TLNoteShape,\n\t\tendShape: TLNoteShape,\n\t\tt: number\n\t): TLNoteShapeProps {\n\t\treturn {\n\t\t\t...(t > 0.5 ? endShape.props : startShape.props),\n\t\t\tscale: lerp(startShape.props.scale, endShape.props.scale, t),\n\t\t}\n\t}\n}\n\n/**\n * Get the growY and fontSizeAdjustment for a shape.\n */\nfunction getNoteSizeAdjustments(editor: Editor, shape: TLNoteShape) {\n\tconst { labelHeight, fontSizeAdjustment } = getLabelSize(editor, shape)\n\t// When the label height is more than the height of the shape, we add extra height to it\n\tconst growY = Math.max(0, labelHeight - NOTE_SIZE)\n\n\tif (growY !== shape.props.growY || fontSizeAdjustment !== shape.props.fontSizeAdjustment) {\n\t\treturn {\n\t\t\t...shape,\n\t\t\tprops: {\n\t\t\t\t...shape.props,\n\t\t\t\tgrowY,\n\t\t\t\tfontSizeAdjustment,\n\t\t\t},\n\t\t}\n\t}\n}\n\n/**\n * Get the label size for a note.\n */\nfunction getNoteLabelSize(editor: Editor, shape: TLNoteShape) {\n\tconst { richText } = shape.props\n\n\tif (isEmptyRichText(richText)) {\n\t\tconst minHeight = LABEL_FONT_SIZES[shape.props.size] * TEXT_PROPS.lineHeight + LABEL_PADDING * 2\n\t\treturn { labelHeight: minHeight, labelWidth: 100, fontSizeAdjustment: 0 }\n\t}\n\n\tconst unadjustedFontSize = LABEL_FONT_SIZES[shape.props.size]\n\n\tlet fontSizeAdjustment = 0\n\tlet iterations = 0\n\tlet labelHeight = NOTE_SIZE\n\tlet labelWidth = NOTE_SIZE\n\n\t// N.B. For some note shapes with text like 'hjhjhjhjhjhjhjhj', you'll run into\n\t// some text measurement fuzziness where the browser swears there's no overflow (scrollWidth === width)\n\t// but really there is when you enable overflow-wrap again. This helps account for that little bit\n\t// of give.\n\tconst FUZZ = 1\n\n\t// We slightly make the font smaller if the text is too big for the note, width-wise.\n\tdo {\n\t\tfontSizeAdjustment = Math.min(unadjustedFontSize, unadjustedFontSize - iterations)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\t\tconst nextTextSize = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize: fontSizeAdjustment,\n\t\t\tmaxWidth: NOTE_SIZE - LABEL_PADDING * 2 - FUZZ,\n\t\t\tdisableOverflowWrapBreaking: true,\n\t\t\tmeasureScrollWidth: true,\n\t\t})\n\n\t\tlabelHeight = nextTextSize.h + LABEL_PADDING * 2\n\t\tlabelWidth = nextTextSize.w + LABEL_PADDING * 2\n\n\t\tif (fontSizeAdjustment <= 14) {\n\t\t\t// Too small, just rely now on CSS `overflow-wrap: break-word`\n\t\t\t// We need to recalculate the text measurement here with break-word enabled.\n\t\t\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\t\t\tconst nextTextSizeWithOverflowBreak = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize: fontSizeAdjustment,\n\t\t\t\tmaxWidth: NOTE_SIZE - LABEL_PADDING * 2 - FUZZ,\n\t\t\t})\n\t\t\tlabelHeight = nextTextSizeWithOverflowBreak.h + LABEL_PADDING * 2\n\t\t\tlabelWidth = nextTextSizeWithOverflowBreak.w + LABEL_PADDING * 2\n\t\t\tbreak\n\t\t}\n\n\t\tif (nextTextSize.scrollWidth.toFixed(0) === nextTextSize.w.toFixed(0)) {\n\t\t\tbreak\n\t\t}\n\t} while (iterations++ < 50)\n\n\treturn {\n\t\tlabelHeight: labelHeight,\n\t\tlabelWidth: labelWidth,\n\t\tfontSizeAdjustment: fontSizeAdjustment,\n\t}\n}\n\nconst labelSizesForNote = new WeakCache<TLShape, ReturnType<typeof getNoteLabelSize>>()\n\nfunction getLabelSize(editor: Editor, shape: TLNoteShape) {\n\treturn labelSizesForNote.get(shape, () => getNoteLabelSize(editor, shape))\n}\n\nfunction useNoteKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\t// Try to get the translation context, but fallback to ltr if it doesn't exist\n\tconst translation = useContext(TranslationsContext)\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tconst shape = editor.getShape<TLNoteShape>(id)\n\t\t\tif (!shape) return\n\n\t\t\tconst isTab = e.key === 'Tab'\n\t\t\tconst isCmdEnter = (e.metaKey || e.ctrlKey) && e.key === 'Enter'\n\t\t\tif (isTab || isCmdEnter) {\n\t\t\t\te.preventDefault()\n\n\t\t\t\tconst pageTransform = editor.getShapePageTransform(id)\n\t\t\t\tconst pageRotation = pageTransform.rotation()\n\n\t\t\t\t// Based on the inputs, calculate the offset to the next note\n\t\t\t\t// tab controls x axis (shift inverts direction set by RTL)\n\t\t\t\t// cmd enter is the y axis (shift inverts direction)\n\t\t\t\tconst isRTL = !!(\n\t\t\t\t\ttranslation?.dir === 'rtl' ||\n\t\t\t\t\t// todo: can we check a partial of the text, so that we don't have to render the whole thing?\n\t\t\t\t\tisRightToLeftLanguage(renderPlaintextFromRichText(editor, shape.props.richText))\n\t\t\t\t)\n\n\t\t\t\tconst offsetLength =\n\t\t\t\t\t(NOTE_SIZE +\n\t\t\t\t\t\teditor.options.adjacentShapeMargin +\n\t\t\t\t\t\t// If we're growing down, we need to account for the current shape's growY\n\t\t\t\t\t\t(isCmdEnter && !e.shiftKey ? shape.props.growY : 0)) *\n\t\t\t\t\tshape.props.scale\n\n\t\t\t\tconst adjacentCenter = new Vec(\n\t\t\t\t\tisTab ? (e.shiftKey != isRTL ? -1 : 1) : 0,\n\t\t\t\t\tisCmdEnter ? (e.shiftKey ? -1 : 1) : 0\n\t\t\t\t)\n\t\t\t\t\t.mul(offsetLength)\n\t\t\t\t\t.add(NOTE_CENTER_OFFSET.clone().mul(shape.props.scale))\n\t\t\t\t\t.rot(pageRotation)\n\t\t\t\t\t.add(pageTransform.point())\n\n\t\t\t\tconst newNote = getNoteShapeForAdjacentPosition(editor, shape, adjacentCenter, pageRotation)\n\n\t\t\t\tif (newNote) {\n\t\t\t\t\tstartEditingShapeWithLabel(editor, newNote, true /* selectAll */)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[id, editor, translation?.dir]\n\t)\n}\n\nfunction getNoteHeight(shape: TLNoteShape) {\n\treturn (NOTE_SIZE + shape.props.growY) * shape.props.scale\n}\n\nfunction getNoteShadow(id: string, rotation: number, scale: number) {\n\tconst random = rng(id) // seeded based on id\n\tconst lift = Math.abs(random()) + 0.5 // 0 to 1.5\n\tconst oy = Math.cos(rotation)\n\tconst a = 5 * scale\n\tconst b = 4 * scale\n\tconst c = 6 * scale\n\tconst d = 7 * scale\n\treturn `0px ${a - lift}px ${a}px -${a}px rgba(15, 23, 31, .6),\n\t0px ${(b + lift * d) * Math.max(0, oy)}px ${c + lift * d}px -${b + lift * c}px rgba(15, 23, 31, ${(0.3 + lift * 0.1).toFixed(2)}), \n\t0px ${48 * scale}px ${10 * scale}px -${10 * scale}px inset rgba(15, 23, 44, ${((0.022 + random() * 0.005) * ((1 + oy) / 2)).toFixed(2)})`\n}\n\nfunction getBoundsForSVG(shape: TLNoteShape) {\n\t// When rendering the SVG we don't want to adjust for scale\n\treturn new Box(0, 0, NOTE_SIZE, NOTE_SIZE + shape.props.growY)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEMPTY_ARRAY,\n\tEditor,\n\tGroup2d,\n\tIndexKey,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLHandle,\n\tTLNoteShape,\n\tTLNoteShapeProps,\n\tTLResizeInfo,\n\tTLShape,\n\tTLShapeId,\n\tVec,\n\tWeakCache,\n\texhaustiveSwitchError,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tlerp,\n\tnoteShapeMigrations,\n\tnoteShapeProps,\n\tresizeScaled,\n\trng,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n\tuseValue,\n} from '@tldraw/editor'\nimport { useCallback, useContext } from 'react'\nimport { startEditingShapeWithLabel } from '../../tools/SelectTool/selectHelpers'\nimport { TranslationsContext } from '../../ui/hooks/useTranslation/useTranslation'\nimport {\n\tisEmptyRichText,\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { isRightToLeftLanguage } from '../../utils/text/text'\nimport { HyperlinkButton } from '../shared/HyperlinkButton'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport {\n\tFONT_FAMILIES,\n\tLABEL_FONT_SIZES,\n\tLABEL_PADDING,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\nimport { useIsReadyForEditing } from '../shared/useEditablePlainText'\nimport { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'\nimport {\n\tCLONE_HANDLE_MARGIN,\n\tNOTE_CENTER_OFFSET,\n\tNOTE_SIZE,\n\tgetNoteShapeForAdjacentPosition,\n} from './noteHelpers'\n\n/** @public */\nexport interface NoteShapeOptions {\n\t/**\n\t * How should the note shape resize? By default it does not resize (except automatically based on its text content),\n\t * but you can set it to be user-resizable using scale.\n\t */\n\tresizeMode: 'none' | 'scale'\n}\n\n/** @public */\nexport class NoteShapeUtil extends ShapeUtil<TLNoteShape> {\n\tstatic override type = 'note' as const\n\tstatic override props = noteShapeProps\n\tstatic override migrations = noteShapeMigrations\n\n\toverride options: NoteShapeOptions = {\n\t\tresizeMode: 'none',\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\toverride hideResizeHandles() {\n\t\tconst { resizeMode } = this.options\n\t\tswitch (resizeMode) {\n\t\t\tcase 'none': {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tcase 'scale': {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthrow exhaustiveSwitchError(resizeMode)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn this.options.resizeMode === 'scale'\n\t}\n\n\toverride hideSelectionBoundsFg() {\n\t\treturn false\n\t}\n\n\tgetDefaultProps(): TLNoteShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\trichText: toRichText(''),\n\t\t\tsize: 'm',\n\t\t\tfont: 'draw',\n\t\t\talign: 'middle',\n\t\t\tverticalAlign: 'middle',\n\t\t\tlabelColor: 'black',\n\t\t\tgrowY: 0,\n\t\t\tfontSizeAdjustment: 0,\n\t\t\turl: '',\n\t\t\tscale: 1,\n\t\t}\n\t}\n\n\tgetGeometry(shape: TLNoteShape) {\n\t\tconst { labelHeight, labelWidth } = getLabelSize(this.editor, shape)\n\t\tconst { scale } = shape.props\n\n\t\tconst lh = labelHeight * scale\n\t\tconst lw = labelWidth * scale\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst nh = getNoteHeight(shape)\n\n\t\treturn new Group2d({\n\t\t\tchildren: [\n\t\t\t\tnew Rectangle2d({ width: nw, height: nh, isFilled: true }),\n\t\t\t\tnew Rectangle2d({\n\t\t\t\t\tx:\n\t\t\t\t\t\tshape.props.align === 'start'\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: shape.props.align === 'end'\n\t\t\t\t\t\t\t\t? nw - lw\n\t\t\t\t\t\t\t\t: (nw - lw) / 2,\n\t\t\t\t\ty:\n\t\t\t\t\t\tshape.props.verticalAlign === 'start'\n\t\t\t\t\t\t\t? 0\n\t\t\t\t\t\t\t: shape.props.verticalAlign === 'end'\n\t\t\t\t\t\t\t\t? nh - lh\n\t\t\t\t\t\t\t\t: (nh - lh) / 2,\n\t\t\t\t\twidth: lw,\n\t\t\t\t\theight: lh,\n\t\t\t\t\tisFilled: true,\n\t\t\t\t\tisLabel: true,\n\t\t\t\t\texcludeFromShapeBounds: true,\n\t\t\t\t}),\n\t\t\t],\n\t\t})\n\t}\n\n\toverride getHandles(shape: TLNoteShape): TLHandle[] {\n\t\tconst { scale } = shape.props\n\t\tconst isCoarsePointer = this.editor.getInstanceState().isCoarsePointer\n\t\tif (isCoarsePointer) return []\n\n\t\tconst zoom = this.editor.getEfficientZoomLevel()\n\t\tif (zoom * scale < 0.25) return []\n\n\t\tconst nh = getNoteHeight(shape)\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst offset = (CLONE_HANDLE_MARGIN / zoom) * scale\n\n\t\tif (zoom * scale < 0.5) {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tid: 'bottom',\n\t\t\t\t\tindex: 'a3' as IndexKey,\n\t\t\t\t\ttype: 'clone',\n\t\t\t\t\tx: nw / 2,\n\t\t\t\t\ty: nh + offset,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\n\t\treturn [\n\t\t\t{\n\t\t\t\tid: 'top',\n\t\t\t\tindex: 'a1' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw / 2,\n\t\t\t\ty: -offset,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'right',\n\t\t\t\tindex: 'a2' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw + offset,\n\t\t\t\ty: nh / 2,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'bottom',\n\t\t\t\tindex: 'a3' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: nw / 2,\n\t\t\t\ty: nh + offset,\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'left',\n\t\t\t\tindex: 'a4' as IndexKey,\n\t\t\t\ttype: 'clone',\n\t\t\t\tx: -offset,\n\t\t\t\ty: nh / 2,\n\t\t\t},\n\t\t]\n\t}\n\n\toverride onResize(shape: any, info: TLResizeInfo<any>) {\n\t\tconst { resizeMode } = this.options\n\t\tswitch (resizeMode) {\n\t\t\tcase 'none': {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tcase 'scale': {\n\t\t\t\treturn resizeScaled(shape, info)\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthrow exhaustiveSwitchError(resizeMode)\n\t\t\t}\n\t\t}\n\t}\n\n\toverride getText(shape: TLNoteShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride getFontFaces(shape: TLNoteShape) {\n\t\tif (isEmptyRichText(shape.props.richText)) {\n\t\t\treturn EMPTY_ARRAY\n\t\t}\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\tcomponent(shape: TLNoteShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\ttype,\n\t\t\tprops: {\n\t\t\t\tlabelColor,\n\t\t\t\tscale,\n\t\t\t\tcolor,\n\t\t\t\tfont,\n\t\t\t\tsize,\n\t\t\t\talign,\n\t\t\t\trichText,\n\t\t\t\tverticalAlign,\n\t\t\t\tfontSizeAdjustment,\n\t\t\t},\n\t\t} = shape\n\n\t\tconst handleKeyDown = useNoteKeydownHandler(id)\n\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst nw = NOTE_SIZE * scale\n\t\tconst nh = getNoteHeight(shape)\n\n\t\tconst rotation = useValue(\n\t\t\t'shape rotation',\n\t\t\t() => this.editor.getShapePageTransform(id)?.rotation() ?? 0,\n\t\t\t[this.editor]\n\t\t)\n\n\t\tconst isDarkMode = useValue('dark mode', () => this.editor.user.getIsDarkMode(), [this.editor])\n\n\t\t// Shadows are hidden when zoomed out far enough or in dark mode\n\t\tlet hideShadows = useEfficientZoomThreshold(scale * 0.25)\n\t\tif (isDarkMode) hideShadows = true\n\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\n\t\tconst isReadyForEditing = useIsReadyForEditing(this.editor, shape.id)\n\t\tconst isEmpty = isEmptyRichText(richText)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<div\n\t\t\t\t\tid={id}\n\t\t\t\t\tclassName=\"tl-note__container\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: nw,\n\t\t\t\t\t\theight: nh,\n\t\t\t\t\t\tbackgroundColor: getColorValue(theme, color, 'noteFill'),\n\t\t\t\t\t\tborderBottom: hideShadows\n\t\t\t\t\t\t\t? isDarkMode\n\t\t\t\t\t\t\t\t? `${2 * scale}px solid rgb(20, 20, 20)`\n\t\t\t\t\t\t\t\t: `${2 * scale}px solid rgb(144, 144, 144)`\n\t\t\t\t\t\t\t: 'none',\n\t\t\t\t\t\tboxShadow: hideShadows ? 'none' : getNoteShadow(shape.id, rotation, scale),\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{(isSelected || isReadyForEditing || !isEmpty) && (\n\t\t\t\t\t\t<RichTextLabel\n\t\t\t\t\t\t\tshapeId={id}\n\t\t\t\t\t\t\ttype={type}\n\t\t\t\t\t\t\tfont={font}\n\t\t\t\t\t\t\tfontSize={(fontSizeAdjustment || LABEL_FONT_SIZES[size]) * scale}\n\t\t\t\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\t\t\t\talign={align}\n\t\t\t\t\t\t\tverticalAlign={verticalAlign}\n\t\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\t\tisSelected={isSelected}\n\t\t\t\t\t\t\tlabelColor={\n\t\t\t\t\t\t\t\tlabelColor === 'black'\n\t\t\t\t\t\t\t\t\t? getColorValue(theme, color, 'noteText')\n\t\t\t\t\t\t\t\t\t: getColorValue(theme, labelColor, 'fill')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twrap\n\t\t\t\t\t\t\tpadding={LABEL_PADDING * scale}\n\t\t\t\t\t\t\thasCustomTabBehavior\n\t\t\t\t\t\t\tshowTextOutline={false}\n\t\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{'url' in shape.props && shape.props.url && <HyperlinkButton url={shape.props.url} />}\n\t\t\t</>\n\t\t)\n\t}\n\n\tindicator(shape: TLNoteShape) {\n\t\tconst { scale } = shape.props\n\t\treturn (\n\t\t\t<rect\n\t\t\t\trx={scale}\n\t\t\t\twidth={toDomPrecision(NOTE_SIZE * scale)}\n\t\t\t\theight={toDomPrecision(getNoteHeight(shape))}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride toSvg(shape: TLNoteShape, ctx: SvgExportContext) {\n\t\tconst theme = getDefaultColorTheme({ isDarkMode: ctx.isDarkMode })\n\t\tconst bounds = getBoundsForSVG(shape)\n\n\t\tconst textLabel = (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={shape.props.fontSizeAdjustment || LABEL_FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.align}\n\t\t\t\tverticalAlign={shape.props.verticalAlign}\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'noteText')}\n\t\t\t\tbounds={bounds}\n\t\t\t\tpadding={LABEL_PADDING}\n\t\t\t\tshowTextOutline={false}\n\t\t\t/>\n\t\t)\n\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<rect x={5} y={5} rx={1} width={NOTE_SIZE - 10} height={bounds.h} fill=\"rgba(0,0,0,.1)\" />\n\t\t\t\t<rect\n\t\t\t\t\trx={1}\n\t\t\t\t\twidth={NOTE_SIZE}\n\t\t\t\t\theight={bounds.h}\n\t\t\t\t\tfill={getColorValue(theme, shape.props.color, 'noteFill')}\n\t\t\t\t/>\n\t\t\t\t{textLabel}\n\t\t\t</>\n\t\t)\n\t}\n\n\toverride onBeforeCreate(next: TLNoteShape) {\n\t\treturn getNoteSizeAdjustments(this.editor, next)\n\t}\n\n\toverride onBeforeUpdate(prev: TLNoteShape, next: TLNoteShape) {\n\t\tif (\n\t\t\tisEqual(prev.props.richText, next.props.richText) &&\n\t\t\tprev.props.font === next.props.font &&\n\t\t\tprev.props.size === next.props.size\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\treturn getNoteSizeAdjustments(this.editor, next)\n\t}\n\n\toverride getInterpolatedProps(\n\t\tstartShape: TLNoteShape,\n\t\tendShape: TLNoteShape,\n\t\tt: number\n\t): TLNoteShapeProps {\n\t\treturn {\n\t\t\t...(t > 0.5 ? endShape.props : startShape.props),\n\t\t\tscale: lerp(startShape.props.scale, endShape.props.scale, t),\n\t\t}\n\t}\n}\n\n/**\n * Get the growY and fontSizeAdjustment for a shape.\n */\nfunction getNoteSizeAdjustments(editor: Editor, shape: TLNoteShape) {\n\tconst { labelHeight, fontSizeAdjustment } = getLabelSize(editor, shape)\n\t// When the label height is more than the height of the shape, we add extra height to it\n\tconst growY = Math.max(0, labelHeight - NOTE_SIZE)\n\n\tif (growY !== shape.props.growY || fontSizeAdjustment !== shape.props.fontSizeAdjustment) {\n\t\treturn {\n\t\t\t...shape,\n\t\t\tprops: {\n\t\t\t\t...shape.props,\n\t\t\t\tgrowY,\n\t\t\t\tfontSizeAdjustment,\n\t\t\t},\n\t\t}\n\t}\n}\n\n/**\n * Get the label size for a note.\n */\nfunction getNoteLabelSize(editor: Editor, shape: TLNoteShape) {\n\tconst { richText } = shape.props\n\n\tif (isEmptyRichText(richText)) {\n\t\tconst minHeight = LABEL_FONT_SIZES[shape.props.size] * TEXT_PROPS.lineHeight + LABEL_PADDING * 2\n\t\treturn { labelHeight: minHeight, labelWidth: 100, fontSizeAdjustment: 0 }\n\t}\n\n\tconst unadjustedFontSize = LABEL_FONT_SIZES[shape.props.size]\n\n\tlet fontSizeAdjustment = 0\n\tlet iterations = 0\n\tlet labelHeight = NOTE_SIZE\n\tlet labelWidth = NOTE_SIZE\n\n\t// N.B. For some note shapes with text like 'hjhjhjhjhjhjhjhj', you'll run into\n\t// some text measurement fuzziness where the browser swears there's no overflow (scrollWidth === width)\n\t// but really there is when you enable overflow-wrap again. This helps account for that little bit\n\t// of give.\n\tconst FUZZ = 1\n\n\t// We slightly make the font smaller if the text is too big for the note, width-wise.\n\tdo {\n\t\tfontSizeAdjustment = Math.min(unadjustedFontSize, unadjustedFontSize - iterations)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\t\tconst nextTextSize = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize: fontSizeAdjustment,\n\t\t\tmaxWidth: NOTE_SIZE - LABEL_PADDING * 2 - FUZZ,\n\t\t\tdisableOverflowWrapBreaking: true,\n\t\t\tmeasureScrollWidth: true,\n\t\t})\n\n\t\tlabelHeight = nextTextSize.h + LABEL_PADDING * 2\n\t\tlabelWidth = nextTextSize.w + LABEL_PADDING * 2\n\n\t\tif (fontSizeAdjustment <= 14) {\n\t\t\t// Too small, just rely now on CSS `overflow-wrap: break-word`\n\t\t\t// We need to recalculate the text measurement here with break-word enabled.\n\t\t\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\t\t\tconst nextTextSizeWithOverflowBreak = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize: fontSizeAdjustment,\n\t\t\t\tmaxWidth: NOTE_SIZE - LABEL_PADDING * 2 - FUZZ,\n\t\t\t})\n\t\t\tlabelHeight = nextTextSizeWithOverflowBreak.h + LABEL_PADDING * 2\n\t\t\tlabelWidth = nextTextSizeWithOverflowBreak.w + LABEL_PADDING * 2\n\t\t\tbreak\n\t\t}\n\n\t\tif (nextTextSize.scrollWidth.toFixed(0) === nextTextSize.w.toFixed(0)) {\n\t\t\tbreak\n\t\t}\n\t} while (iterations++ < 50)\n\n\treturn {\n\t\tlabelHeight: labelHeight,\n\t\tlabelWidth: labelWidth,\n\t\tfontSizeAdjustment: fontSizeAdjustment,\n\t}\n}\n\nconst labelSizesForNote = new WeakCache<TLShape, ReturnType<typeof getNoteLabelSize>>()\n\nfunction getLabelSize(editor: Editor, shape: TLNoteShape) {\n\treturn labelSizesForNote.get(shape, () => getNoteLabelSize(editor, shape))\n}\n\nfunction useNoteKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\t// Try to get the translation context, but fallback to ltr if it doesn't exist\n\tconst translation = useContext(TranslationsContext)\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tconst shape = editor.getShape<TLNoteShape>(id)\n\t\t\tif (!shape) return\n\n\t\t\tconst isTab = e.key === 'Tab'\n\t\t\tconst isCmdEnter = (e.metaKey || e.ctrlKey) && e.key === 'Enter'\n\t\t\tif (isTab || isCmdEnter) {\n\t\t\t\te.preventDefault()\n\n\t\t\t\tconst pageTransform = editor.getShapePageTransform(id)\n\t\t\t\tconst pageRotation = pageTransform.rotation()\n\n\t\t\t\t// Based on the inputs, calculate the offset to the next note\n\t\t\t\t// tab controls x axis (shift inverts direction set by RTL)\n\t\t\t\t// cmd enter is the y axis (shift inverts direction)\n\t\t\t\tconst isRTL = !!(\n\t\t\t\t\ttranslation?.dir === 'rtl' ||\n\t\t\t\t\t// todo: can we check a partial of the text, so that we don't have to render the whole thing?\n\t\t\t\t\tisRightToLeftLanguage(renderPlaintextFromRichText(editor, shape.props.richText))\n\t\t\t\t)\n\n\t\t\t\tconst offsetLength =\n\t\t\t\t\t(NOTE_SIZE +\n\t\t\t\t\t\teditor.options.adjacentShapeMargin +\n\t\t\t\t\t\t// If we're growing down, we need to account for the current shape's growY\n\t\t\t\t\t\t(isCmdEnter && !e.shiftKey ? shape.props.growY : 0)) *\n\t\t\t\t\tshape.props.scale\n\n\t\t\t\tconst adjacentCenter = new Vec(\n\t\t\t\t\tisTab ? (e.shiftKey != isRTL ? -1 : 1) : 0,\n\t\t\t\t\tisCmdEnter ? (e.shiftKey ? -1 : 1) : 0\n\t\t\t\t)\n\t\t\t\t\t.mul(offsetLength)\n\t\t\t\t\t.add(NOTE_CENTER_OFFSET.clone().mul(shape.props.scale))\n\t\t\t\t\t.rot(pageRotation)\n\t\t\t\t\t.add(pageTransform.point())\n\n\t\t\t\tconst newNote = getNoteShapeForAdjacentPosition(editor, shape, adjacentCenter, pageRotation)\n\n\t\t\t\tif (newNote) {\n\t\t\t\t\tstartEditingShapeWithLabel(editor, newNote, true /* selectAll */)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[id, editor, translation?.dir]\n\t)\n}\n\nfunction getNoteHeight(shape: TLNoteShape) {\n\treturn (NOTE_SIZE + shape.props.growY) * shape.props.scale\n}\n\nfunction getNoteShadow(id: string, rotation: number, scale: number) {\n\tconst random = rng(id) // seeded based on id\n\tconst lift = Math.abs(random()) + 0.5 // 0 to 1.5\n\tconst oy = Math.cos(rotation)\n\tconst a = 5 * scale\n\tconst b = 4 * scale\n\tconst c = 6 * scale\n\tconst d = 7 * scale\n\treturn `0px ${a - lift}px ${a}px -${a}px rgba(15, 23, 31, .6),\n\t0px ${(b + lift * d) * Math.max(0, oy)}px ${c + lift * d}px -${b + lift * c}px rgba(15, 23, 31, ${(0.3 + lift * 0.1).toFixed(2)}), \n\t0px ${48 * scale}px ${10 * scale}px -${10 * scale}px inset rgba(15, 23, 44, ${((0.022 + random() * 0.005) * ((1 + oy) / 2)).toFixed(2)})`\n}\n\nfunction getBoundsForSVG(shape: TLNoteShape) {\n\t// When rendering the SVG we don't want to adjust for scale\n\treturn new Box(0, 0, NOTE_SIZE, NOTE_SIZE + shape.props.growY)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["html"]
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import classNames from "classnames";
2
3
  import React from "react";
3
4
  import { PlainTextArea } from "../text/PlainTextArea.mjs";
4
5
  import { TextHelpers } from "./TextHelpers.mjs";
@@ -21,7 +22,8 @@ const PlainTextLabel = React.memo(function PlainTextLabel2({
21
22
  classNamePrefix,
22
23
  style,
23
24
  textWidth,
24
- textHeight
25
+ textHeight,
26
+ showTextOutline = true
25
27
  }) {
26
28
  const { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } = useEditablePlainText(shapeId, type, plaintext);
27
29
  const finalPlainText = TextHelpers.normalizeTextForDom(plaintext || "");
@@ -63,7 +65,17 @@ const PlainTextLabel = React.memo(function PlainTextLabel2({
63
65
  height: textHeight ? Math.ceil(textHeight) : void 0
64
66
  },
65
67
  children: [
66
- /* @__PURE__ */ jsx("div", { className: `${cssPrefix} tl-text tl-text-content`, dir: "auto", children: finalPlainText.split("\n").map((lineOfText, index) => /* @__PURE__ */ jsx("div", { dir: "auto", children: lineOfText }, index)) }),
68
+ /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: classNames(
72
+ `${cssPrefix} tl-text tl-text-content`,
73
+ showTextOutline ? "tl-text__outline" : "tl-text__no-outline"
74
+ ),
75
+ dir: "auto",
76
+ children: finalPlainText.split("\n").map((lineOfText, index) => /* @__PURE__ */ jsx("div", { dir: "auto", children: lineOfText }, index))
77
+ }
78
+ ),
67
79
  (isReadyForEditing || isSelected) && /* @__PURE__ */ jsx(
68
80
  PlainTextArea,
69
81
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/shared/PlainTextLabel.tsx"],
4
- "sourcesContent": ["import {\n\tBox,\n\tExtractShapeByProps,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLShapeId,\n} from '@tldraw/editor'\nimport React from 'react'\nimport { PlainTextArea } from '../text/PlainTextArea'\nimport { TextHelpers } from './TextHelpers'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditablePlainText } from './useEditablePlainText'\n\n/** @public */\nexport interface PlainTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: ExtractShapeByProps<{ text: string }>['type']\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\ttext?: string\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const PlainTextLabel = React.memo(function PlainTextLabel({\n\tshapeId,\n\ttype,\n\ttext: plaintext,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n}: PlainTextLabelProps) {\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditablePlainText(shapeId, type, plaintext)\n\n\tconst finalPlainText = TextHelpers.normalizeTextForDom(plaintext || '')\n\tconst hasText = finalPlainText.length > 0\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tif (!isEditing && !hasText) {\n\t\treturn null\n\t}\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\t// In case you're grepping for this, it breaks down as follows:\n\t// tl-text-label, tl-text-label__inner, tl-text-shape-label, tl-text\n\t// tl-arrow-label, tl-arrow-label__inner, tl-arrow\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={`${cssPrefix}-label tl-text-wrapper tl-plain-text-wrapper`}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className={`${cssPrefix} tl-text tl-text-content`} dir=\"auto\">\n\t\t\t\t\t{finalPlainText.split('\\n').map((lineOfText, index) => (\n\t\t\t\t\t\t<div key={index} dir=\"auto\">\n\t\t\t\t\t\t\t{lineOfText}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<PlainTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\ttext={plaintext}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n"],
5
- "mappings": "AAmGG,SAcG,KAdH;AA1FH,OAAO,WAAW;AAClB,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AAgC9B,MAAM,iBAAiB,MAAM,KAAK,SAASA,gBAAe;AAAA,EAChE;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAwB;AACvB,QAAM,EAAE,QAAQ,SAAS,WAAW,mBAAmB,GAAG,iBAAiB,IAC1E,qBAAqB,SAAS,MAAM,SAAS;AAE9C,QAAM,iBAAiB,YAAY,oBAAoB,aAAa,EAAE;AACtE,QAAM,UAAU,eAAe,SAAS;AAExC,QAAM,cAAc,cAAc,KAAK;AAEvC,MAAI,CAAC,aAAa,CAAC,SAAS;AAC3B,WAAO;AAAA,EACR;AAMA,QAAM,YAAY,mBAAmB;AACrC,SACC;AAAA,IAAC;AAAA;AAAA,MACA,WAAW,GAAG,SAAS;AAAA,MACvB,eAAa,CAAC;AAAA,MACd,aAAW;AAAA,MACX,cAAY;AAAA,MACZ,gBAAc,CAAC;AAAA,MACf,kBAAgB;AAAA,MAChB,6BAA2B;AAAA,MAC3B,iBAAe,CAAC,CAAC;AAAA,MACjB,mBAAiB;AAAA,MACjB,OAAO;AAAA,QACN,gBAAgB,UAAU,YAAY,cAAc,WAAW;AAAA,QAC/D,YAAY,kBAAkB,WAAW,WAAW;AAAA,QACpD;AAAA,QACA,GAAG;AAAA,MACJ;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACA,WAAW,GAAG,SAAS;AAAA,UACvB,OAAO;AAAA,YACN;AAAA,YACA,YAAY,WAAW,SAAS;AAAA,YAChC,WAAW,KAAK,MAAM,WAAW,UAAU,IAAI;AAAA,YAC/C,UAAU,KAAK,KAAK,aAAa,CAAC;AAAA,YAClC,OAAO;AAAA,YACP,OAAO,YAAY,KAAK,KAAK,SAAS,IAAI;AAAA,YAC1C,QAAQ,aAAa,KAAK,KAAK,UAAU,IAAI;AAAA,UAC9C;AAAA,UAEA;AAAA,gCAAC,SAAI,WAAW,GAAG,SAAS,4BAA4B,KAAI,QAC1D,yBAAe,MAAM,IAAI,EAAE,IAAI,CAAC,YAAY,UAC5C,oBAAC,SAAgB,KAAI,QACnB,wBADQ,KAEV,CACA,GACF;AAAA,aACE,qBAAqB,eACtB;AAAA,cAAC;AAAA;AAAA,gBAEA,KAAK;AAAA,gBACL,MAAM;AAAA,gBACN;AAAA,gBACA;AAAA,gBACC,GAAG;AAAA,gBACJ,eAAe,uBAAuB,iBAAiB;AAAA;AAAA,YACxD;AAAA;AAAA;AAAA,MAEF;AAAA;AAAA,EACD;AAEF,CAAC;",
4
+ "sourcesContent": ["import {\n\tBox,\n\tExtractShapeByProps,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLShapeId,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport React from 'react'\nimport { PlainTextArea } from '../text/PlainTextArea'\nimport { TextHelpers } from './TextHelpers'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditablePlainText } from './useEditablePlainText'\n\n/** @public */\nexport interface PlainTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: ExtractShapeByProps<{ text: string }>['type']\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\ttext?: string\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const PlainTextLabel = React.memo(function PlainTextLabel({\n\tshapeId,\n\ttype,\n\ttext: plaintext,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n\tshowTextOutline = true,\n}: PlainTextLabelProps) {\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditablePlainText(shapeId, type, plaintext)\n\n\tconst finalPlainText = TextHelpers.normalizeTextForDom(plaintext || '')\n\tconst hasText = finalPlainText.length > 0\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tif (!isEditing && !hasText) {\n\t\treturn null\n\t}\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\t// In case you're grepping for this, it breaks down as follows:\n\t// tl-text-label, tl-text-label__inner, tl-text-shape-label, tl-text\n\t// tl-arrow-label, tl-arrow-label__inner, tl-arrow\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={`${cssPrefix}-label tl-text-wrapper tl-plain-text-wrapper`}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName={classNames(\n\t\t\t\t\t\t`${cssPrefix} tl-text tl-text-content`,\n\t\t\t\t\t\tshowTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'\n\t\t\t\t\t)}\n\t\t\t\t\tdir=\"auto\"\n\t\t\t\t>\n\t\t\t\t\t{finalPlainText.split('\\n').map((lineOfText, index) => (\n\t\t\t\t\t\t<div key={index} dir=\"auto\">\n\t\t\t\t\t\t\t{lineOfText}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<PlainTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\ttext={plaintext}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n"],
5
+ "mappings": "AAsGG,SAoBG,KApBH;AA7FH,OAAO,gBAAgB;AACvB,OAAO,WAAW;AAClB,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AAiC9B,MAAM,iBAAiB,MAAM,KAAK,SAASA,gBAAe;AAAA,EAChE;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACnB,GAAwB;AACvB,QAAM,EAAE,QAAQ,SAAS,WAAW,mBAAmB,GAAG,iBAAiB,IAC1E,qBAAqB,SAAS,MAAM,SAAS;AAE9C,QAAM,iBAAiB,YAAY,oBAAoB,aAAa,EAAE;AACtE,QAAM,UAAU,eAAe,SAAS;AAExC,QAAM,cAAc,cAAc,KAAK;AAEvC,MAAI,CAAC,aAAa,CAAC,SAAS;AAC3B,WAAO;AAAA,EACR;AAMA,QAAM,YAAY,mBAAmB;AACrC,SACC;AAAA,IAAC;AAAA;AAAA,MACA,WAAW,GAAG,SAAS;AAAA,MACvB,eAAa,CAAC;AAAA,MACd,aAAW;AAAA,MACX,cAAY;AAAA,MACZ,gBAAc,CAAC;AAAA,MACf,kBAAgB;AAAA,MAChB,6BAA2B;AAAA,MAC3B,iBAAe,CAAC,CAAC;AAAA,MACjB,mBAAiB;AAAA,MACjB,OAAO;AAAA,QACN,gBAAgB,UAAU,YAAY,cAAc,WAAW;AAAA,QAC/D,YAAY,kBAAkB,WAAW,WAAW;AAAA,QACpD;AAAA,QACA,GAAG;AAAA,MACJ;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACA,WAAW,GAAG,SAAS;AAAA,UACvB,OAAO;AAAA,YACN;AAAA,YACA,YAAY,WAAW,SAAS;AAAA,YAChC,WAAW,KAAK,MAAM,WAAW,UAAU,IAAI;AAAA,YAC/C,UAAU,KAAK,KAAK,aAAa,CAAC;AAAA,YAClC,OAAO;AAAA,YACP,OAAO,YAAY,KAAK,KAAK,SAAS,IAAI;AAAA,YAC1C,QAAQ,aAAa,KAAK,KAAK,UAAU,IAAI;AAAA,UAC9C;AAAA,UAEA;AAAA;AAAA,cAAC;AAAA;AAAA,gBACA,WAAW;AAAA,kBACV,GAAG,SAAS;AAAA,kBACZ,kBAAkB,qBAAqB;AAAA,gBACxC;AAAA,gBACA,KAAI;AAAA,gBAEH,yBAAe,MAAM,IAAI,EAAE,IAAI,CAAC,YAAY,UAC5C,oBAAC,SAAgB,KAAI,QACnB,wBADQ,KAEV,CACA;AAAA;AAAA,YACF;AAAA,aACE,qBAAqB,eACtB;AAAA,cAAC;AAAA;AAAA,gBAEA,KAAK;AAAA,gBACL,MAAM;AAAA,gBACN;AAAA,gBACA;AAAA,gBACC,GAAG;AAAA,gBACJ,eAAe,uBAAuB,iBAAiB;AAAA;AAAA,YACxD;AAAA;AAAA;AAAA,MAEF;AAAA;AAAA,EACD;AAEF,CAAC;",
6
6
  "names": ["PlainTextLabel"]
7
7
  }
@@ -7,6 +7,7 @@ import {
7
7
  useReactor,
8
8
  useValue
9
9
  } from "@tldraw/editor";
10
+ import classNames from "classnames";
10
11
  import React, { useMemo } from "react";
11
12
  import { renderHtmlFromRichText } from "../../utils/text/richText.mjs";
12
13
  import { RichTextArea } from "../text/RichTextArea.mjs";
@@ -31,7 +32,8 @@ const RichTextLabel = React.memo(function RichTextLabel2({
31
32
  style,
32
33
  textWidth,
33
34
  textHeight,
34
- hasCustomTabBehavior
35
+ hasCustomTabBehavior,
36
+ showTextOutline = true
35
37
  }) {
36
38
  const editor = useEditor();
37
39
  const isDragging = React.useRef(false);
@@ -75,7 +77,10 @@ const RichTextLabel = React.memo(function RichTextLabel2({
75
77
  return /* @__PURE__ */ jsx(
76
78
  "div",
77
79
  {
78
- className: `${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`,
80
+ className: classNames(
81
+ `${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`,
82
+ showTextOutline ? "tl-text__outline" : "tl-text__no-outline"
83
+ ),
79
84
  "aria-hidden": !isEditing,
80
85
  "data-font": font,
81
86
  "data-align": align,
@@ -175,7 +180,10 @@ function RichTextSVG({
175
180
  y: bounds.minY,
176
181
  width: bounds.w,
177
182
  height: bounds.h,
178
- className: "tl-export-embed-styles tl-rich-text tl-rich-text-svg",
183
+ className: classNames(
184
+ "tl-export-embed-styles tl-rich-text tl-rich-text-svg",
185
+ showTextOutline ? "tl-text__outline" : "tl-text__no-outline"
186
+ ),
179
187
  children: /* @__PURE__ */ jsx("div", { style: wrapperStyle, children: /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html }, style }) })
180
188
  }
181
189
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/shared/RichTextLabel.tsx"],
4
- "sourcesContent": ["import {\n\tBox,\n\tDefaultFontFamilies,\n\tExtractShapeByProps,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLEventInfo,\n\tTLRichText,\n\tTLShapeId,\n\topenWindow,\n\tpreventDefault,\n\tuseEditor,\n\tuseReactor,\n\tuseValue,\n} from '@tldraw/editor'\nimport React, { useMemo } from 'react'\nimport { renderHtmlFromRichText } from '../../utils/text/richText'\nimport { RichTextArea } from '../text/RichTextArea'\nimport { TEXT_PROPS } from './default-shape-constants'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditableRichText } from './useEditableRichText'\n\n/** @public */\nexport interface RichTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: ExtractShapeByProps<{ richText: TLRichText }>['type']\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\trichText?: TLRichText\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n\thasCustomTabBehavior?: boolean\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const RichTextLabel = React.memo(function RichTextLabel({\n\tshapeId,\n\ttype,\n\trichText,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n\thasCustomTabBehavior,\n}: RichTextLabelProps) {\n\tconst editor = useEditor()\n\tconst isDragging = React.useRef(false)\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditableRichText(shapeId, type, richText)\n\n\tconst html = useMemo(() => {\n\t\tif (richText) {\n\t\t\treturn renderHtmlFromRichText(editor, richText)\n\t\t}\n\t}, [editor, richText])\n\n\tconst selectToolActive = useValue(\n\t\t'isSelectToolActive',\n\t\t() => editor.getCurrentToolId() === 'select',\n\t\t[editor]\n\t)\n\n\tuseReactor(\n\t\t'isDragging',\n\t\t() => {\n\t\t\teditor.getInstanceState()\n\t\t\tisDragging.current = editor.inputs.isDragging\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tconst handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {\n\t\tif (e.target instanceof HTMLElement && (e.target.tagName === 'A' || e.target.closest('a'))) {\n\t\t\t// This mousedown prevent default is to let dragging when over a link work.\n\t\t\tpreventDefault(e)\n\n\t\t\tif (!selectToolActive) return\n\t\t\tconst link = e.target.closest('a')?.getAttribute('href') ?? ''\n\t\t\t// We don't get the mouseup event later because we preventDefault\n\t\t\t// so we have to do it manually.\n\t\t\tconst handlePointerUp = (e: TLEventInfo) => {\n\t\t\t\tif (e.name !== 'pointer_up' || !link) return\n\n\t\t\t\tif (!isDragging.current) {\n\t\t\t\t\topenWindow(link, '_blank', false)\n\t\t\t\t}\n\t\t\t\teditor.off('event', handlePointerUp)\n\t\t\t}\n\t\t\teditor.on('event', handlePointerUp)\n\t\t}\n\t}\n\n\t// Should be guarded higher up so that this doesn't render... but repeated here. This should never be true.\n\tif (!isEditing && isEmpty) return null\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className={`${cssPrefix} tl-text tl-text-content`} dir=\"auto\">\n\t\t\t\t\t{richText && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-rich-text\"\n\t\t\t\t\t\t\tdata-is-select-tool-active={selectToolActive}\n\t\t\t\t\t\t\t// todo: see if I can abuse this\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: html || '' }}\n\t\t\t\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\t\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<RichTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thasCustomTabBehavior={hasCustomTabBehavior}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n\n/** @public */\nexport interface RichTextSVGProps {\n\tbounds: Box\n\trichText: TLRichText\n\tfontSize: number\n\tfont: TLDefaultFontStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\tlabelColor: string\n\tpadding: number\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a rich text string as SVG given bounds and text properties.\n *\n * @public @react\n */\nexport function RichTextSVG({\n\tbounds,\n\trichText,\n\tfontSize,\n\tfont,\n\talign,\n\tverticalAlign,\n\twrap,\n\tlabelColor,\n\tpadding,\n\tshowTextOutline = true,\n}: RichTextSVGProps) {\n\tconst editor = useEditor()\n\tconst html = renderHtmlFromRichText(editor, richText)\n\tconst textAlign =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('start' as const)\n\t\t\t\t: ('end' as const)\n\tconst justifyContent =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('flex-start' as const)\n\t\t\t\t: ('flex-end' as const)\n\tconst alignItems =\n\t\tverticalAlign === 'middle' ? 'center' : verticalAlign === 'start' ? 'flex-start' : 'flex-end'\n\tconst wrapperStyle = {\n\t\tdisplay: 'flex',\n\t\tfontFamily: DefaultFontFamilies[font],\n\t\theight: `100%`,\n\t\tjustifyContent,\n\t\talignItems,\n\t\tpadding: `${padding}px`,\n\t}\n\tconst style = {\n\t\tfontSize: `${fontSize}px`,\n\t\twrap: wrap ? 'wrap' : 'nowrap',\n\t\tcolor: labelColor,\n\t\tlineHeight: TEXT_PROPS.lineHeight,\n\t\ttextAlign,\n\t\twidth: '100%',\n\t\twordWrap: 'break-word' as const,\n\t\toverflowWrap: 'break-word' as const,\n\t\twhiteSpace: 'pre-wrap',\n\t\ttextShadow: showTextOutline ? 'var(--tl-text-outline)' : 'none',\n\t}\n\n\treturn (\n\t\t<foreignObject\n\t\t\tx={bounds.minX}\n\t\t\ty={bounds.minY}\n\t\t\twidth={bounds.w}\n\t\t\theight={bounds.h}\n\t\t\tclassName=\"tl-export-embed-styles tl-rich-text tl-rich-text-svg\"\n\t\t>\n\t\t\t<div style={wrapperStyle}>\n\t\t\t\t<div dangerouslySetInnerHTML={{ __html: html }} style={style} />\n\t\t\t</div>\n\t\t</foreignObject>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tBox,\n\tDefaultFontFamilies,\n\tExtractShapeByProps,\n\tTLDefaultFillStyle,\n\tTLDefaultFontStyle,\n\tTLDefaultHorizontalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n\tTLEventInfo,\n\tTLRichText,\n\tTLShapeId,\n\topenWindow,\n\tpreventDefault,\n\tuseEditor,\n\tuseReactor,\n\tuseValue,\n} from '@tldraw/editor'\nimport classNames from 'classnames'\nimport React, { useMemo } from 'react'\nimport { renderHtmlFromRichText } from '../../utils/text/richText'\nimport { RichTextArea } from '../text/RichTextArea'\nimport { TEXT_PROPS } from './default-shape-constants'\nimport { isLegacyAlign } from './legacyProps'\nimport { useEditableRichText } from './useEditableRichText'\n\n/** @public */\nexport interface RichTextLabelProps {\n\tshapeId: TLShapeId\n\ttype: ExtractShapeByProps<{ richText: TLRichText }>['type']\n\tfont: TLDefaultFontStyle\n\tfontSize: number\n\tlineHeight: number\n\tfill?: TLDefaultFillStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\trichText?: TLRichText\n\tlabelColor: string\n\tbounds?: Box\n\tisSelected: boolean\n\tonKeyDown?(e: KeyboardEvent): void\n\tclassNamePrefix?: string\n\tstyle?: React.CSSProperties\n\ttextWidth?: number\n\ttextHeight?: number\n\tpadding?: number\n\thasCustomTabBehavior?: boolean\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a text label that can be used inside of shapes.\n * The component has the ability to be edited in place and furthermore\n * supports rich text editing.\n *\n * @public @react\n */\nexport const RichTextLabel = React.memo(function RichTextLabel({\n\tshapeId,\n\ttype,\n\trichText,\n\tlabelColor,\n\tfont,\n\tfontSize,\n\tlineHeight,\n\talign,\n\tverticalAlign,\n\twrap,\n\tisSelected,\n\tpadding = 0,\n\tonKeyDown: handleKeyDownCustom,\n\tclassNamePrefix,\n\tstyle,\n\ttextWidth,\n\ttextHeight,\n\thasCustomTabBehavior,\n\tshowTextOutline = true,\n}: RichTextLabelProps) {\n\tconst editor = useEditor()\n\tconst isDragging = React.useRef(false)\n\tconst { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =\n\t\tuseEditableRichText(shapeId, type, richText)\n\n\tconst html = useMemo(() => {\n\t\tif (richText) {\n\t\t\treturn renderHtmlFromRichText(editor, richText)\n\t\t}\n\t}, [editor, richText])\n\n\tconst selectToolActive = useValue(\n\t\t'isSelectToolActive',\n\t\t() => editor.getCurrentToolId() === 'select',\n\t\t[editor]\n\t)\n\n\tuseReactor(\n\t\t'isDragging',\n\t\t() => {\n\t\t\teditor.getInstanceState()\n\t\t\tisDragging.current = editor.inputs.isDragging\n\t\t},\n\t\t[editor]\n\t)\n\n\tconst legacyAlign = isLegacyAlign(align)\n\n\tconst handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {\n\t\tif (e.target instanceof HTMLElement && (e.target.tagName === 'A' || e.target.closest('a'))) {\n\t\t\t// This mousedown prevent default is to let dragging when over a link work.\n\t\t\tpreventDefault(e)\n\n\t\t\tif (!selectToolActive) return\n\t\t\tconst link = e.target.closest('a')?.getAttribute('href') ?? ''\n\t\t\t// We don't get the mouseup event later because we preventDefault\n\t\t\t// so we have to do it manually.\n\t\t\tconst handlePointerUp = (e: TLEventInfo) => {\n\t\t\t\tif (e.name !== 'pointer_up' || !link) return\n\n\t\t\t\tif (!isDragging.current) {\n\t\t\t\t\topenWindow(link, '_blank', false)\n\t\t\t\t}\n\t\t\t\teditor.off('event', handlePointerUp)\n\t\t\t}\n\t\t\teditor.on('event', handlePointerUp)\n\t\t}\n\t}\n\n\t// Should be guarded higher up so that this doesn't render... but repeated here. This should never be true.\n\tif (!isEditing && isEmpty) return null\n\n\t// TODO: probably combine tl-text and tl-arrow eventually\n\tconst cssPrefix = classNamePrefix || 'tl-text'\n\treturn (\n\t\t<div\n\t\t\tclassName={classNames(\n\t\t\t\t`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`,\n\t\t\t\tshowTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'\n\t\t\t)}\n\t\t\taria-hidden={!isEditing}\n\t\t\tdata-font={font}\n\t\t\tdata-align={align}\n\t\t\tdata-hastext={!isEmpty}\n\t\t\tdata-isediting={isEditing}\n\t\t\tdata-textwrap={!!wrap}\n\t\t\tdata-isselected={isSelected}\n\t\t\tstyle={{\n\t\t\t\tjustifyContent: align === 'middle' || legacyAlign ? 'center' : align,\n\t\t\t\talignItems: verticalAlign === 'middle' ? 'center' : verticalAlign,\n\t\t\t\tpadding,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={`${cssPrefix}-label__inner tl-text-content__wrapper`}\n\t\t\t\tstyle={{\n\t\t\t\t\tfontSize,\n\t\t\t\t\tlineHeight: lineHeight.toString(),\n\t\t\t\t\tminHeight: Math.floor(fontSize * lineHeight) + 'px',\n\t\t\t\t\tminWidth: Math.ceil(textWidth || 0),\n\t\t\t\t\tcolor: labelColor,\n\t\t\t\t\twidth: textWidth ? Math.ceil(textWidth) : undefined,\n\t\t\t\t\theight: textHeight ? Math.ceil(textHeight) : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className={`${cssPrefix} tl-text tl-text-content`} dir=\"auto\">\n\t\t\t\t\t{richText && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"tl-rich-text\"\n\t\t\t\t\t\t\tdata-is-select-tool-active={selectToolActive}\n\t\t\t\t\t\t\t// todo: see if I can abuse this\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: html || '' }}\n\t\t\t\t\t\t\tonPointerDown={handlePointerDown}\n\t\t\t\t\t\t\tdata-is-ready-for-editing={isReadyForEditing}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{(isReadyForEditing || isSelected) && (\n\t\t\t\t\t<RichTextArea\n\t\t\t\t\t\t// Fudge the ref type because we're using forwardRef and it's not typed correctly.\n\t\t\t\t\t\tref={rInput as any}\n\t\t\t\t\t\trichText={richText}\n\t\t\t\t\t\tisEditing={isEditing}\n\t\t\t\t\t\tshapeId={shapeId}\n\t\t\t\t\t\t{...editableTextRest}\n\t\t\t\t\t\thasCustomTabBehavior={hasCustomTabBehavior}\n\t\t\t\t\t\thandleKeyDown={handleKeyDownCustom ?? editableTextRest.handleKeyDown}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t)\n})\n\n/** @public */\nexport interface RichTextSVGProps {\n\tbounds: Box\n\trichText: TLRichText\n\tfontSize: number\n\tfont: TLDefaultFontStyle\n\talign: TLDefaultHorizontalAlignStyle\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\twrap?: boolean\n\tlabelColor: string\n\tpadding: number\n\tshowTextOutline?: boolean\n}\n\n/**\n * Renders a rich text string as SVG given bounds and text properties.\n *\n * @public @react\n */\nexport function RichTextSVG({\n\tbounds,\n\trichText,\n\tfontSize,\n\tfont,\n\talign,\n\tverticalAlign,\n\twrap,\n\tlabelColor,\n\tpadding,\n\tshowTextOutline = true,\n}: RichTextSVGProps) {\n\tconst editor = useEditor()\n\tconst html = renderHtmlFromRichText(editor, richText)\n\tconst textAlign =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('start' as const)\n\t\t\t\t: ('end' as const)\n\tconst justifyContent =\n\t\talign === 'middle'\n\t\t\t? ('center' as const)\n\t\t\t: align === 'start'\n\t\t\t\t? ('flex-start' as const)\n\t\t\t\t: ('flex-end' as const)\n\tconst alignItems =\n\t\tverticalAlign === 'middle' ? 'center' : verticalAlign === 'start' ? 'flex-start' : 'flex-end'\n\tconst wrapperStyle = {\n\t\tdisplay: 'flex',\n\t\tfontFamily: DefaultFontFamilies[font],\n\t\theight: `100%`,\n\t\tjustifyContent,\n\t\talignItems,\n\t\tpadding: `${padding}px`,\n\t}\n\tconst style = {\n\t\tfontSize: `${fontSize}px`,\n\t\twrap: wrap ? 'wrap' : 'nowrap',\n\t\tcolor: labelColor,\n\t\tlineHeight: TEXT_PROPS.lineHeight,\n\t\ttextAlign,\n\t\twidth: '100%',\n\t\twordWrap: 'break-word' as const,\n\t\toverflowWrap: 'break-word' as const,\n\t\twhiteSpace: 'pre-wrap',\n\t\ttextShadow: showTextOutline ? 'var(--tl-text-outline)' : 'none',\n\t}\n\n\treturn (\n\t\t<foreignObject\n\t\t\tx={bounds.minX}\n\t\t\ty={bounds.minY}\n\t\t\twidth={bounds.w}\n\t\t\theight={bounds.h}\n\t\t\tclassName={classNames(\n\t\t\t\t'tl-export-embed-styles tl-rich-text tl-rich-text-svg',\n\t\t\t\tshowTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'\n\t\t\t)}\n\t\t>\n\t\t\t<div style={wrapperStyle}>\n\t\t\t\t<div dangerouslySetInnerHTML={{ __html: html }} style={style} />\n\t\t\t</div>\n\t\t</foreignObject>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["RichTextLabel", "e"]
7
7
  }
@@ -37,7 +37,8 @@ class TextShapeUtil extends ShapeUtil {
37
37
  static props = textShapeProps;
38
38
  static migrations = textShapeMigrations;
39
39
  options = {
40
- extraArrowHorizontalPadding: 10
40
+ extraArrowHorizontalPadding: 10,
41
+ showTextOutline: true
41
42
  };
42
43
  getDefaultProps() {
43
44
  return {
@@ -108,6 +109,7 @@ class TextShapeUtil extends ShapeUtil {
108
109
  isSelected,
109
110
  textWidth: width,
110
111
  textHeight: height,
112
+ showTextOutline: this.options.showTextOutline,
111
113
  style: {
112
114
  transform: `scale(${scale})`,
113
115
  transformOrigin: "top left"
@@ -139,7 +141,8 @@ class TextShapeUtil extends ShapeUtil {
139
141
  richText: shape.props.richText,
140
142
  labelColor: getColorValue(theme, shape.props.color, "solid"),
141
143
  bounds: exportBounds,
142
- padding: 0
144
+ padding: 0,
145
+ showTextOutline: this.options.showTextOutline
143
146
  }
144
147
  );
145
148
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/text/TextShapeUtil.tsx"],
4
- "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEditor,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLGeometryOpts,\n\tTLResizeInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateComputedCache,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tresizeScaled,\n\ttextShapeMigrations,\n\ttextShapeProps,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n} from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport {\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\n\nconst sizeCache = createComputedCache(\n\t'text size',\n\t(editor: Editor, shape: TLTextShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\treturn getTextSize(editor, shape.props)\n\t},\n\t{ areRecordsEqual: (a, b) => a.props === b.props }\n)\n/** @public */\nexport interface TextShapeOptions {\n\t/** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */\n\textraArrowHorizontalPadding: number\n}\n\n/** @public */\nexport class TextShapeUtil extends ShapeUtil<TLTextShape> {\n\tstatic override type = 'text' as const\n\tstatic override props = textShapeProps\n\tstatic override migrations = textShapeMigrations\n\n\toverride options: TextShapeOptions = {\n\t\textraArrowHorizontalPadding: 10,\n\t}\n\n\tgetDefaultProps(): TLTextShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\tsize: 'm',\n\t\t\tw: 8,\n\t\t\tfont: 'draw',\n\t\t\ttextAlign: 'start',\n\t\t\tautoSize: true,\n\t\t\tscale: 1,\n\t\t\trichText: toRichText(''),\n\t\t}\n\t}\n\n\tgetMinDimensions(shape: TLTextShape) {\n\t\treturn sizeCache.get(this.editor, shape.id)!\n\t}\n\n\tgetGeometry(shape: TLTextShape, opts: TLGeometryOpts) {\n\t\tconst { scale } = shape.props\n\t\tconst { width, height } = this.getMinDimensions(shape)!\n\t\tconst context = opts?.context ?? 'none'\n\t\treturn new Rectangle2d({\n\t\t\tx:\n\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t? -this.options.extraArrowHorizontalPadding\n\t\t\t\t\t: 0) * scale,\n\t\t\twidth:\n\t\t\t\t(width +\n\t\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t\t? this.options.extraArrowHorizontalPadding * 2\n\t\t\t\t\t\t: 0)) *\n\t\t\t\tscale,\n\t\t\theight: height * scale,\n\t\t\tisFilled: true,\n\t\t\tisLabel: true,\n\t\t})\n\t}\n\n\toverride getFontFaces(shape: TLTextShape) {\n\t\t// no need for an empty rich text check here\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\toverride getText(shape: TLTextShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t} // WAIT NO THIS IS HARD CODED IN THE RESIZE HANDLER\n\n\tcomponent(shape: TLTextShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\tprops: { font, size, richText, color, scale, textAlign },\n\t\t} = shape\n\n\t\tconst { width, height } = this.getMinDimensions(shape)\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst handleKeyDown = useTextShapeKeydownHandler(id)\n\n\t\treturn (\n\t\t\t<RichTextLabel\n\t\t\t\tshapeId={id}\n\t\t\t\tclassNamePrefix=\"tl-text-shape\"\n\t\t\t\ttype=\"text\"\n\t\t\t\tfont={font}\n\t\t\t\tfontSize={FONT_SIZES[size]}\n\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\talign={textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={richText}\n\t\t\t\tlabelColor={getColorValue(theme, color, 'solid')}\n\t\t\t\tisSelected={isSelected}\n\t\t\t\ttextWidth={width}\n\t\t\t\ttextHeight={height}\n\t\t\t\tstyle={{\n\t\t\t\t\ttransform: `scale(${scale})`,\n\t\t\t\t\ttransformOrigin: 'top left',\n\t\t\t\t}}\n\t\t\t\twrap\n\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t/>\n\t\t)\n\t}\n\n\tindicator(shape: TLTextShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst editor = useEditor()\n\t\tif (shape.props.autoSize && editor.getEditingShapeId() === shape.id) return null\n\t\treturn <rect width={toDomPrecision(bounds.width)} height={toDomPrecision(bounds.height)} />\n\t}\n\n\toverride toSvg(shape: TLTextShape, ctx: SvgExportContext) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst width = bounds.width / (shape.props.scale ?? 1)\n\t\tconst height = bounds.height / (shape.props.scale ?? 1)\n\n\t\tconst theme = getDefaultColorTheme(ctx)\n\n\t\tconst exportBounds = new Box(0, 0, width, height)\n\t\treturn (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'solid')}\n\t\t\t\tbounds={exportBounds}\n\t\t\t\tpadding={0}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride onResize(shape: TLTextShape, info: TLResizeInfo<TLTextShape>) {\n\t\tconst { newPoint, initialBounds, initialShape, scaleX, handle } = info\n\n\t\tif (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) {\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\t...resizeScaled(shape, info),\n\t\t\t}\n\t\t} else {\n\t\t\tconst nextWidth = Math.max(1, Math.abs(initialBounds.width * scaleX))\n\t\t\tconst { x, y } =\n\t\t\t\tscaleX < 0 ? Vec.Sub(newPoint, Vec.FromAngle(shape.rotation).mul(nextWidth)) : newPoint\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tprops: {\n\t\t\t\t\tw: nextWidth / initialShape.props.scale,\n\t\t\t\t\tautoSize: false,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onEditEnd(shape: TLTextShape) {\n\t\t// todo: find a way to check if the rich text has any nodes that aren't empty spaces\n\t\tconst trimmedText = renderPlaintextFromRichText(this.editor, shape.props.richText).trimEnd()\n\n\t\tif (trimmedText.length === 0) {\n\t\t\tthis.editor.deleteShapes([shape.id])\n\t\t}\n\t}\n\n\toverride onBeforeUpdate(prev: TLTextShape, next: TLTextShape) {\n\t\tif (!next.props.autoSize) return\n\n\t\tconst styleDidChange =\n\t\t\tprev.props.size !== next.props.size ||\n\t\t\tprev.props.textAlign !== next.props.textAlign ||\n\t\t\tprev.props.font !== next.props.font ||\n\t\t\t(prev.props.scale !== 1 && next.props.scale === 1)\n\n\t\tconst textDidChange = !isEqual(prev.props.richText, next.props.richText)\n\n\t\t// Only update position if either changed\n\t\tif (!styleDidChange && !textDidChange) return\n\n\t\t// Might return a cached value for the bounds\n\t\tconst boundsA = this.getMinDimensions(prev)\n\n\t\t// Will always be a fresh call to getTextSize\n\t\tconst boundsB = getTextSize(this.editor, next.props)\n\n\t\tconst wA = boundsA.width * prev.props.scale\n\t\tconst hA = boundsA.height * prev.props.scale\n\t\tconst wB = boundsB.width * next.props.scale\n\t\tconst hB = boundsB.height * next.props.scale\n\n\t\tlet delta: Vec | undefined\n\n\t\tswitch (next.props.textAlign) {\n\t\t\tcase 'middle': {\n\t\t\t\tdelta = new Vec((wB - wA) / 2, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'end': {\n\t\t\t\tdelta = new Vec(wB - wA, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tif (textDidChange) break\n\t\t\t\tdelta = new Vec(0, (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (delta) {\n\t\t\t// account for shape rotation when writing text:\n\t\t\tdelta.rot(next.rotation)\n\t\t\tconst { x, y } = next\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tx: x - delta.x,\n\t\t\t\ty: y - delta.y,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t}\n\t}\n\n\t// \ttodo: The edge doubleclicking feels like a mistake more often than\n\t// not, especially on multiline text. Removed June 16 2024\n\n\t// override onDoubleClickEdge = (shape: TLTextShape) => {\n\t// \t// If the shape has a fixed width, set it to autoSize.\n\t// \tif (!shape.props.autoSize) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tautoSize: true,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// \t// If the shape is scaled, reset the scale to 1.\n\t// \tif (shape.props.scale !== 1) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tscale: 1,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// }\n}\n\nfunction getTextSize(editor: Editor, props: TLTextShape['props']) {\n\tconst { font, richText, size, w } = props\n\n\tconst minWidth = 16\n\tconst fontSize = FONT_SIZES[size]\n\n\tconst maybeFixedWidth = props.autoSize ? null : Math.max(minWidth, Math.floor(w))\n\n\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\tconst result = editor.textMeasure.measureHtml(html, {\n\t\t...TEXT_PROPS,\n\t\tfontFamily: FONT_FAMILIES[font],\n\t\tfontSize: fontSize,\n\t\tmaxWidth: maybeFixedWidth,\n\t})\n\n\t// If we're autosizing the measureText will essentially `Math.floor`\n\t// the numbers so `19` rather than `19.3`, this means we must +1 to\n\t// whatever we get to avoid wrapping.\n\treturn {\n\t\twidth: maybeFixedWidth ?? Math.max(minWidth, result.w + 1),\n\t\theight: Math.max(fontSize, result.h),\n\t}\n}\n\nfunction useTextShapeKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (editor.getEditingShapeId() !== id) return\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase 'Enter': {\n\t\t\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t\t\teditor.complete()\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, id]\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\nimport {\n\tBox,\n\tEditor,\n\tRectangle2d,\n\tShapeUtil,\n\tSvgExportContext,\n\tTLGeometryOpts,\n\tTLResizeInfo,\n\tTLShapeId,\n\tTLTextShape,\n\tVec,\n\tcreateComputedCache,\n\tgetColorValue,\n\tgetDefaultColorTheme,\n\tgetFontsFromRichText,\n\tisEqual,\n\tresizeScaled,\n\ttextShapeMigrations,\n\ttextShapeProps,\n\ttoDomPrecision,\n\ttoRichText,\n\tuseEditor,\n} from '@tldraw/editor'\nimport { useCallback } from 'react'\nimport {\n\trenderHtmlFromRichTextForMeasurement,\n\trenderPlaintextFromRichText,\n} from '../../utils/text/richText'\nimport { RichTextLabel, RichTextSVG } from '../shared/RichTextLabel'\nimport { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shared/default-shape-constants'\nimport { useDefaultColorTheme } from '../shared/useDefaultColorTheme'\n\nconst sizeCache = createComputedCache(\n\t'text size',\n\t(editor: Editor, shape: TLTextShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\treturn getTextSize(editor, shape.props)\n\t},\n\t{ areRecordsEqual: (a, b) => a.props === b.props }\n)\n/** @public */\nexport interface TextShapeOptions {\n\t/** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */\n\textraArrowHorizontalPadding: number\n\t/** Whether to show the outline of the text shape (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */\n\tshowTextOutline: boolean\n}\n\n/** @public */\nexport class TextShapeUtil extends ShapeUtil<TLTextShape> {\n\tstatic override type = 'text' as const\n\tstatic override props = textShapeProps\n\tstatic override migrations = textShapeMigrations\n\n\toverride options: TextShapeOptions = {\n\t\textraArrowHorizontalPadding: 10,\n\t\tshowTextOutline: true,\n\t}\n\n\tgetDefaultProps(): TLTextShape['props'] {\n\t\treturn {\n\t\t\tcolor: 'black',\n\t\t\tsize: 'm',\n\t\t\tw: 8,\n\t\t\tfont: 'draw',\n\t\t\ttextAlign: 'start',\n\t\t\tautoSize: true,\n\t\t\tscale: 1,\n\t\t\trichText: toRichText(''),\n\t\t}\n\t}\n\n\tgetMinDimensions(shape: TLTextShape) {\n\t\treturn sizeCache.get(this.editor, shape.id)!\n\t}\n\n\tgetGeometry(shape: TLTextShape, opts: TLGeometryOpts) {\n\t\tconst { scale } = shape.props\n\t\tconst { width, height } = this.getMinDimensions(shape)!\n\t\tconst context = opts?.context ?? 'none'\n\t\treturn new Rectangle2d({\n\t\t\tx:\n\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t? -this.options.extraArrowHorizontalPadding\n\t\t\t\t\t: 0) * scale,\n\t\t\twidth:\n\t\t\t\t(width +\n\t\t\t\t\t(context === '@tldraw/arrow-without-arrowhead'\n\t\t\t\t\t\t? this.options.extraArrowHorizontalPadding * 2\n\t\t\t\t\t\t: 0)) *\n\t\t\t\tscale,\n\t\t\theight: height * scale,\n\t\t\tisFilled: true,\n\t\t\tisLabel: true,\n\t\t})\n\t}\n\n\toverride getFontFaces(shape: TLTextShape) {\n\t\t// no need for an empty rich text check here\n\t\treturn getFontsFromRichText(this.editor, shape.props.richText, {\n\t\t\tfamily: `tldraw_${shape.props.font}`,\n\t\t\tweight: 'normal',\n\t\t\tstyle: 'normal',\n\t\t})\n\t}\n\n\toverride getText(shape: TLTextShape) {\n\t\treturn renderPlaintextFromRichText(this.editor, shape.props.richText)\n\t}\n\n\toverride canEdit() {\n\t\treturn true\n\t}\n\n\toverride isAspectRatioLocked() {\n\t\treturn true\n\t} // WAIT NO THIS IS HARD CODED IN THE RESIZE HANDLER\n\n\tcomponent(shape: TLTextShape) {\n\t\tconst {\n\t\t\tid,\n\t\t\tprops: { font, size, richText, color, scale, textAlign },\n\t\t} = shape\n\n\t\tconst { width, height } = this.getMinDimensions(shape)\n\t\tconst isSelected = shape.id === this.editor.getOnlySelectedShapeId()\n\t\tconst theme = useDefaultColorTheme()\n\t\tconst handleKeyDown = useTextShapeKeydownHandler(id)\n\n\t\treturn (\n\t\t\t<RichTextLabel\n\t\t\t\tshapeId={id}\n\t\t\t\tclassNamePrefix=\"tl-text-shape\"\n\t\t\t\ttype=\"text\"\n\t\t\t\tfont={font}\n\t\t\t\tfontSize={FONT_SIZES[size]}\n\t\t\t\tlineHeight={TEXT_PROPS.lineHeight}\n\t\t\t\talign={textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={richText}\n\t\t\t\tlabelColor={getColorValue(theme, color, 'solid')}\n\t\t\t\tisSelected={isSelected}\n\t\t\t\ttextWidth={width}\n\t\t\t\ttextHeight={height}\n\t\t\t\tshowTextOutline={this.options.showTextOutline}\n\t\t\t\tstyle={{\n\t\t\t\t\ttransform: `scale(${scale})`,\n\t\t\t\t\ttransformOrigin: 'top left',\n\t\t\t\t}}\n\t\t\t\twrap\n\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t/>\n\t\t)\n\t}\n\n\tindicator(shape: TLTextShape) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst editor = useEditor()\n\t\tif (shape.props.autoSize && editor.getEditingShapeId() === shape.id) return null\n\t\treturn <rect width={toDomPrecision(bounds.width)} height={toDomPrecision(bounds.height)} />\n\t}\n\n\toverride toSvg(shape: TLTextShape, ctx: SvgExportContext) {\n\t\tconst bounds = this.editor.getShapeGeometry(shape).bounds\n\t\tconst width = bounds.width / (shape.props.scale ?? 1)\n\t\tconst height = bounds.height / (shape.props.scale ?? 1)\n\n\t\tconst theme = getDefaultColorTheme(ctx)\n\n\t\tconst exportBounds = new Box(0, 0, width, height)\n\t\treturn (\n\t\t\t<RichTextSVG\n\t\t\t\tfontSize={FONT_SIZES[shape.props.size]}\n\t\t\t\tfont={shape.props.font}\n\t\t\t\talign={shape.props.textAlign}\n\t\t\t\tverticalAlign=\"middle\"\n\t\t\t\trichText={shape.props.richText}\n\t\t\t\tlabelColor={getColorValue(theme, shape.props.color, 'solid')}\n\t\t\t\tbounds={exportBounds}\n\t\t\t\tpadding={0}\n\t\t\t\tshowTextOutline={this.options.showTextOutline}\n\t\t\t/>\n\t\t)\n\t}\n\n\toverride onResize(shape: TLTextShape, info: TLResizeInfo<TLTextShape>) {\n\t\tconst { newPoint, initialBounds, initialShape, scaleX, handle } = info\n\n\t\tif (info.mode === 'scale_shape' || (handle !== 'right' && handle !== 'left')) {\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\t...resizeScaled(shape, info),\n\t\t\t}\n\t\t} else {\n\t\t\tconst nextWidth = Math.max(1, Math.abs(initialBounds.width * scaleX))\n\t\t\tconst { x, y } =\n\t\t\t\tscaleX < 0 ? Vec.Sub(newPoint, Vec.FromAngle(shape.rotation).mul(nextWidth)) : newPoint\n\n\t\t\treturn {\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tprops: {\n\t\t\t\t\tw: nextWidth / initialShape.props.scale,\n\t\t\t\t\tautoSize: false,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onEditEnd(shape: TLTextShape) {\n\t\t// todo: find a way to check if the rich text has any nodes that aren't empty spaces\n\t\tconst trimmedText = renderPlaintextFromRichText(this.editor, shape.props.richText).trimEnd()\n\n\t\tif (trimmedText.length === 0) {\n\t\t\tthis.editor.deleteShapes([shape.id])\n\t\t}\n\t}\n\n\toverride onBeforeUpdate(prev: TLTextShape, next: TLTextShape) {\n\t\tif (!next.props.autoSize) return\n\n\t\tconst styleDidChange =\n\t\t\tprev.props.size !== next.props.size ||\n\t\t\tprev.props.textAlign !== next.props.textAlign ||\n\t\t\tprev.props.font !== next.props.font ||\n\t\t\t(prev.props.scale !== 1 && next.props.scale === 1)\n\n\t\tconst textDidChange = !isEqual(prev.props.richText, next.props.richText)\n\n\t\t// Only update position if either changed\n\t\tif (!styleDidChange && !textDidChange) return\n\n\t\t// Might return a cached value for the bounds\n\t\tconst boundsA = this.getMinDimensions(prev)\n\n\t\t// Will always be a fresh call to getTextSize\n\t\tconst boundsB = getTextSize(this.editor, next.props)\n\n\t\tconst wA = boundsA.width * prev.props.scale\n\t\tconst hA = boundsA.height * prev.props.scale\n\t\tconst wB = boundsB.width * next.props.scale\n\t\tconst hB = boundsB.height * next.props.scale\n\n\t\tlet delta: Vec | undefined\n\n\t\tswitch (next.props.textAlign) {\n\t\t\tcase 'middle': {\n\t\t\t\tdelta = new Vec((wB - wA) / 2, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'end': {\n\t\t\t\tdelta = new Vec(wB - wA, textDidChange ? 0 : (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tif (textDidChange) break\n\t\t\t\tdelta = new Vec(0, (hB - hA) / 2)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (delta) {\n\t\t\t// account for shape rotation when writing text:\n\t\t\tdelta.rot(next.rotation)\n\t\t\tconst { x, y } = next\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tx: x - delta.x,\n\t\t\t\ty: y - delta.y,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\t...next,\n\t\t\t\tprops: { ...next.props, w: wB },\n\t\t\t}\n\t\t}\n\t}\n\n\t// \ttodo: The edge doubleclicking feels like a mistake more often than\n\t// not, especially on multiline text. Removed June 16 2024\n\n\t// override onDoubleClickEdge = (shape: TLTextShape) => {\n\t// \t// If the shape has a fixed width, set it to autoSize.\n\t// \tif (!shape.props.autoSize) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tautoSize: true,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// \t// If the shape is scaled, reset the scale to 1.\n\t// \tif (shape.props.scale !== 1) {\n\t// \t\treturn {\n\t// \t\t\tid: shape.id,\n\t// \t\t\ttype: shape.type,\n\t// \t\t\tprops: {\n\t// \t\t\t\tscale: 1,\n\t// \t\t\t},\n\t// \t\t}\n\t// \t}\n\t// }\n}\n\nfunction getTextSize(editor: Editor, props: TLTextShape['props']) {\n\tconst { font, richText, size, w } = props\n\n\tconst minWidth = 16\n\tconst fontSize = FONT_SIZES[size]\n\n\tconst maybeFixedWidth = props.autoSize ? null : Math.max(minWidth, Math.floor(w))\n\n\tconst html = renderHtmlFromRichTextForMeasurement(editor, richText)\n\tconst result = editor.textMeasure.measureHtml(html, {\n\t\t...TEXT_PROPS,\n\t\tfontFamily: FONT_FAMILIES[font],\n\t\tfontSize: fontSize,\n\t\tmaxWidth: maybeFixedWidth,\n\t})\n\n\t// If we're autosizing the measureText will essentially `Math.floor`\n\t// the numbers so `19` rather than `19.3`, this means we must +1 to\n\t// whatever we get to avoid wrapping.\n\treturn {\n\t\twidth: maybeFixedWidth ?? Math.max(minWidth, result.w + 1),\n\t\theight: Math.max(fontSize, result.h),\n\t}\n}\n\nfunction useTextShapeKeydownHandler(id: TLShapeId) {\n\tconst editor = useEditor()\n\n\treturn useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (editor.getEditingShapeId() !== id) return\n\n\t\t\tswitch (e.key) {\n\t\t\t\tcase 'Enter': {\n\t\t\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t\t\teditor.complete()\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[editor, id]\n\t)\n}\n"],
5
+ "mappings": "AAmIG;AAlIH;AAAA,EACC;AAAA,EAEA;AAAA,EACA;AAAA,EAMA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,mBAAmB;AAC5B;AAAA,EACC;AAAA,EACA;AAAA,OACM;AACP,SAAS,eAAe,mBAAmB;AAC3C,SAAS,eAAe,YAAY,kBAAkB;AACtD,SAAS,4BAA4B;AAErC,MAAM,YAAY;AAAA,EACjB;AAAA,EACA,CAAC,QAAgB,UAAuB;AACvC,WAAO,MAAM,mBAAmB,KAAK;AACrC,WAAO,YAAY,QAAQ,MAAM,KAAK;AAAA,EACvC;AAAA,EACA,EAAE,iBAAiB,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM;AAClD;AAUO,MAAM,sBAAsB,UAAuB;AAAA,EACzD,OAAgB,OAAO;AAAA,EACvB,OAAgB,QAAQ;AAAA,EACxB,OAAgB,aAAa;AAAA,EAEpB,UAA4B;AAAA,IACpC,6BAA6B;AAAA,IAC7B,iBAAiB;AAAA,EAClB;AAAA,EAEA,kBAAwC;AACvC,WAAO;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,GAAG;AAAA,MACH,MAAM;AAAA,MACN,WAAW;AAAA,MACX,UAAU;AAAA,MACV,OAAO;AAAA,MACP,UAAU,WAAW,EAAE;AAAA,IACxB;AAAA,EACD;AAAA,EAEA,iBAAiB,OAAoB;AACpC,WAAO,UAAU,IAAI,KAAK,QAAQ,MAAM,EAAE;AAAA,EAC3C;AAAA,EAEA,YAAY,OAAoB,MAAsB;AACrD,UAAM,EAAE,MAAM,IAAI,MAAM;AACxB,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK,iBAAiB,KAAK;AACrD,UAAM,UAAU,MAAM,WAAW;AACjC,WAAO,IAAI,YAAY;AAAA,MACtB,IACE,YAAY,oCACV,CAAC,KAAK,QAAQ,8BACd,KAAK;AAAA,MACT,QACE,SACC,YAAY,oCACV,KAAK,QAAQ,8BAA8B,IAC3C,MACJ;AAAA,MACD,QAAQ,SAAS;AAAA,MACjB,UAAU;AAAA,MACV,SAAS;AAAA,IACV,CAAC;AAAA,EACF;AAAA,EAES,aAAa,OAAoB;AAEzC,WAAO,qBAAqB,KAAK,QAAQ,MAAM,MAAM,UAAU;AAAA,MAC9D,QAAQ,UAAU,MAAM,MAAM,IAAI;AAAA,MAClC,QAAQ;AAAA,MACR,OAAO;AAAA,IACR,CAAC;AAAA,EACF;AAAA,EAES,QAAQ,OAAoB;AACpC,WAAO,4BAA4B,KAAK,QAAQ,MAAM,MAAM,QAAQ;AAAA,EACrE;AAAA,EAES,UAAU;AAClB,WAAO;AAAA,EACR;AAAA,EAES,sBAAsB;AAC9B,WAAO;AAAA,EACR;AAAA;AAAA,EAEA,UAAU,OAAoB;AAC7B,UAAM;AAAA,MACL;AAAA,MACA,OAAO,EAAE,MAAM,MAAM,UAAU,OAAO,OAAO,UAAU;AAAA,IACxD,IAAI;AAEJ,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK,iBAAiB,KAAK;AACrD,UAAM,aAAa,MAAM,OAAO,KAAK,OAAO,uBAAuB;AACnE,UAAM,QAAQ,qBAAqB;AACnC,UAAM,gBAAgB,2BAA2B,EAAE;AAEnD,WACC;AAAA,MAAC;AAAA;AAAA,QACA,SAAS;AAAA,QACT,iBAAgB;AAAA,QAChB,MAAK;AAAA,QACL;AAAA,QACA,UAAU,WAAW,IAAI;AAAA,QACzB,YAAY,WAAW;AAAA,QACvB,OAAO;AAAA,QACP,eAAc;AAAA,QACd;AAAA,QACA,YAAY,cAAc,OAAO,OAAO,OAAO;AAAA,QAC/C;AAAA,QACA,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,iBAAiB,KAAK,QAAQ;AAAA,QAC9B,OAAO;AAAA,UACN,WAAW,SAAS,KAAK;AAAA,UACzB,iBAAiB;AAAA,QAClB;AAAA,QACA,MAAI;AAAA,QACJ,WAAW;AAAA;AAAA,IACZ;AAAA,EAEF;AAAA,EAEA,UAAU,OAAoB;AAC7B,UAAM,SAAS,KAAK,OAAO,iBAAiB,KAAK,EAAE;AACnD,UAAM,SAAS,UAAU;AACzB,QAAI,MAAM,MAAM,YAAY,OAAO,kBAAkB,MAAM,MAAM,GAAI,QAAO;AAC5E,WAAO,oBAAC,UAAK,OAAO,eAAe,OAAO,KAAK,GAAG,QAAQ,eAAe,OAAO,MAAM,GAAG;AAAA,EAC1F;AAAA,EAES,MAAM,OAAoB,KAAuB;AACzD,UAAM,SAAS,KAAK,OAAO,iBAAiB,KAAK,EAAE;AACnD,UAAM,QAAQ,OAAO,SAAS,MAAM,MAAM,SAAS;AACnD,UAAM,SAAS,OAAO,UAAU,MAAM,MAAM,SAAS;AAErD,UAAM,QAAQ,qBAAqB,GAAG;AAEtC,UAAM,eAAe,IAAI,IAAI,GAAG,GAAG,OAAO,MAAM;AAChD,WACC;AAAA,MAAC;AAAA;AAAA,QACA,UAAU,WAAW,MAAM,MAAM,IAAI;AAAA,QACrC,MAAM,MAAM,MAAM;AAAA,QAClB,OAAO,MAAM,MAAM;AAAA,QACnB,eAAc;AAAA,QACd,UAAU,MAAM,MAAM;AAAA,QACtB,YAAY,cAAc,OAAO,MAAM,MAAM,OAAO,OAAO;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,iBAAiB,KAAK,QAAQ;AAAA;AAAA,IAC/B;AAAA,EAEF;AAAA,EAES,SAAS,OAAoB,MAAiC;AACtE,UAAM,EAAE,UAAU,eAAe,cAAc,QAAQ,OAAO,IAAI;AAElE,QAAI,KAAK,SAAS,iBAAkB,WAAW,WAAW,WAAW,QAAS;AAC7E,aAAO;AAAA,QACN,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,GAAG,aAAa,OAAO,IAAI;AAAA,MAC5B;AAAA,IACD,OAAO;AACN,YAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,QAAQ,MAAM,CAAC;AACpE,YAAM,EAAE,GAAG,EAAE,IACZ,SAAS,IAAI,IAAI,IAAI,UAAU,IAAI,UAAU,MAAM,QAAQ,EAAE,IAAI,SAAS,CAAC,IAAI;AAEhF,aAAO;AAAA,QACN,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ;AAAA,QACA;AAAA,QACA,OAAO;AAAA,UACN,GAAG,YAAY,aAAa,MAAM;AAAA,UAClC,UAAU;AAAA,QACX;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAES,UAAU,OAAoB;AAEtC,UAAM,cAAc,4BAA4B,KAAK,QAAQ,MAAM,MAAM,QAAQ,EAAE,QAAQ;AAE3F,QAAI,YAAY,WAAW,GAAG;AAC7B,WAAK,OAAO,aAAa,CAAC,MAAM,EAAE,CAAC;AAAA,IACpC;AAAA,EACD;AAAA,EAES,eAAe,MAAmB,MAAmB;AAC7D,QAAI,CAAC,KAAK,MAAM,SAAU;AAE1B,UAAM,iBACL,KAAK,MAAM,SAAS,KAAK,MAAM,QAC/B,KAAK,MAAM,cAAc,KAAK,MAAM,aACpC,KAAK,MAAM,SAAS,KAAK,MAAM,QAC9B,KAAK,MAAM,UAAU,KAAK,KAAK,MAAM,UAAU;AAEjD,UAAM,gBAAgB,CAAC,QAAQ,KAAK,MAAM,UAAU,KAAK,MAAM,QAAQ;AAGvE,QAAI,CAAC,kBAAkB,CAAC,cAAe;AAGvC,UAAM,UAAU,KAAK,iBAAiB,IAAI;AAG1C,UAAM,UAAU,YAAY,KAAK,QAAQ,KAAK,KAAK;AAEnD,UAAM,KAAK,QAAQ,QAAQ,KAAK,MAAM;AACtC,UAAM,KAAK,QAAQ,SAAS,KAAK,MAAM;AACvC,UAAM,KAAK,QAAQ,QAAQ,KAAK,MAAM;AACtC,UAAM,KAAK,QAAQ,SAAS,KAAK,MAAM;AAEvC,QAAI;AAEJ,YAAQ,KAAK,MAAM,WAAW;AAAA,MAC7B,KAAK,UAAU;AACd,gBAAQ,IAAI,KAAK,KAAK,MAAM,GAAG,gBAAgB,KAAK,KAAK,MAAM,CAAC;AAChE;AAAA,MACD;AAAA,MACA,KAAK,OAAO;AACX,gBAAQ,IAAI,IAAI,KAAK,IAAI,gBAAgB,KAAK,KAAK,MAAM,CAAC;AAC1D;AAAA,MACD;AAAA,MACA,SAAS;AACR,YAAI,cAAe;AACnB,gBAAQ,IAAI,IAAI,IAAI,KAAK,MAAM,CAAC;AAChC;AAAA,MACD;AAAA,IACD;AAEA,QAAI,OAAO;AAEV,YAAM,IAAI,KAAK,QAAQ;AACvB,YAAM,EAAE,GAAG,EAAE,IAAI;AACjB,aAAO;AAAA,QACN,GAAG;AAAA,QACH,GAAG,IAAI,MAAM;AAAA,QACb,GAAG,IAAI,MAAM;AAAA,QACb,OAAO,EAAE,GAAG,KAAK,OAAO,GAAG,GAAG;AAAA,MAC/B;AAAA,IACD,OAAO;AACN,aAAO;AAAA,QACN,GAAG;AAAA,QACH,OAAO,EAAE,GAAG,KAAK,OAAO,GAAG,GAAG;AAAA,MAC/B;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BD;AAEA,SAAS,YAAY,QAAgB,OAA6B;AACjE,QAAM,EAAE,MAAM,UAAU,MAAM,EAAE,IAAI;AAEpC,QAAM,WAAW;AACjB,QAAM,WAAW,WAAW,IAAI;AAEhC,QAAM,kBAAkB,MAAM,WAAW,OAAO,KAAK,IAAI,UAAU,KAAK,MAAM,CAAC,CAAC;AAEhF,QAAM,OAAO,qCAAqC,QAAQ,QAAQ;AAClE,QAAM,SAAS,OAAO,YAAY,YAAY,MAAM;AAAA,IACnD,GAAG;AAAA,IACH,YAAY,cAAc,IAAI;AAAA,IAC9B;AAAA,IACA,UAAU;AAAA,EACX,CAAC;AAKD,SAAO;AAAA,IACN,OAAO,mBAAmB,KAAK,IAAI,UAAU,OAAO,IAAI,CAAC;AAAA,IACzD,QAAQ,KAAK,IAAI,UAAU,OAAO,CAAC;AAAA,EACpC;AACD;AAEA,SAAS,2BAA2B,IAAe;AAClD,QAAM,SAAS,UAAU;AAEzB,SAAO;AAAA,IACN,CAAC,MAAqB;AACrB,UAAI,OAAO,kBAAkB,MAAM,GAAI;AAEvC,cAAQ,EAAE,KAAK;AAAA,QACd,KAAK,SAAS;AACb,cAAI,EAAE,WAAW,EAAE,SAAS;AAC3B,mBAAO,SAAS;AAAA,UACjB;AACA;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,CAAC,QAAQ,EAAE;AAAA,EACZ;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "4.3.0-canary.b2e9b1218a6b";
1
+ const version = "4.3.0-canary.b9cf1eed518f";
2
2
  const publishDates = {
3
3
  major: "2025-09-18T14:39:22.803Z",
4
- minor: "2025-12-06T16:52:15.885Z",
5
- patch: "2025-12-06T16:52:15.885Z"
4
+ minor: "2025-12-07T14:49:42.949Z",
5
+ patch: "2025-12-07T14:49:42.949Z"
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.3.0-canary.b2e9b1218a6b'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-06T16:52:15.885Z',\n\tpatch: '2025-12-06T16:52:15.885Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.3.0-canary.b9cf1eed518f'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-07T14:49:42.949Z',\n\tpatch: '2025-12-07T14:49:42.949Z',\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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tldraw",
3
3
  "description": "A tiny little drawing editor.",
4
- "version": "4.3.0-canary.b2e9b1218a6b",
4
+ "version": "4.3.0-canary.b9cf1eed518f",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -62,8 +62,8 @@
62
62
  "@tiptap/pm": "^3.6.2",
63
63
  "@tiptap/react": "^3.6.2",
64
64
  "@tiptap/starter-kit": "^3.6.2",
65
- "@tldraw/editor": "4.3.0-canary.b2e9b1218a6b",
66
- "@tldraw/store": "4.3.0-canary.b2e9b1218a6b",
65
+ "@tldraw/editor": "4.3.0-canary.b9cf1eed518f",
66
+ "@tldraw/store": "4.3.0-canary.b9cf1eed518f",
67
67
  "classnames": "^2.5.1",
68
68
  "hotkeys-js": "^3.13.9",
69
69
  "idb": "^7.1.1",
@@ -120,6 +120,8 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
120
120
 
121
121
  shouldBeExact: (editor: Editor) => editor.inputs.altKey,
122
122
  shouldIgnoreTargets: (editor: Editor) => editor.inputs.ctrlKey,
123
+
124
+ showTextOutline: true,
123
125
  }
124
126
 
125
127
  override canEdit() {
@@ -795,6 +797,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
795
797
  textWidth={labelPosition.box.w - ARROW_LABEL_PADDING * 2 * shape.props.scale}
796
798
  isSelected={isSelected}
797
799
  padding={0}
800
+ showTextOutline={this.options.showTextOutline}
798
801
  style={{
799
802
  transform: `translate(${labelPosition.box.center.x}px, ${labelPosition.box.center.y}px)`,
800
803
  }}
@@ -945,7 +948,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
945
948
  .box.clone()
946
949
  .expandBy(-ARROW_LABEL_PADDING * shape.props.scale)}
947
950
  padding={0}
948
- showTextOutline={true}
951
+ showTextOutline={this.options.showTextOutline}
949
952
  />
950
953
  </g>
951
954
  )
@@ -95,6 +95,8 @@ export interface ArrowShapeOptions {
95
95
  * When creating an arrow, should it bind to the target shape.
96
96
  */
97
97
  shouldIgnoreTargets(editor: Editor): boolean
98
+ /** Whether to show the outline of the arrow shape's label (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */
99
+ readonly showTextOutline: boolean
98
100
  }
99
101
 
100
102
  /** @public */
@@ -55,6 +55,10 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
55
55
  static override props = geoShapeProps
56
56
  static override migrations = geoShapeMigrations
57
57
 
58
+ override options = {
59
+ showTextOutline: true,
60
+ }
61
+
58
62
  override canEdit() {
59
63
  return true
60
64
  }
@@ -225,6 +229,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
225
229
  isSelected={isOnlySelected}
226
230
  labelColor={getColorValue(theme, props.labelColor, 'solid')}
227
231
  wrap
232
+ showTextOutline={this.options.showTextOutline}
228
233
  />
229
234
  </HTMLContainer>
230
235
  )}
@@ -282,6 +287,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
282
287
  labelColor={getColorValue(theme, props.labelColor, 'solid')}
283
288
  bounds={bounds}
284
289
  padding={LABEL_PADDING}
290
+ showTextOutline={this.options.showTextOutline}
285
291
  />
286
292
  )
287
293
  }
@@ -316,6 +316,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
316
316
  wrap
317
317
  padding={LABEL_PADDING * scale}
318
318
  hasCustomTabBehavior
319
+ showTextOutline={false}
319
320
  onKeyDown={handleKeyDown}
320
321
  />
321
322
  )}
@@ -7,6 +7,7 @@ import {
7
7
  TLDefaultVerticalAlignStyle,
8
8
  TLShapeId,
9
9
  } from '@tldraw/editor'
10
+ import classNames from 'classnames'
10
11
  import React from 'react'
11
12
  import { PlainTextArea } from '../text/PlainTextArea'
12
13
  import { TextHelpers } from './TextHelpers'
@@ -34,6 +35,7 @@ export interface PlainTextLabelProps {
34
35
  textWidth?: number
35
36
  textHeight?: number
36
37
  padding?: number
38
+ showTextOutline?: boolean
37
39
  }
38
40
 
39
41
  /**
@@ -61,6 +63,7 @@ export const PlainTextLabel = React.memo(function PlainTextLabel({
61
63
  style,
62
64
  textWidth,
63
65
  textHeight,
66
+ showTextOutline = true,
64
67
  }: PlainTextLabelProps) {
65
68
  const { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =
66
69
  useEditablePlainText(shapeId, type, plaintext)
@@ -109,7 +112,13 @@ export const PlainTextLabel = React.memo(function PlainTextLabel({
109
112
  height: textHeight ? Math.ceil(textHeight) : undefined,
110
113
  }}
111
114
  >
112
- <div className={`${cssPrefix} tl-text tl-text-content`} dir="auto">
115
+ <div
116
+ className={classNames(
117
+ `${cssPrefix} tl-text tl-text-content`,
118
+ showTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'
119
+ )}
120
+ dir="auto"
121
+ >
113
122
  {finalPlainText.split('\n').map((lineOfText, index) => (
114
123
  <div key={index} dir="auto">
115
124
  {lineOfText}