tldraw 4.1.0-canary.54e71ea20e0d → 4.1.0-canary.55679d7781d6

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 (144) hide show
  1. package/dist-cjs/index.d.ts +46 -12
  2. package/dist-cjs/index.js +6 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/defaultEmbedDefinitions.js +25 -30
  5. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js +9 -32
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +3 -0
  9. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +43 -101
  11. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +138 -0
  13. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +7 -0
  14. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +25 -3
  15. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +3 -0
  17. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +20 -4
  19. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  20. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js +1 -1
  21. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
  22. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +23 -11
  23. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
  24. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +27 -6
  25. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  26. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +1 -1
  27. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  28. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +21 -9
  29. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  30. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +24 -8
  31. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
  32. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +21 -9
  33. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
  34. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +23 -8
  35. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  36. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +21 -9
  37. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  38. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +26 -11
  39. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  40. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +5 -0
  41. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  42. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js +6 -2
  43. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js.map +2 -2
  44. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +63 -55
  45. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  46. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +54 -47
  47. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +3 -3
  48. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js +62 -55
  49. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.js.map +2 -2
  50. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js +12 -5
  51. package/dist-cjs/lib/ui/components/StylePanel/StylePanelDropdownPicker.js.map +2 -2
  52. package/dist-cjs/lib/ui/context/actions.js +23 -29
  53. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  54. package/dist-cjs/lib/ui/version.js +3 -3
  55. package/dist-cjs/lib/ui/version.js.map +1 -1
  56. package/dist-esm/index.d.mts +46 -12
  57. package/dist-esm/index.mjs +12 -4
  58. package/dist-esm/index.mjs.map +2 -2
  59. package/dist-esm/lib/defaultEmbedDefinitions.mjs +25 -30
  60. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  61. package/dist-esm/lib/defaultExternalContentHandlers.mjs +9 -32
  62. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  63. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +3 -0
  64. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  65. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +46 -101
  66. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  67. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +124 -0
  68. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +7 -0
  69. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +26 -3
  70. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  71. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +3 -0
  72. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  73. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +20 -4
  74. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  75. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs +1 -1
  76. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
  77. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +23 -11
  78. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
  79. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +29 -7
  80. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  81. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +1 -1
  82. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  83. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +21 -9
  84. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  85. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +24 -8
  86. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
  87. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +21 -9
  88. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
  89. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +23 -8
  90. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  91. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +21 -9
  92. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  93. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +26 -11
  94. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  95. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +5 -0
  96. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  97. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs +6 -2
  98. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs.map +2 -2
  99. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +68 -57
  100. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  101. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +54 -47
  102. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +3 -3
  103. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs +63 -56
  104. package/dist-esm/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.mjs.map +2 -2
  105. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs +12 -5
  106. package/dist-esm/lib/ui/components/StylePanel/StylePanelDropdownPicker.mjs.map +2 -2
  107. package/dist-esm/lib/ui/context/actions.mjs +23 -29
  108. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  109. package/dist-esm/lib/ui/version.mjs +3 -3
  110. package/dist-esm/lib/ui/version.mjs.map +1 -1
  111. package/package.json +3 -3
  112. package/src/index.ts +4 -0
  113. package/src/lib/defaultEmbedDefinitions.ts +20 -24
  114. package/src/lib/defaultExternalContentHandlers.ts +11 -36
  115. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +3 -0
  116. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +51 -135
  117. package/src/lib/shapes/bookmark/bookmarks.ts +170 -0
  118. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +28 -2
  119. package/src/lib/shapes/line/LineShapeUtil.tsx +3 -0
  120. package/src/lib/shapes/text/TextShapeTool.test.ts +74 -0
  121. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +23 -6
  122. package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +1 -1
  123. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +24 -12
  124. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +34 -11
  125. package/src/lib/tools/SelectTool/childStates/Idle.ts +1 -1
  126. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +23 -11
  127. package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +26 -9
  128. package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +23 -10
  129. package/src/lib/tools/SelectTool/childStates/Resizing.ts +24 -9
  130. package/src/lib/tools/SelectTool/childStates/Rotating.ts +27 -11
  131. package/src/lib/tools/SelectTool/childStates/Translating.ts +28 -12
  132. package/src/lib/ui/components/Minimap/MinimapManager.ts +6 -0
  133. package/src/lib/ui/components/SharePanel/PeopleMenu.tsx +6 -2
  134. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +60 -49
  135. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +70 -53
  136. package/src/lib/ui/components/StylePanel/StylePanelDoubleDropdownPicker.tsx +105 -90
  137. package/src/lib/ui/components/StylePanel/StylePanelDropdownPicker.tsx +72 -51
  138. package/src/lib/ui/context/actions.tsx +27 -31
  139. package/src/lib/ui/version.ts +3 -3
  140. package/src/lib/utils/embeds/embeds.test.ts +16 -34
  141. package/src/test/SelectTool.test.ts +251 -0
  142. package/src/test/bookmark-shapes.test.ts +129 -7
  143. package/src/test/customSnapping.test.tsx +55 -11
  144. package/tldraw.css +7 -2
@@ -29,12 +29,27 @@ export interface StylePanelDoubleDropdownPickerProps<T extends string> {
29
29
  onValueChange?(style: StyleProp<T>, value: T): void
30
30
  }
31
31
 
32
- function DoubleDropdownPickerInner<T extends string>(
32
+ function StylePanelDoubleDropdownPickerInner<T extends string>(
33
+ props: StylePanelDoubleDropdownPickerProps<T>
34
+ ) {
35
+ const msg = useTranslation()
36
+ return (
37
+ <div className="tlui-style-panel__double-select-picker">
38
+ <div title={msg(props.label)} className="tlui-style-panel__double-select-picker-label">
39
+ {msg(props.label)}
40
+ </div>
41
+ <TldrawUiToolbar orientation="horizontal" label={msg(props.label)}>
42
+ <StylePanelDoubleDropdownPickerInline {...props} />
43
+ </TldrawUiToolbar>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ function StylePanelDoubleDropdownPickerInlineInner<T extends string>(
33
49
  props: StylePanelDoubleDropdownPickerProps<T>
34
50
  ) {
35
51
  const ctx = useStylePanelContext()
36
52
  const {
37
- label,
38
53
  uiTypeA,
39
54
  uiTypeB,
40
55
  labelA,
@@ -70,100 +85,100 @@ function DoubleDropdownPickerInner<T extends string>(
70
85
  const idA = `style panel ${uiTypeA} A`
71
86
  const idB = `style panel ${uiTypeB} B`
72
87
  return (
73
- <div className="tlui-style-panel__double-select-picker">
74
- <div title={msg(label)} className="tlui-style-panel__double-select-picker-label">
75
- {msg(label)}
76
- </div>
77
- <TldrawUiToolbar orientation="horizontal" label={msg(label)}>
78
- <TldrawUiPopover id={idA} open={isOpenA} onOpenChange={setIsOpenA}>
79
- <TldrawUiPopoverTrigger>
80
- <TldrawUiToolbarButton
81
- type="icon"
82
- data-testid={`style.${uiTypeA}`}
83
- title={
84
- msg(labelA) +
85
- ' — ' +
86
- (valueA === null || valueA.type === 'mixed'
87
- ? msg('style-panel.mixed')
88
- : msg(`${uiTypeA}-style.${valueA.value}` as TLUiTranslationKey))
89
- }
90
- >
91
- <TldrawUiButtonIcon icon={iconA} small invertIcon />
92
- </TldrawUiToolbarButton>
93
- </TldrawUiPopoverTrigger>
94
- <TldrawUiPopoverContent side="left" align="center" sideOffset={80} alignOffset={0}>
95
- <TldrawUiToolbar orientation="grid" label={msg(labelA)}>
96
- <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
97
- {itemsA.map((item) => {
98
- return (
99
- <TldrawUiToolbarButton
100
- data-testid={`style.${uiTypeA}.${item.value}`}
101
- type="icon"
102
- key={item.value}
103
- onClick={() => {
104
- onValueChange(styleA, item.value)
105
- tlmenus.deleteOpenMenu(idA, editor.contextId)
106
- setIsOpenA(false)
107
- }}
108
- title={`${msg(labelA)} — ${msg(`${uiTypeA}-style.${item.value}`)}`}
109
- >
110
- <TldrawUiButtonIcon icon={item.icon} invertIcon />
111
- </TldrawUiToolbarButton>
112
- )
113
- })}
114
- </TldrawUiMenuContextProvider>
115
- </TldrawUiToolbar>
116
- </TldrawUiPopoverContent>
117
- </TldrawUiPopover>
118
- <TldrawUiPopover id={idB} open={isOpenB} onOpenChange={setIsOpenB}>
119
- <TldrawUiPopoverTrigger>
120
- <TldrawUiToolbarButton
121
- type="icon"
122
- data-testid={`style.${uiTypeB}`}
123
- title={
124
- msg(labelB) +
125
- ' — ' +
126
- (valueB === null || valueB.type === 'mixed'
127
- ? msg('style-panel.mixed')
128
- : msg(`${uiTypeB}-style.${valueB.value}` as TLUiTranslationKey))
129
- }
130
- >
131
- <TldrawUiButtonIcon icon={iconB} small />
132
- </TldrawUiToolbarButton>
133
- </TldrawUiPopoverTrigger>
134
- <TldrawUiPopoverContent side="left" align="center" sideOffset={116} alignOffset={0}>
135
- <TldrawUiToolbar orientation="grid" label={msg(labelB)}>
136
- <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
137
- {itemsB.map((item) => {
138
- return (
139
- <TldrawUiToolbarButton
140
- key={item.value}
141
- type="icon"
142
- title={`${msg(labelB)} — ${msg(`${uiTypeB}-style.${item.value}` as TLUiTranslationKey)}`}
143
- data-testid={`style.${uiTypeB}.${item.value}`}
144
- onClick={() => {
145
- onValueChange(styleB, item.value)
146
- tlmenus.deleteOpenMenu(idB, editor.contextId)
147
- setIsOpenB(false)
148
- }}
149
- >
150
- <TldrawUiButtonIcon icon={item.icon} />
151
- </TldrawUiToolbarButton>
152
- )
153
- })}
154
- </TldrawUiMenuContextProvider>
155
- </TldrawUiToolbar>
156
- </TldrawUiPopoverContent>
157
- </TldrawUiPopover>
158
- </TldrawUiToolbar>
159
- </div>
88
+ <>
89
+ <TldrawUiPopover id={idA} open={isOpenA} onOpenChange={setIsOpenA}>
90
+ <TldrawUiPopoverTrigger>
91
+ <TldrawUiToolbarButton
92
+ type="icon"
93
+ data-testid={`style.${uiTypeA}`}
94
+ title={
95
+ msg(labelA) +
96
+ ' — ' +
97
+ (valueA === null || valueA.type === 'mixed'
98
+ ? msg('style-panel.mixed')
99
+ : msg(`${uiTypeA}-style.${valueA.value}` as TLUiTranslationKey))
100
+ }
101
+ >
102
+ <TldrawUiButtonIcon icon={iconA} small invertIcon />
103
+ </TldrawUiToolbarButton>
104
+ </TldrawUiPopoverTrigger>
105
+ <TldrawUiPopoverContent side="left" align="center" sideOffset={80} alignOffset={0}>
106
+ <TldrawUiToolbar orientation="grid" label={msg(labelA)}>
107
+ <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
108
+ {itemsA.map((item) => {
109
+ return (
110
+ <TldrawUiToolbarButton
111
+ data-testid={`style.${uiTypeA}.${item.value}`}
112
+ type="icon"
113
+ key={item.value}
114
+ onClick={() => {
115
+ onValueChange(styleA, item.value)
116
+ tlmenus.deleteOpenMenu(idA, editor.contextId)
117
+ setIsOpenA(false)
118
+ }}
119
+ title={`${msg(labelA)} — ${msg(`${uiTypeA}-style.${item.value}`)}`}
120
+ >
121
+ <TldrawUiButtonIcon icon={item.icon} invertIcon />
122
+ </TldrawUiToolbarButton>
123
+ )
124
+ })}
125
+ </TldrawUiMenuContextProvider>
126
+ </TldrawUiToolbar>
127
+ </TldrawUiPopoverContent>
128
+ </TldrawUiPopover>
129
+ <TldrawUiPopover id={idB} open={isOpenB} onOpenChange={setIsOpenB}>
130
+ <TldrawUiPopoverTrigger>
131
+ <TldrawUiToolbarButton
132
+ type="icon"
133
+ data-testid={`style.${uiTypeB}`}
134
+ title={
135
+ msg(labelB) +
136
+ ' — ' +
137
+ (valueB === null || valueB.type === 'mixed'
138
+ ? msg('style-panel.mixed')
139
+ : msg(`${uiTypeB}-style.${valueB.value}` as TLUiTranslationKey))
140
+ }
141
+ >
142
+ <TldrawUiButtonIcon icon={iconB} small />
143
+ </TldrawUiToolbarButton>
144
+ </TldrawUiPopoverTrigger>
145
+ <TldrawUiPopoverContent side="left" align="center" sideOffset={116} alignOffset={0}>
146
+ <TldrawUiToolbar orientation="grid" label={msg(labelB)}>
147
+ <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
148
+ {itemsB.map((item) => {
149
+ return (
150
+ <TldrawUiToolbarButton
151
+ key={item.value}
152
+ type="icon"
153
+ title={`${msg(labelB)} — ${msg(`${uiTypeB}-style.${item.value}` as TLUiTranslationKey)}`}
154
+ data-testid={`style.${uiTypeB}.${item.value}`}
155
+ onClick={() => {
156
+ onValueChange(styleB, item.value)
157
+ tlmenus.deleteOpenMenu(idB, editor.contextId)
158
+ setIsOpenB(false)
159
+ }}
160
+ >
161
+ <TldrawUiButtonIcon icon={item.icon} />
162
+ </TldrawUiToolbarButton>
163
+ )
164
+ })}
165
+ </TldrawUiMenuContextProvider>
166
+ </TldrawUiToolbar>
167
+ </TldrawUiPopoverContent>
168
+ </TldrawUiPopover>
169
+ </>
160
170
  )
161
171
  }
162
172
 
163
173
  // need to memo like this to get generics
164
174
  /** @public @react */
165
- export const StylePanelDoubleDropdownPicker = React.memo(DoubleDropdownPickerInner) as <
175
+ export const StylePanelDoubleDropdownPicker = React.memo(StylePanelDoubleDropdownPickerInner) as <
166
176
  T extends string,
167
177
  >(
168
178
  props: StylePanelDoubleDropdownPickerProps<T>
169
179
  ) => React.JSX.Element
180
+
181
+ /** @public @react */
182
+ export const StylePanelDoubleDropdownPickerInline = React.memo(
183
+ StylePanelDoubleDropdownPickerInlineInner
184
+ ) as <T extends string>(props: StylePanelDoubleDropdownPickerProps<T>) => React.JSX.Element
@@ -28,7 +28,21 @@ export interface StylePanelDropdownPickerProps<T extends string> {
28
28
  onValueChange?(style: StyleProp<T>, value: T): void
29
29
  }
30
30
 
31
- function DropdownPickerInner<T extends string>(props: StylePanelDropdownPickerProps<T>) {
31
+ function StylePanelDropdownPickerInner<T extends string>(props: StylePanelDropdownPickerProps<T>) {
32
+ const msg = useTranslation()
33
+ const toolbarLabel = props.label
34
+ ? msg(props.label)
35
+ : msg(`style-panel.${props.stylePanelType}` as TLUiTranslationKey)
36
+ return (
37
+ <TldrawUiToolbar label={toolbarLabel}>
38
+ <StylePanelDropdownPickerInline {...props} />
39
+ </TldrawUiToolbar>
40
+ )
41
+ }
42
+
43
+ function StylePanelDropdownPickerInlineInner<T extends string>(
44
+ props: StylePanelDropdownPickerProps<T>
45
+ ) {
32
46
  const ctx = useStylePanelContext()
33
47
  const {
34
48
  id,
@@ -60,60 +74,67 @@ function DropdownPickerInner<T extends string>(props: StylePanelDropdownPickerPr
60
74
 
61
75
  const popoverId = `style panel ${id}`
62
76
  return (
63
- <TldrawUiToolbar label={stylePanelName}>
64
- <TldrawUiPopover
65
- id={popoverId}
66
- open={isOpen}
67
- onOpenChange={setIsOpen}
68
- className="tlui-style-panel__dropdown-picker"
69
- >
70
- <TldrawUiPopoverTrigger>
71
- <TldrawUiToolbarButton
72
- type={type}
73
- data-testid={`style.${uiType}`}
74
- data-direction="left"
75
- title={titleStr}
76
- >
77
- {labelStr && <TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>}
78
- <TldrawUiButtonIcon icon={(icon as TLUiIconType) ?? 'mixed'} />
79
- </TldrawUiToolbarButton>
80
- </TldrawUiPopoverTrigger>
81
- <TldrawUiPopoverContent side="left" align="center">
82
- <TldrawUiToolbar orientation={items.length > 4 ? 'grid' : 'horizontal'} label={labelStr}>
83
- <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
84
- {items.map((item) => {
85
- return (
86
- <TldrawUiToolbarButton
87
- key={item.value}
88
- type="icon"
89
- data-testid={`style.${uiType}.${item.value}`}
90
- title={
91
- stylePanelName +
92
- ' ' +
93
- msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
94
- }
95
- isActive={icon === item.icon}
96
- onClick={() => {
97
- ctx.onHistoryMark('select style dropdown item')
98
- onValueChange(style, item.value)
99
- tlmenus.deleteOpenMenu(popoverId, editor.contextId)
100
- setIsOpen(false)
101
- }}
102
- >
103
- <TldrawUiButtonIcon icon={item.icon} />
104
- </TldrawUiToolbarButton>
105
- )
106
- })}
107
- </TldrawUiMenuContextProvider>
108
- </TldrawUiToolbar>
109
- </TldrawUiPopoverContent>
110
- </TldrawUiPopover>
111
- </TldrawUiToolbar>
77
+ <TldrawUiPopover
78
+ id={popoverId}
79
+ open={isOpen}
80
+ onOpenChange={setIsOpen}
81
+ className="tlui-style-panel__dropdown-picker"
82
+ >
83
+ <TldrawUiPopoverTrigger>
84
+ <TldrawUiToolbarButton
85
+ type={type}
86
+ data-testid={`style.${uiType}`}
87
+ data-direction="left"
88
+ title={titleStr}
89
+ >
90
+ {labelStr && <TldrawUiButtonLabel>{labelStr}</TldrawUiButtonLabel>}
91
+ <TldrawUiButtonIcon icon={(icon as TLUiIconType) ?? 'mixed'} />
92
+ </TldrawUiToolbarButton>
93
+ </TldrawUiPopoverTrigger>
94
+ <TldrawUiPopoverContent side="left" align="center">
95
+ <TldrawUiToolbar orientation={items.length > 4 ? 'grid' : 'horizontal'} label={labelStr}>
96
+ <TldrawUiMenuContextProvider type="icons" sourceId="style-panel">
97
+ {items.map((item) => {
98
+ return (
99
+ <TldrawUiToolbarButton
100
+ key={item.value}
101
+ type="icon"
102
+ data-testid={`style.${uiType}.${item.value}`}
103
+ title={
104
+ stylePanelName +
105
+ ' — ' +
106
+ msg(`${uiType}-style.${item.value}` as TLUiTranslationKey)
107
+ }
108
+ isActive={icon === item.icon}
109
+ onClick={() => {
110
+ ctx.onHistoryMark('select style dropdown item')
111
+ onValueChange(style, item.value)
112
+ tlmenus.deleteOpenMenu(popoverId, editor.contextId)
113
+ setIsOpen(false)
114
+ }}
115
+ >
116
+ <TldrawUiButtonIcon icon={item.icon} />
117
+ </TldrawUiToolbarButton>
118
+ )
119
+ })}
120
+ </TldrawUiMenuContextProvider>
121
+ </TldrawUiToolbar>
122
+ </TldrawUiPopoverContent>
123
+ </TldrawUiPopover>
112
124
  )
113
125
  }
114
126
 
115
127
  // need to export like this to get generics
116
128
  /** @public @react */
117
- export const StylePanelDropdownPicker = React.memo(DropdownPickerInner) as <T extends string>(
129
+ export const StylePanelDropdownPicker = React.memo(StylePanelDropdownPickerInner) as <
130
+ T extends string,
131
+ >(
132
+ props: StylePanelDropdownPickerProps<T>
133
+ ) => React.JSX.Element
134
+
135
+ /** @public @react */
136
+ export const StylePanelDropdownPickerInline = React.memo(StylePanelDropdownPickerInlineInner) as <
137
+ T extends string,
138
+ >(
118
139
  props: StylePanelDropdownPickerProps<T>
119
140
  ) => React.JSX.Element
@@ -5,6 +5,7 @@ import {
5
5
  Editor,
6
6
  HALF_PI,
7
7
  PageRecordType,
8
+ Result,
8
9
  TLBookmarkShape,
9
10
  TLEmbedShape,
10
11
  TLFrameShape,
@@ -24,6 +25,7 @@ import {
24
25
  useMaybeEditor,
25
26
  } from '@tldraw/editor'
26
27
  import * as React from 'react'
28
+ import { createBookmarkFromUrl } from '../../shapes/bookmark/bookmarks'
27
29
  import { fitFrameToContent, removeFrame } from '../../utils/frames/frames'
28
30
  import { generateShapeAnnouncementMessage } from '../components/A11y'
29
31
  import { EditLinkDialog } from '../components/EditLinkDialog'
@@ -387,45 +389,39 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
387
389
  {
388
390
  id: 'convert-to-bookmark',
389
391
  label: 'action.convert-to-bookmark',
390
- onSelect(source) {
392
+ async onSelect(source) {
391
393
  if (!canApplySelectionAction()) return
392
394
  if (mustGoBackToSelectToolFirst()) return
393
395
 
394
- editor.run(() => {
395
- trackEvent('convert-to-bookmark', { source })
396
- const shapes = editor.getSelectedShapes()
396
+ trackEvent('convert-to-bookmark', { source })
397
+ const shapes = editor.getSelectedShapes()
397
398
 
398
- const createList: TLShapePartial[] = []
399
- const deleteList: TLShapeId[] = []
400
- for (const shape of shapes) {
401
- if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
402
- continue
399
+ const markId = editor.markHistoryStoppingPoint('convert shapes to bookmark')
403
400
 
404
- const newPos = new Vec(shape.x, shape.y)
405
- newPos.rot(-shape.rotation)
406
- newPos.add(new Vec(shape.props.w / 2 - 300 / 2, shape.props.h / 2 - 320 / 2)) // see bookmark shape util
407
- newPos.rot(shape.rotation)
408
- const partial: TLShapePartial<TLBookmarkShape> = {
409
- id: createShapeId(),
410
- type: 'bookmark',
411
- rotation: shape.rotation,
412
- x: newPos.x,
413
- y: newPos.y,
414
- opacity: 1,
415
- props: {
416
- url: shape.props.url,
417
- },
418
- }
401
+ const creationPromises: Promise<Result<any, any>>[] = []
419
402
 
420
- createList.push(partial)
421
- deleteList.push(shape.id)
422
- }
403
+ for (const shape of shapes) {
404
+ if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
405
+ continue
423
406
 
424
- editor.markHistoryStoppingPoint('convert shapes to bookmark')
407
+ const center = editor.getShapePageBounds(shape)?.center
425
408
 
426
- // Should be able to create the shape since we're about to delete the other other
427
- editor.deleteShapes(deleteList)
428
- editor.createShapes(createList)
409
+ if (!center) continue
410
+ editor.deleteShapes([shape.id])
411
+
412
+ creationPromises.push(
413
+ createBookmarkFromUrl(editor, { url: shape.props.url, center }).then((res) => {
414
+ if (!res.ok) {
415
+ throw new Error(res.error)
416
+ }
417
+ return res
418
+ })
419
+ )
420
+ }
421
+
422
+ await Promise.all(creationPromises).catch((error) => {
423
+ editor.bailToMark(markId)
424
+ console.error(error)
429
425
  })
430
426
  },
431
427
  },
@@ -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 = '4.1.0-canary.54e71ea20e0d'
4
+ export const version = '4.1.0-canary.55679d7781d6'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2025-09-30T10:45:56.956Z',
8
- patch: '2025-09-30T10:45:56.956Z',
7
+ minor: '2025-10-15T10:10:43.277Z',
8
+ patch: '2025-10-15T10:10:43.277Z',
9
9
  }
@@ -275,6 +275,14 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
275
275
  embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true`,
276
276
  },
277
277
  },
278
+ {
279
+ url: 'https://replit.com/@omar/Blob-Generator#index.html',
280
+ match: true,
281
+ output: {
282
+ type: 'replit',
283
+ embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true#index.html`,
284
+ },
285
+ },
278
286
  {
279
287
  url: 'https://replit.com/foobar',
280
288
  match: false,
@@ -347,23 +355,6 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
347
355
  url: 'https://vimeo.com/foobar',
348
356
  match: false,
349
357
  },
350
- // excalidraw
351
- {
352
- url: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
353
- match: true,
354
- output: {
355
- type: 'excalidraw',
356
- embedUrl: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
357
- },
358
- },
359
- {
360
- url: 'https://excalidraw.com',
361
- match: false,
362
- },
363
- {
364
- url: 'https://excalidraw.com/help',
365
- match: false,
366
- },
367
358
  //desmos
368
359
  {
369
360
  url: 'https://www.desmos.com/calculator/js9hryvejc',
@@ -599,6 +590,14 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
599
590
  url: `https://replit.com/@omar/Blob-Generator`,
600
591
  },
601
592
  },
593
+ {
594
+ embedUrl: 'https://replit.com/@omar/Blob-Generator?embed=true#index.html',
595
+ match: true,
596
+ output: {
597
+ type: 'replit',
598
+ url: `https://replit.com/@omar/Blob-Generator#index.html`,
599
+ },
600
+ },
602
601
  {
603
602
  embedUrl: 'https://replit.com/@omar/Blob-Generator',
604
603
  match: false,
@@ -671,23 +670,6 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
671
670
  embedUrl: 'https://vimeo.com/foobar',
672
671
  match: false,
673
672
  },
674
- // excalidraw
675
- {
676
- embedUrl: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
677
- match: true,
678
- output: {
679
- type: 'excalidraw',
680
- url: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
681
- },
682
- },
683
- {
684
- embedUrl: 'https://excalidraw.com',
685
- match: false,
686
- },
687
- {
688
- embedUrl: 'https://excalidraw.com/help',
689
- match: false,
690
- },
691
673
  // desmos
692
674
  {
693
675
  embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?embed',