@tldraw/editor 3.13.0-canary.6cb770c78958 → 3.13.0-canary.8754dedf4cf0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist-cjs/index.js +1 -1
  2. package/dist-cjs/lib/components/Shape.js +10 -7
  3. package/dist-cjs/lib/components/Shape.js.map +2 -2
  4. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +21 -2
  5. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +17 -11
  7. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +1 -1
  9. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  10. package/dist-cjs/lib/exports/getSvgJsx.js +12 -3
  11. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  12. package/dist-cjs/lib/utils/nearestMultiple.js +34 -0
  13. package/dist-cjs/lib/utils/nearestMultiple.js.map +7 -0
  14. package/dist-cjs/version.js +3 -3
  15. package/dist-cjs/version.js.map +1 -1
  16. package/dist-esm/index.mjs +1 -1
  17. package/dist-esm/lib/components/Shape.mjs +10 -7
  18. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  19. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +21 -2
  20. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  21. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +17 -11
  22. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  23. package/dist-esm/lib/editor/Editor.mjs +1 -1
  24. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  25. package/dist-esm/lib/exports/getSvgJsx.mjs +12 -3
  26. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  27. package/dist-esm/lib/utils/nearestMultiple.mjs +14 -0
  28. package/dist-esm/lib/utils/nearestMultiple.mjs.map +7 -0
  29. package/dist-esm/version.mjs +3 -3
  30. package/dist-esm/version.mjs.map +1 -1
  31. package/package.json +7 -7
  32. package/src/lib/components/Shape.tsx +12 -6
  33. package/src/lib/components/default-components/DefaultCanvas.tsx +21 -2
  34. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +17 -8
  35. package/src/lib/editor/Editor.ts +1 -1
  36. package/src/lib/exports/getSvgJsx.tsx +16 -7
  37. package/src/lib/utils/nearestMultiple.ts +13 -0
  38. package/src/version.ts +3 -3
@@ -275,6 +275,17 @@ function SvgExport({
275
275
  if (shapeElements === null) return;
276
276
  onMount();
277
277
  }, [onMount, shapeElements]);
278
+ let backgroundColor = background ? theme.background : "transparent";
279
+ if (singleFrameShapeId && background) {
280
+ const frameShapeUtil = editor.getShapeUtil("frame");
281
+ if (frameShapeUtil?.options.showColors) {
282
+ const shape = editor.getShape(singleFrameShapeId);
283
+ const color = theme[shape.props.color];
284
+ backgroundColor = color.frame.fill;
285
+ } else {
286
+ backgroundColor = theme.solid;
287
+ }
288
+ }
278
289
  return /* @__PURE__ */ jsx(SvgExportContextProvider, { editor, context: exportContext, children: /* @__PURE__ */ jsxs(
279
290
  "svg",
280
291
  {
@@ -285,9 +296,7 @@ function SvgExport({
285
296
  viewBox: `${bbox.minX} ${bbox.minY} ${bbox.width} ${bbox.height}`,
286
297
  strokeLinecap: "round",
287
298
  strokeLinejoin: "round",
288
- style: {
289
- backgroundColor: background ? singleFrameShapeId ? theme.solid : theme.background : "transparent"
290
- },
299
+ style: { backgroundColor },
291
300
  "data-color-mode": isDarkMode ? "dark" : "light",
292
301
  className: `tl-container tl-theme__force-sRGB ${isDarkMode ? "tl-theme__dark" : "tl-theme__light"}`,
293
302
  children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/exports/getSvgJsx.tsx"],
4
- "sourcesContent": ["import { useAtom, useValue } from '@tldraw/state-react'\nimport {\n\tTLFrameShape,\n\tTLGroupShape,\n\tTLShape,\n\tTLShapeId,\n\tgetDefaultColorTheme,\n} from '@tldraw/tlschema'\nimport { hasOwnProperty, promiseWithResolve, uniqueId } from '@tldraw/utils'\nimport {\n\tComponentType,\n\tFragment,\n\tReactElement,\n\tReactNode,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport { ErrorBoundary } from '../components/ErrorBoundary'\nimport { InnerShape, InnerShapeBackground } from '../components/Shape'\nimport { Editor, TLRenderingShape } from '../editor/Editor'\nimport { TLFontFace } from '../editor/managers/FontManager'\nimport { ShapeUtil } from '../editor/shapes/ShapeUtil'\nimport {\n\tSvgExportContext,\n\tSvgExportContextProvider,\n\tSvgExportDef,\n} from '../editor/types/SvgExportContext'\nimport { TLImageExportOptions } from '../editor/types/misc-types'\nimport { useEditor } from '../hooks/useEditor'\nimport { useEvent } from '../hooks/useEvent'\nimport { suffixSafeId, useUniqueSafeId } from '../hooks/useSafeId'\nimport { Box } from '../primitives/Box'\nimport { Mat } from '../primitives/Mat'\nimport { ExportDelay } from './ExportDelay'\n\nexport function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportOptions = {}) {\n\tif (!window.document) throw Error('No document')\n\n\tconst {\n\t\tscale = 1,\n\t\t// should we include the background in the export? or is it transparent?\n\t\tbackground = editor.getInstanceState().exportBackground,\n\t\tpadding = editor.options.defaultSvgPadding,\n\t\tpreserveAspectRatio,\n\t} = opts\n\n\tconst isDarkMode = opts.darkMode ?? editor.user.getIsDarkMode()\n\n\t// ---Figure out which shapes we need to include\n\tconst shapeIdsToInclude = editor.getShapeAndDescendantIds(ids)\n\tconst renderingShapes = editor\n\t\t.getUnorderedRenderingShapes(false)\n\t\t.filter(({ id }) => shapeIdsToInclude.has(id))\n\n\t// --- Common bounding box of all shapes\n\tlet bbox: null | Box = null\n\tif (opts.bounds) {\n\t\tbbox = opts.bounds\n\t} else {\n\t\tfor (const { id } of renderingShapes) {\n\t\t\tconst maskedPageBounds = editor.getShapeMaskedPageBounds(id)\n\t\t\tif (!maskedPageBounds) continue\n\t\t\tif (bbox) {\n\t\t\t\tbbox.union(maskedPageBounds)\n\t\t\t} else {\n\t\t\t\tbbox = maskedPageBounds.clone()\n\t\t\t}\n\t\t}\n\t}\n\n\t// no unmasked shapes to export\n\tif (!bbox) return\n\n\tconst singleFrameShapeId =\n\t\tids.length === 1 && editor.isShapeOfType<TLFrameShape>(editor.getShape(ids[0])!, 'frame')\n\t\t\t? ids[0]\n\t\t\t: null\n\tif (!singleFrameShapeId) {\n\t\t// Expand by an extra 32 pixels\n\t\tbbox.expandBy(padding)\n\t}\n\n\t// We want the svg image to be BIGGER THAN USUAL to account for image quality\n\tconst w = bbox.width * scale\n\tconst h = bbox.height * scale\n\n\ttry {\n\t\tdocument.body.focus?.() // weird but necessary\n\t} catch {\n\t\t// not implemented\n\t}\n\n\tconst exportDelay = new ExportDelay(editor.options.maxExportDelayMs)\n\n\tconst initialEffectPromise = promiseWithResolve<void>()\n\texportDelay.waitUntil(initialEffectPromise)\n\n\tconst svg = (\n\t\t<SvgExport\n\t\t\teditor={editor}\n\t\t\tpreserveAspectRatio={preserveAspectRatio}\n\t\t\tscale={scale}\n\t\t\tpixelRatio={opts.pixelRatio ?? null}\n\t\t\tbbox={bbox}\n\t\t\tbackground={background}\n\t\t\tsingleFrameShapeId={singleFrameShapeId}\n\t\t\tisDarkMode={isDarkMode}\n\t\t\trenderingShapes={renderingShapes}\n\t\t\tonMount={initialEffectPromise.resolve}\n\t\t\twaitUntil={exportDelay.waitUntil}\n\t\t>\n\t\t\t{}\n\t\t</SvgExport>\n\t)\n\n\treturn { jsx: svg, width: w, height: h, exportDelay }\n}\n\nfunction SvgExport({\n\teditor,\n\tpreserveAspectRatio,\n\tscale,\n\tpixelRatio,\n\tbbox,\n\tbackground,\n\tsingleFrameShapeId,\n\tisDarkMode,\n\trenderingShapes,\n\tonMount,\n\twaitUntil,\n}: {\n\teditor: Editor\n\tpreserveAspectRatio?: string\n\tscale: number\n\tpixelRatio: number | null\n\tbbox: Box\n\tbackground: boolean\n\tsingleFrameShapeId: TLShapeId | null\n\tisDarkMode: boolean\n\trenderingShapes: TLRenderingShape[]\n\tonMount(): void\n\twaitUntil(promise: Promise<void>): void\n}) {\n\tconst masksId = useUniqueSafeId()\n\tconst theme = getDefaultColorTheme({ isDarkMode })\n\n\tconst stateAtom = useAtom<{\n\t\tdefsById: Record<\n\t\t\tstring,\n\t\t\t{ pending: false; element: ReactNode } | { pending: true; element: Promise<ReactNode> }\n\t\t>\n\t\tshapeElements: ReactElement[] | null\n\t}>('export state', { defsById: {}, shapeElements: null })\n\tconst { defsById, shapeElements } = useValue(stateAtom)\n\n\tconst addExportDef = useEvent((def: SvgExportDef) => {\n\t\tstateAtom.update((state) => {\n\t\t\tif (hasOwnProperty(state.defsById, def.key)) return state\n\n\t\t\tconst promise = Promise.resolve(def.getElement())\n\t\t\twaitUntil(\n\t\t\t\tpromise.then((result) => {\n\t\t\t\t\tstateAtom.update((state) => ({\n\t\t\t\t\t\t...state,\n\t\t\t\t\t\tdefsById: { ...state.defsById, [def.key]: { pending: false, element: result } },\n\t\t\t\t\t}))\n\t\t\t\t})\n\t\t\t)\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tdefsById: { ...state.defsById, [def.key]: { pending: true, element: promise } },\n\t\t\t}\n\t\t})\n\t})\n\n\tconst exportContext = useMemo(\n\t\t(): SvgExportContext => ({\n\t\t\tisDarkMode,\n\t\t\twaitUntil,\n\t\t\taddExportDef,\n\t\t\tscale,\n\t\t\tpixelRatio,\n\t\t\tasync resolveAssetUrl(assetId, width) {\n\t\t\t\tconst asset = editor.getAsset(assetId)\n\t\t\t\tif (!asset || (asset.type !== 'image' && asset.type !== 'video')) return null\n\n\t\t\t\treturn await editor.resolveAssetUrl(assetId, {\n\t\t\t\t\tscreenScale: scale * (width / asset.props.w),\n\t\t\t\t\tshouldResolveToOriginal: pixelRatio === null,\n\t\t\t\t\tdpr: pixelRatio ?? undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t}),\n\t\t[isDarkMode, waitUntil, addExportDef, scale, pixelRatio, editor]\n\t)\n\n\tconst didRenderRef = useRef(false)\n\tuseLayoutEffect(() => {\n\t\tif (didRenderRef.current) {\n\t\t\tthrow new Error('SvgExport should only render once - do not use with react strict mode')\n\t\t}\n\t\tdidRenderRef.current = true\n\t\t;(async () => {\n\t\t\tconst shapeDefs: Record<string, { pending: false; element: ReactElement }> = {}\n\n\t\t\t// Then render everything. The shapes with assets should all hit the cache\n\t\t\tconst unorderedShapeElementPromises = renderingShapes.map(\n\t\t\t\tasync ({ id, opacity, index, backgroundIndex }) => {\n\t\t\t\t\t// Don't render the frame if we're only exporting a single frame and it's children\n\t\t\t\t\tif (id === singleFrameShapeId) return []\n\n\t\t\t\t\tconst shape = editor.getShape(id)!\n\n\t\t\t\t\tif (editor.isShapeOfType<TLGroupShape>(shape, 'group')) return []\n\n\t\t\t\t\tconst elements = []\n\t\t\t\t\tconst util = editor.getShapeUtil(shape)\n\n\t\t\t\t\tif (util.toSvg || util.toBackgroundSvg) {\n\t\t\t\t\t\t// If the shape has any sort of custom svg export, let's use that.\n\t\t\t\t\t\tconst [toSvgResult, toBackgroundSvgResult] = await Promise.all([\n\t\t\t\t\t\t\tutil.toSvg?.(shape, exportContext),\n\t\t\t\t\t\t\tutil.toBackgroundSvg?.(shape, exportContext),\n\t\t\t\t\t\t])\n\n\t\t\t\t\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\t\t\t\t\tlet pageTransformString = pageTransform!.toCssString()\n\t\t\t\t\t\tlet scale = 1\n\t\t\t\t\t\tif ('scale' in shape.props) {\n\t\t\t\t\t\t\tif (shape.props.scale !== 1) {\n\t\t\t\t\t\t\t\tscale = shape.props.scale\n\t\t\t\t\t\t\t\tpageTransformString = `${pageTransformString} scale(${shape.props.scale}, ${shape.props.scale})`\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create svg mask if shape has a frame as parent\n\t\t\t\t\t\tconst pageMask = editor.getShapeMask(shape.id)\n\t\t\t\t\t\tconst shapeMask = pageMask\n\t\t\t\t\t\t\t? Mat.From(Mat.Inverse(pageTransform)).applyToPoints(pageMask)\n\t\t\t\t\t\t\t: null\n\t\t\t\t\t\tconst shapeMaskId = suffixSafeId(masksId, shape.id)\n\t\t\t\t\t\tif (shapeMask) {\n\t\t\t\t\t\t\t// Create a clip path and add it to defs\n\t\t\t\t\t\t\tshapeDefs[shapeMaskId] = {\n\t\t\t\t\t\t\t\tpending: false,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<clipPath id={shapeMaskId}>\n\t\t\t\t\t\t\t\t\t\t{/* Create a polyline mask that does the clipping */}\n\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\td={`M${shapeMask.map(({ x, y }) => `${x / scale},${y / scale}`).join('L')}Z`}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</clipPath>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (toSvgResult) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: index,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<g\n\t\t\t\t\t\t\t\t\t\tkey={`fg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\ttransform={pageTransformString}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t\tclipPath={pageMask ? `url(#${shapeMaskId})` : undefined}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{toSvgResult}\n\t\t\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (toBackgroundSvgResult) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: backgroundIndex,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<g\n\t\t\t\t\t\t\t\t\t\tkey={`bg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\ttransform={pageTransformString}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t\tclipPath={pageMask ? `url(#${shapeMaskId})` : undefined}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{toBackgroundSvgResult}\n\t\t\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the shape doesn't have a custom svg export, we'll use its normal HTML\n\t\t\t\t\t\t// renderer in a foreignObject.\n\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\tzIndex: index,\n\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t<ForeignObjectShape\n\t\t\t\t\t\t\t\t\tkey={`fg_${shape.id}`}\n\t\t\t\t\t\t\t\t\tshape={shape}\n\t\t\t\t\t\t\t\t\tutil={util}\n\t\t\t\t\t\t\t\t\tcomponent={InnerShape}\n\t\t\t\t\t\t\t\t\tclassName=\"tl-shape\"\n\t\t\t\t\t\t\t\t\tbbox={bbox}\n\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tif (util.backgroundComponent) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: backgroundIndex,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<ForeignObjectShape\n\t\t\t\t\t\t\t\t\t\tkey={`bg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\tshape={shape}\n\t\t\t\t\t\t\t\t\t\tutil={util}\n\t\t\t\t\t\t\t\t\t\tcomponent={InnerShapeBackground}\n\t\t\t\t\t\t\t\t\t\tclassName=\"tl-shape tl-shape-background\"\n\t\t\t\t\t\t\t\t\t\tbbox={bbox}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn elements\n\t\t\t\t}\n\t\t\t)\n\n\t\t\tconst unorderedShapeElements = (await Promise.all(unorderedShapeElementPromises)).flat()\n\n\t\t\tflushSync(() => {\n\t\t\t\tstateAtom.update((state) => ({\n\t\t\t\t\t...state,\n\t\t\t\t\tshapeElements: unorderedShapeElements\n\t\t\t\t\t\t.sort((a, b) => a.zIndex - b.zIndex)\n\t\t\t\t\t\t.map(({ element }) => element),\n\t\t\t\t\tdefsById: { ...state.defsById, ...shapeDefs },\n\t\t\t\t}))\n\t\t\t})\n\t\t})()\n\t}, [bbox, editor, exportContext, masksId, renderingShapes, singleFrameShapeId, stateAtom])\n\n\tuseEffect(() => {\n\t\tconst fontsInUse = new Set<TLFontFace>()\n\t\tfor (const { id } of renderingShapes) {\n\t\t\tfor (const font of editor.fonts.getShapeFontFaces(id)) {\n\t\t\t\tfontsInUse.add(font)\n\t\t\t}\n\t\t}\n\n\t\tfor (const font of fontsInUse) {\n\t\t\taddExportDef({\n\t\t\t\tkey: uniqueId(),\n\t\t\t\tgetElement: async () => {\n\t\t\t\t\tconst declaration = await editor.fonts.toEmbeddedCssDeclaration(font)\n\t\t\t\t\treturn <style nonce={editor.options.nonce}>{declaration}</style>\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t}, [editor, renderingShapes, addExportDef])\n\n\tuseEffect(() => {\n\t\tif (shapeElements === null) return\n\t\tonMount()\n\t}, [onMount, shapeElements])\n\n\treturn (\n\t\t<SvgExportContextProvider editor={editor} context={exportContext}>\n\t\t\t<svg\n\t\t\t\tpreserveAspectRatio={preserveAspectRatio}\n\t\t\t\tdirection=\"ltr\"\n\t\t\t\twidth={bbox.width * scale}\n\t\t\t\theight={bbox.height * scale}\n\t\t\t\tviewBox={`${bbox.minX} ${bbox.minY} ${bbox.width} ${bbox.height}`}\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tstyle={{\n\t\t\t\t\tbackgroundColor: background\n\t\t\t\t\t\t? singleFrameShapeId\n\t\t\t\t\t\t\t? theme.solid\n\t\t\t\t\t\t\t: theme.background\n\t\t\t\t\t\t: 'transparent',\n\t\t\t\t}}\n\t\t\t\tdata-color-mode={isDarkMode ? 'dark' : 'light'}\n\t\t\t\tclassName={`tl-container tl-theme__force-sRGB ${isDarkMode ? 'tl-theme__dark' : 'tl-theme__light'}`}\n\t\t\t>\n\t\t\t\t<defs>\n\t\t\t\t\t{Object.entries(defsById).map(([key, def]) =>\n\t\t\t\t\t\tdef.pending ? null : <Fragment key={key}>{def.element}</Fragment>\n\t\t\t\t\t)}\n\t\t\t\t</defs>\n\t\t\t\t{shapeElements}\n\t\t\t</svg>\n\t\t</SvgExportContextProvider>\n\t)\n}\n\nfunction ForeignObjectShape({\n\tshape,\n\tutil,\n\tclassName,\n\tcomponent: Component,\n\tbbox,\n\topacity,\n}: {\n\tshape: TLShape\n\tutil: ShapeUtil\n\tclassName?: string\n\tcomponent: ComponentType<{ shape: TLShape; util: ShapeUtil }>\n\tbbox: Box\n\topacity: number\n}) {\n\tconst editor = useEditor()\n\n\tconst transform = Mat.Translate(-bbox.minX, -bbox.minY).multiply(\n\t\teditor.getShapePageTransform(shape.id)!\n\t)\n\n\tconst bounds = editor.getShapeGeometry(shape.id).bounds\n\tconst width = Math.max(bounds.width, 1)\n\tconst height = Math.max(bounds.height, 1)\n\n\treturn (\n\t\t<ErrorBoundary fallback={() => null}>\n\t\t\t<foreignObject\n\t\t\t\tx={bbox.minX}\n\t\t\t\ty={bbox.minY}\n\t\t\t\twidth={bbox.w}\n\t\t\t\theight={bbox.h}\n\t\t\t\tclassName=\"tl-shape-foreign-object tl-export-embed-styles\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tdata-shape-type={shape.type}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tclipPath: editor.getShapeClipPath(shape.id),\n\t\t\t\t\t\ttransform: transform.toCssString(),\n\t\t\t\t\t\twidth,\n\t\t\t\t\t\theight,\n\t\t\t\t\t\topacity,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Component shape={shape} util={util} />\n\t\t\t\t</div>\n\t\t\t</foreignObject>\n\t\t</ErrorBoundary>\n\t)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useAtom, useValue } from '@tldraw/state-react'\nimport {\n\tTLFrameShape,\n\tTLGroupShape,\n\tTLShape,\n\tTLShapeId,\n\tgetDefaultColorTheme,\n} from '@tldraw/tlschema'\nimport { hasOwnProperty, promiseWithResolve, uniqueId } from '@tldraw/utils'\nimport {\n\tComponentType,\n\tFragment,\n\tReactElement,\n\tReactNode,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport { ErrorBoundary } from '../components/ErrorBoundary'\nimport { InnerShape, InnerShapeBackground } from '../components/Shape'\nimport { Editor, TLRenderingShape } from '../editor/Editor'\nimport { TLFontFace } from '../editor/managers/FontManager'\nimport { ShapeUtil } from '../editor/shapes/ShapeUtil'\nimport {\n\tSvgExportContext,\n\tSvgExportContextProvider,\n\tSvgExportDef,\n} from '../editor/types/SvgExportContext'\nimport { TLImageExportOptions } from '../editor/types/misc-types'\nimport { useEditor } from '../hooks/useEditor'\nimport { useEvent } from '../hooks/useEvent'\nimport { suffixSafeId, useUniqueSafeId } from '../hooks/useSafeId'\nimport { Box } from '../primitives/Box'\nimport { Mat } from '../primitives/Mat'\nimport { ExportDelay } from './ExportDelay'\n\nexport function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportOptions = {}) {\n\tif (!window.document) throw Error('No document')\n\n\tconst {\n\t\tscale = 1,\n\t\t// should we include the background in the export? or is it transparent?\n\t\tbackground = editor.getInstanceState().exportBackground,\n\t\tpadding = editor.options.defaultSvgPadding,\n\t\tpreserveAspectRatio,\n\t} = opts\n\n\tconst isDarkMode = opts.darkMode ?? editor.user.getIsDarkMode()\n\n\t// ---Figure out which shapes we need to include\n\tconst shapeIdsToInclude = editor.getShapeAndDescendantIds(ids)\n\tconst renderingShapes = editor\n\t\t.getUnorderedRenderingShapes(false)\n\t\t.filter(({ id }) => shapeIdsToInclude.has(id))\n\n\t// --- Common bounding box of all shapes\n\tlet bbox: null | Box = null\n\tif (opts.bounds) {\n\t\tbbox = opts.bounds\n\t} else {\n\t\tfor (const { id } of renderingShapes) {\n\t\t\tconst maskedPageBounds = editor.getShapeMaskedPageBounds(id)\n\t\t\tif (!maskedPageBounds) continue\n\t\t\tif (bbox) {\n\t\t\t\tbbox.union(maskedPageBounds)\n\t\t\t} else {\n\t\t\t\tbbox = maskedPageBounds.clone()\n\t\t\t}\n\t\t}\n\t}\n\n\t// no unmasked shapes to export\n\tif (!bbox) return\n\n\tconst singleFrameShapeId =\n\t\tids.length === 1 && editor.isShapeOfType<TLFrameShape>(editor.getShape(ids[0])!, 'frame')\n\t\t\t? ids[0]\n\t\t\t: null\n\tif (!singleFrameShapeId) {\n\t\t// Expand by an extra 32 pixels\n\t\tbbox.expandBy(padding)\n\t}\n\n\t// We want the svg image to be BIGGER THAN USUAL to account for image quality\n\tconst w = bbox.width * scale\n\tconst h = bbox.height * scale\n\n\ttry {\n\t\tdocument.body.focus?.() // weird but necessary\n\t} catch {\n\t\t// not implemented\n\t}\n\n\tconst exportDelay = new ExportDelay(editor.options.maxExportDelayMs)\n\n\tconst initialEffectPromise = promiseWithResolve<void>()\n\texportDelay.waitUntil(initialEffectPromise)\n\n\tconst svg = (\n\t\t<SvgExport\n\t\t\teditor={editor}\n\t\t\tpreserveAspectRatio={preserveAspectRatio}\n\t\t\tscale={scale}\n\t\t\tpixelRatio={opts.pixelRatio ?? null}\n\t\t\tbbox={bbox}\n\t\t\tbackground={background}\n\t\t\tsingleFrameShapeId={singleFrameShapeId}\n\t\t\tisDarkMode={isDarkMode}\n\t\t\trenderingShapes={renderingShapes}\n\t\t\tonMount={initialEffectPromise.resolve}\n\t\t\twaitUntil={exportDelay.waitUntil}\n\t\t>\n\t\t\t{}\n\t\t</SvgExport>\n\t)\n\n\treturn { jsx: svg, width: w, height: h, exportDelay }\n}\n\nfunction SvgExport({\n\teditor,\n\tpreserveAspectRatio,\n\tscale,\n\tpixelRatio,\n\tbbox,\n\tbackground,\n\tsingleFrameShapeId,\n\tisDarkMode,\n\trenderingShapes,\n\tonMount,\n\twaitUntil,\n}: {\n\teditor: Editor\n\tpreserveAspectRatio?: string\n\tscale: number\n\tpixelRatio: number | null\n\tbbox: Box\n\tbackground: boolean\n\tsingleFrameShapeId: TLShapeId | null\n\tisDarkMode: boolean\n\trenderingShapes: TLRenderingShape[]\n\tonMount(): void\n\twaitUntil(promise: Promise<void>): void\n}) {\n\tconst masksId = useUniqueSafeId()\n\tconst theme = getDefaultColorTheme({ isDarkMode })\n\n\tconst stateAtom = useAtom<{\n\t\tdefsById: Record<\n\t\t\tstring,\n\t\t\t{ pending: false; element: ReactNode } | { pending: true; element: Promise<ReactNode> }\n\t\t>\n\t\tshapeElements: ReactElement[] | null\n\t}>('export state', { defsById: {}, shapeElements: null })\n\tconst { defsById, shapeElements } = useValue(stateAtom)\n\n\tconst addExportDef = useEvent((def: SvgExportDef) => {\n\t\tstateAtom.update((state) => {\n\t\t\tif (hasOwnProperty(state.defsById, def.key)) return state\n\n\t\t\tconst promise = Promise.resolve(def.getElement())\n\t\t\twaitUntil(\n\t\t\t\tpromise.then((result) => {\n\t\t\t\t\tstateAtom.update((state) => ({\n\t\t\t\t\t\t...state,\n\t\t\t\t\t\tdefsById: { ...state.defsById, [def.key]: { pending: false, element: result } },\n\t\t\t\t\t}))\n\t\t\t\t})\n\t\t\t)\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tdefsById: { ...state.defsById, [def.key]: { pending: true, element: promise } },\n\t\t\t}\n\t\t})\n\t})\n\n\tconst exportContext = useMemo(\n\t\t(): SvgExportContext => ({\n\t\t\tisDarkMode,\n\t\t\twaitUntil,\n\t\t\taddExportDef,\n\t\t\tscale,\n\t\t\tpixelRatio,\n\t\t\tasync resolveAssetUrl(assetId, width) {\n\t\t\t\tconst asset = editor.getAsset(assetId)\n\t\t\t\tif (!asset || (asset.type !== 'image' && asset.type !== 'video')) return null\n\n\t\t\t\treturn await editor.resolveAssetUrl(assetId, {\n\t\t\t\t\tscreenScale: scale * (width / asset.props.w),\n\t\t\t\t\tshouldResolveToOriginal: pixelRatio === null,\n\t\t\t\t\tdpr: pixelRatio ?? undefined,\n\t\t\t\t})\n\t\t\t},\n\t\t}),\n\t\t[isDarkMode, waitUntil, addExportDef, scale, pixelRatio, editor]\n\t)\n\n\tconst didRenderRef = useRef(false)\n\tuseLayoutEffect(() => {\n\t\tif (didRenderRef.current) {\n\t\t\tthrow new Error('SvgExport should only render once - do not use with react strict mode')\n\t\t}\n\t\tdidRenderRef.current = true\n\t\t;(async () => {\n\t\t\tconst shapeDefs: Record<string, { pending: false; element: ReactElement }> = {}\n\n\t\t\t// Then render everything. The shapes with assets should all hit the cache\n\t\t\tconst unorderedShapeElementPromises = renderingShapes.map(\n\t\t\t\tasync ({ id, opacity, index, backgroundIndex }) => {\n\t\t\t\t\t// Don't render the frame if we're only exporting a single frame and it's children\n\t\t\t\t\tif (id === singleFrameShapeId) return []\n\n\t\t\t\t\tconst shape = editor.getShape(id)!\n\n\t\t\t\t\tif (editor.isShapeOfType<TLGroupShape>(shape, 'group')) return []\n\n\t\t\t\t\tconst elements = []\n\t\t\t\t\tconst util = editor.getShapeUtil(shape)\n\n\t\t\t\t\tif (util.toSvg || util.toBackgroundSvg) {\n\t\t\t\t\t\t// If the shape has any sort of custom svg export, let's use that.\n\t\t\t\t\t\tconst [toSvgResult, toBackgroundSvgResult] = await Promise.all([\n\t\t\t\t\t\t\tutil.toSvg?.(shape, exportContext),\n\t\t\t\t\t\t\tutil.toBackgroundSvg?.(shape, exportContext),\n\t\t\t\t\t\t])\n\n\t\t\t\t\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\t\t\t\t\tlet pageTransformString = pageTransform!.toCssString()\n\t\t\t\t\t\tlet scale = 1\n\t\t\t\t\t\tif ('scale' in shape.props) {\n\t\t\t\t\t\t\tif (shape.props.scale !== 1) {\n\t\t\t\t\t\t\t\tscale = shape.props.scale\n\t\t\t\t\t\t\t\tpageTransformString = `${pageTransformString} scale(${shape.props.scale}, ${shape.props.scale})`\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create svg mask if shape has a frame as parent\n\t\t\t\t\t\tconst pageMask = editor.getShapeMask(shape.id)\n\t\t\t\t\t\tconst shapeMask = pageMask\n\t\t\t\t\t\t\t? Mat.From(Mat.Inverse(pageTransform)).applyToPoints(pageMask)\n\t\t\t\t\t\t\t: null\n\t\t\t\t\t\tconst shapeMaskId = suffixSafeId(masksId, shape.id)\n\t\t\t\t\t\tif (shapeMask) {\n\t\t\t\t\t\t\t// Create a clip path and add it to defs\n\t\t\t\t\t\t\tshapeDefs[shapeMaskId] = {\n\t\t\t\t\t\t\t\tpending: false,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<clipPath id={shapeMaskId}>\n\t\t\t\t\t\t\t\t\t\t{/* Create a polyline mask that does the clipping */}\n\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\td={`M${shapeMask.map(({ x, y }) => `${x / scale},${y / scale}`).join('L')}Z`}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</clipPath>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (toSvgResult) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: index,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<g\n\t\t\t\t\t\t\t\t\t\tkey={`fg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\ttransform={pageTransformString}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t\tclipPath={pageMask ? `url(#${shapeMaskId})` : undefined}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{toSvgResult}\n\t\t\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (toBackgroundSvgResult) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: backgroundIndex,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<g\n\t\t\t\t\t\t\t\t\t\tkey={`bg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\ttransform={pageTransformString}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t\tclipPath={pageMask ? `url(#${shapeMaskId})` : undefined}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{toBackgroundSvgResult}\n\t\t\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If the shape doesn't have a custom svg export, we'll use its normal HTML\n\t\t\t\t\t\t// renderer in a foreignObject.\n\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\tzIndex: index,\n\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t<ForeignObjectShape\n\t\t\t\t\t\t\t\t\tkey={`fg_${shape.id}`}\n\t\t\t\t\t\t\t\t\tshape={shape}\n\t\t\t\t\t\t\t\t\tutil={util}\n\t\t\t\t\t\t\t\t\tcomponent={InnerShape}\n\t\t\t\t\t\t\t\t\tclassName=\"tl-shape\"\n\t\t\t\t\t\t\t\t\tbbox={bbox}\n\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tif (util.backgroundComponent) {\n\t\t\t\t\t\t\telements.push({\n\t\t\t\t\t\t\t\tzIndex: backgroundIndex,\n\t\t\t\t\t\t\t\telement: (\n\t\t\t\t\t\t\t\t\t<ForeignObjectShape\n\t\t\t\t\t\t\t\t\t\tkey={`bg_${shape.id}`}\n\t\t\t\t\t\t\t\t\t\tshape={shape}\n\t\t\t\t\t\t\t\t\t\tutil={util}\n\t\t\t\t\t\t\t\t\t\tcomponent={InnerShapeBackground}\n\t\t\t\t\t\t\t\t\t\tclassName=\"tl-shape tl-shape-background\"\n\t\t\t\t\t\t\t\t\t\tbbox={bbox}\n\t\t\t\t\t\t\t\t\t\topacity={opacity}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn elements\n\t\t\t\t}\n\t\t\t)\n\n\t\t\tconst unorderedShapeElements = (await Promise.all(unorderedShapeElementPromises)).flat()\n\n\t\t\tflushSync(() => {\n\t\t\t\tstateAtom.update((state) => ({\n\t\t\t\t\t...state,\n\t\t\t\t\tshapeElements: unorderedShapeElements\n\t\t\t\t\t\t.sort((a, b) => a.zIndex - b.zIndex)\n\t\t\t\t\t\t.map(({ element }) => element),\n\t\t\t\t\tdefsById: { ...state.defsById, ...shapeDefs },\n\t\t\t\t}))\n\t\t\t})\n\t\t})()\n\t}, [bbox, editor, exportContext, masksId, renderingShapes, singleFrameShapeId, stateAtom])\n\n\tuseEffect(() => {\n\t\tconst fontsInUse = new Set<TLFontFace>()\n\t\tfor (const { id } of renderingShapes) {\n\t\t\tfor (const font of editor.fonts.getShapeFontFaces(id)) {\n\t\t\t\tfontsInUse.add(font)\n\t\t\t}\n\t\t}\n\n\t\tfor (const font of fontsInUse) {\n\t\t\taddExportDef({\n\t\t\t\tkey: uniqueId(),\n\t\t\t\tgetElement: async () => {\n\t\t\t\t\tconst declaration = await editor.fonts.toEmbeddedCssDeclaration(font)\n\t\t\t\t\treturn <style nonce={editor.options.nonce}>{declaration}</style>\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t}, [editor, renderingShapes, addExportDef])\n\n\tuseEffect(() => {\n\t\tif (shapeElements === null) return\n\t\tonMount()\n\t}, [onMount, shapeElements])\n\n\tlet backgroundColor = background ? theme.background : 'transparent'\n\n\tif (singleFrameShapeId && background) {\n\t\tconst frameShapeUtil = editor.getShapeUtil('frame') as any as\n\t\t\t| undefined\n\t\t\t| { options: { showColors: boolean } }\n\t\tif (frameShapeUtil?.options.showColors) {\n\t\t\tconst shape = editor.getShape(singleFrameShapeId)! as TLFrameShape\n\t\t\tconst color = theme[shape.props.color]\n\t\t\tbackgroundColor = color.frame.fill\n\t\t} else {\n\t\t\tbackgroundColor = theme.solid\n\t\t}\n\t}\n\n\treturn (\n\t\t<SvgExportContextProvider editor={editor} context={exportContext}>\n\t\t\t<svg\n\t\t\t\tpreserveAspectRatio={preserveAspectRatio}\n\t\t\t\tdirection=\"ltr\"\n\t\t\t\twidth={bbox.width * scale}\n\t\t\t\theight={bbox.height * scale}\n\t\t\t\tviewBox={`${bbox.minX} ${bbox.minY} ${bbox.width} ${bbox.height}`}\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tstyle={{ backgroundColor }}\n\t\t\t\tdata-color-mode={isDarkMode ? 'dark' : 'light'}\n\t\t\t\tclassName={`tl-container tl-theme__force-sRGB ${isDarkMode ? 'tl-theme__dark' : 'tl-theme__light'}`}\n\t\t\t>\n\t\t\t\t<defs>\n\t\t\t\t\t{Object.entries(defsById).map(([key, def]) =>\n\t\t\t\t\t\tdef.pending ? null : <Fragment key={key}>{def.element}</Fragment>\n\t\t\t\t\t)}\n\t\t\t\t</defs>\n\t\t\t\t{shapeElements}\n\t\t\t</svg>\n\t\t</SvgExportContextProvider>\n\t)\n}\n\nfunction ForeignObjectShape({\n\tshape,\n\tutil,\n\tclassName,\n\tcomponent: Component,\n\tbbox,\n\topacity,\n}: {\n\tshape: TLShape\n\tutil: ShapeUtil\n\tclassName?: string\n\tcomponent: ComponentType<{ shape: TLShape; util: ShapeUtil }>\n\tbbox: Box\n\topacity: number\n}) {\n\tconst editor = useEditor()\n\n\tconst transform = Mat.Translate(-bbox.minX, -bbox.minY).multiply(\n\t\teditor.getShapePageTransform(shape.id)!\n\t)\n\n\tconst bounds = editor.getShapeGeometry(shape.id).bounds\n\tconst width = Math.max(bounds.width, 1)\n\tconst height = Math.max(bounds.height, 1)\n\n\treturn (\n\t\t<ErrorBoundary fallback={() => null}>\n\t\t\t<foreignObject\n\t\t\t\tx={bbox.minX}\n\t\t\t\ty={bbox.minY}\n\t\t\t\twidth={bbox.w}\n\t\t\t\theight={bbox.h}\n\t\t\t\tclassName=\"tl-shape-foreign-object tl-export-embed-styles\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tdata-shape-type={shape.type}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tclipPath: editor.getShapeClipPath(shape.id),\n\t\t\t\t\t\ttransform: transform.toCssString(),\n\t\t\t\t\t\twidth,\n\t\t\t\t\t\theight,\n\t\t\t\t\t\topacity,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Component shape={shape} util={util} />\n\t\t\t\t</div>\n\t\t\t</foreignObject>\n\t\t</ErrorBoundary>\n\t)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["state", "scale"]
7
7
  }
@@ -0,0 +1,14 @@
1
+ function gcd(a, b) {
2
+ return b === 0 ? a : gcd(b, a % b);
3
+ }
4
+ function nearestMultiple(float) {
5
+ const decimal = float.toString().split(".")[1];
6
+ if (!decimal) return 1;
7
+ const denominator = Math.pow(10, decimal.length);
8
+ const numerator = parseInt(decimal, 10);
9
+ return denominator / gcd(numerator, denominator);
10
+ }
11
+ export {
12
+ nearestMultiple
13
+ };
14
+ //# sourceMappingURL=nearestMultiple.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/utils/nearestMultiple.ts"],
4
+ "sourcesContent": ["// Euclidean algorithm to find the GCD\nfunction gcd(a: number, b: number): number {\n\treturn b === 0 ? a : gcd(b, a % b)\n}\n\n// Returns the lowest value that the given number can be multiplied by to reach an integer\nexport function nearestMultiple(float: number) {\n\tconst decimal = float.toString().split('.')[1]\n\tif (!decimal) return 1\n\tconst denominator = Math.pow(10, decimal.length)\n\tconst numerator = parseInt(decimal, 10)\n\treturn denominator / gcd(numerator, denominator)\n}\n"],
5
+ "mappings": "AACA,SAAS,IAAI,GAAW,GAAmB;AAC1C,SAAO,MAAM,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;AAClC;AAGO,SAAS,gBAAgB,OAAe;AAC9C,QAAM,UAAU,MAAM,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC;AAC7C,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,cAAc,KAAK,IAAI,IAAI,QAAQ,MAAM;AAC/C,QAAM,YAAY,SAAS,SAAS,EAAE;AACtC,SAAO,cAAc,IAAI,WAAW,WAAW;AAChD;",
6
+ "names": []
7
+ }
@@ -1,8 +1,8 @@
1
- const version = "3.13.0-canary.6cb770c78958";
1
+ const version = "3.13.0-canary.8754dedf4cf0";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-04-30T12:29:15.138Z",
5
- patch: "2025-04-30T12:29:15.138Z"
4
+ minor: "2025-05-02T10:16:31.753Z",
5
+ patch: "2025-05-02T10:16:31.753Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.6cb770c78958'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-04-30T12:29:15.138Z',\n\tpatch: '2025-04-30T12:29:15.138Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.13.0-canary.8754dedf4cf0'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-02T10:16:31.753Z',\n\tpatch: '2025-05-02T10:16:31.753Z',\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/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.13.0-canary.6cb770c78958",
4
+ "version": "3.13.0-canary.8754dedf4cf0",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -48,12 +48,12 @@
48
48
  "@tiptap/core": "^2.9.1",
49
49
  "@tiptap/pm": "^2.9.1",
50
50
  "@tiptap/react": "^2.9.1",
51
- "@tldraw/state": "3.13.0-canary.6cb770c78958",
52
- "@tldraw/state-react": "3.13.0-canary.6cb770c78958",
53
- "@tldraw/store": "3.13.0-canary.6cb770c78958",
54
- "@tldraw/tlschema": "3.13.0-canary.6cb770c78958",
55
- "@tldraw/utils": "3.13.0-canary.6cb770c78958",
56
- "@tldraw/validate": "3.13.0-canary.6cb770c78958",
51
+ "@tldraw/state": "3.13.0-canary.8754dedf4cf0",
52
+ "@tldraw/state-react": "3.13.0-canary.8754dedf4cf0",
53
+ "@tldraw/store": "3.13.0-canary.8754dedf4cf0",
54
+ "@tldraw/tlschema": "3.13.0-canary.8754dedf4cf0",
55
+ "@tldraw/utils": "3.13.0-canary.8754dedf4cf0",
56
+ "@tldraw/validate": "3.13.0-canary.8754dedf4cf0",
57
57
  "@types/core-js": "^2.5.8",
58
58
  "@use-gesture/react": "^10.3.1",
59
59
  "classnames": "^2.5.1",
@@ -27,6 +27,7 @@ export const Shape = memo(function Shape({
27
27
  index,
28
28
  backgroundIndex,
29
29
  opacity,
30
+ dprMultiple,
30
31
  }: {
31
32
  id: TLShapeId
32
33
  shape: TLShape
@@ -34,6 +35,7 @@ export const Shape = memo(function Shape({
34
35
  index: number
35
36
  backgroundIndex: number
36
37
  opacity: number
38
+ dprMultiple: number
37
39
  }) {
38
40
  const editor = useEditor()
39
41
 
@@ -88,14 +90,18 @@ export const Shape = memo(function Shape({
88
90
  }
89
91
 
90
92
  // Width / Height
91
- const width = Math.max(bounds.width, 1)
92
- const height = Math.max(bounds.height, 1)
93
+ // We round the shape width and height up to the nearest multiple of dprMultiple
94
+ // to avoid the browser making miscalculations when applying the transform.
95
+ const widthRemainder = bounds.w % dprMultiple
96
+ const heightRemainder = bounds.h % dprMultiple
97
+ const width = widthRemainder === 0 ? bounds.w : bounds.w + (dprMultiple - widthRemainder)
98
+ const height = heightRemainder === 0 ? bounds.h : bounds.h + (dprMultiple - heightRemainder)
93
99
 
94
100
  if (width !== prev.width || height !== prev.height) {
95
- setStyleProperty(containerRef.current, 'width', width + 'px')
96
- setStyleProperty(containerRef.current, 'height', height + 'px')
97
- setStyleProperty(bgContainerRef.current, 'width', width + 'px')
98
- setStyleProperty(bgContainerRef.current, 'height', height + 'px')
101
+ setStyleProperty(containerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
102
+ setStyleProperty(containerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
103
+ setStyleProperty(bgContainerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
104
+ setStyleProperty(bgContainerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
99
105
  prev.width = width
100
106
  prev.height = height
101
107
  }
@@ -22,6 +22,7 @@ import { Vec } from '../../primitives/Vec'
22
22
  import { toDomPrecision } from '../../primitives/utils'
23
23
  import { debugFlags } from '../../utils/debug-flags'
24
24
  import { setStyleProperty } from '../../utils/dom'
25
+ import { nearestMultiple } from '../../utils/nearestMultiple'
25
26
  import { GeometryDebuggingView } from '../GeometryDebuggingView'
26
27
  import { LiveCollaborators } from '../LiveCollaborators'
27
28
  import { MenuClickCapture } from '../MenuClickCapture'
@@ -388,9 +389,18 @@ function ShapesWithSVGs() {
388
389
 
389
390
  const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
390
391
 
392
+ const dprMultiple = useValue(
393
+ 'dpr multiple',
394
+ () =>
395
+ // dprMultiple is the smallest number we can multiply dpr by to get an integer
396
+ // it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
397
+ nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
398
+ [editor]
399
+ )
400
+
391
401
  return renderingShapes.map((result) => (
392
402
  <Fragment key={result.id + '_fragment'}>
393
- <Shape {...result} />
403
+ <Shape {...result} dprMultiple={dprMultiple} />
394
404
  <DebugSvgCopy id={result.id} mode="iframe" />
395
405
  </Fragment>
396
406
  ))
@@ -425,10 +435,19 @@ function ShapesToDisplay() {
425
435
 
426
436
  const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
427
437
 
438
+ const dprMultiple = useValue(
439
+ 'dpr multiple',
440
+ () =>
441
+ // dprMultiple is the smallest number we can multiply dpr by to get an integer
442
+ // it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
443
+ nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
444
+ [editor]
445
+ )
446
+
428
447
  return (
429
448
  <>
430
449
  {renderingShapes.map((result) => (
431
- <Shape key={result.id + '_shape'} {...result} />
450
+ <Shape key={result.id + '_shape'} {...result} dprMultiple={dprMultiple} />
432
451
  ))}
433
452
  {tlenv.isSafari && <ReflowIfNeeded />}
434
453
  </>
@@ -9,13 +9,21 @@ import { useEditorComponents } from '../../hooks/useEditorComponents'
9
9
  import { OptionalErrorBoundary } from '../ErrorBoundary'
10
10
 
11
11
  // need an extra layer of indirection here to allow hooks to be used inside the indicator render
12
- const EvenInnererIndicator = memo(({ shape, util }: { shape: TLShape; util: ShapeUtil<any> }) => {
13
- return useStateTracking('Indicator: ' + shape.type, () =>
14
- // always fetch the latest shape from the store even if the props/meta have not changed, to avoid
15
- // calling the render method with stale data.
16
- util.indicator(util.editor.store.unsafeGetWithoutCapture(shape.id) as TLShape)
17
- )
18
- })
12
+ const EvenInnererIndicator = memo(
13
+ ({ shape, util }: { shape: TLShape; util: ShapeUtil<any> }) => {
14
+ return useStateTracking('Indicator: ' + shape.type, () =>
15
+ // always fetch the latest shape from the store even if the props/meta have not changed, to avoid
16
+ // calling the render method with stale data.
17
+ util.indicator(util.editor.store.unsafeGetWithoutCapture(shape.id) as TLShape)
18
+ )
19
+ },
20
+ (prevProps, nextProps) => {
21
+ return (
22
+ prevProps.shape.props === nextProps.shape.props &&
23
+ prevProps.shape.meta === nextProps.shape.meta
24
+ )
25
+ }
26
+ )
19
27
 
20
28
  const InnerIndicator = memo(({ editor, id }: { editor: Editor; id: TLShapeId }) => {
21
29
  const shape = useValue('shape for indicator', () => editor.store.get(id), [editor, id])
@@ -61,13 +69,14 @@ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
61
69
  useQuickReactor(
62
70
  'indicator transform',
63
71
  () => {
72
+ if (hidden) return
64
73
  const elm = rIndicator.current
65
74
  if (!elm) return
66
75
  const pageTransform = editor.getShapePageTransform(shapeId)
67
76
  if (!pageTransform) return
68
77
  elm.style.setProperty('transform', pageTransform.toCssString())
69
78
  },
70
- [editor, shapeId]
79
+ [editor, shapeId, hidden]
71
80
  )
72
81
 
73
82
  useLayoutEffect(() => {
@@ -10214,7 +10214,7 @@ export class Editor extends EventEmitter<TLEventMap> {
10214
10214
 
10215
10215
  // If the camera behavior is "zoom" and the ctrl key is pressed, then pan;
10216
10216
  // If the camera behavior is "pan" and the ctrl key is not pressed, then zoom
10217
- if (inputs.ctrlKey) behavior = wheelBehavior === 'pan' ? 'zoom' : 'pan'
10217
+ if (info.ctrlKey) behavior = wheelBehavior === 'pan' ? 'zoom' : 'pan'
10218
10218
 
10219
10219
  switch (behavior) {
10220
10220
  case 'zoom': {
@@ -365,6 +365,21 @@ function SvgExport({
365
365
  onMount()
366
366
  }, [onMount, shapeElements])
367
367
 
368
+ let backgroundColor = background ? theme.background : 'transparent'
369
+
370
+ if (singleFrameShapeId && background) {
371
+ const frameShapeUtil = editor.getShapeUtil('frame') as any as
372
+ | undefined
373
+ | { options: { showColors: boolean } }
374
+ if (frameShapeUtil?.options.showColors) {
375
+ const shape = editor.getShape(singleFrameShapeId)! as TLFrameShape
376
+ const color = theme[shape.props.color]
377
+ backgroundColor = color.frame.fill
378
+ } else {
379
+ backgroundColor = theme.solid
380
+ }
381
+ }
382
+
368
383
  return (
369
384
  <SvgExportContextProvider editor={editor} context={exportContext}>
370
385
  <svg
@@ -375,13 +390,7 @@ function SvgExport({
375
390
  viewBox={`${bbox.minX} ${bbox.minY} ${bbox.width} ${bbox.height}`}
376
391
  strokeLinecap="round"
377
392
  strokeLinejoin="round"
378
- style={{
379
- backgroundColor: background
380
- ? singleFrameShapeId
381
- ? theme.solid
382
- : theme.background
383
- : 'transparent',
384
- }}
393
+ style={{ backgroundColor }}
385
394
  data-color-mode={isDarkMode ? 'dark' : 'light'}
386
395
  className={`tl-container tl-theme__force-sRGB ${isDarkMode ? 'tl-theme__dark' : 'tl-theme__light'}`}
387
396
  >
@@ -0,0 +1,13 @@
1
+ // Euclidean algorithm to find the GCD
2
+ function gcd(a: number, b: number): number {
3
+ return b === 0 ? a : gcd(b, a % b)
4
+ }
5
+
6
+ // Returns the lowest value that the given number can be multiplied by to reach an integer
7
+ export function nearestMultiple(float: number) {
8
+ const decimal = float.toString().split('.')[1]
9
+ if (!decimal) return 1
10
+ const denominator = Math.pow(10, decimal.length)
11
+ const numerator = parseInt(decimal, 10)
12
+ return denominator / gcd(numerator, denominator)
13
+ }
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.13.0-canary.6cb770c78958'
4
+ export const version = '3.13.0-canary.8754dedf4cf0'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-04-30T12:29:15.138Z',
8
- patch: '2025-04-30T12:29:15.138Z',
7
+ minor: '2025-05-02T10:16:31.753Z',
8
+ patch: '2025-05-02T10:16:31.753Z',
9
9
  }