@tldraw/editor 5.6.0-next.c4e9c54a816e → 5.6.0-next.f151f7f3ebd6

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 (90) hide show
  1. package/dist-cjs/index.d.ts +157 -2
  2. package/dist-cjs/index.js +7 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +18 -5
  5. package/dist-cjs/lib/TldrawEditor.js.map +3 -3
  6. package/dist-cjs/lib/components/LiveCollaborators.js +3 -3
  7. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +57 -15
  9. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  10. package/dist-cjs/lib/config/TLI18n.js +64 -0
  11. package/dist-cjs/lib/config/TLI18n.js.map +7 -0
  12. package/dist-cjs/lib/editor/Editor.js +29 -12
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +10 -3
  15. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
  16. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +1 -1
  17. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +19 -2
  19. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +3 -5
  21. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +7 -1
  23. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +17 -0
  25. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  26. package/dist-cjs/lib/hooks/useDocumentEvents.js +28 -3
  27. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  28. package/dist-cjs/lib/license/LicenseManager.js +62 -4
  29. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  30. package/dist-cjs/lib/options.js +1 -1
  31. package/dist-cjs/lib/options.js.map +1 -1
  32. package/dist-cjs/version.js +3 -3
  33. package/dist-cjs/version.js.map +1 -1
  34. package/dist-esm/index.d.mts +157 -2
  35. package/dist-esm/index.mjs +13 -1
  36. package/dist-esm/index.mjs.map +2 -2
  37. package/dist-esm/lib/TldrawEditor.mjs +18 -5
  38. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  39. package/dist-esm/lib/components/LiveCollaborators.mjs +3 -3
  40. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  41. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +61 -16
  42. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  43. package/dist-esm/lib/config/TLI18n.mjs +44 -0
  44. package/dist-esm/lib/config/TLI18n.mjs.map +7 -0
  45. package/dist-esm/lib/editor/Editor.mjs +29 -12
  46. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  47. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +10 -3
  48. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  49. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +1 -1
  50. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  51. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +20 -3
  52. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  53. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +3 -5
  54. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
  55. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +7 -1
  56. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
  57. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +17 -0
  58. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  59. package/dist-esm/lib/hooks/useDocumentEvents.mjs +28 -3
  60. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  61. package/dist-esm/lib/license/LicenseManager.mjs +62 -4
  62. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  63. package/dist-esm/lib/options.mjs +1 -1
  64. package/dist-esm/lib/options.mjs.map +1 -1
  65. package/dist-esm/version.mjs +3 -3
  66. package/dist-esm/version.mjs.map +1 -1
  67. package/editor.css +2 -1
  68. package/package.json +7 -7
  69. package/src/index.ts +14 -2
  70. package/src/lib/TldrawEditor.tsx +44 -4
  71. package/src/lib/components/LiveCollaborators.tsx +3 -3
  72. package/src/lib/components/default-components/DefaultErrorFallback.tsx +66 -18
  73. package/src/lib/config/TLI18n.test.ts +84 -0
  74. package/src/lib/config/TLI18n.ts +172 -0
  75. package/src/lib/editor/Editor.ts +52 -24
  76. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +19 -3
  77. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +17 -0
  78. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +2 -2
  79. package/src/lib/editor/managers/FontManager/FontManager.test.ts +1 -0
  80. package/src/lib/editor/managers/FontManager/FontManager.ts +26 -3
  81. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +32 -0
  82. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +4 -6
  83. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +16 -7
  84. package/src/lib/editor/shapes/ShapeUtil.ts +18 -0
  85. package/src/lib/hooks/useDocumentEvents.ts +51 -3
  86. package/src/lib/license/LicenseManager.test.ts +204 -0
  87. package/src/lib/license/LicenseManager.ts +88 -4
  88. package/src/lib/options.ts +1 -1
  89. package/src/lib/test/useDocumentEvents.test.tsx +153 -22
  90. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/components/default-components/DefaultErrorFallback.tsx"],
4
- "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { noop } from '@tldraw/utils'\nimport classNames from 'classnames'\nimport { ComponentType, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { Editor } from '../../editor/Editor'\nimport { getOwnerDocument } from '../../exports/domUtils'\nimport { useEditorComponents } from '../../hooks/EditorComponentsContext'\nimport { EditorProvider } from '../../hooks/useEditor'\nimport { getGlobalWindow } from '../../utils/dom'\nimport { hardResetEditor, refreshPage } from '../../utils/runtime'\nimport { ErrorBoundary } from '../ErrorBoundary'\n\nconst BASE_ERROR_URL = 'https://github.com/tldraw/tldraw/issues/new'\n\n/** @public */\nexport interface TLErrorFallbackProps {\n\t/** The error that was caught. */\n\terror: unknown\n\t/**\n\t * Only set when the caller renders the fallback with an editor; the editor's own error\n\t * boundaries pass just `error`.\n\t */\n\teditor?: Editor\n}\n\n/** @public */\nexport type TLErrorFallbackComponent = ComponentType<TLErrorFallbackProps>\n\n/** @public @react */\nexport function DefaultErrorFallback({ error, editor }: TLErrorFallbackProps) {\n\tconst containerRef = useRef<HTMLDivElement>(null)\n\tconst [shouldShowError, setShouldShowError] = useState(process.env.NODE_ENV === 'development')\n\tconst [didCopy, setDidCopy] = useState(false)\n\tconst [shouldShowResetConfirmation, setShouldShowResetConfirmation] = useState(false)\n\n\tlet Canvas: React.ComponentType | null = null\n\ttry {\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\tconst components = useEditorComponents()\n\t\tCanvas = components.Canvas ?? null\n\t} catch {\n\t\t// allow this to fail silently\n\t}\n\n\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\tconst errorStack = error instanceof Error ? error.stack : null\n\n\tconst isDarkModeFromApp = useValue(\n\t\t'isDarkMode',\n\t\t() => {\n\t\t\ttry {\n\t\t\t\tif (editor) {\n\t\t\t\t\treturn editor.user.getIsDarkMode()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// we're in a funky error state so this might not work for spooky\n\t\t\t\t// reasons. if not, we'll have another attempt later:\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\t\t[editor]\n\t)\n\tconst [isDarkMode, setIsDarkMode] = useState<null | boolean>(null)\n\tuseLayoutEffect(() => {\n\t\t// if we found a theme class from the app, we can just use that\n\t\tif (isDarkModeFromApp !== null) {\n\t\t\tsetIsDarkMode(isDarkModeFromApp)\n\t\t}\n\n\t\t// do any of our parents have a theme class? if yes then we can just\n\t\t// rely on that and don't need to set our own class\n\t\tlet parent = containerRef.current?.parentElement\n\t\tlet foundParentThemeClass = false\n\t\twhile (parent) {\n\t\t\tif (\n\t\t\t\tparent.classList.contains('tl-theme__dark') ||\n\t\t\t\tparent.classList.contains('tl-theme__light')\n\t\t\t) {\n\t\t\t\tfoundParentThemeClass = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tparent = parent.parentElement\n\t\t}\n\t\tif (foundParentThemeClass) {\n\t\t\tsetIsDarkMode(null)\n\t\t\treturn\n\t\t}\n\n\t\t// if we can't find a theme class from the app or from a parent, we have\n\t\t// to fall back on using a media query:\n\t\tif (typeof window !== 'undefined' && getGlobalWindow().matchMedia) {\n\t\t\tsetIsDarkMode(getGlobalWindow().matchMedia('(prefers-color-scheme: dark)').matches)\n\t\t}\n\t}, [isDarkModeFromApp])\n\n\tuseEffect(() => {\n\t\tif (didCopy) {\n\t\t\tconst timeout = editor?.timers.setTimeout(() => {\n\t\t\t\tsetDidCopy(false)\n\t\t\t}, 2000)\n\t\t\treturn () => clearTimeout(timeout)\n\t\t}\n\t}, [didCopy, editor])\n\n\tconst copyError = () => {\n\t\tconst doc = getOwnerDocument(containerRef.current)\n\t\tconst textarea = doc.createElement('textarea')\n\t\ttextarea.value = errorStack ?? errorMessage\n\t\tdoc.body.appendChild(textarea)\n\t\ttextarea.select()\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tdoc.execCommand('copy')\n\t\ttextarea.remove()\n\t\tsetDidCopy(true)\n\t}\n\n\tconst refresh = () => {\n\t\trefreshPage()\n\t}\n\n\tconst resetLocalState = async () => {\n\t\thardResetEditor()\n\t}\n\n\tconst url = new URL(BASE_ERROR_URL)\n\turl.searchParams.set('title', errorMessage)\n\turl.searchParams.set('labels', `bug`)\n\turl.searchParams.set(\n\t\t'body',\n\t\t`Hey, I ran into an error while using tldraw:\n\n\\`\\`\\`js\n${errorStack ?? errorMessage}\n\\`\\`\\`\n\nMy browser: ${navigator.userAgent}`\n\t)\n\n\treturn (\n\t\t<div\n\t\t\tref={containerRef}\n\t\t\tclassName={classNames(\n\t\t\t\t'tl-container tl-error-boundary',\n\t\t\t\t// error-boundary is sometimes used outside of the theme\n\t\t\t\t// container, so we need to provide it with a theme for our\n\t\t\t\t// styles to work correctly\n\t\t\t\tisDarkMode === null ? '' : isDarkMode ? 'tl-theme__dark' : 'tl-theme__light'\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"tl-error-boundary__overlay\" />\n\t\t\t{editor && (\n\t\t\t\t// opportunistically attempt to render the canvas to reassure\n\t\t\t\t// the user that their document is still there. there's a good\n\t\t\t\t// chance this won't work (ie the error that we're currently\n\t\t\t\t// notifying the user about originates in the canvas) so it's\n\t\t\t\t// not a big deal if it doesn't work - in that case we just have\n\t\t\t\t// a plain grey background.\n\t\t\t\t<ErrorBoundary onError={noop} fallback={() => null}>\n\t\t\t\t\t<EditorProvider editor={editor}>\n\t\t\t\t\t\t<div className=\"tl-overlay tl-error-boundary__canvas\">{Canvas ? <Canvas /> : null}</div>\n\t\t\t\t\t</EditorProvider>\n\t\t\t\t</ErrorBoundary>\n\t\t\t)}\n\t\t\t<div\n\t\t\t\tclassName={classNames('tl-modal', 'tl-error-boundary__content', {\n\t\t\t\t\t'tl-error-boundary__content__expanded': shouldShowError && !shouldShowResetConfirmation,\n\t\t\t\t})}\n\t\t\t>\n\t\t\t\t{shouldShowResetConfirmation ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<h2>Are you sure?</h2>\n\t\t\t\t\t\t<p>Resetting your data will delete your drawing and cannot be undone.</p>\n\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions\">\n\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={() => setShouldShowResetConfirmation(false)}>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button className=\"tlui-button tl-error-boundary__reset\" onClick={resetLocalState}>\n\t\t\t\t\t\t\t\tReset data\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<h2>Something went wrong</h2>\n\t\t\t\t\t\t<p>Please refresh your browser.</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tIf the issue continues after refreshing, you may need to reset the tldraw data stored\n\t\t\t\t\t\t\ton your device.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t<strong>Note:</strong> Resetting will erase your current project and any unsaved work.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{process.env.NODE_ENV !== 'production' && (\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tIf you&apos;re developing with the SDK and need help, join us on{' '}\n\t\t\t\t\t\t\t\t<a href=\"https://discord.tldraw.com/?utm_source=sdk&utm_medium=organic&utm_campaign=error-screen\">\n\t\t\t\t\t\t\t\t\tDiscord\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{shouldShowError && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\tMessage:\n\t\t\t\t\t\t\t\t<h4>\n\t\t\t\t\t\t\t\t\t<code>{errorMessage}</code>\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\tStack trace:\n\t\t\t\t\t\t\t\t<div className=\"tl-error-boundary__content__error\">\n\t\t\t\t\t\t\t\t\t<pre>\n\t\t\t\t\t\t\t\t\t\t<code>{errorStack ?? errorMessage}</code>\n\t\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={copyError}>\n\t\t\t\t\t\t\t\t\t\t{didCopy ? 'Copied!' : 'Copy'}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions\">\n\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={() => setShouldShowError(!shouldShowError)}>\n\t\t\t\t\t\t\t\t{shouldShowError ? 'Hide details' : 'Show details'}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions__group\">\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-button tl-error-boundary__reset\"\n\t\t\t\t\t\t\t\t\tonClick={() => setShouldShowResetConfirmation(true)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tReset data\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t<button className=\"tlui-button tl-error-boundary__refresh\" onClick={refresh}>\n\t\t\t\t\t\t\t\t\tRefresh page\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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": "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",
4
+ "sourcesContent": ["import { useValue } from '@tldraw/state-react'\nimport { noop } from '@tldraw/utils'\nimport classNames from 'classnames'\nimport { ComponentType, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport {\n\tdefineMessages,\n\ttranslateMessage,\n\ttype TLI18nMessage,\n\ttype TLI18nValues,\n} from '../../config/TLI18n'\nimport { Editor } from '../../editor/Editor'\nimport { getOwnerDocument } from '../../exports/domUtils'\nimport { useEditorComponents } from '../../hooks/EditorComponentsContext'\nimport { EditorProvider } from '../../hooks/useEditor'\nimport { getGlobalWindow } from '../../utils/dom'\nimport { hardResetEditor, refreshPage } from '../../utils/runtime'\nimport { ErrorBoundary } from '../ErrorBoundary'\n\nconst BASE_ERROR_URL = 'https://github.com/tldraw/tldraw/issues/new'\n\nconst messages = defineMessages({\n\tconfirmResetTitle: { id: 'error.confirm-reset.title', defaultMessage: 'Are you sure?' },\n\tconfirmResetDescription: {\n\t\tid: 'error.confirm-reset.description',\n\t\tdefaultMessage: 'Resetting your data will delete your drawing and cannot be undone.',\n\t},\n\tcancel: { id: 'error.cancel', defaultMessage: 'Cancel' },\n\tresetData: { id: 'error.reset-data', defaultMessage: 'Reset data' },\n\ttitle: { id: 'error.title', defaultMessage: 'Something went wrong' },\n\trefreshHint: { id: 'error.refresh-hint', defaultMessage: 'Please refresh your browser.' },\n\tresetHint: {\n\t\tid: 'error.reset-hint',\n\t\tdefaultMessage:\n\t\t\t'If the issue continues after refreshing, you may need to reset the tldraw data stored on your device.',\n\t},\n\tnoteLabel: { id: 'error.note-label', defaultMessage: 'Note:' },\n\tresetNote: {\n\t\tid: 'error.reset-note',\n\t\tdefaultMessage: 'Resetting will erase your current project and any unsaved work.',\n\t},\n\tmessageLabel: { id: 'error.message-label', defaultMessage: 'Message:' },\n\tstackTraceLabel: { id: 'error.stack-trace-label', defaultMessage: 'Stack trace:' },\n\tcopy: { id: 'error.copy', defaultMessage: 'Copy' },\n\tcopied: { id: 'error.copied', defaultMessage: 'Copied!' },\n\tshowDetails: { id: 'error.show-details', defaultMessage: 'Show details' },\n\thideDetails: { id: 'error.hide-details', defaultMessage: 'Hide details' },\n\trefreshPage: { id: 'error.refresh-page', defaultMessage: 'Refresh page' },\n})\n\n// This renders because something already threw, so reading the editor is done defensively the way\n// the rest of this component reads it: English beats a second crash on the error screen.\nfunction useErrorMsg(editor: Editor | undefined) {\n\treturn useCallback(\n\t\t(message: TLI18nMessage, values?: TLI18nValues) => {\n\t\t\ttry {\n\t\t\t\treturn translateMessage(editor?.i18n(), message, values)\n\t\t\t} catch {\n\t\t\t\treturn message.defaultMessage\n\t\t\t}\n\t\t},\n\t\t[editor]\n\t)\n}\n\n/** @public */\nexport interface TLErrorFallbackProps {\n\t/** The error that was caught. */\n\terror: unknown\n\t/**\n\t * Only set when the caller renders the fallback with an editor; the editor's own error\n\t * boundaries pass just `error`.\n\t */\n\teditor?: Editor\n}\n\n/** @public */\nexport type TLErrorFallbackComponent = ComponentType<TLErrorFallbackProps>\n\n/** @public @react */\nexport function DefaultErrorFallback({ error, editor }: TLErrorFallbackProps) {\n\tconst msg = useErrorMsg(editor)\n\tconst containerRef = useRef<HTMLDivElement>(null)\n\tconst [shouldShowError, setShouldShowError] = useState(process.env.NODE_ENV === 'development')\n\tconst [didCopy, setDidCopy] = useState(false)\n\tconst [shouldShowResetConfirmation, setShouldShowResetConfirmation] = useState(false)\n\n\tlet Canvas: React.ComponentType | null = null\n\ttry {\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\tconst components = useEditorComponents()\n\t\tCanvas = components.Canvas ?? null\n\t} catch {\n\t\t// allow this to fail silently\n\t}\n\n\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\tconst errorStack = error instanceof Error ? error.stack : null\n\n\tconst isDarkModeFromApp = useValue(\n\t\t'isDarkMode',\n\t\t() => {\n\t\t\ttry {\n\t\t\t\tif (editor) {\n\t\t\t\t\treturn editor.user.getIsDarkMode()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// we're in a funky error state so this might not work for spooky\n\t\t\t\t// reasons. if not, we'll have another attempt later:\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\t\t[editor]\n\t)\n\tconst [isDarkMode, setIsDarkMode] = useState<null | boolean>(null)\n\tuseLayoutEffect(() => {\n\t\t// if we found a theme class from the app, we can just use that\n\t\tif (isDarkModeFromApp !== null) {\n\t\t\tsetIsDarkMode(isDarkModeFromApp)\n\t\t}\n\n\t\t// do any of our parents have a theme class? if yes then we can just\n\t\t// rely on that and don't need to set our own class\n\t\tlet parent = containerRef.current?.parentElement\n\t\tlet foundParentThemeClass = false\n\t\twhile (parent) {\n\t\t\tif (\n\t\t\t\tparent.classList.contains('tl-theme__dark') ||\n\t\t\t\tparent.classList.contains('tl-theme__light')\n\t\t\t) {\n\t\t\t\tfoundParentThemeClass = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tparent = parent.parentElement\n\t\t}\n\t\tif (foundParentThemeClass) {\n\t\t\tsetIsDarkMode(null)\n\t\t\treturn\n\t\t}\n\n\t\t// if we can't find a theme class from the app or from a parent, we have\n\t\t// to fall back on using a media query:\n\t\tif (typeof window !== 'undefined' && getGlobalWindow().matchMedia) {\n\t\t\tsetIsDarkMode(getGlobalWindow().matchMedia('(prefers-color-scheme: dark)').matches)\n\t\t}\n\t}, [isDarkModeFromApp])\n\n\tuseEffect(() => {\n\t\tif (didCopy) {\n\t\t\tconst timeout = editor?.timers.setTimeout(() => {\n\t\t\t\tsetDidCopy(false)\n\t\t\t}, 2000)\n\t\t\treturn () => clearTimeout(timeout)\n\t\t}\n\t}, [didCopy, editor])\n\n\tconst copyError = () => {\n\t\tconst doc = getOwnerDocument(containerRef.current)\n\t\tconst textarea = doc.createElement('textarea')\n\t\ttextarea.value = errorStack ?? errorMessage\n\t\tdoc.body.appendChild(textarea)\n\t\ttextarea.select()\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\tdoc.execCommand('copy')\n\t\ttextarea.remove()\n\t\tsetDidCopy(true)\n\t}\n\n\tconst refresh = () => {\n\t\trefreshPage()\n\t}\n\n\tconst resetLocalState = async () => {\n\t\thardResetEditor()\n\t}\n\n\tconst url = new URL(BASE_ERROR_URL)\n\turl.searchParams.set('title', errorMessage)\n\turl.searchParams.set('labels', `bug`)\n\turl.searchParams.set(\n\t\t'body',\n\t\t`Hey, I ran into an error while using tldraw:\n\n\\`\\`\\`js\n${errorStack ?? errorMessage}\n\\`\\`\\`\n\nMy browser: ${navigator.userAgent}`\n\t)\n\n\treturn (\n\t\t<div\n\t\t\tref={containerRef}\n\t\t\tclassName={classNames(\n\t\t\t\t'tl-container tl-error-boundary',\n\t\t\t\t// error-boundary is sometimes used outside of the theme\n\t\t\t\t// container, so we need to provide it with a theme for our\n\t\t\t\t// styles to work correctly\n\t\t\t\tisDarkMode === null ? '' : isDarkMode ? 'tl-theme__dark' : 'tl-theme__light'\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"tl-error-boundary__overlay\" />\n\t\t\t{editor && (\n\t\t\t\t// opportunistically attempt to render the canvas to reassure\n\t\t\t\t// the user that their document is still there. there's a good\n\t\t\t\t// chance this won't work (ie the error that we're currently\n\t\t\t\t// notifying the user about originates in the canvas) so it's\n\t\t\t\t// not a big deal if it doesn't work - in that case we just have\n\t\t\t\t// a plain grey background.\n\t\t\t\t<ErrorBoundary onError={noop} fallback={() => null}>\n\t\t\t\t\t<EditorProvider editor={editor}>\n\t\t\t\t\t\t<div className=\"tl-overlay tl-error-boundary__canvas\">{Canvas ? <Canvas /> : null}</div>\n\t\t\t\t\t</EditorProvider>\n\t\t\t\t</ErrorBoundary>\n\t\t\t)}\n\t\t\t<div\n\t\t\t\tclassName={classNames('tl-modal', 'tl-error-boundary__content', {\n\t\t\t\t\t'tl-error-boundary__content__expanded': shouldShowError && !shouldShowResetConfirmation,\n\t\t\t\t})}\n\t\t\t>\n\t\t\t\t{shouldShowResetConfirmation ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<h2>{msg(messages.confirmResetTitle)}</h2>\n\t\t\t\t\t\t<p>{msg(messages.confirmResetDescription)}</p>\n\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions\">\n\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={() => setShouldShowResetConfirmation(false)}>\n\t\t\t\t\t\t\t\t{msg(messages.cancel)}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button className=\"tlui-button tl-error-boundary__reset\" onClick={resetLocalState}>\n\t\t\t\t\t\t\t\t{msg(messages.resetData)}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<h2>{msg(messages.title)}</h2>\n\t\t\t\t\t\t<p>{msg(messages.refreshHint)}</p>\n\t\t\t\t\t\t<p>{msg(messages.resetHint)}</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t<strong>{msg(messages.noteLabel)}</strong> {msg(messages.resetNote)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{process.env.NODE_ENV !== 'production' && (\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tIf you&apos;re developing with the SDK and need help, join us on{' '}\n\t\t\t\t\t\t\t\t<a href=\"https://discord.tldraw.com/?utm_source=sdk&utm_medium=organic&utm_campaign=error-screen\">\n\t\t\t\t\t\t\t\t\tDiscord\n\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{shouldShowError && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{msg(messages.messageLabel)}\n\t\t\t\t\t\t\t\t<h4>\n\t\t\t\t\t\t\t\t\t<code>{errorMessage}</code>\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t{msg(messages.stackTraceLabel)}\n\t\t\t\t\t\t\t\t<div className=\"tl-error-boundary__content__error\">\n\t\t\t\t\t\t\t\t\t<pre>\n\t\t\t\t\t\t\t\t\t\t<code>{errorStack ?? errorMessage}</code>\n\t\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={copyError}>\n\t\t\t\t\t\t\t\t\t\t{didCopy ? msg(messages.copied) : msg(messages.copy)}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions\">\n\t\t\t\t\t\t\t<button className=\"tlui-button\" onClick={() => setShouldShowError(!shouldShowError)}>\n\t\t\t\t\t\t\t\t{shouldShowError ? msg(messages.hideDetails) : msg(messages.showDetails)}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<div className=\"tl-error-boundary__content__actions__group\">\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclassName=\"tlui-button tl-error-boundary__reset\"\n\t\t\t\t\t\t\t\t\tonClick={() => setShouldShowResetConfirmation(true)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{msg(messages.resetData)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t<button className=\"tlui-button tl-error-boundary__refresh\" onClick={refresh}>\n\t\t\t\t\t\t\t\t\t{msg(messages.refreshPage)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\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": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,44 @@
1
+ import { atom } from "@tldraw/state";
2
+ function defaultI18n() {
3
+ return {
4
+ locale: "en",
5
+ dir: "ltr",
6
+ translate(message) {
7
+ return message.defaultMessage;
8
+ }
9
+ };
10
+ }
11
+ const declaredMessages = {};
12
+ const declaredVersion = atom("declaredMessagesVersion", 0);
13
+ let snapshot = {};
14
+ let snapshotVersion = -1;
15
+ function defineMessages(msgs) {
16
+ const withIds = {};
17
+ for (const key in msgs) {
18
+ const message = msgs[key];
19
+ const withId = message.id ? message : { ...message, id: message.defaultMessage };
20
+ withIds[key] = withId;
21
+ declaredMessages[withId.id] = withId.defaultMessage;
22
+ }
23
+ declaredVersion.update((v) => v + 1);
24
+ return withIds;
25
+ }
26
+ function getDeclaredMessages() {
27
+ const version = declaredVersion.get();
28
+ if (version !== snapshotVersion) {
29
+ snapshot = { ...declaredMessages };
30
+ snapshotVersion = version;
31
+ }
32
+ return snapshot;
33
+ }
34
+ function translateMessage(i18n, message, values) {
35
+ if (!i18n) return message.defaultMessage;
36
+ return i18n.translate(message, values);
37
+ }
38
+ export {
39
+ defaultI18n,
40
+ defineMessages,
41
+ getDeclaredMessages,
42
+ translateMessage
43
+ };
44
+ //# sourceMappingURL=TLI18n.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/config/TLI18n.ts"],
4
+ "sourcesContent": ["import { atom } from '@tldraw/state'\n\n/**\n * Values substituted into a message's ICU placeholders.\n *\n * @public\n */\nexport type TLI18nValues = Record<\n\tstring,\n\tstring | number | bigint | boolean | Date | null | undefined\n>\n\n/**\n * The editor's view of the host app's translations. Lets code with no access to React context \u2014\n * a {@link ShapeUtil}, say \u2014 translate a string in the user's locale.\n *\n * @public\n */\nexport interface TLI18n {\n\tlocale: string\n\tdir: 'ltr' | 'rtl'\n\t/**\n\t * Translate `message` in the current locale, substituting `values` into its ICU placeholders.\n\t *\n\t * Takes the whole message rather than its id so an implementation can fall back to the English\n\t * and still substitute into it. A message absent from the catalog \u2014 anything not yet through\n\t * Lokalise \u2014 would otherwise have to come back as its own source text, placeholders and all.\n\t */\n\ttranslate(message: TLI18nMessage, values?: TLI18nValues): string\n}\n\n/**\n * Supplies a {@link TLI18n}. Called on each use rather than held as a value, so the editor reads\n * the current locale instead of pinning whichever one was active when it was constructed.\n *\n * @public\n */\nexport type TLI18nAdapter = () => TLI18n\n\n/**\n * The i18n used when the host app supplies none: every message comes back as its own English.\n *\n * Placeholders are left as they are, because formatting ICU needs a formatter and this package\n * deliberately has none \u2014 it is the floor for an editor running with no translations at all.\n *\n * @public\n */\nexport function defaultI18n(): TLI18n {\n\treturn {\n\t\tlocale: 'en',\n\t\tdir: 'ltr',\n\t\ttranslate(message: TLI18nMessage) {\n\t\t\treturn message.defaultMessage\n\t\t},\n\t}\n}\n\n/**\n * One translatable message: a stable `id` naming it in the catalog, and the English it falls back\n * to. `defaultMessage` is required, because this package's strings have to read correctly with no\n * translations loaded at all \u2014 `@tldraw/editor` can be used without any of tldraw's UI.\n *\n * @public\n */\nexport interface TLI18nMessage {\n\tid: string\n\tdefaultMessage: string\n\tdescription?: string\n}\n\n/**\n * A message as it's written. The id is optional because a bundler plugin may supply it:\n * `@formatjs/unplugin` with an `idInterpolationPattern` derives one from the message's own text,\n * which is how tldraw.com does it. Everywhere downstream takes a {@link TLI18nMessage}, whose id\n * is required, because {@link defineMessages} fills one in.\n *\n * @public\n */\nexport type TLI18nMessageInput = Omit<TLI18nMessage, 'id'> & { id?: string }\n\nconst declaredMessages: Record<string, string> = {}\n// Bumped per `defineMessages` call, so a reader re-renders when a chunk loaded after mount \u2014 a\n// dynamically imported @tldraw/commenting, say \u2014 declares messages its labels look up by id.\nconst declaredVersion = atom('declaredMessagesVersion', 0)\nlet snapshot: Readonly<Record<string, string>> = {}\nlet snapshotVersion = -1\n\n/**\n * Declares messages so the extractor can find them, next to the UI that shows them \u2014 the English\n * then lives with the code rather than in a catalog edited by hand. Call it by this name:\n * `formatjs extract` matches the call by name, wherever it was imported from, so a wrapper under\n * another name extracts nothing.\n *\n * Write the id yourself, as the SDK's own messages do \u2014 an app overrides by id, so an id has to\n * survive a reworded English string. Leave it out only where a bundler plugin injects one; with no\n * plugin either the English stands in, so the message still renders as itself. An id missing from\n * a real build is caught by `pnpm i18n-extract --check`.\n *\n * The type parameter is `const` so each written `id` keeps its literal type, which is what lets a\n * declared id go where a `TLUiTranslationKey` is expected \u2014 a menu item's `label`, say \u2014 instead\n * of widening to `string` and needing a cast.\n *\n * For a message rendered as an element, write it inline on the `<F>` instead; the extractor reads\n * those props directly. This is for the ones that can't be: a label the UI takes as data, and\n * anything translated as a string through `useTranslation`.\n *\n * @example\n *\n * ```ts\n * const messages = defineMessages({\n * \tcopy: { id: 'action.copy', defaultMessage: 'Copy' },\n * })\n * ```\n *\n * @public\n */\nexport function defineMessages<const Messages extends Record<string, TLI18nMessageInput>>(\n\tmsgs: Messages\n): { [K in keyof Messages]: Messages[K] & TLI18nMessage } {\n\tconst withIds = {} as { [K in keyof Messages]: Messages[K] & TLI18nMessage }\n\tfor (const key in msgs) {\n\t\tconst message = msgs[key]\n\t\tconst withId = (\n\t\t\tmessage.id ? message : { ...message, id: message.defaultMessage }\n\t\t) as Messages[typeof key] & TLI18nMessage\n\t\twithIds[key] = withId\n\t\tdeclaredMessages[withId.id] = withId.defaultMessage\n\t}\n\tdeclaredVersion.update((v) => v + 1)\n\treturn withIds\n}\n\n/**\n * The English of every message declared so far, by id. This is the English catalog: building it\n * from the declarations means it isn't bundled a second time as a generated file. A key looked up\n * by id alone \u2014 a menu label, or an app calling `msg('action.copy')` \u2014 resolves against it.\n *\n * Declarations run as their modules load, so this is complete once the package has been imported.\n * Reactive: read it inside `useValue` or a `computed` to pick up a module that loads later. The\n * returned object changes identity only when something new is declared.\n *\n * @internal\n */\nexport function getDeclaredMessages(): Readonly<Record<string, string>> {\n\tconst version = declaredVersion.get()\n\tif (version !== snapshotVersion) {\n\t\tsnapshot = { ...declaredMessages }\n\t\tsnapshotVersion = version\n\t}\n\treturn snapshot\n}\n\n/**\n * Translates `message` through an editor's i18n, falling back to its English.\n *\n * Takes the editor's i18n rather than reading a context, so it works in the places this package\n * has to keep working: a `ShapeUtil` with no hooks available, or an error fallback rendered\n * outside the editor it's reporting on.\n *\n * With no i18n at all the English is returned unformatted, for the reason given on\n * {@link defaultI18n}. Every other case goes through the adapter, which does substitute.\n *\n * @public\n */\nexport function translateMessage(\n\ti18n: TLI18n | null | undefined,\n\tmessage: TLI18nMessage,\n\tvalues?: TLI18nValues\n): string {\n\tif (!i18n) return message.defaultMessage\n\treturn i18n.translate(message, values)\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AA+Cd,SAAS,cAAsB;AACrC,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,UAAU,SAAwB;AACjC,aAAO,QAAQ;AAAA,IAChB;AAAA,EACD;AACD;AAyBA,MAAM,mBAA2C,CAAC;AAGlD,MAAM,kBAAkB,KAAK,2BAA2B,CAAC;AACzD,IAAI,WAA6C,CAAC;AAClD,IAAI,kBAAkB;AA+Bf,SAAS,eACf,MACyD;AACzD,QAAM,UAAU,CAAC;AACjB,aAAW,OAAO,MAAM;AACvB,UAAM,UAAU,KAAK,GAAG;AACxB,UAAM,SACL,QAAQ,KAAK,UAAU,EAAE,GAAG,SAAS,IAAI,QAAQ,eAAe;AAEjE,YAAQ,GAAG,IAAI;AACf,qBAAiB,OAAO,EAAE,IAAI,OAAO;AAAA,EACtC;AACA,kBAAgB,OAAO,CAAC,MAAM,IAAI,CAAC;AACnC,SAAO;AACR;AAaO,SAAS,sBAAwD;AACvE,QAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,YAAY,iBAAiB;AAChC,eAAW,EAAE,GAAG,iBAAiB;AACjC,sBAAkB;AAAA,EACnB;AACA,SAAO;AACR;AAcO,SAAS,iBACf,MACA,SACA,QACS;AACT,MAAI,CAAC,KAAM,QAAO,QAAQ;AAC1B,SAAO,KAAK,UAAU,SAAS,MAAM;AACtC;",
6
+ "names": []
7
+ }
@@ -72,6 +72,7 @@ import {
72
72
  getSnapshot,
73
73
  loadSnapshot
74
74
  } from "../config/TLEditorSnapshot.mjs";
75
+ import { defaultI18n } from "../config/TLI18n.mjs";
75
76
  import {
76
77
  DEFAULT_ANIMATION_OPTIONS,
77
78
  DEFAULT_CAMERA_OPTIONS,
@@ -165,9 +166,11 @@ class Editor extends EventEmitter {
165
166
  colorScheme,
166
167
  fontAssetUrls,
167
168
  themes,
168
- initialTheme
169
+ initialTheme,
170
+ i18n
169
171
  }) {
170
172
  super();
173
+ this.i18n = i18n ?? defaultI18n;
171
174
  this._getShapeVisibility = getShapeVisibility;
172
175
  const options = _textOptions ? { ..._options, text: _options?.text ?? _textOptions } : _options;
173
176
  this.options = { ...defaultTldrawOptions, ...options };
@@ -679,6 +682,12 @@ class Editor extends EventEmitter {
679
682
  */
680
683
  licenseManager;
681
684
  contextId = uniqueId();
685
+ /**
686
+ * The i18n adapter for translating strings.
687
+ *
688
+ * @public
689
+ */
690
+ i18n;
682
691
  /**
683
692
  * The editor's store
684
693
  *
@@ -1658,7 +1667,7 @@ class Editor extends EventEmitter {
1658
1667
  const firstParentId = selectedShapeIds[0] ? this.getShape(selectedShapeIds[0])?.parentId : null;
1659
1668
  const isSelectedWithinContainer = firstParentId && selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) && !isPageId(firstParentId);
1660
1669
  const filteredShapes = this.getCurrentPageShapes().filter(
1661
- (shape2) => !this.isShapeOrAncestorLocked(shape2) && (isSelectedWithinContainer ? shape2.parentId === firstParentId : isPageId(shape2.parentId))
1670
+ (shape2) => isSelectedWithinContainer ? shape2.parentId === firstParentId : isPageId(shape2.parentId)
1662
1671
  );
1663
1672
  const readingOrderShapes = this._getShapesInReadingOrder(filteredShapes);
1664
1673
  const currentShapeId = selectedShapeIds.length === 1 ? selectedShapeIds[0] : readingOrderShapes.find((shape2) => selectedShapeIds.includes(shape2.id))?.id;
@@ -1801,6 +1810,11 @@ class Editor extends EventEmitter {
1801
1810
  this._selectShapesAndZoom([sortedChildren[0].id]);
1802
1811
  }
1803
1812
  _selectShapesAndZoom(ids) {
1813
+ const selectedShapeIds = this.getSelectedShapeIds();
1814
+ const isSelectionChanging = ids.length !== selectedShapeIds.length || ids.some((id) => !selectedShapeIds.includes(id));
1815
+ if (isSelectionChanging) {
1816
+ this.markHistoryStoppingPoint("selecting adjacent shape");
1817
+ }
1804
1818
  this.setSelectedShapes(ids);
1805
1819
  this.zoomToSelectionIfOffscreen(256, {
1806
1820
  animation: {
@@ -5970,9 +5984,10 @@ class Editor extends EventEmitter {
5970
5984
  }
5971
5985
  }
5972
5986
  let workingShape = null;
5987
+ const initialPagePoint = Mat.applyToPoint(pageTransform, new Vec());
5973
5988
  if (util.onResize && util.canResize(initialShape)) {
5974
5989
  const newPagePoint = this._scalePagePoint(
5975
- Mat.applyToPoint(pageTransform, new Vec(0, 0)),
5990
+ initialPagePoint,
5976
5991
  scaleOrigin,
5977
5992
  scale,
5978
5993
  scaleAxisRotation
@@ -5985,7 +6000,6 @@ class Editor extends EventEmitter {
5985
6000
  );
5986
6001
  myScale.x = areWidthAndHeightAlignedWithCorrectAxis ? scale.x : scale.y;
5987
6002
  myScale.y = areWidthAndHeightAlignedWithCorrectAxis ? scale.y : scale.x;
5988
- const initialPagePoint = Mat.applyToPoint(pageTransform, new Vec());
5989
6003
  const { x, y } = this.getPointInParentSpace(initialShape.id, initialPagePoint);
5990
6004
  workingShape = initialShape;
5991
6005
  if (!opts.skipStartAndEndCallbacks) {
@@ -6031,24 +6045,26 @@ class Editor extends EventEmitter {
6031
6045
  scale,
6032
6046
  scaleAxisRotation
6033
6047
  );
6034
- const initialPageCenterInParentSpace = this.getPointInParentSpace(
6048
+ const repositionedPagePoint = Vec.Add(
6049
+ newPageCenter,
6050
+ Vec.Sub(initialPagePoint, initialPageCenter)
6051
+ );
6052
+ const repositionedLocalPoint = this.getPointInParentSpace(
6035
6053
  initialShape.id,
6036
- initialPageCenter
6054
+ repositionedPagePoint
6037
6055
  );
6038
- const newPageCenterInParentSpace = this.getPointInParentSpace(initialShape.id, newPageCenter);
6039
- const delta = Vec.Sub(newPageCenterInParentSpace, initialPageCenterInParentSpace);
6040
6056
  if (workingShape) {
6041
6057
  return {
6042
6058
  ...workingShape,
6043
- x: initialShape.x + delta.x,
6044
- y: initialShape.y + delta.y
6059
+ x: repositionedLocalPoint.x,
6060
+ y: repositionedLocalPoint.y
6045
6061
  };
6046
6062
  }
6047
6063
  return {
6048
6064
  id,
6049
6065
  type: initialShape.type,
6050
- x: initialShape.x + delta.x,
6051
- y: initialShape.y + delta.y
6066
+ x: repositionedLocalPoint.x,
6067
+ y: repositionedLocalPoint.y
6052
6068
  };
6053
6069
  }
6054
6070
  /** @internal */
@@ -8396,6 +8412,7 @@ class Editor extends EventEmitter {
8396
8412
  break;
8397
8413
  }
8398
8414
  case "key_repeat": {
8415
+ inputs.keys.add(info.code);
8399
8416
  break;
8400
8417
  }
8401
8418
  }