tldraw 4.3.0-canary.35392ae6dc0d → 4.3.0-canary.37e6bf0fa8c6
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.
- package/dist-cjs/index.d.ts +9 -0
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
- package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +9 -12
- package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
- package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +10 -6
- package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +5 -5
- package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +2 -1
- package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
- package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js +11 -3
- package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
- package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
- package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
- package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
- package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
- package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
- package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
- package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +30 -10
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
- package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js +1 -1
- package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js.map +2 -2
- package/dist-cjs/lib/ui/components/menu-items.js +3 -1
- package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +1 -13
- package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-cjs/lib/utils/text/richText.js +7 -17
- package/dist-cjs/lib/utils/text/richText.js.map +3 -3
- package/dist-esm/index.d.mts +9 -0
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
- package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +10 -14
- package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -3
- package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +10 -6
- package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -5
- package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +3 -2
- package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
- package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +11 -3
- package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
- package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
- package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
- package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
- package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
- package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +30 -10
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
- package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs +1 -1
- package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs.map +2 -2
- package/dist-esm/lib/ui/components/menu-items.mjs +3 -1
- package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +9 -14
- package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/dist-esm/lib/utils/text/richText.mjs +3 -3
- package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
- package/package.json +3 -3
- package/src/lib/canvas/TldrawSelectionForeground.tsx +2 -2
- package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +10 -12
- package/src/lib/shapes/arrow/arrow-types.ts +2 -0
- package/src/lib/shapes/draw/DrawShapeUtil.tsx +3 -3
- package/src/lib/shapes/frame/FrameShapeUtil.tsx +1 -1
- package/src/lib/shapes/geo/GeoShapeUtil.tsx +9 -4
- package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +1 -1
- package/src/lib/shapes/note/NoteShapeUtil.tsx +7 -8
- package/src/lib/shapes/shared/HyperlinkButton.tsx +3 -2
- package/src/lib/shapes/shared/PlainTextLabel.tsx +10 -1
- package/src/lib/shapes/shared/RichTextLabel.tsx +11 -2
- package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
- package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
- package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
- package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
- package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
- package/src/lib/tools/SelectTool/childStates/EditingShape.ts +44 -11
- package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
- package/src/lib/ui/components/ZoomMenu/DefaultZoomMenu.tsx +1 -1
- package/src/lib/ui/components/menu-items.tsx +3 -1
- package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +10 -15
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/text/richText.ts +3 -3
- package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
- package/src/test/commands/cameraState.test.ts +299 -0
- package/tldraw.css +8 -4
- package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
- package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
- package/src/lib/shapes/shared/useForceSolid.ts +0 -6
|
@@ -43,6 +43,8 @@ const sizeCache = createComputedCache(
|
|
|
43
43
|
export interface TextShapeOptions {
|
|
44
44
|
/** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */
|
|
45
45
|
extraArrowHorizontalPadding: number
|
|
46
|
+
/** Whether to show the outline of the text shape (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */
|
|
47
|
+
showTextOutline: boolean
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/** @public */
|
|
@@ -53,6 +55,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|
|
53
55
|
|
|
54
56
|
override options: TextShapeOptions = {
|
|
55
57
|
extraArrowHorizontalPadding: 10,
|
|
58
|
+
showTextOutline: true,
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
getDefaultProps(): TLTextShape['props'] {
|
|
@@ -140,6 +143,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|
|
140
143
|
isSelected={isSelected}
|
|
141
144
|
textWidth={width}
|
|
142
145
|
textHeight={height}
|
|
146
|
+
showTextOutline={this.options.showTextOutline}
|
|
143
147
|
style={{
|
|
144
148
|
transform: `scale(${scale})`,
|
|
145
149
|
transformOrigin: 'top left',
|
|
@@ -175,6 +179,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|
|
175
179
|
labelColor={getColorValue(theme, shape.props.color, 'solid')}
|
|
176
180
|
bounds={exportBounds}
|
|
177
181
|
padding={0}
|
|
182
|
+
showTextOutline={this.options.showTextOutline}
|
|
178
183
|
/>
|
|
179
184
|
)
|
|
180
185
|
}
|
|
@@ -95,7 +95,8 @@ export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
|
|
|
95
95
|
|
|
96
96
|
const VideoShape = memo(function VideoShape({ shape }: { shape: TLVideoShape }) {
|
|
97
97
|
const editor = useEditor()
|
|
98
|
-
const showControls =
|
|
98
|
+
const showControls =
|
|
99
|
+
editor.getShapeGeometry(shape).bounds.w * editor.getEfficientZoomLevel() >= 110
|
|
99
100
|
const isEditing = useIsEditing(shape.id)
|
|
100
101
|
const prefersReducedMotion = usePrefersReducedMotion()
|
|
101
102
|
const { Spinner } = useEditorComponents()
|
|
@@ -18,13 +18,25 @@ interface EditingShapeInfo {
|
|
|
18
18
|
export class EditingShape extends StateNode {
|
|
19
19
|
static override id = 'editing_shape'
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
hitLabelOnShapeForPointerUp: TLShape | null = null
|
|
22
22
|
private info = {} as EditingShapeInfo
|
|
23
|
+
private didPointerDownOnEditingShape = false
|
|
24
|
+
|
|
25
|
+
private isTextInputFocused(): boolean {
|
|
26
|
+
const container = this.editor.getContainer()
|
|
27
|
+
return (
|
|
28
|
+
container.contains(document.activeElement) &&
|
|
29
|
+
(document.activeElement?.nodeName === 'INPUT' ||
|
|
30
|
+
document.activeElement?.nodeName === 'TEXTAREA' ||
|
|
31
|
+
(document.activeElement as HTMLElement)?.isContentEditable)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
23
34
|
|
|
24
35
|
override onEnter(info: EditingShapeInfo) {
|
|
25
36
|
const editingShape = this.editor.getEditingShape()
|
|
26
37
|
if (!editingShape) throw Error('Entered editing state without an editing shape')
|
|
27
|
-
this.
|
|
38
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
39
|
+
this.didPointerDownOnEditingShape = false
|
|
28
40
|
|
|
29
41
|
this.info = info
|
|
30
42
|
|
|
@@ -54,15 +66,34 @@ export class EditingShape extends StateNode {
|
|
|
54
66
|
override onPointerMove(info: TLPointerEventInfo) {
|
|
55
67
|
// In the case where on pointer down we hit a shape's label, we need to check if the user is dragging.
|
|
56
68
|
// and if they are, we need to transition to translating instead.
|
|
57
|
-
if (this.
|
|
69
|
+
if (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {
|
|
58
70
|
if (this.editor.getIsReadonly()) return
|
|
59
|
-
if (this.
|
|
60
|
-
|
|
71
|
+
if (this.hitLabelOnShapeForPointerUp.isLocked) return
|
|
72
|
+
|
|
73
|
+
this.editor.select(this.hitLabelOnShapeForPointerUp)
|
|
61
74
|
this.parent.transition('translating', info)
|
|
62
|
-
this.
|
|
75
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
63
76
|
return
|
|
64
77
|
}
|
|
65
78
|
|
|
79
|
+
// Check if dragging from editing shape with blurred input
|
|
80
|
+
if (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {
|
|
81
|
+
if (this.editor.getIsReadonly()) return
|
|
82
|
+
|
|
83
|
+
const editingShape = this.editor.getEditingShape()
|
|
84
|
+
if (!editingShape || editingShape.isLocked) return
|
|
85
|
+
|
|
86
|
+
if (!this.isTextInputFocused()) {
|
|
87
|
+
// Input blurred during drag - exit edit mode and start translating
|
|
88
|
+
this.editor.select(editingShape)
|
|
89
|
+
this.parent.transition('translating', info)
|
|
90
|
+
this.didPointerDownOnEditingShape = false
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
// Input still focused - user is selecting text, stay in edit mode
|
|
94
|
+
this.didPointerDownOnEditingShape = false
|
|
95
|
+
}
|
|
96
|
+
|
|
66
97
|
switch (info.target) {
|
|
67
98
|
case 'shape':
|
|
68
99
|
case 'canvas': {
|
|
@@ -73,7 +104,8 @@ export class EditingShape extends StateNode {
|
|
|
73
104
|
}
|
|
74
105
|
|
|
75
106
|
override onPointerDown(info: TLPointerEventInfo) {
|
|
76
|
-
this.
|
|
107
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
108
|
+
this.didPointerDownOnEditingShape = false
|
|
77
109
|
|
|
78
110
|
switch (info.target) {
|
|
79
111
|
// N.B. This bit of logic has a bit of history to it.
|
|
@@ -120,10 +152,11 @@ export class EditingShape extends StateNode {
|
|
|
120
152
|
) {
|
|
121
153
|
// it's a hit to the label!
|
|
122
154
|
if (selectingShape.id === editingShape.id) {
|
|
123
|
-
//
|
|
155
|
+
// Track click on editing shape for drag detection
|
|
156
|
+
this.didPointerDownOnEditingShape = true
|
|
124
157
|
return
|
|
125
158
|
} else {
|
|
126
|
-
this.
|
|
159
|
+
this.hitLabelOnShapeForPointerUp = selectingShape
|
|
127
160
|
|
|
128
161
|
this.editor.markHistoryStoppingPoint('editing on pointer up')
|
|
129
162
|
this.editor.select(selectingShape.id)
|
|
@@ -157,9 +190,9 @@ export class EditingShape extends StateNode {
|
|
|
157
190
|
|
|
158
191
|
override onPointerUp(info: TLPointerEventInfo) {
|
|
159
192
|
// If we're not dragging, and it's a hit to the label, begin editing the shape.
|
|
160
|
-
const hitShape = this.
|
|
193
|
+
const hitShape = this.hitLabelOnShapeForPointerUp
|
|
161
194
|
if (!hitShape) return
|
|
162
|
-
this.
|
|
195
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
163
196
|
|
|
164
197
|
// Stay in edit mode to maintain flow of editing.
|
|
165
198
|
const util = this.editor.getShapeUtil(hitShape)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useEditor, useValue } from '@tldraw/editor'
|
|
2
1
|
import { PORTRAIT_BREAKPOINT } from '../../constants'
|
|
3
2
|
import { useBreakpoint } from '../../context/breakpoints'
|
|
4
3
|
import {
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
useThreeStackableItems,
|
|
10
9
|
useUnlockedSelectedShapesCount,
|
|
11
10
|
} from '../../hooks/menu-hooks'
|
|
11
|
+
import { ZoomTo100MenuItem } from '../menu-items'
|
|
12
12
|
import { TldrawUiMenuActionItem } from '../primitives/menus/TldrawUiMenuActionItem'
|
|
13
13
|
|
|
14
14
|
/** @public @react */
|
|
@@ -99,14 +99,6 @@ export function ZoomOrRotateMenuItem() {
|
|
|
99
99
|
}
|
|
100
100
|
/** @public @react */
|
|
101
101
|
|
|
102
|
-
export function ZoomTo100MenuItem() {
|
|
103
|
-
const editor = useEditor()
|
|
104
|
-
const isZoomedTo100 = useValue('zoom is 1', () => editor.getZoomLevel() === 1, [editor])
|
|
105
|
-
|
|
106
|
-
return <TldrawUiMenuActionItem actionId="zoom-to-100" disabled={isZoomedTo100} />
|
|
107
|
-
}
|
|
108
|
-
/** @public @react */
|
|
109
|
-
|
|
110
102
|
export function RotateCCWMenuItem() {
|
|
111
103
|
const oneSelected = useUnlockedSelectedShapesCount(1)
|
|
112
104
|
const isInSelectState = useIsInSelectState()
|
|
@@ -48,7 +48,7 @@ export const DefaultZoomMenu = memo(function DefaultZoomMenu({ children }: TLUiZ
|
|
|
48
48
|
const ZoomTriggerButton = () => {
|
|
49
49
|
const editor = useEditor()
|
|
50
50
|
const breakpoint = useBreakpoint()
|
|
51
|
-
const zoom = useValue('zoom', () => editor.
|
|
51
|
+
const zoom = useValue('zoom', () => editor.getEfficientZoomLevel(), [editor])
|
|
52
52
|
const msg = useTranslation()
|
|
53
53
|
|
|
54
54
|
const handleDoubleClick = useCallback(() => {
|
|
@@ -182,7 +182,9 @@ export function UnlockAllMenuItem() {
|
|
|
182
182
|
/** @public @react */
|
|
183
183
|
export function ZoomTo100MenuItem() {
|
|
184
184
|
const editor = useEditor()
|
|
185
|
-
const isZoomedTo100 = useValue('zoomed to 100', () => editor.
|
|
185
|
+
const isZoomedTo100 = useValue('zoomed to 100', () => editor.getEfficientZoomLevel() === 1, [
|
|
186
|
+
editor,
|
|
187
|
+
])
|
|
186
188
|
|
|
187
189
|
return <TldrawUiMenuActionItem actionId="zoom-to-100" noClose disabled={isZoomedTo100} />
|
|
188
190
|
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
assert,
|
|
3
|
+
atom,
|
|
4
|
+
Editor,
|
|
5
|
+
tlenvReactive,
|
|
6
|
+
uniqueId,
|
|
7
|
+
useMaybeEditor,
|
|
8
|
+
useValue,
|
|
9
|
+
} from '@tldraw/editor'
|
|
2
10
|
import { Tooltip as _Tooltip } from 'radix-ui'
|
|
3
11
|
import React, {
|
|
4
12
|
createContext,
|
|
@@ -161,22 +169,9 @@ class TooltipManager {
|
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
if (!tooltip) return null
|
|
164
|
-
if (
|
|
172
|
+
if (tlenvReactive.get().isCoarsePointer && !tooltip.showOnMobile) return null
|
|
165
173
|
return tooltip
|
|
166
174
|
}
|
|
167
|
-
|
|
168
|
-
private supportsHoverAtom: Atom<boolean> | null = null
|
|
169
|
-
supportsHover() {
|
|
170
|
-
if (!this.supportsHoverAtom) {
|
|
171
|
-
const mediaQuery = window.matchMedia('(hover: hover)')
|
|
172
|
-
const supportsHover = atom('has hover', mediaQuery.matches)
|
|
173
|
-
this.supportsHoverAtom = supportsHover
|
|
174
|
-
mediaQuery.addEventListener('change', (e) => {
|
|
175
|
-
supportsHover.set(e.matches)
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
return this.supportsHoverAtom.get()
|
|
179
|
-
}
|
|
180
175
|
}
|
|
181
176
|
|
|
182
177
|
const tooltipManager = TooltipManager.getInstance()
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '4.3.0-canary.
|
|
4
|
+
export const version = '4.3.0-canary.37e6bf0fa8c6'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
|
-
minor: '2025-12-
|
|
8
|
-
patch: '2025-12-
|
|
7
|
+
minor: '2025-12-09T10:38:11.018Z',
|
|
8
|
+
patch: '2025-12-09T10:38:11.018Z',
|
|
9
9
|
}
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
generateText,
|
|
7
7
|
JSONContent,
|
|
8
8
|
} from '@tiptap/core'
|
|
9
|
-
import Code from '@tiptap/extension-code'
|
|
10
|
-
import Highlight from '@tiptap/extension-highlight'
|
|
9
|
+
import { Code } from '@tiptap/extension-code'
|
|
10
|
+
import { Highlight } from '@tiptap/extension-highlight'
|
|
11
11
|
import { Node } from '@tiptap/pm/model'
|
|
12
|
-
import StarterKit from '@tiptap/starter-kit'
|
|
12
|
+
import { StarterKit } from '@tiptap/starter-kit'
|
|
13
13
|
import {
|
|
14
14
|
Editor,
|
|
15
15
|
getOwnProperty,
|
|
@@ -83,7 +83,7 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
|
|
|
83
83
|
stroke-width="3.5"
|
|
84
84
|
/>
|
|
85
85
|
<foreignobject
|
|
86
|
-
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
|
|
86
|
+
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
|
|
87
87
|
height="100"
|
|
88
88
|
width="100"
|
|
89
89
|
x="0"
|
|
@@ -223,7 +223,7 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
|
|
|
223
223
|
stroke-width="3.5"
|
|
224
224
|
/>
|
|
225
225
|
<foreignobject
|
|
226
|
-
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
|
|
226
|
+
class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
|
|
227
227
|
height="100"
|
|
228
228
|
width="100"
|
|
229
229
|
x="0"
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Box } from '@tldraw/editor'
|
|
2
|
+
import { TestEditor } from '../TestEditor'
|
|
3
|
+
|
|
4
|
+
let editor: TestEditor
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
editor = new TestEditor()
|
|
8
|
+
editor.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('getCameraState', () => {
|
|
12
|
+
it('starts as idle', () => {
|
|
13
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('becomes moving when the camera changes via setCamera', () => {
|
|
17
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
18
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
19
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('becomes moving when the camera changes via pan', () => {
|
|
23
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
24
|
+
editor.pan({ x: 100, y: 100 })
|
|
25
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('becomes moving when the camera changes via zoomIn', () => {
|
|
29
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
30
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
31
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns to idle after the timeout elapses', () => {
|
|
35
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
36
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
37
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
38
|
+
|
|
39
|
+
// The default timeout is 64ms (options.cameraMovingTimeoutMs)
|
|
40
|
+
// Each tick is 16ms, so we need ~4 ticks to elapse
|
|
41
|
+
editor.forceTick(5)
|
|
42
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('stays moving while camera continues to change', () => {
|
|
46
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
47
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
48
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
49
|
+
|
|
50
|
+
// Move again before timeout elapses
|
|
51
|
+
editor.forceTick(2)
|
|
52
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
53
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
54
|
+
|
|
55
|
+
// Move again
|
|
56
|
+
editor.forceTick(2)
|
|
57
|
+
editor.setCamera({ x: 300, y: 300, z: 1 })
|
|
58
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
59
|
+
|
|
60
|
+
// Now let it settle
|
|
61
|
+
editor.forceTick(5)
|
|
62
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('stays idle when camera position does not actually change', () => {
|
|
66
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
67
|
+
|
|
68
|
+
// Setting the same camera position should not trigger moving state
|
|
69
|
+
const currentCamera = editor.getCamera()
|
|
70
|
+
editor.setCamera({ x: currentCamera.x, y: currentCamera.y, z: currentCamera.z })
|
|
71
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('does not add multiple tick listeners when camera changes rapidly', () => {
|
|
75
|
+
// This test verifies the fix: we should not have redundant listeners
|
|
76
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
77
|
+
|
|
78
|
+
// Change camera multiple times rapidly
|
|
79
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
80
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
81
|
+
editor.setCamera({ x: 300, y: 300, z: 1 })
|
|
82
|
+
|
|
83
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
84
|
+
|
|
85
|
+
// After timeout, should return to idle exactly once
|
|
86
|
+
// If there were multiple listeners, the state might behave unexpectedly
|
|
87
|
+
editor.forceTick(5)
|
|
88
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('resets timeout when camera changes while already moving', () => {
|
|
92
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
93
|
+
editor.setCamera({ x: 100, y: 100, z: 1 })
|
|
94
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
95
|
+
|
|
96
|
+
// Wait almost until timeout
|
|
97
|
+
editor.forceTick(3)
|
|
98
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
99
|
+
|
|
100
|
+
// Change camera again - should reset timeout
|
|
101
|
+
editor.setCamera({ x: 200, y: 200, z: 1 })
|
|
102
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
103
|
+
|
|
104
|
+
// Wait 3 more ticks - would have been idle if timeout wasn't reset
|
|
105
|
+
editor.forceTick(3)
|
|
106
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
107
|
+
|
|
108
|
+
// Now let it fully settle
|
|
109
|
+
editor.forceTick(3)
|
|
110
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('camera state with zoom', () => {
|
|
115
|
+
it('becomes moving on zoomOut', () => {
|
|
116
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
117
|
+
editor.zoomOut(undefined, { immediate: true })
|
|
118
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('becomes moving on centerOnPoint', () => {
|
|
122
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
123
|
+
editor.centerOnPoint({ x: 500, y: 500 })
|
|
124
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('becomes moving on zoomToFit', () => {
|
|
128
|
+
// Create a shape so zoomToFit has something to fit
|
|
129
|
+
editor.createShape({ type: 'geo', x: 100, y: 100, props: { w: 200, h: 200 } })
|
|
130
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
131
|
+
editor.zoomToFit({ immediate: true })
|
|
132
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe('getDebouncedZoomLevel', () => {
|
|
137
|
+
it('returns the current zoom level when camera is idle', () => {
|
|
138
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
139
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
140
|
+
|
|
141
|
+
// Change zoom and let it settle
|
|
142
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
143
|
+
editor.forceTick(5)
|
|
144
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
145
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('captures zoom when camera starts moving', () => {
|
|
149
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
150
|
+
|
|
151
|
+
// Start zooming - the debounced zoom is captured when movement starts
|
|
152
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
153
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
154
|
+
|
|
155
|
+
// The debounced zoom is captured at the moment movement starts (after first change)
|
|
156
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
157
|
+
expect(capturedZoom).toBe(editor.getZoomLevel())
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('keeps captured zoom during continued camera movement', () => {
|
|
161
|
+
// Start zooming
|
|
162
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
163
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
164
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
165
|
+
|
|
166
|
+
// Zoom again while still moving - debounced value should stay the same
|
|
167
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
168
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
169
|
+
expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
|
|
170
|
+
|
|
171
|
+
// But current zoom should have changed
|
|
172
|
+
expect(editor.getZoomLevel()).not.toBe(capturedZoom)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('updates debounced zoom when camera becomes idle again', () => {
|
|
176
|
+
// Start zooming
|
|
177
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
178
|
+
const capturedZoom = editor.getDebouncedZoomLevel()
|
|
179
|
+
|
|
180
|
+
// Zoom again while moving to change the current zoom
|
|
181
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
182
|
+
expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
|
|
183
|
+
|
|
184
|
+
// Let camera settle
|
|
185
|
+
editor.forceTick(5)
|
|
186
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
187
|
+
|
|
188
|
+
// Debounced zoom should now match current zoom
|
|
189
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('captures new zoom at the start of each new movement', () => {
|
|
193
|
+
// First zoom and settle
|
|
194
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
195
|
+
const firstCapturedZoom = editor.getDebouncedZoomLevel()
|
|
196
|
+
editor.forceTick(5)
|
|
197
|
+
expect(editor.getCameraState()).toBe('idle')
|
|
198
|
+
|
|
199
|
+
// Second zoom - should capture new zoom level
|
|
200
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
201
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
202
|
+
// The captured zoom should be different from the first capture
|
|
203
|
+
expect(editor.getDebouncedZoomLevel()).not.toBe(firstCapturedZoom)
|
|
204
|
+
// And it should match the current zoom (since we just started moving)
|
|
205
|
+
expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
describe('with debouncedZoom option disabled', () => {
|
|
209
|
+
let editorWithoutDebouncedZoom: TestEditor
|
|
210
|
+
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
editorWithoutDebouncedZoom = new TestEditor({
|
|
213
|
+
options: { debouncedZoom: false },
|
|
214
|
+
})
|
|
215
|
+
editorWithoutDebouncedZoom.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('always returns the current zoom level even when camera is moving', () => {
|
|
219
|
+
const initialZoom = editorWithoutDebouncedZoom.getZoomLevel()
|
|
220
|
+
|
|
221
|
+
editorWithoutDebouncedZoom.zoomIn(undefined, { immediate: true })
|
|
222
|
+
expect(editorWithoutDebouncedZoom.getCameraState()).toBe('moving')
|
|
223
|
+
|
|
224
|
+
// Should return the current zoom, not the captured one
|
|
225
|
+
expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).toBe(
|
|
226
|
+
editorWithoutDebouncedZoom.getZoomLevel()
|
|
227
|
+
)
|
|
228
|
+
expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).not.toBe(initialZoom)
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
describe('getEfficientZoomLevel', () => {
|
|
234
|
+
it('returns current zoom level when below shape threshold', () => {
|
|
235
|
+
// Default threshold is 500 shapes, we have 0
|
|
236
|
+
expect(editor.getZoomLevel()).toBe(editor.getEfficientZoomLevel())
|
|
237
|
+
|
|
238
|
+
// Add a few shapes - still below threshold
|
|
239
|
+
for (let i = 0; i < 10; i++) {
|
|
240
|
+
editor.createShape({ type: 'geo', x: i * 100, y: 0, props: { w: 50, h: 50 } })
|
|
241
|
+
}
|
|
242
|
+
expect(editor.getCurrentPageShapeIds().size).toBe(10)
|
|
243
|
+
|
|
244
|
+
// Start zooming
|
|
245
|
+
editor.zoomIn(undefined, { immediate: true })
|
|
246
|
+
expect(editor.getCameraState()).toBe('moving')
|
|
247
|
+
|
|
248
|
+
// Should still return current zoom because we're below threshold
|
|
249
|
+
expect(editor.getEfficientZoomLevel()).toBe(editor.getZoomLevel())
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
describe('with many shapes above threshold', () => {
|
|
253
|
+
let editorWithManyShapes: TestEditor
|
|
254
|
+
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
// Use a lower threshold for testing
|
|
257
|
+
editorWithManyShapes = new TestEditor({
|
|
258
|
+
options: { debouncedZoomThreshold: 5 },
|
|
259
|
+
})
|
|
260
|
+
editorWithManyShapes.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
|
261
|
+
|
|
262
|
+
// Add shapes above the threshold
|
|
263
|
+
for (let i = 0; i < 10; i++) {
|
|
264
|
+
editorWithManyShapes.createShape({
|
|
265
|
+
type: 'geo',
|
|
266
|
+
x: i * 100,
|
|
267
|
+
y: 0,
|
|
268
|
+
props: { w: 50, h: 50 },
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('returns debounced zoom level when above shape threshold and camera is moving', () => {
|
|
274
|
+
// First zoom to capture a debounced value
|
|
275
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
276
|
+
const capturedZoom = editorWithManyShapes.getEfficientZoomLevel()
|
|
277
|
+
expect(editorWithManyShapes.getCameraState()).toBe('moving')
|
|
278
|
+
|
|
279
|
+
// Zoom again while still moving
|
|
280
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
281
|
+
expect(editorWithManyShapes.getCameraState()).toBe('moving')
|
|
282
|
+
|
|
283
|
+
// Should return the captured zoom, not the current zoom
|
|
284
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(capturedZoom)
|
|
285
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).not.toBe(
|
|
286
|
+
editorWithManyShapes.getZoomLevel()
|
|
287
|
+
)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('returns current zoom level when above threshold but camera is idle', () => {
|
|
291
|
+
editorWithManyShapes.zoomIn(undefined, { immediate: true })
|
|
292
|
+
editorWithManyShapes.forceTick(5)
|
|
293
|
+
expect(editorWithManyShapes.getCameraState()).toBe('idle')
|
|
294
|
+
|
|
295
|
+
// Should return current zoom because camera is idle
|
|
296
|
+
expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(editorWithManyShapes.getZoomLevel())
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
})
|
package/tldraw.css
CHANGED
|
@@ -611,7 +611,6 @@ input,
|
|
|
611
611
|
pointer-events: all;
|
|
612
612
|
white-space: pre-wrap;
|
|
613
613
|
overflow-wrap: break-word;
|
|
614
|
-
text-shadow: var(--tl-text-outline);
|
|
615
614
|
}
|
|
616
615
|
|
|
617
616
|
.tl-text-wrapper[data-font='draw'] {
|
|
@@ -774,7 +773,6 @@ input,
|
|
|
774
773
|
justify-content: center;
|
|
775
774
|
align-items: center;
|
|
776
775
|
color: var(--tl-color-text);
|
|
777
|
-
text-shadow: var(--tl-text-outline);
|
|
778
776
|
line-height: inherit;
|
|
779
777
|
position: absolute;
|
|
780
778
|
inset: 0px;
|
|
@@ -974,6 +972,14 @@ input,
|
|
|
974
972
|
display: block;
|
|
975
973
|
}
|
|
976
974
|
|
|
975
|
+
.tl-text__outline {
|
|
976
|
+
text-shadow: var(--tl-text-outline);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.tl-text__no-outline {
|
|
980
|
+
text-shadow: none;
|
|
981
|
+
}
|
|
982
|
+
|
|
977
983
|
/* --------------------- Loading -------------------- */
|
|
978
984
|
|
|
979
985
|
.tl-loading {
|
|
@@ -1221,7 +1227,6 @@ input,
|
|
|
1221
1227
|
align-items: center;
|
|
1222
1228
|
text-align: center;
|
|
1223
1229
|
color: var(--tl-color-text);
|
|
1224
|
-
text-shadow: var(--tl-text-outline);
|
|
1225
1230
|
}
|
|
1226
1231
|
|
|
1227
1232
|
.tl-shape[data-shape-type='arrow'] .tl-text-label__inner {
|
|
@@ -1450,7 +1455,6 @@ input,
|
|
|
1450
1455
|
}
|
|
1451
1456
|
|
|
1452
1457
|
.tl-note__container > .tl-text-label {
|
|
1453
|
-
text-shadow: none;
|
|
1454
1458
|
color: currentColor;
|
|
1455
1459
|
}
|
|
1456
1460
|
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/lib/shapes/shared/useForceSolid.ts"],
|
|
4
|
-
"sourcesContent": ["import { useEditor, useValue } from '@tldraw/editor'\n\nexport function useForceSolid() {\n\tconst editor = useEditor()\n\treturn useValue('zoom', () => editor.getZoomLevel() < 0.35, [editor])\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAoC;AAE7B,SAAS,gBAAgB;AAC/B,QAAM,aAAS,yBAAU;AACzB,aAAO,wBAAS,QAAQ,MAAM,OAAO,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC;AACrE;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { useEditor, useValue } from "@tldraw/editor";
|
|
2
|
-
function useForceSolid() {
|
|
3
|
-
const editor = useEditor();
|
|
4
|
-
return useValue("zoom", () => editor.getZoomLevel() < 0.35, [editor]);
|
|
5
|
-
}
|
|
6
|
-
export {
|
|
7
|
-
useForceSolid
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=useForceSolid.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/lib/shapes/shared/useForceSolid.ts"],
|
|
4
|
-
"sourcesContent": ["import { useEditor, useValue } from '@tldraw/editor'\n\nexport function useForceSolid() {\n\tconst editor = useEditor()\n\treturn useValue('zoom', () => editor.getZoomLevel() < 0.35, [editor])\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,WAAW,gBAAgB;AAE7B,SAAS,gBAAgB;AAC/B,QAAM,SAAS,UAAU;AACzB,SAAO,SAAS,QAAQ,MAAM,OAAO,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC;AACrE;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|