tldraw 3.15.0-next.e136ad205948 → 3.15.0-next.e1af71086875

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 (58) hide show
  1. package/dist-cjs/index.d.ts +11 -8
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawImage.js +5 -2
  5. package/dist-cjs/lib/TldrawImage.js.map +3 -3
  6. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -1
  7. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  8. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -1
  9. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  10. package/dist-cjs/lib/styles.js.map +2 -2
  11. package/dist-cjs/lib/ui/components/Spinner.js +2 -25
  12. package/dist-cjs/lib/ui/components/Spinner.js.map +2 -2
  13. package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButtonIcon.js.map +2 -2
  14. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +35 -1
  15. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
  16. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
  17. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  18. package/dist-cjs/lib/ui/context/actions.js.map +1 -1
  19. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  20. package/dist-cjs/lib/ui/version.js +3 -3
  21. package/dist-cjs/lib/ui/version.js.map +1 -1
  22. package/dist-esm/index.d.mts +11 -8
  23. package/dist-esm/index.mjs +4 -2
  24. package/dist-esm/index.mjs.map +2 -2
  25. package/dist-esm/lib/TldrawImage.mjs +5 -2
  26. package/dist-esm/lib/TldrawImage.mjs.map +2 -2
  27. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -1
  28. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  29. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +1 -1
  30. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  31. package/dist-esm/lib/styles.mjs.map +2 -2
  32. package/dist-esm/lib/ui/components/Spinner.mjs +3 -26
  33. package/dist-esm/lib/ui/components/Spinner.mjs.map +2 -2
  34. package/dist-esm/lib/ui/components/primitives/Button/TldrawUiButtonIcon.mjs.map +2 -2
  35. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +36 -2
  36. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
  37. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
  38. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  39. package/dist-esm/lib/ui/context/actions.mjs.map +1 -1
  40. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  41. package/dist-esm/lib/ui/version.mjs +3 -3
  42. package/dist-esm/lib/ui/version.mjs.map +1 -1
  43. package/package.json +3 -3
  44. package/src/index.ts +5 -1
  45. package/src/lib/TldrawImage.tsx +6 -2
  46. package/src/lib/shapes/shared/PlainTextLabel.tsx +1 -1
  47. package/src/lib/shapes/shared/RichTextLabel.tsx +1 -1
  48. package/src/lib/styles.tsx +3 -1
  49. package/src/lib/ui/components/Spinner.tsx +2 -24
  50. package/src/lib/ui/components/primitives/Button/TldrawUiButtonIcon.tsx +2 -2
  51. package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +41 -3
  52. package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +2 -2
  53. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +3 -2
  54. package/src/lib/ui/context/actions.tsx +1 -1
  55. package/src/lib/ui/hooks/useTools.tsx +2 -1
  56. package/src/lib/ui/version.ts +3 -3
  57. package/src/lib/ui.css +8 -8
  58. package/tldraw.css +23 -10
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/hooks/useTools.tsx"],
4
- "sourcesContent": ["import { Editor, GeoShapeGeoStyle, useMaybeEditor } from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType\n\tonSelect(source: TLUiEventSource): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((id) => ({\n\t\t\t\tid,\n\t\t\t\tlabel: `tool.${id}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo: id,\n\t\t\t\t},\n\t\t\t\tkbd: id === 'rectangle' ? 'r' : id === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + id) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, id)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${id}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Editor, GeoShapeGeoStyle, useMaybeEditor } from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType | TLUiIconJsx\n\tonSelect(source: TLUiEventSource): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((id) => ({\n\t\t\t\tid,\n\t\t\t\tlabel: `tool.${id}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo: id,\n\t\t\t\t},\n\t\t\t\tkbd: id === 'rectangle' ? 'r' : id === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + id) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, id)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${id}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["tools"]
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "3.15.0-next.e136ad205948";
1
+ const version = "3.15.0-next.e1af71086875";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-07-16T10:25:56.932Z",
5
- patch: "2025-07-16T10:25:56.932Z"
4
+ minor: "2025-07-28T10:38:17.512Z",
5
+ patch: "2025-07-28T10:38:17.512Z"
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 = '3.15.0-next.e136ad205948'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-07-16T10:25:56.932Z',\n\tpatch: '2025-07-16T10:25:56.932Z',\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.15.0-next.e1af71086875'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-07-28T10:38:17.512Z',\n\tpatch: '2025-07-28T10:38:17.512Z',\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": "3.15.0-next.e136ad205948",
4
+ "version": "3.15.0-next.e1af71086875",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -54,8 +54,8 @@
54
54
  "@tiptap/pm": "^2.9.1",
55
55
  "@tiptap/react": "^2.9.1",
56
56
  "@tiptap/starter-kit": "^2.9.1",
57
- "@tldraw/editor": "3.15.0-next.e136ad205948",
58
- "@tldraw/store": "3.15.0-next.e136ad205948",
57
+ "@tldraw/editor": "3.15.0-next.e1af71086875",
58
+ "@tldraw/store": "3.15.0-next.e1af71086875",
59
59
  "classnames": "^2.5.1",
60
60
  "hotkeys-js": "^3.13.9",
61
61
  "idb": "^7.1.1",
package/src/index.ts CHANGED
@@ -382,7 +382,11 @@ export {
382
382
  type TLUiDropdownMenuSubTriggerProps,
383
383
  type TLUiDropdownMenuTriggerProps,
384
384
  } from './lib/ui/components/primitives/TldrawUiDropdownMenu'
385
- export { TldrawUiIcon, type TLUiIconProps } from './lib/ui/components/primitives/TldrawUiIcon'
385
+ export {
386
+ TldrawUiIcon,
387
+ type TLUiIconJsx,
388
+ type TLUiIconProps,
389
+ } from './lib/ui/components/primitives/TldrawUiIcon'
386
390
  export { TldrawUiInput, type TLUiInputProps } from './lib/ui/components/primitives/TldrawUiInput'
387
391
  export { TldrawUiKbd, type TLUiKbdProps } from './lib/ui/components/primitives/TldrawUiKbd'
388
392
  export {
@@ -15,6 +15,7 @@ import { memo, useEffect, useLayoutEffect, useMemo, useState } from 'react'
15
15
  import { defaultBindingUtils } from './defaultBindingUtils'
16
16
  import { defaultShapeUtils } from './defaultShapeUtils'
17
17
  import { TLUiAssetUrlOverrides } from './ui/assetUrls'
18
+ import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
18
19
  import { defaultAddFontsFromNode, tipTapDefaultExtensions } from './utils/text/richText'
19
20
 
20
21
  /** @public */
@@ -111,6 +112,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
111
112
  assetUrls,
112
113
  textOptions = defaultTextOptions,
113
114
  } = props
115
+ const assetUrlsWithOverrides = useDefaultEditorAssetsWithOverrides(assetUrls)
114
116
 
115
117
  useLayoutEffect(() => {
116
118
  if (!container) return
@@ -129,7 +131,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
129
131
  tools: [],
130
132
  getContainer: () => tempElm,
131
133
  licenseKey,
132
- fontAssetUrls: assetUrls?.fonts,
134
+ fontAssetUrls: assetUrlsWithOverrides.fonts,
133
135
  textOptions,
134
136
  })
135
137
 
@@ -138,6 +140,8 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
138
140
  const shapeIds = editor.getCurrentPageShapeIds()
139
141
 
140
142
  async function setSvg() {
143
+ // We have to wait for the fonts to load so that we can correctly measure text sizes
144
+ await editor.fonts.loadRequiredFontsForCurrentPage(editor.options.maxFontsToLoadBeforeRender)
141
145
  const imageResult = await editor.toImage([...shapeIds], {
142
146
  bounds,
143
147
  scale,
@@ -175,7 +179,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
175
179
  preserveAspectRatio,
176
180
  licenseKey,
177
181
  pixelRatio,
178
- assetUrls,
182
+ assetUrlsWithOverrides,
179
183
  textOptions,
180
184
  ])
181
185
 
@@ -81,7 +81,7 @@ export const PlainTextLabel = React.memo(function PlainTextLabel({
81
81
  return (
82
82
  <div
83
83
  className={`${cssPrefix}-label tl-text-wrapper tl-plain-text-wrapper`}
84
- aria-hidden="true"
84
+ aria-hidden={!isEditing}
85
85
  data-font={font}
86
86
  data-align={align}
87
87
  data-hastext={!isEmpty}
@@ -128,7 +128,7 @@ export const RichTextLabel = React.memo(function RichTextLabel({
128
128
  return (
129
129
  <div
130
130
  className={`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`}
131
- aria-hidden="true"
131
+ aria-hidden={!isEditing}
132
132
  data-font={font}
133
133
  data-align={align}
134
134
  data-hastext={!isEmpty}
@@ -1,7 +1,9 @@
1
+ import { TLUiIconJsx } from './ui/components/primitives/TldrawUiIcon'
2
+
1
3
  /** @public */
2
4
  export type StyleValuesForUi<T> = readonly {
3
5
  readonly value: T
4
- readonly icon: string
6
+ readonly icon: string | TLUiIconJsx
5
7
  }[]
6
8
 
7
9
  // todo: default styles prop?
@@ -1,3 +1,4 @@
1
+ import { DefaultSpinner } from '@tldraw/editor'
1
2
  import React from 'react'
2
3
  import { useTranslation } from '../hooks/useTranslation/useTranslation'
3
4
 
@@ -5,28 +6,5 @@ import { useTranslation } from '../hooks/useTranslation/useTranslation'
5
6
  export function Spinner(props: React.SVGProps<SVGSVGElement>) {
6
7
  const msg = useTranslation()
7
8
 
8
- return (
9
- <svg
10
- width={16}
11
- height={16}
12
- viewBox="0 0 16 16"
13
- {...props}
14
- aria-label={msg('app.loading')}
15
- aria-hidden="false"
16
- >
17
- <g strokeWidth={2} fill="none" fillRule="evenodd">
18
- <circle strokeOpacity={0.25} cx={8} cy={8} r={7} stroke="currentColor" />
19
- <path strokeLinecap="round" d="M15 8c0-4.5-4.5-7-7-7" stroke="currentColor">
20
- <animateTransform
21
- attributeName="transform"
22
- type="rotate"
23
- from="0 8 8"
24
- to="360 8 8"
25
- dur="1s"
26
- repeatCount="indefinite"
27
- />
28
- </path>
29
- </g>
30
- </svg>
31
- )
9
+ return <DefaultSpinner aria-label={msg('app.loading')} {...props} />
32
10
  }
@@ -1,8 +1,8 @@
1
- import { TldrawUiIcon } from '../TldrawUiIcon'
1
+ import { TldrawUiIcon, TLUiIconJsx } from '../TldrawUiIcon'
2
2
 
3
3
  /** @public */
4
4
  export interface TLUiButtonIconProps {
5
- icon: string
5
+ icon: string | TLUiIconJsx
6
6
  small?: boolean
7
7
  invertIcon?: boolean
8
8
  }
@@ -1,11 +1,14 @@
1
1
  import classNames from 'classnames'
2
- import { memo, useLayoutEffect, useRef } from 'react'
2
+ import { cloneElement, memo, ReactElement, useLayoutEffect, useRef } from 'react'
3
3
  import { useAssetUrls } from '../../context/asset-urls'
4
4
  import { TLUiIconType } from '../../icon-types'
5
5
 
6
+ /** @public */
7
+ export type TLUiIconJsx = ReactElement<React.HTMLAttributes<HTMLDivElement>>
8
+
6
9
  /** @public */
7
10
  export interface TLUiIconProps extends React.HTMLAttributes<HTMLDivElement> {
8
- icon: TLUiIconType | Exclude<string, TLUiIconType>
11
+ icon: TLUiIconType | Exclude<string, TLUiIconType> | TLUiIconJsx
9
12
  label: string
10
13
  small?: boolean
11
14
  color?: string
@@ -24,6 +27,41 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
24
27
  className,
25
28
  ...props
26
29
  }: TLUiIconProps) {
30
+ if (typeof icon === 'string') {
31
+ return (
32
+ <TldrawUIIconInner
33
+ label={label}
34
+ small={small}
35
+ invertIcon={invertIcon}
36
+ icon={icon}
37
+ color={color}
38
+ className={className}
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+
44
+ return cloneElement(icon, {
45
+ ...props,
46
+ className: classNames({ 'tlui-icon__small': small }, className, icon.props.className),
47
+ 'aria-label': label,
48
+ style: {
49
+ color,
50
+ transform: invertIcon ? 'scale(-1, 1)' : undefined,
51
+ ...icon.props.style,
52
+ },
53
+ })
54
+ })
55
+
56
+ function TldrawUIIconInner({
57
+ label,
58
+ small,
59
+ invertIcon,
60
+ icon,
61
+ color,
62
+ className,
63
+ ...props
64
+ }: TLUiIconProps & { icon: TLUiIconType | Exclude<string, TLUiIconType> }) {
27
65
  const assetUrls = useAssetUrls()
28
66
  const asset = assetUrls.icons[icon as TLUiIconType] ?? assetUrls.icons['question-mark-circle']
29
67
  const ref = useRef<HTMLDivElement>(null)
@@ -69,4 +107,4 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
69
107
  }}
70
108
  />
71
109
  )
72
- })
110
+ }
@@ -5,7 +5,7 @@ import { TLUiEventSource } from '../../../context/events'
5
5
  import { useReadonly } from '../../../hooks/useReadonly'
6
6
  import { TLUiTranslationKey } from '../../../hooks/useTranslation/TLUiTranslationKey'
7
7
  import { useTranslation } from '../../../hooks/useTranslation/useTranslation'
8
- import { TldrawUiIcon } from '../TldrawUiIcon'
8
+ import { TldrawUiIcon, TLUiIconJsx } from '../TldrawUiIcon'
9
9
  import { TldrawUiKbd } from '../TldrawUiKbd'
10
10
  import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
11
11
 
@@ -14,7 +14,7 @@ export interface TLUiMenuCheckboxItemProps<
14
14
  TranslationKey extends string = string,
15
15
  IconType extends string = string,
16
16
  > {
17
- icon?: IconType
17
+ icon?: IconType | TLUiIconJsx
18
18
  id: string
19
19
  kbd?: string
20
20
  title?: string
@@ -12,6 +12,7 @@ import { TldrawUiButton } from '../Button/TldrawUiButton'
12
12
  import { TldrawUiButtonIcon } from '../Button/TldrawUiButtonIcon'
13
13
  import { TldrawUiButtonLabel } from '../Button/TldrawUiButtonLabel'
14
14
  import { TldrawUiDropdownMenuItem } from '../TldrawUiDropdownMenu'
15
+ import { TLUiIconJsx } from '../TldrawUiIcon'
15
16
  import { TldrawUiKbd } from '../TldrawUiKbd'
16
17
  import { TldrawUiToolbarButton } from '../TldrawUiToolbar'
17
18
  import { useTldrawUiMenuContext } from './TldrawUiMenuContext'
@@ -25,11 +26,11 @@ export interface TLUiMenuItemProps<
25
26
  /**
26
27
  * The icon to display on the item. Icons are only shown in certain menu types.
27
28
  */
28
- icon?: IconType
29
+ icon?: IconType | TLUiIconJsx
29
30
  /**
30
31
  * An icon to display to the left of the menu item.
31
32
  */
32
- iconLeft?: IconType
33
+ iconLeft?: IconType | TLUiIconJsx
33
34
  /**
34
35
  * The keyboard shortcut to display on the item.
35
36
  */
@@ -43,7 +43,7 @@ export interface TLUiActionItem<
43
43
  TransationKey extends string = string,
44
44
  IconType extends string = string,
45
45
  > {
46
- icon?: IconType
46
+ icon?: IconType | React.ReactElement
47
47
  id: string
48
48
  kbd?: string
49
49
  label?: TransationKey | { [key: string]: TransationKey }
@@ -1,6 +1,7 @@
1
1
  import { Editor, GeoShapeGeoStyle, useMaybeEditor } from '@tldraw/editor'
2
2
  import * as React from 'react'
3
3
  import { EmbedDialog } from '../components/EmbedDialog'
4
+ import { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'
4
5
  import { useA11y } from '../context/a11y'
5
6
  import { TLUiEventSource, useUiEvents } from '../context/events'
6
7
  import { TLUiIconType } from '../icon-types'
@@ -16,7 +17,7 @@ export interface TLUiToolItem<
16
17
  id: string
17
18
  label: TranslationKey
18
19
  shortcutsLabel?: TranslationKey
19
- icon: IconType
20
+ icon: IconType | TLUiIconJsx
20
21
  onSelect(source: TLUiEventSource): void
21
22
  /**
22
23
  * The keyboard shortcut for this tool. This is a string that can be a single key,
@@ -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.15.0-next.e136ad205948'
4
+ export const version = '3.15.0-next.e1af71086875'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-16T10:25:56.932Z',
8
- patch: '2025-07-16T10:25:56.932Z',
7
+ minor: '2025-07-28T10:38:17.512Z',
8
+ patch: '2025-07-28T10:38:17.512Z',
9
9
  }
package/src/lib/ui.css CHANGED
@@ -964,7 +964,7 @@
964
964
  justify-content: center;
965
965
  border-radius: 99px;
966
966
  opacity: 0;
967
- animation: fade-in;
967
+ animation: tl-fade-in;
968
968
  animation-duration: 0.12s;
969
969
  animation-delay: 2s;
970
970
  animation-fill-mode: forwards;
@@ -1365,11 +1365,11 @@
1365
1365
 
1366
1366
  @media (prefers-reduced-motion: no-preference) {
1367
1367
  .tlui-toast__container[data-state='open'] {
1368
- animation: slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
1368
+ animation: tlui-slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
1369
1369
  }
1370
1370
 
1371
1371
  .tlui-toast__container[data-state='closed'] {
1372
- animation: hide 100ms ease-in;
1372
+ animation: tlui-fade-out 100ms ease-in;
1373
1373
  }
1374
1374
 
1375
1375
  .tlui-toast__container[data-swipe='move'] {
@@ -1382,7 +1382,7 @@
1382
1382
  }
1383
1383
 
1384
1384
  .tlui-toast__container[data-swipe='end'] {
1385
- animation: swipe-out 100ms ease-out;
1385
+ animation: tlui-slide-out 100ms ease-out;
1386
1386
  }
1387
1387
  }
1388
1388
 
@@ -1397,7 +1397,7 @@
1397
1397
  z-index: var(--layer-canvas-overlays);
1398
1398
  background-color: var(--color-overlay);
1399
1399
  pointer-events: all;
1400
- animation: fadeIn 0.12s ease-out;
1400
+ animation: tl-fade-in 0.12s ease-out;
1401
1401
  display: grid;
1402
1402
  place-items: center;
1403
1403
  overflow-y: auto;
@@ -1964,7 +1964,7 @@
1964
1964
  }
1965
1965
 
1966
1966
  /* ------------------- Animations ------------------- */
1967
- @keyframes hide {
1967
+ @keyframes tlui-fade-out {
1968
1968
  0% {
1969
1969
  opacity: 1;
1970
1970
  }
@@ -1973,7 +1973,7 @@
1973
1973
  }
1974
1974
  }
1975
1975
 
1976
- @keyframes slide-in {
1976
+ @keyframes tlui-slide-in {
1977
1977
  from {
1978
1978
  transform: translateX(calc(100% + var(--space-3)));
1979
1979
  }
@@ -1982,7 +1982,7 @@
1982
1982
  }
1983
1983
  }
1984
1984
 
1985
- @keyframes swipe-out {
1985
+ @keyframes tlui-slide-out {
1986
1986
  from {
1987
1987
  transform: translateX(var(--radix-toast-swipe-end-x));
1988
1988
  }
package/tldraw.css CHANGED
@@ -972,14 +972,14 @@ input,
972
972
  font-size: 14px;
973
973
  font-weight: 500;
974
974
  opacity: 0;
975
- animation: fade-in 0.2s ease-in-out forwards;
975
+ animation: tl-fade-in 0.2s ease-in-out forwards;
976
976
  animation-delay: 0.2s;
977
977
  position: absolute;
978
978
  inset: 0px;
979
979
  z-index: var(--layer-canvas-blocker);
980
980
  }
981
981
 
982
- @keyframes fade-in {
982
+ @keyframes tl-fade-in {
983
983
  0% {
984
984
  opacity: 0;
985
985
  }
@@ -988,6 +988,19 @@ input,
988
988
  }
989
989
  }
990
990
 
991
+ .tl-spinner {
992
+ animation: tl-spin 1s linear infinite;
993
+ }
994
+
995
+ @keyframes tl-spin {
996
+ 0% {
997
+ transform: rotate(0deg);
998
+ }
999
+ 100% {
1000
+ transform: rotate(360deg);
1001
+ }
1002
+ }
1003
+
991
1004
  /* ---------------------- Brush --------------------- */
992
1005
 
993
1006
  .tl-brush {
@@ -2748,7 +2761,7 @@ it from receiving any pointer events or affecting the cursor. */
2748
2761
  justify-content: center;
2749
2762
  border-radius: 99px;
2750
2763
  opacity: 0;
2751
- animation: fade-in;
2764
+ animation: tl-fade-in;
2752
2765
  animation-duration: 0.12s;
2753
2766
  animation-delay: 2s;
2754
2767
  animation-fill-mode: forwards;
@@ -3149,11 +3162,11 @@ it from receiving any pointer events or affecting the cursor. */
3149
3162
 
3150
3163
  @media (prefers-reduced-motion: no-preference) {
3151
3164
  .tlui-toast__container[data-state='open'] {
3152
- animation: slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
3165
+ animation: tlui-slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
3153
3166
  }
3154
3167
 
3155
3168
  .tlui-toast__container[data-state='closed'] {
3156
- animation: hide 100ms ease-in;
3169
+ animation: tlui-fade-out 100ms ease-in;
3157
3170
  }
3158
3171
 
3159
3172
  .tlui-toast__container[data-swipe='move'] {
@@ -3166,7 +3179,7 @@ it from receiving any pointer events or affecting the cursor. */
3166
3179
  }
3167
3180
 
3168
3181
  .tlui-toast__container[data-swipe='end'] {
3169
- animation: swipe-out 100ms ease-out;
3182
+ animation: tlui-slide-out 100ms ease-out;
3170
3183
  }
3171
3184
  }
3172
3185
 
@@ -3181,7 +3194,7 @@ it from receiving any pointer events or affecting the cursor. */
3181
3194
  z-index: var(--layer-canvas-overlays);
3182
3195
  background-color: var(--color-overlay);
3183
3196
  pointer-events: all;
3184
- animation: fadeIn 0.12s ease-out;
3197
+ animation: tl-fade-in 0.12s ease-out;
3185
3198
  display: grid;
3186
3199
  place-items: center;
3187
3200
  overflow-y: auto;
@@ -3748,7 +3761,7 @@ it from receiving any pointer events or affecting the cursor. */
3748
3761
  }
3749
3762
 
3750
3763
  /* ------------------- Animations ------------------- */
3751
- @keyframes hide {
3764
+ @keyframes tlui-fade-out {
3752
3765
  0% {
3753
3766
  opacity: 1;
3754
3767
  }
@@ -3757,7 +3770,7 @@ it from receiving any pointer events or affecting the cursor. */
3757
3770
  }
3758
3771
  }
3759
3772
 
3760
- @keyframes slide-in {
3773
+ @keyframes tlui-slide-in {
3761
3774
  from {
3762
3775
  transform: translateX(calc(100% + var(--space-3)));
3763
3776
  }
@@ -3766,7 +3779,7 @@ it from receiving any pointer events or affecting the cursor. */
3766
3779
  }
3767
3780
  }
3768
3781
 
3769
- @keyframes swipe-out {
3782
+ @keyframes tlui-slide-out {
3770
3783
  from {
3771
3784
  transform: translateX(var(--radix-toast-swipe-end-x));
3772
3785
  }