tldraw 5.4.0 → 5.5.0-next.6d9e51e9f3f7
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/DOCS.md +60 -1
- package/RELEASE_NOTES.md +1 -170
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/shapes/shared/crop.js +13 -24
- package/dist-cjs/lib/shapes/shared/crop.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/embeds/embeds.js +1 -1
- package/dist-cjs/lib/utils/embeds/embeds.js.map +2 -2
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/shapes/shared/crop.mjs +13 -24
- package/dist-esm/lib/shapes/shared/crop.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/embeds/embeds.mjs +1 -1
- package/dist-esm/lib/utils/embeds/embeds.mjs.map +2 -2
- package/package.json +6 -6
- package/src/lib/shapes/shared/crop.ts +19 -33
- package/src/lib/ui/version.ts +3 -3
- package/src/lib/utils/embeds/embeds.test.ts +9 -0
- package/src/lib/utils/embeds/embeds.ts +4 -1
- package/src/test/commands/setCamera.test.ts +25 -5
- package/src/test/navigation.test.ts +33 -0
- package/src/test/selection-omnibus.test.ts +19 -16
- package/src/test/translating.test.ts +18 -4
package/DOCS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tldraw Documentation
|
|
2
2
|
|
|
3
|
-
Version: `5.
|
|
3
|
+
Version: `5.5.0-next.6d9e51e9f3f7`
|
|
4
4
|
|
|
5
5
|
This file is generated during package publishing from the tldraw docs content for this exact package version.
|
|
6
6
|
|
|
@@ -20899,3 +20899,62 @@ While the tldraw SDK is [source available](https://github.com/tldraw/tldraw), it
|
|
|
20899
20899
|
If you wish to include tldraw in an open source project, you may do so but the SDK itself must remain under its original license. This means that you and your downstream users will require their own trial, commercial, or hobby license in order to use the SDK in production. In this case, please also see our [trademark guidelines](https://tldraw.dev/legal/trademark-guidelines) that limit the use of the tldraw name and branding.
|
|
20900
20900
|
|
|
20901
20901
|
The tldraw SDK is a product of a company. See [tldraw.dev](https://tldraw.dev/company) for more information about the company and the team.
|
|
20902
|
+
|
|
20903
|
+
### Dependencies
|
|
20904
|
+
|
|
20905
|
+
{/* This page is generated by `yarn license-report --prod`. Do not edit it by hand. */}
|
|
20906
|
+
|
|
20907
|
+
The tldraw SDK itself is published under the [tldraw license](https://tldraw.dev/legal/tldraw-license). It also depends on the third-party packages listed below, each under its own license.
|
|
20908
|
+
|
|
20909
|
+
This covers the runtime dependencies that the `@tldraw/*` packages declare directly. It does not include their own transitive dependencies, our development and build tooling, or anything in the tldraw.com application. If you need a full transitive list for the exact versions you have installed, generate one from your own lockfile.
|
|
20910
|
+
|
|
20911
|
+
#### Dependencies
|
|
20912
|
+
|
|
20913
|
+
| Dependency | Version | License | Used by |
|
|
20914
|
+
| ------------------------------------------------------------------- | ------- | ------- | -------------------------------------------- |
|
|
20915
|
+
| [@tiptap/core](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `commenting`, `editor`, `mentions`, `tldraw` |
|
|
20916
|
+
| [@tiptap/extension-code](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `tldraw` |
|
|
20917
|
+
| [@tiptap/extension-highlight](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `tldraw` |
|
|
20918
|
+
| [@tiptap/extension-list](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `tldraw` |
|
|
20919
|
+
| [@tiptap/extension-mention](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `mentions` |
|
|
20920
|
+
| [@tiptap/pm](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `commenting`, `editor`, `mentions`, `tldraw` |
|
|
20921
|
+
| [@tiptap/react](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `commenting`, `editor`, `mentions`, `tldraw` |
|
|
20922
|
+
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `tldraw` |
|
|
20923
|
+
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap) | 3.13.0 | MIT | `mentions` |
|
|
20924
|
+
| [classnames](https://github.com/JedWatson/classnames) | 2.5.1 | MIT | `editor`, `tldraw` |
|
|
20925
|
+
| [eventemitter3](https://github.com/primus/eventemitter3) | 4.0.7 | MIT | `editor` |
|
|
20926
|
+
| [idb](https://github.com/jakearchibald/idb) | 7.1.1 | ISC | `editor`, `tldraw` |
|
|
20927
|
+
| [is-plain-object](https://github.com/jonschlinkert/is-plain-object) | 5.0.0 | MIT | `editor` |
|
|
20928
|
+
| [lodash.isequal](https://github.com/lodash/lodash) | 4.5.0 | MIT | `utils` |
|
|
20929
|
+
| [lodash.isequalwith](https://github.com/lodash/lodash) | 4.4.0 | MIT | `utils` |
|
|
20930
|
+
| [lodash.throttle](https://github.com/lodash/lodash) | 4.1.1 | MIT | `utils` |
|
|
20931
|
+
| [lodash.uniq](https://github.com/lodash/lodash) | 4.5.0 | MIT | `utils` |
|
|
20932
|
+
| [lz-string](https://github.com/pieroxy/lz-string) | 1.5.0 | MIT | `tldraw` |
|
|
20933
|
+
| [mermaid](https://github.com/mermaid-js/mermaid) | 11.16.1 | MIT | `mermaid` |
|
|
20934
|
+
| [nanoevents](https://github.com/ai/nanoevents) | 7.0.1 | MIT | `sync`, `sync-core` |
|
|
20935
|
+
| [radix-ui](https://github.com/radix-ui/primitives) | 1.4.2 | MIT | `tldraw` |
|
|
20936
|
+
| [rbush](https://github.com/mourner/rbush) | 3.0.1 | MIT | `editor` |
|
|
20937
|
+
| [ws](https://github.com/websockets/ws) | 8.21.0 | MIT | `sync`, `sync-core` |
|
|
20938
|
+
|
|
20939
|
+
#### Inline code
|
|
20940
|
+
|
|
20941
|
+
Some third-party code is included directly in our source rather than as a dependency. Each of these files carries its license in a comment at the top.
|
|
20942
|
+
|
|
20943
|
+
| File | License | Copyright |
|
|
20944
|
+
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
20945
|
+
| `packages/create-tldraw/src/group-select.ts` | [MIT](https://github.com/bombshell-dev/clack/blob/1adb270af5509ead3d22c4fa602b465f39c7a8c2/packages/prompts/LICENSE) | Nate Moore |
|
|
20946
|
+
| `packages/create-tldraw/src/wrap-ansi.ts` | [MIT](https://github.com/chalk/wrap-ansi/blob/163b878a6eb5d8c32b7bbea65036eeadc0e4def9/license) | Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) |
|
|
20947
|
+
| `packages/editor/src/lib/hooks/useScreenBounds.ts` | [MIT](https://github.com/excalidraw/excalidraw/blob/master/LICENSE) | |
|
|
20948
|
+
| `packages/editor/src/lib/hooks/useViewportHeight.ts` | [BSD](https://github.com/outline/rich-markdown-editor/blob/main/LICENSE) | 2020 General Outline, Inc (https://www.getoutline.com/) and individual contributors. |
|
|
20949
|
+
| `packages/editor/src/lib/utils/browserCanvasMaxSize.ts` | [MIT](https://github.com/jhildenbiddle/canvas-size/blob/master/LICENSE) | John Hildenbiddle |
|
|
20950
|
+
| `packages/store/src/lib/ImmutableMap.ts` | [MIT](https://github.com/immutable-js/immutable-js/blob/main/LICENSE) | 2014-present, Lee Byron and other contributors. |
|
|
20951
|
+
| `packages/tldraw/src/lib/shapes/shared/PathBuilder.tsx` | [Apache](https://github.com/adobe-webplatform/Snap.svg/blob/master/LICENSE) | |
|
|
20952
|
+
| `packages/tldraw/src/lib/ui/hooks/useKeyboardShortcuts.ts` | [MIT](https://github.com/jaywcjlove/hotkeys-js/blob/master/LICENSE) | 2015-present, Kenny Wong |
|
|
20953
|
+
| `packages/tldraw/src/lib/ui/kbd-utils.ts` | [MIT](https://github.com/jaywcjlove/hotkeys-js/blob/master/LICENSE) | 2015-present, Kenny Wong |
|
|
20954
|
+
| `packages/tldraw/src/lib/utils/svg/sanitizeSvg.ts` | MIT | 2015 Mario Heiderich |
|
|
20955
|
+
| `packages/utils/src/lib/bind.ts` | [MIT](https://github.com/NoHomey/bind-decorator/blob/master/License) | 2016 Ivo Stratev |
|
|
20956
|
+
| `packages/utils/src/lib/id.ts` | [MIT](https://github.com/ai/nanoid/blob/main/LICENSE) | 2017 Andrey Sitnik <andrey@sitnik.ru> |
|
|
20957
|
+
| `packages/utils/src/lib/media/apng.ts` | [MIT](https://github.com/vHeemstra/is-apng/blob/main/license) | Philip van Heemstra |
|
|
20958
|
+
| `packages/utils/src/lib/media/gif.ts` | MIT | 2016 Józef Sokołowski <j.k.sokolowski@gmail.com> |
|
|
20959
|
+
| `packages/utils/src/lib/media/png.ts` | [MIT](https://github.com/alexgorbatchev/crc/blob/master/LICENSE) | 2014 Alex Gorbatchev |
|
|
20960
|
+
| `packages/utils/src/lib/media/webp.ts` | [MIT](https://github.com/sindresorhus/is-webp/blob/main/license) | Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) |
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,178 +1,9 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
-
Version: `5.
|
|
3
|
+
Version: `5.5.0-next.6d9e51e9f3f7`
|
|
4
4
|
|
|
5
5
|
This file is generated during package publishing from the tldraw release notes content for this exact package version.
|
|
6
6
|
|
|
7
|
-
## v5.4.0
|
|
8
|
-
|
|
9
|
-
This release adds a `center` operation to `Editor.alignShapes`, a `tleditors` registry of mounted editors, `iterateGraphemes` to `@tldraw/utils`, and two new reactive methods on `AtomMap`. It also deprecates `useViewportHeight`, speeds up the signals and store layers, and lands a broad reliability pass across the SDK, with over a hundred fixes to undo history, selection and tool interactions, cropping, arrows, the signals and store layers, sync, and local persistence.
|
|
10
|
-
|
|
11
|
-
### API changes
|
|
12
|
-
|
|
13
|
-
- 🔜 **`useViewportHeight()`** is deprecated. Read `window.visualViewport` directly instead. The hook returns only the visible viewport's bottom edge, and keeping a panel clear of the software keyboard generally needs the whole visible rectangle. ([#9826](https://github.com/tldraw/tldraw/pull/9826))
|
|
14
|
-
- Add `'center'` as an `Editor.alignShapes` operation. It applies the existing `'center-horizontal'` and `'center-vertical'` operations together, so shapes center on both axes in one command. ([#9074](https://github.com/tldraw/tldraw/pull/9074))
|
|
15
|
-
- Add `tleditors`, a reactive global registry of currently mounted editors. Use it to reach live editors from outside the React tree, for example from sidebar chrome or a keyboard shortcut handler. ([#9904](https://github.com/tldraw/tldraw/pull/9904))
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { tleditors, useValue } from 'tldraw'
|
|
19
|
-
|
|
20
|
-
const mounted = useValue('mounted editors', () => tleditors.getMounted(), [])
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
- Add `getOrInsert(key, defaultValue)` and `getOrInsertComputed(key, callback)` to `AtomMap`, matching the `Map` methods added in newer JavaScript runtimes. Both are reactive, and the callback only runs when the key is absent. ([#9752](https://github.com/tldraw/tldraw/pull/9752))
|
|
24
|
-
- Add `isPage(record)` to `@tldraw/tlschema`, a type guard that narrows an unknown record to `TLPage`, matching the guards already exported for the other record types. ([#9774](https://github.com/tldraw/tldraw/pull/9774))
|
|
25
|
-
- Add `iterateGraphemes(str)` to `@tldraw/utils`. It iterates a string's grapheme clusters, using `Intl.Segmenter` when it's available and falling back to code-point iteration when it isn't. ([#9896](https://github.com/tldraw/tldraw/pull/9896))
|
|
26
|
-
- `EmojiPickerProps.onSelect` now receives the originating event as an optional second argument. Custom reaction palettes can forward it to opt into shift multi-select. ([#9999](https://github.com/tldraw/tldraw/pull/9999))
|
|
27
|
-
|
|
28
|
-
### Improvements
|
|
29
|
-
|
|
30
|
-
- Shift-click an emoji in a comment's reaction palette to add several reactions without the palette closing. A plain click still applies the reaction and closes the palette. ([#9999](https://github.com/tldraw/tldraw/pull/9999))
|
|
31
|
-
- Keyboard input now counts as collaborator presence activity, so peers no longer fade out and disappear while someone is typing. ([#9929](https://github.com/tldraw/tldraw/pull/9929))
|
|
32
|
-
- Reduce per-pointer-event allocations in `Group2d` and `Editor` hit-testing. ([#8600](https://github.com/tldraw/tldraw/pull/8600))
|
|
33
|
-
- Speed up signal reads in `@tldraw/state`. Capturing a parent used to scan the capturing signal's parent set twice, once to check for the parent and again to add it. ([#9690](https://github.com/tldraw/tldraw/pull/9690))
|
|
34
|
-
- Reduce per-signal memory use and per-flush allocations in `@tldraw/state`. ([#10155](https://github.com/tldraw/tldraw/pull/10155))
|
|
35
|
-
- Reduce the work and allocations in `@tldraw/store` history bookkeeping, so squashing record diffs and filtering query history no longer build intermediate copies on every tick. ([#9689](https://github.com/tldraw/tldraw/pull/9689), [#9869](https://github.com/tldraw/tldraw/pull/9869))
|
|
36
|
-
- Move the `exportBackground` default (`true`) into the `TLInstance` record type in `@tldraw/tlschema`, so every code path that creates the instance record gets the same value. ([#10534](https://github.com/tldraw/tldraw/pull/10534))
|
|
37
|
-
|
|
38
|
-
### Bug fixes
|
|
39
|
-
|
|
40
|
-
#### Crashes
|
|
41
|
-
|
|
42
|
-
- Fix the editor failing to load on older browsers that lack `Intl.Segmenter`, such as Firefox before 125. ([#9896](https://github.com/tldraw/tldraw/pull/9896))
|
|
43
|
-
- Fix the frame loop stopping permanently when a `tick` or `frame` listener threw, which silently disabled camera animations, edge scrolling, and other frame-driven behavior for the rest of the session. ([#9946](https://github.com/tldraw/tldraw/pull/9946))
|
|
44
|
-
- Fix a crash when computing a frame's geometry if its heading text couldn't be measured yet. ([#9496](https://github.com/tldraw/tldraw/pull/9496))
|
|
45
|
-
- Fix a crash in hit testing when a custom frame-like shape uses a plain, non-group geometry. ([#10328](https://github.com/tldraw/tldraw/pull/10328))
|
|
46
|
-
- Fix a crash when pressing Enter with a frame, video, or embed selected while the geo, arrow, or text tool is active. ([#10195](https://github.com/tldraw/tldraw/pull/10195))
|
|
47
|
-
- Fix default UI components such as `DefaultToolbar` crashing when mounted outside the `Tldraw` component. ([#10007](https://github.com/tldraw/tldraw/pull/10007))
|
|
48
|
-
|
|
49
|
-
#### Undo and history
|
|
50
|
-
|
|
51
|
-
- Fix undo after a cut also reverting the change made before it. ([#9920](https://github.com/tldraw/tldraw/pull/9920))
|
|
52
|
-
- Fix several actions merging into the previous undo step instead of creating their own: **Unlock all**, changing an arrowhead, keyboard changes to the opacity and image zoom sliders, alt+double-clicking a rectangle into a check-box, saving alt text, and dropping a file or url onto the canvas. ([#10468](https://github.com/tldraw/tldraw/pull/10468), [#10469](https://github.com/tldraw/tldraw/pull/10469), [#10470](https://github.com/tldraw/tldraw/pull/10470), [#10473](https://github.com/tldraw/tldraw/pull/10473), [#10474](https://github.com/tldraw/tldraw/pull/10474), [#10478](https://github.com/tldraw/tldraw/pull/10478))
|
|
53
|
-
- Fix exiting an empty text shape leaving a phantom undo entry: document changes made by `onEditEnd` are now recorded in history. ([#10369](https://github.com/tldraw/tldraw/pull/10369))
|
|
54
|
-
- Fix undo after `bailToMark` removing records that were not part of the undone change. ([#10207](https://github.com/tldraw/tldraw/pull/10207))
|
|
55
|
-
- Fix undo/redo not removing an optional record property that the change had deleted. ([#10215](https://github.com/tldraw/tldraw/pull/10215))
|
|
56
|
-
- Fix collaboration-mode readonly syncs ending up in the user's undo history. ([#10372](https://github.com/tldraw/tldraw/pull/10372))
|
|
57
|
-
- Fix changes made by `operationComplete` handlers after a remote merge being attributed to the remote source. ([#10229](https://github.com/tldraw/tldraw/pull/10229))
|
|
58
|
-
|
|
59
|
-
#### Selection and tools
|
|
60
|
-
|
|
61
|
-
- Fix Escape being ignored while pressing on the canvas with the select tool. ([#10175](https://github.com/tldraw/tldraw/pull/10175))
|
|
62
|
-
- Fix Escape during a scribble selection dropping the prior selection. ([#10489](https://github.com/tldraw/tldraw/pull/10489))
|
|
63
|
-
- Fix Space+arrow and Alt+arrow also nudging the selected shapes. ([#10453](https://github.com/tldraw/tldraw/pull/10453))
|
|
64
|
-
- Fix Alt not suppressing Shift/Ctrl additive selection when releasing the pointer over a selected shape. ([#10510](https://github.com/tldraw/tldraw/pull/10510))
|
|
65
|
-
- Fix bookmarks and other non-resizable shapes being left behind when resizing a multi-selection. ([#10491](https://github.com/tldraw/tldraw/pull/10491))
|
|
66
|
-
- Fix alt-cloning mid-drag reparenting the original shape instead of the clone when dropping into a frame. ([#10186](https://github.com/tldraw/tldraw/pull/10186))
|
|
67
|
-
- Fix the cursor staying as a resize or rotate arrow when Ctrl/Cmd+dragging a selection handle to brush-select. ([#10487](https://github.com/tldraw/tldraw/pull/10487))
|
|
68
|
-
- Fix handle snapping on rotated shapes landing off the snap target. ([#10168](https://github.com/tldraw/tldraw/pull/10168))
|
|
69
|
-
- Fix an interrupted press with the geo, frame, or note tool (undo, opening a menu, or changing page before releasing) creating a default-size shape. ([#10506](https://github.com/tldraw/tldraw/pull/10506))
|
|
70
|
-
- Fix an invisible empty text shape being left behind when switching tools while dragging out a text box. ([#10457](https://github.com/tldraw/tldraw/pull/10457))
|
|
71
|
-
- Fix the text tool undoing earlier work when cancelling a pointer-down after a drag-created text shape. ([#10194](https://github.com/tldraw/tldraw/pull/10194))
|
|
72
|
-
- Fix shift-click with the draw, highlight, and line tools editing a shape on the previous page after a page change. ([#10455](https://github.com/tldraw/tldraw/pull/10455))
|
|
73
|
-
- Fix the draw tool's shift segment threshold depending on the zoom level. ([#10240](https://github.com/tldraw/tldraw/pull/10240))
|
|
74
|
-
- Fix the first point of a pen stroke losing its pressure value. ([#10243](https://github.com/tldraw/tldraw/pull/10243))
|
|
75
|
-
- Fix small, slowly drawn loops sometimes failing to close, because dense input sampling made the stroke measure shorter than it was. ([#9930](https://github.com/tldraw/tldraw/pull/9930))
|
|
76
|
-
- Fix the eraser dropping a frame it was pressed on once the drag begins. ([#10507](https://github.com/tldraw/tldraw/pull/10507))
|
|
77
|
-
- Fix a camera jump when a second finger touches down during a one-finger zoom with the hand tool. ([#10178](https://github.com/tldraw/tldraw/pull/10178))
|
|
78
|
-
- Fix Escape ending a spacebar pan while Space is still held. ([#10504](https://github.com/tldraw/tldraw/pull/10504))
|
|
79
|
-
- Fix tools whose `onEnter` transitions to a child state ending up with two active children. ([#10354](https://github.com/tldraw/tldraw/pull/10354))
|
|
80
|
-
- Fix double clicks being cancelled by small pointer movements when the editor container is offset from the page origin. ([#10349](https://github.com/tldraw/tldraw/pull/10349))
|
|
81
|
-
- Fix modifier keys (Shift, Alt, Ctrl, Cmd) briefly being treated as held at the start of a new click or drag right after being released, which could momentarily affect snapping and other modifier-dependent behavior. ([#9523](https://github.com/tldraw/tldraw/pull/9523))
|
|
82
|
-
- Fix held keys, including Space panning, staying active after the window loses focus. ([#10500](https://github.com/tldraw/tldraw/pull/10500))
|
|
83
|
-
|
|
84
|
-
#### Cropping
|
|
85
|
-
|
|
86
|
-
- Fix stale crop state after leaving crop mode by switching tools. ([#10167](https://github.com/tldraw/tldraw/pull/10167))
|
|
87
|
-
- Fix Escape during a crop drag reverting the whole crop session instead of only the drag in progress. ([#10462](https://github.com/tldraw/tldraw/pull/10462))
|
|
88
|
-
- Fix Escape in image crop mode clearing the selection. ([#10463](https://github.com/tldraw/tldraw/pull/10463))
|
|
89
|
-
- Fix crop mode staying active when the cropping shape is deleted. ([#10370](https://github.com/tldraw/tldraw/pull/10370))
|
|
90
|
-
- Fix pressing Enter on a selected image entering crop mode in a readonly editor. ([#10492](https://github.com/tldraw/tldraw/pull/10492))
|
|
91
|
-
|
|
92
|
-
#### Arrows and shapes
|
|
93
|
-
|
|
94
|
-
- Fix arrows bound to custom shapes with non-standard `size` values rendering as `NaN`. ([#10214](https://github.com/tldraw/tldraw/pull/10214))
|
|
95
|
-
- Fix elbow arrows with a free terminal sometimes routing through the bound shape. ([#10219](https://github.com/tldraw/tldraw/pull/10219))
|
|
96
|
-
- Fix arrow geometry ignoring a custom stroke width from `getCustomDisplayValues`. ([#10230](https://github.com/tldraw/tldraw/pull/10230))
|
|
97
|
-
- Fix the arrow binding target highlight staying visible after clicking an arrow handle without dragging. ([#10191](https://github.com/tldraw/tldraw/pull/10191))
|
|
98
|
-
- Fix a stub arrow being left on the canvas when switching tools during an arrow press. ([#10461](https://github.com/tldraw/tldraw/pull/10461))
|
|
99
|
-
- Fix arrow label drag reverting when a menu opens or undo is pressed mid-drag. ([#10493](https://github.com/tldraw/tldraw/pull/10493))
|
|
100
|
-
- Fix Cmd+Enter / Tab on a note creating a duplicate on top of an existing neighbour. ([#10199](https://github.com/tldraw/tldraw/pull/10199))
|
|
101
|
-
- Fix adjacent-note snapping under a scaled, grown note. ([#10204](https://github.com/tldraw/tldraw/pull/10204))
|
|
102
|
-
- Fix the note tool not snapping a new note beside the note it just created when tool lock is on. ([#10484](https://github.com/tldraw/tldraw/pull/10484))
|
|
103
|
-
- Fix attributed notes losing their font in exports. ([#10249](https://github.com/tldraw/tldraw/pull/10249))
|
|
104
|
-
- Fix shift-extending a line inside a rotated frame placing the point incorrectly. ([#10238](https://github.com/tldraw/tldraw/pull/10238))
|
|
105
|
-
- Fix the second diagonal of an x-box shape being treated as an outer edge. ([#10245](https://github.com/tldraw/tldraw/pull/10245))
|
|
106
|
-
- Fix a replaced image being zoomed when the original had an explicit default crop. ([#10234](https://github.com/tldraw/tldraw/pull/10234))
|
|
107
|
-
- Fix video shapes showing a floating spinner with no bounding box while their asset uploads or loads. They now show the same loading plate as image shapes. ([#9994](https://github.com/tldraw/tldraw/pull/9994))
|
|
108
|
-
- Fix **Flatten to image** producing different groupings depending on selection order, and not merging clusters that were only connected through a bridging shape. ([#8661](https://github.com/tldraw/tldraw/pull/8661))
|
|
109
|
-
|
|
110
|
-
#### Editor
|
|
111
|
-
|
|
112
|
-
- Fix dragging a frame out of its parent when grabbed over a nested child, and restore a child's original z-order when dragged back into a frame. ([#10329](https://github.com/tldraw/tldraw/pull/10329))
|
|
113
|
-
- Fix camera constraints for bounds away from the page origin, and clamp padding against the correct viewport axis. ([#10337](https://github.com/tldraw/tldraw/pull/10337))
|
|
114
|
-
- Fix `setCameraOptions({ zoomSteps: undefined })` breaking every later camera move. ([#10387](https://github.com/tldraw/tldraw/pull/10387))
|
|
115
|
-
- Fix `slideCamera` zoom momentum driving the zoom to zero on a long frame. ([#10388](https://github.com/tldraw/tldraw/pull/10388))
|
|
116
|
-
- Fix malformed viewport deep links moving the camera to the origin instead of falling back to fitting the page content. ([#10508](https://github.com/tldraw/tldraw/pull/10508))
|
|
117
|
-
- Fix following a user whose own leader has left or has no presence yet. ([#10389](https://github.com/tldraw/tldraw/pull/10389))
|
|
118
|
-
- Fix `getSelectionRotatedScreenBounds` going stale when the editor container moves. ([#10390](https://github.com/tldraw/tldraw/pull/10390))
|
|
119
|
-
- Fix `duplicateShapes` throwing on unknown ids and offsetting nested duplicates twice. ([#10355](https://github.com/tldraw/tldraw/pull/10355))
|
|
120
|
-
- Fix `packShapes` packing shapes of different heights into a single column. ([#10381](https://github.com/tldraw/tldraw/pull/10381))
|
|
121
|
-
- Fix `stackShapes` reordering shapes when a gap is given. ([#10383](https://github.com/tldraw/tldraw/pull/10383))
|
|
122
|
-
- Fix nested 270° shapes being dropped from axis-aligned shape clustering. ([#10384](https://github.com/tldraw/tldraw/pull/10384))
|
|
123
|
-
- Fix `putContentOntoCurrentPage` mutating the content passed to it. ([#10367](https://github.com/tldraw/tldraw/pull/10367))
|
|
124
|
-
- Fix `setErasingShapes` mutating its input array, so it accepts store-owned (frozen) arrays. ([#10371](https://github.com/tldraw/tldraw/pull/10371))
|
|
125
|
-
- Fix the teardown returned by the `createTLStore` `onMount` option never running when the editor unmounts. ([#10375](https://github.com/tldraw/tldraw/pull/10375))
|
|
126
|
-
- Fix drop events inside the canvas never reaching React drop handlers, and keep Safari gesture suppression working with multiple editors on a page. ([#10360](https://github.com/tldraw/tldraw/pull/10360))
|
|
127
|
-
- Fix object URLs for locally persisted assets leaking across remounts. ([#10361](https://github.com/tldraw/tldraw/pull/10361))
|
|
128
|
-
- Fix the click-capture overlay staying over the canvas after right-click panning with a menu open. ([#10359](https://github.com/tldraw/tldraw/pull/10359))
|
|
129
|
-
- Fix hiding and showing menus by context id, and tolerate corrupt user-preference data in localStorage. ([#10344](https://github.com/tldraw/tldraw/pull/10344))
|
|
130
|
-
- Fix `Vec.Slope`, `Vec.cross`, and `areAnglesCompatible` for negative angles, `rotateSelectionHandle` for negative rotations, and dotted dash arrays for very short paths. ([#10352](https://github.com/tldraw/tldraw/pull/10352))
|
|
131
|
-
- Fix laser trails staying visible after changing page. ([#10509](https://github.com/tldraw/tldraw/pull/10509))
|
|
132
|
-
- Fix locking a mixed selection of locked and unlocked shapes leaving the shapes selected. ([#10480](https://github.com/tldraw/tldraw/pull/10480))
|
|
133
|
-
- Fix wheel event normalization passing through non-finite delta values. ([#9586](https://github.com/tldraw/tldraw/pull/9586))
|
|
134
|
-
|
|
135
|
-
#### UI
|
|
136
|
-
|
|
137
|
-
- Fix the **Frame selection** shortcut (Ctrl+Alt+G) not working on Windows and Linux. ([#10481](https://github.com/tldraw/tldraw/pull/10481))
|
|
138
|
-
- Fix pasted text shapes and bookmarks not being selected after paste. ([#10485](https://github.com/tldraw/tldraw/pull/10485))
|
|
139
|
-
- Fix context-menu paste landing under the pointer instead of at the right-click point. ([#10488](https://github.com/tldraw/tldraw/pull/10488))
|
|
140
|
-
- Fix file drops and pastes silently doing nothing when one of the files is an image that can't be decoded. The bad file is now skipped with an error toast and the other files are placed. ([#10495](https://github.com/tldraw/tldraw/pull/10495))
|
|
141
|
-
- Fix the **Edit link** dialog staying open as an empty panel when its shape is deleted or deselected. ([#10496](https://github.com/tldraw/tldraw/pull/10496))
|
|
142
|
-
- Fix wheel and trackpad scrolling over the minimap moving the camera at double rate. ([#10499](https://github.com/tldraw/tldraw/pull/10499))
|
|
143
|
-
- Fix the actions menu button appearing, and opening empty, in readonly mode with the hand and zoom tools. ([#10503](https://github.com/tldraw/tldraw/pull/10503))
|
|
144
|
-
- Fix closing a stacked dialog by touch also closing the dialog beneath it. ([#10002](https://github.com/tldraw/tldraw/pull/10002))
|
|
145
|
-
- Fix the loading screen not honoring the system theme on a dark OS, and the reduce motion preference not following the OS setting until a reload. ([#10528](https://github.com/tldraw/tldraw/pull/10528))
|
|
146
|
-
- Fix the storage read-failure alert promising a reload that never happened. ([#10530](https://github.com/tldraw/tldraw/pull/10530))
|
|
147
|
-
- Fix the casing of the error fallback's refresh button. ([#10362](https://github.com/tldraw/tldraw/pull/10362))
|
|
148
|
-
- Fix pasted links showing a broken preview image when the linked page has no Open Graph image. ([#9909](https://github.com/tldraw/tldraw/pull/9909))
|
|
149
|
-
- Fix the counterclockwise rotation tooltip not showing its comma key. ([#9772](https://github.com/tldraw/tldraw/pull/9772))
|
|
150
|
-
- Fix the spacing around the cursor chat item in the context menu. ([#9921](https://github.com/tldraw/tldraw/pull/9921))
|
|
151
|
-
- Fix the people menu's collaborator rows not lining up with your own row above them: the follow icon sat too close to the right edge, and collaborator names sat slightly left of your own. ([#9900](https://github.com/tldraw/tldraw/pull/9900))
|
|
152
|
-
- Fix the comment thread popover briefly rendering with a doubled shadow the first time a thread is opened. ([#9886](https://github.com/tldraw/tldraw/pull/9886))
|
|
153
|
-
#### Persistence, sync, and licensing
|
|
154
|
-
|
|
155
|
-
- Fix edits made in the last 350 ms before closing or reloading a tab being lost from local persistence. ([#10526](https://github.com/tldraw/tldraw/pull/10526))
|
|
156
|
-
- Fix background tabs repeatedly dropping and re-establishing their sync connection when the browser throttles timers. ([#9964](https://github.com/tldraw/tldraw/pull/9964))
|
|
157
|
-
- Fix `useSyncDemo` creating assets for uploads the server rejected. ([#10109](https://github.com/tldraw/tldraw/pull/10109))
|
|
158
|
-
- Fix `loadSnapshot` failing on better-sqlite3, and `NodeSqliteWrapper` getting stuck in a transaction when `COMMIT` fails. ([#10111](https://github.com/tldraw/tldraw/pull/10111), [#10113](https://github.com/tldraw/tldraw/pull/10113))
|
|
159
|
-
|
|
160
|
-
#### Signals and store
|
|
161
|
-
|
|
162
|
-
- Fix computeds returning stale values when read inside a transaction during an effect, from a deferred effect, or after a reactor restarts. ([#10145](https://github.com/tldraw/tldraw/pull/10145))
|
|
163
|
-
- Fix effects with no signal dependencies re-running on every `start()`. ([#10148](https://github.com/tldraw/tldraw/pull/10148))
|
|
164
|
-
- Fix nested transactions inside effects not rolling back with their parent. ([#10149](https://github.com/tldraw/tldraw/pull/10149))
|
|
165
|
-
- Fix effects observing half-restored state when a `deferAsyncEffects` call throws. ([#10150](https://github.com/tldraw/tldraw/pull/10150))
|
|
166
|
-
- Fix `@computed` on a subclass override that calls `super`, and `@computed()` with no arguments. ([#10151](https://github.com/tldraw/tldraw/pull/10151))
|
|
167
|
-
- Fix computeds re-running their compute function on every read after their first derive throws. ([#10152](https://github.com/tldraw/tldraw/pull/10152))
|
|
168
|
-
- Fix computeds read inside an atom's `computeDiff` never seeing that change, and an explicit `null` diff passed to `atom.set()` or returned from `withDiff()` clearing history. ([#10153](https://github.com/tldraw/tldraw/pull/10153))
|
|
169
|
-
- Fix `localStorageAtom` throwing outside the browser. ([#10154](https://github.com/tldraw/tldraw/pull/10154))
|
|
170
|
-
- Fix a stack overflow in `ImmutableMap` and `AtomMap` for keys named after `Object.prototype` members, and make `NaN` and `-0` key equality match native `Map`. ([#10202](https://github.com/tldraw/tldraw/pull/10202))
|
|
171
|
-
- Fix `AtomMap` throwing when an entry is deleted mid-iteration. ([#10209](https://github.com/tldraw/tldraw/pull/10209))
|
|
172
|
-
- Fix `StoreSchema.create` reporting a redundant migration dependency as circular. ([#10213](https://github.com/tldraw/tldraw/pull/10213))
|
|
173
|
-
- Fix the `store.query.ids()` query creator being called with a computed's `(previousValue, epoch)` arguments. ([#10218](https://github.com/tldraw/tldraw/pull/10218))
|
|
174
|
-
- Fix `CollectionDiff`s from `store.query.ids()` and `index()` containing an empty `added` or `removed` set after an add/remove round trip. ([#10221](https://github.com/tldraw/tldraw/pull/10221))
|
|
175
|
-
|
|
176
7
|
## v5.3.0
|
|
177
8
|
|
|
178
9
|
[View on GitHub](https://github.com/tldraw/tldraw/releases/tag/v5.3.0)
|
package/dist-cjs/index.js
CHANGED
|
@@ -653,7 +653,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
653
653
|
var import_file = require("./lib/utils/tldr/file");
|
|
654
654
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
655
655
|
"tldraw",
|
|
656
|
-
"5.
|
|
656
|
+
"5.5.0-next.6d9e51e9f3f7",
|
|
657
657
|
"cjs"
|
|
658
658
|
);
|
|
659
659
|
//# sourceMappingURL=index.js.map
|
|
@@ -325,11 +325,15 @@ function getCropBox(shape, info, opts = {}) {
|
|
|
325
325
|
}
|
|
326
326
|
};
|
|
327
327
|
}
|
|
328
|
+
function getPositionKeepingCenter(imageShape, newW, newH) {
|
|
329
|
+
const delta = new import_editor.Vec((imageShape.props.w - newW) / 2, (imageShape.props.h - newH) / 2).rot(
|
|
330
|
+
imageShape.rotation
|
|
331
|
+
);
|
|
332
|
+
return { x: imageShape.x + delta.x, y: imageShape.y + delta.y };
|
|
333
|
+
}
|
|
328
334
|
function calculateCropChange(imageShape, newCropWidth, newCropHeight, centerOnCurrentCrop = true, isCircle = false) {
|
|
329
335
|
const { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop());
|
|
330
336
|
const currentCrop = imageShape.props.crop || getDefaultCrop();
|
|
331
|
-
const imageCenterX = imageShape.x + imageShape.props.w / 2;
|
|
332
|
-
const imageCenterY = imageShape.y + imageShape.props.h / 2;
|
|
333
337
|
let cropCenterX, cropCenterY;
|
|
334
338
|
if (centerOnCurrentCrop) {
|
|
335
339
|
const { x, y } = getCropCenter(currentCrop);
|
|
@@ -352,8 +356,7 @@ function calculateCropChange(imageShape, newCropWidth, newCropHeight, centerOnCu
|
|
|
352
356
|
crop: newCrop,
|
|
353
357
|
w: croppedW,
|
|
354
358
|
h: croppedH,
|
|
355
|
-
|
|
356
|
-
y: imageCenterY - croppedH / 2
|
|
359
|
+
...getPositionKeepingCenter(imageShape, croppedW, croppedH)
|
|
357
360
|
};
|
|
358
361
|
}
|
|
359
362
|
const MAX_ZOOM = 3;
|
|
@@ -375,10 +378,9 @@ function getCroppedImageDataWhenZooming(zoom, imageShape, maxZoom) {
|
|
|
375
378
|
const scaleFactor = Math.min(MAX_ZOOM, oldWidth / newWidth);
|
|
376
379
|
result.w *= scaleFactor;
|
|
377
380
|
result.h *= scaleFactor;
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
result.
|
|
381
|
-
result.y = imageCenterY - result.h / 2;
|
|
381
|
+
const { x, y } = getPositionKeepingCenter(imageShape, result.w, result.h);
|
|
382
|
+
result.x = x;
|
|
383
|
+
result.y = y;
|
|
382
384
|
return result;
|
|
383
385
|
}
|
|
384
386
|
function getCroppedImageDataForReplacedImage(imageShape, newImageWidth, newImageHeight) {
|
|
@@ -432,29 +434,21 @@ function getCroppedImageDataForReplacedImage(imageShape, newImageWidth, newImage
|
|
|
432
434
|
currentCrop.isCircle
|
|
433
435
|
);
|
|
434
436
|
}
|
|
435
|
-
const pageCenterX = imageShape.x + origDisplayW / 2;
|
|
436
|
-
const pageCenterY = imageShape.y + origDisplayH / 2;
|
|
437
|
-
const newX = pageCenterX - newDisplayW / 2;
|
|
438
|
-
const newY = pageCenterY - newDisplayH / 2;
|
|
439
437
|
return {
|
|
440
438
|
crop,
|
|
441
439
|
w: newDisplayW,
|
|
442
440
|
h: newDisplayH,
|
|
443
|
-
|
|
444
|
-
y: newY
|
|
441
|
+
...getPositionKeepingCenter(imageShape, newDisplayW, newDisplayH)
|
|
445
442
|
};
|
|
446
443
|
}
|
|
447
444
|
function getCroppedImageDataForAspectRatio(aspectRatioOption, imageShape) {
|
|
448
445
|
if (aspectRatioOption === "original") {
|
|
449
446
|
const { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop());
|
|
450
|
-
const imageCenterX = imageShape.x + imageShape.props.w / 2;
|
|
451
|
-
const imageCenterY = imageShape.y + imageShape.props.h / 2;
|
|
452
447
|
return {
|
|
453
448
|
crop: getDefaultCrop(),
|
|
454
449
|
w,
|
|
455
450
|
h,
|
|
456
|
-
|
|
457
|
-
y: imageCenterY - h / 2
|
|
451
|
+
...getPositionKeepingCenter(imageShape, w, h)
|
|
458
452
|
};
|
|
459
453
|
}
|
|
460
454
|
const targetRatio = ASPECT_RATIO_TO_VALUE[aspectRatioOption];
|
|
@@ -525,16 +519,11 @@ function getCroppedImageDataForAspectRatio(aspectRatioOption, imageShape) {
|
|
|
525
519
|
}
|
|
526
520
|
const newW = baseW * currentScale;
|
|
527
521
|
const newH = baseH * currentScale;
|
|
528
|
-
const currentCenterXPage = imageShape.x + imageShape.props.w / 2;
|
|
529
|
-
const currentCenterYPage = imageShape.y + imageShape.props.h / 2;
|
|
530
|
-
const newX = currentCenterXPage - newW / 2;
|
|
531
|
-
const newY = currentCenterYPage - newH / 2;
|
|
532
522
|
return {
|
|
533
523
|
crop: newCrop,
|
|
534
524
|
w: newW,
|
|
535
525
|
h: newH,
|
|
536
|
-
|
|
537
|
-
y: newY
|
|
526
|
+
...getPositionKeepingCenter(imageShape, newW, newH)
|
|
538
527
|
};
|
|
539
528
|
}
|
|
540
529
|
//# sourceMappingURL=crop.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/shapes/shared/crop.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBox,\n\tShapeWithCrop,\n\tTLCropInfo,\n\tTLImageShape,\n\tTLShapeCrop,\n\tTLShapeId,\n\tVec,\n\tclamp,\n\tisEqual,\n} from '@tldraw/editor'\n\n/** @internal */\nexport const MIN_CROP_SIZE = 8\n\n/** @public */\nexport interface CropBoxOptions {\n\tminWidth?: number\n\tminHeight?: number\n}\n\n/** @public */\nexport function getDefaultCrop(): TLShapeCrop {\n\treturn {\n\t\ttopLeft: { x: 0, y: 0 },\n\t\tbottomRight: { x: 1, y: 1 },\n\t}\n}\n\n/** @public */\nexport type ASPECT_RATIO_OPTION =\n\t| 'original'\n\t| 'square'\n\t| 'circle'\n\t| 'landscape'\n\t| 'portrait'\n\t| 'wide'\n\n/** @public */\nexport const ASPECT_RATIO_OPTIONS: ASPECT_RATIO_OPTION[] = [\n\t'original',\n\t'square',\n\t'circle',\n\t'landscape',\n\t'portrait',\n\t'wide',\n]\n\n/** @public */\nexport const ASPECT_RATIO_TO_VALUE: Record<ASPECT_RATIO_OPTION, number> = {\n\toriginal: 0,\n\tsquare: 1,\n\tcircle: 1,\n\tlandscape: 4 / 3,\n\tportrait: 3 / 4,\n\twide: 16 / 9,\n}\n\n/**\n * Original (uncropped) width and height of shape.\n *\n * @public\n */\nexport function getUncroppedSize(\n\tshapeSize: { w: number; h: number },\n\tcrop: TLShapeCrop | null\n): { w: number; h: number } {\n\tif (!crop) return { w: shapeSize.w, h: shapeSize.h }\n\tconst w = shapeSize.w / (crop.bottomRight.x - crop.topLeft.x)\n\tconst h = shapeSize.h / (crop.bottomRight.y - crop.topLeft.y)\n\treturn { w, h }\n}\n\n// Utility function to get crop dimensions\nfunction getCropDimensions(crop: TLShapeCrop) {\n\treturn {\n\t\twidth: crop.bottomRight.x - crop.topLeft.x,\n\t\theight: crop.bottomRight.y - crop.topLeft.y,\n\t}\n}\n\n// Utility function to get crop center\nfunction getCropCenter(crop: TLShapeCrop) {\n\tconst { width, height } = getCropDimensions(crop)\n\treturn {\n\t\tx: crop.topLeft.x + width / 2,\n\t\ty: crop.topLeft.y + height / 2,\n\t}\n}\n\n// Utility function to create crop with specified dimensions centered on given point\nfunction createCropAroundCenter(\n\tcenterX: number,\n\tcenterY: number,\n\twidth: number,\n\theight: number,\n\tisCircle?: boolean\n) {\n\tconst topLeftX = Math.max(0, Math.min(1 - width, centerX - width / 2))\n\tconst topLeftY = Math.max(0, Math.min(1 - height, centerY - height / 2))\n\n\treturn {\n\t\ttopLeft: { x: topLeftX, y: topLeftY },\n\t\tbottomRight: { x: topLeftX + width, y: topLeftY + height },\n\t\tisCircle,\n\t}\n}\n\n/** @public */\nexport function getCropBox<T extends ShapeWithCrop>(\n\tshape: T,\n\tinfo: TLCropInfo<T>,\n\topts = {} as CropBoxOptions\n):\n\t| {\n\t\t\tid: TLShapeId\n\t\t\ttype: T['type']\n\t\t\tx: number\n\t\t\ty: number\n\t\t\tprops: ShapeWithCrop['props']\n\t }\n\t| undefined {\n\tconst { handle, change, crop, aspectRatioLocked, isResizingFromCenter } = info\n\tconst { w, h } = info.uncroppedSize\n\tconst { minWidth = MIN_CROP_SIZE, minHeight = MIN_CROP_SIZE } = opts\n\n\tif (w < minWidth || h < minHeight || (change.x === 0 && change.y === 0)) {\n\t\treturn\n\t}\n\n\t// Lets get a box here in pixel space. For simplicity, we'll do all the math in\n\t// pixel space, then convert to normalized space at the end.\n\tconst prevCropBox = new Box(\n\t\tcrop.topLeft.x * w,\n\t\tcrop.topLeft.y * h,\n\t\t(crop.bottomRight.x - crop.topLeft.x) * w,\n\t\t(crop.bottomRight.y - crop.topLeft.y) * h\n\t)\n\n\tconst targetRatio = prevCropBox.aspectRatio\n\tconst tempBox = prevCropBox.clone()\n\n\t// Which axes does the dragged handle move?\n\tconst movesLeft = handle === 'top_left' || handle === 'bottom_left' || handle === 'left'\n\tconst movesRight = handle === 'top_right' || handle === 'bottom_right' || handle === 'right'\n\tconst movesTop = handle === 'top_left' || handle === 'top_right' || handle === 'top'\n\tconst movesBottom = handle === 'bottom_left' || handle === 'bottom_right' || handle === 'bottom'\n\n\tif (isResizingFromCenter) {\n\t\t// Resizing from the center (alt/option): the crop center stays fixed and the\n\t\t// opposite edge mirrors the dragged edge, so the crop grows/shrinks symmetrically.\n\t\tconst cx = prevCropBox.midX\n\t\tconst cy = prevCropBox.midY\n\n\t\t// Desired half-extents from the dragged edge. Undragged axes keep their size.\n\t\tlet halfW = prevCropBox.w / 2\n\t\tlet halfH = prevCropBox.h / 2\n\t\tif (movesLeft) halfW = cx - (prevCropBox.x + change.x)\n\t\telse if (movesRight) halfW = prevCropBox.maxX + change.x - cx\n\t\tif (movesTop) halfH = cy - (prevCropBox.y + change.y)\n\t\telse if (movesBottom) halfH = prevCropBox.maxY + change.y - cy\n\n\t\tif (aspectRatioLocked) {\n\t\t\t// Keep the aspect ratio while staying centered. Pick the driving dimension,\n\t\t\t// derive the other from the ratio, then clamp both at once so the ratio holds.\n\t\t\tconst isCorner = (movesLeft || movesRight) && (movesTop || movesBottom)\n\t\t\tlet drivingHalfW: number\n\t\t\tif (isCorner) {\n\t\t\t\tdrivingHalfW = halfW / halfH > targetRatio ? halfW : halfH * targetRatio\n\t\t\t} else if (movesLeft || movesRight) {\n\t\t\t\tdrivingHalfW = halfW\n\t\t\t} else {\n\t\t\t\tdrivingHalfW = halfH * targetRatio\n\t\t\t}\n\n\t\t\tconst minHalfW = Math.max(minWidth / 2, (minHeight / 2) * targetRatio)\n\t\t\tconst maxHalfW = Math.min(cx, w - cx, cy * targetRatio, (h - cy) * targetRatio)\n\t\t\tif (maxHalfW < minHalfW) return\n\n\t\t\thalfW = clamp(drivingHalfW, minHalfW, maxHalfW)\n\t\t\thalfH = halfW / targetRatio\n\t\t} else {\n\t\t\t// Clamp each dragged axis independently against the image bounds and min size.\n\t\t\tif (movesLeft || movesRight) {\n\t\t\t\tconst maxHalfW = Math.min(cx, w - cx)\n\t\t\t\tif (maxHalfW < minWidth / 2) return\n\t\t\t\thalfW = clamp(halfW, minWidth / 2, maxHalfW)\n\t\t\t}\n\t\t\tif (movesTop || movesBottom) {\n\t\t\t\tconst maxHalfH = Math.min(cy, h - cy)\n\t\t\t\tif (maxHalfH < minHeight / 2) return\n\t\t\t\thalfH = clamp(halfH, minHeight / 2, maxHalfH)\n\t\t\t}\n\t\t}\n\n\t\ttempBox.x = cx - halfW\n\t\ttempBox.y = cy - halfH\n\t\ttempBox.w = halfW * 2\n\t\ttempBox.h = halfH * 2\n\t} else {\n\t\t// Basic resizing logic based on the handles\n\n\t\tif (movesLeft) {\n\t\t\ttempBox.x = clamp(tempBox.x + change.x, 0, prevCropBox.maxX - minWidth)\n\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t} else if (movesRight) {\n\t\t\tconst tempRight = clamp(tempBox.maxX + change.x, prevCropBox.x + minWidth, w)\n\t\t\ttempBox.w = tempRight - tempBox.x\n\t\t}\n\n\t\tif (movesTop) {\n\t\t\ttempBox.y = clamp(tempBox.y + change.y, 0, prevCropBox.maxY - minHeight)\n\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t} else if (movesBottom) {\n\t\t\tconst tempBottom = clamp(tempBox.maxY + change.y, prevCropBox.y + minHeight, h)\n\t\t\ttempBox.h = tempBottom - tempBox.y\n\t\t}\n\t}\n\n\t// Aspect ratio locked resizing logic\n\n\tif (aspectRatioLocked && !isResizingFromCenter) {\n\t\tconst isXLimiting = tempBox.aspectRatio > targetRatio\n\n\t\tif (isXLimiting) {\n\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t} else {\n\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t}\n\n\t\tswitch (handle) {\n\t\t\tcase 'top_left': {\n\t\t\t\t// preserve the bottom right corner\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top_right': {\n\t\t\t\t// preserve the bottom left corner\n\t\t\t\ttempBox.x = prevCropBox.x\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\ttempBox.w = w - prevCropBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom_left': {\n\t\t\t\t// preserve the top right corner\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\ttempBox.y = prevCropBox.y\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\ttempBox.h = h - prevCropBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom_right': {\n\t\t\t\t// preserve the top left corner\n\t\t\t\ttempBox.x = prevCropBox.x\n\t\t\t\ttempBox.y = prevCropBox.y\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\ttempBox.w = w - prevCropBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\ttempBox.h = h - prevCropBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top': {\n\t\t\t\t// preserve the bottom edge center\n\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\ttempBox.x -= (tempBox.w - prevCropBox.w) / 2\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\tconst leftSide = prevCropBox.midX\n\t\t\t\t\ttempBox.w = leftSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\tconst rightSide = w - prevCropBox.midX\n\t\t\t\t\ttempBox.w = rightSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = w - tempBox.w\n\t\t\t\t}\n\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\t// preserve the left edge center\n\t\t\t\ttempBox.w = tempBox.maxX - prevCropBox.x\n\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\ttempBox.y -= (tempBox.h - prevCropBox.h) / 2\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\tconst topSide = prevCropBox.midY\n\t\t\t\t\ttempBox.h = topSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\tconst bottomSide = h - prevCropBox.midY\n\t\t\t\t\ttempBox.h = bottomSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = h - tempBox.h\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\t// preserve the top edge center\n\t\t\t\ttempBox.h = tempBox.maxY - prevCropBox.y\n\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\ttempBox.x -= (tempBox.w - prevCropBox.w) / 2\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\tconst leftSide = prevCropBox.midX\n\t\t\t\t\ttempBox.w = leftSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\tconst rightSide = w - prevCropBox.midX\n\t\t\t\t\ttempBox.w = rightSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = w - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\t// preserve the right edge center\n\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\ttempBox.y -= (tempBox.h - prevCropBox.h) / 2\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\tconst topSide = prevCropBox.midY\n\t\t\t\t\ttempBox.h = topSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\tconst bottomSide = h - prevCropBox.midY\n\t\t\t\t\ttempBox.h = bottomSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = h - tempBox.h\n\t\t\t\t}\n\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// Convert the box back to normalized space\n\tconst newCrop: TLShapeCrop = {\n\t\ttopLeft: { x: tempBox.x / w, y: tempBox.y / h },\n\t\tbottomRight: { x: tempBox.maxX / w, y: tempBox.maxY / h },\n\t}\n\tif (crop.isCircle != null) {\n\t\tnewCrop.isCircle = crop.isCircle\n\t}\n\n\t// If the crop hasn't changed, we can return early\n\tif (\n\t\tnewCrop.topLeft.x === crop.topLeft.x &&\n\t\tnewCrop.topLeft.y === crop.topLeft.y &&\n\t\tnewCrop.bottomRight.x === crop.bottomRight.x &&\n\t\tnewCrop.bottomRight.y === crop.bottomRight.y\n\t) {\n\t\treturn\n\t}\n\n\t// Adjust the shape's position to keep the crop's absolute coordinates correct\n\tconst newPoint = new Vec(tempBox.x - crop.topLeft.x * w, tempBox.y - crop.topLeft.y * h)\n\t\t.rot(shape.rotation)\n\t\t.add(shape)\n\n\treturn {\n\t\tid: shape.id,\n\t\ttype: shape.type,\n\t\tx: newPoint.x,\n\t\ty: newPoint.y,\n\t\tprops: {\n\t\t\t...shape.props,\n\t\t\tw: tempBox.w,\n\t\t\th: tempBox.h,\n\t\t\tcrop: newCrop,\n\t\t},\n\t}\n}\n\ninterface CropChange {\n\tcrop: {\n\t\ttopLeft: { x: number; y: number }\n\t\tbottomRight: { x: number; y: number }\n\t\tisCircle?: boolean\n\t}\n\tw: number\n\th: number\n\tx: number\n\ty: number\n}\n\n// Base function for calculating crop changes\nfunction calculateCropChange(\n\timageShape: TLImageShape,\n\tnewCropWidth: number,\n\tnewCropHeight: number,\n\tcenterOnCurrentCrop: boolean = true,\n\tisCircle: boolean = false\n): CropChange {\n\tconst { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop())\n\tconst currentCrop = imageShape.props.crop || getDefaultCrop()\n\n\t// Calculate image and crop centers\n\tconst imageCenterX = imageShape.x + imageShape.props.w / 2\n\tconst imageCenterY = imageShape.y + imageShape.props.h / 2\n\n\tlet cropCenterX, cropCenterY\n\tif (centerOnCurrentCrop) {\n\t\tconst { x, y } = getCropCenter(currentCrop)\n\t\tcropCenterX = x\n\t\tcropCenterY = y\n\t} else {\n\t\tcropCenterX = 0.5\n\t\tcropCenterY = 0.5\n\t}\n\n\t// Create new crop\n\tconst newCrop = createCropAroundCenter(\n\t\tcropCenterX,\n\t\tcropCenterY,\n\t\tnewCropWidth,\n\t\tnewCropHeight,\n\t\tisCircle\n\t)\n\n\t// Calculate new dimensions\n\tconst croppedW = newCropWidth * w\n\tconst croppedH = newCropHeight * h\n\n\treturn {\n\t\tcrop: newCrop,\n\t\tw: croppedW,\n\t\th: croppedH,\n\t\tx: imageCenterX - croppedW / 2,\n\t\ty: imageCenterY - croppedH / 2,\n\t}\n}\n\n/** @internal */\nexport const MAX_ZOOM = 3\n\n/**\n * Calculate new crop dimensions and position when zooming\n */\nexport function getCroppedImageDataWhenZooming(\n\tzoom: number,\n\timageShape: TLImageShape,\n\tmaxZoom?: number\n): CropChange {\n\tconst oldCrop = imageShape.props.crop || getDefaultCrop()\n\tconst { width: oldWidth, height: oldHeight } = getCropDimensions(oldCrop)\n\tconst aspectRatio = oldWidth / oldHeight\n\n\t// Calculate new crop size with zoom scale\n\tconst derivedMaxZoom = maxZoom ? 1 / (1 - maxZoom) : MAX_ZOOM\n\tconst zoomScale = 1 + zoom * (derivedMaxZoom - 1)\n\tlet newWidth, newHeight\n\n\tif (aspectRatio > 1) {\n\t\tnewWidth = Math.min(1, 1 / zoomScale)\n\t\tnewHeight = newWidth / aspectRatio\n\t} else {\n\t\tnewHeight = Math.min(1, 1 / zoomScale)\n\t\tnewWidth = newHeight * aspectRatio\n\t}\n\n\t// Calculate result with base function\n\tconst result = calculateCropChange(imageShape, newWidth, newHeight, true, oldCrop.isCircle)\n\n\t// Apply zoom factor to display dimensions\n\tconst scaleFactor = Math.min(MAX_ZOOM, oldWidth / newWidth)\n\tresult.w *= scaleFactor\n\tresult.h *= scaleFactor\n\n\t// Recenter\n\tconst imageCenterX = imageShape.x + imageShape.props.w / 2\n\tconst imageCenterY = imageShape.y + imageShape.props.h / 2\n\tresult.x = imageCenterX - result.w / 2\n\tresult.y = imageCenterY - result.h / 2\n\n\treturn result\n}\n\n/**\n * Calculate new crop dimensions and position when replacing an image\n */\nexport function getCroppedImageDataForReplacedImage(\n\timageShape: TLImageShape,\n\tnewImageWidth: number,\n\tnewImageHeight: number\n): CropChange {\n\tconst defaultCrop = getDefaultCrop()\n\tconst currentCrop = imageShape.props.crop || defaultCrop\n\tconst origDisplayW = imageShape.props.w\n\tconst origDisplayH = imageShape.props.h\n\tconst newImageAspectRatio = newImageWidth / newImageHeight\n\n\tlet crop = defaultCrop\n\tlet newDisplayW = origDisplayW\n\tlet newDisplayH = origDisplayH\n\t// Compare the bounds, not the whole object: an explicit `isCircle: false` must still count\n\t// as the original crop.\n\tconst isOriginalCrop =\n\t\t!!imageShape.props.crop &&\n\t\tisEqual(imageShape.props.crop.topLeft, defaultCrop.topLeft) &&\n\t\tisEqual(imageShape.props.crop.bottomRight, defaultCrop.bottomRight) &&\n\t\t!imageShape.props.crop.isCircle\n\n\tif (isOriginalCrop) {\n\t\tnewDisplayW = origDisplayW\n\t\tnewDisplayH = (origDisplayW * newImageHeight) / newImageWidth\n\t} else {\n\t\tconst { w: uncroppedW, h: uncroppedH } = getUncroppedSize(\n\t\t\timageShape.props,\n\t\t\timageShape.props.crop || getDefaultCrop() // Use the ACTUAL current crop to correctly infer uncropped size\n\t\t)\n\t\tconst { width: cropW, height: cropH } = getCropDimensions(currentCrop)\n\t\tconst targetRatio = cropW / cropH\n\t\tconst oldImageAspectRatio = uncroppedW / uncroppedH\n\t\tlet newRelativeWidth: number\n\t\tlet newRelativeHeight: number\n\n\t\tconst currentCropCenter = getCropCenter(currentCrop)\n\n\t\t// Adjust the new crop dimensions to match the current crop zoom\n\t\tnewRelativeWidth = cropW\n\t\tconst ratioConversion = newImageAspectRatio / oldImageAspectRatio / targetRatio\n\t\tnewRelativeHeight = newRelativeWidth * ratioConversion\n\n\t\t// Check that our new crop dimensions are within the MAX_ZOOM bounds\n\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\tif (ratioConversion > maxRatioConversion) {\n\t\t\tconst minDimension = 1 / MAX_ZOOM\n\t\t\tif (1 / newRelativeHeight < 1 / newRelativeWidth) {\n\t\t\t\tconst scale = newRelativeHeight / minDimension\n\t\t\t\tnewRelativeHeight = newRelativeHeight / scale\n\t\t\t\tnewRelativeWidth = newRelativeWidth / scale\n\t\t\t} else {\n\t\t\t\tconst scale = newRelativeWidth / minDimension\n\t\t\t\tnewRelativeWidth = newRelativeWidth / scale\n\t\t\t\tnewRelativeHeight = newRelativeHeight / scale\n\t\t\t}\n\t\t}\n\n\t\t// Ensure dimensions are within [0, 1] bounds after adjustment\n\t\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\t\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\n\t\t// Create the new crop object, centered around the CURRENT crop's center\n\t\tcrop = createCropAroundCenter(\n\t\t\tcurrentCropCenter.x,\n\t\t\tcurrentCropCenter.y,\n\t\t\tnewRelativeWidth,\n\t\t\tnewRelativeHeight,\n\t\t\tcurrentCrop.isCircle\n\t\t)\n\t}\n\n\t// Position so visual center stays put\n\tconst pageCenterX = imageShape.x + origDisplayW / 2\n\tconst pageCenterY = imageShape.y + origDisplayH / 2\n\n\tconst newX = pageCenterX - newDisplayW / 2\n\tconst newY = pageCenterY - newDisplayH / 2\n\n\treturn {\n\t\tcrop,\n\t\tw: newDisplayW,\n\t\th: newDisplayH,\n\t\tx: newX,\n\t\ty: newY,\n\t}\n}\n\n/**\n * Calculate new crop dimensions and position when changing aspect ratio\n */\nexport function getCroppedImageDataForAspectRatio(\n\taspectRatioOption: ASPECT_RATIO_OPTION,\n\timageShape: TLImageShape\n): CropChange {\n\t// If original aspect ratio is requested, use default crop\n\tif (aspectRatioOption === 'original') {\n\t\tconst { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop())\n\t\tconst imageCenterX = imageShape.x + imageShape.props.w / 2\n\t\tconst imageCenterY = imageShape.y + imageShape.props.h / 2\n\n\t\treturn {\n\t\t\tcrop: getDefaultCrop(),\n\t\t\tw,\n\t\t\th,\n\t\t\tx: imageCenterX - w / 2,\n\t\t\ty: imageCenterY - h / 2,\n\t\t}\n\t}\n\n\t// Get target ratio and uncropped image properties\n\tconst targetRatio = ASPECT_RATIO_TO_VALUE[aspectRatioOption] // Assume valid option\n\tconst isCircle = aspectRatioOption === 'circle'\n\t// Use default crop to get uncropped size relative to the *original* image bounds\n\tconst { w: uncroppedW, h: uncroppedH } = getUncroppedSize(\n\t\timageShape.props,\n\t\timageShape.props.crop || getDefaultCrop() // Use the ACTUAL current crop to correctly infer uncropped size\n\t)\n\t// Calculate the original image aspect ratio\n\tconst imageAspectRatio = uncroppedW / uncroppedH\n\n\t// Get the current crop and its relative dimensions\n\tconst currentCrop = imageShape.props.crop || getDefaultCrop()\n\tconst { width: cropW, height: cropH } = getCropDimensions(currentCrop)\n\tconst currentCropCenter = getCropCenter(currentCrop)\n\n\t// Calculate the current crop zoom level\n\tconst currentCropZoom = Math.min(1 / cropW, 1 / cropH)\n\n\t// Calculate the relative width and height of the crop rectangle (0-1 scale)\n\t// Try to preserve the longest dimension of the current crop when changing aspect ratios\n\tlet newRelativeWidth: number\n\tlet newRelativeHeight: number\n\n\tif (imageAspectRatio === 0 || !Number.isFinite(imageAspectRatio) || targetRatio === 0) {\n\t\t// Avoid division by zero or NaN issues if image dimensions are invalid or target ratio is 0\n\t\tnewRelativeWidth = 1\n\t\tnewRelativeHeight = 1\n\t} else {\n\t\t// Get current crop dimensions in absolute units\n\t\tconst currentAbsoluteWidth = cropW * uncroppedW\n\t\tconst currentAbsoluteHeight = cropH * uncroppedH\n\n\t\t// Find the longest current dimension to preserve\n\t\tconst longestCurrentDimension = Math.max(currentAbsoluteWidth, currentAbsoluteHeight)\n\t\tconst isWidthLongest = currentAbsoluteWidth >= currentAbsoluteHeight\n\n\t\t// Calculate new dimensions preserving the longest dimension\n\t\tlet newAbsoluteWidth: number\n\t\tlet newAbsoluteHeight: number\n\n\t\tif (isWidthLongest) {\n\t\t\t// Preserve width, calculate height based on target ratio\n\t\t\tnewAbsoluteWidth = longestCurrentDimension\n\t\t\tnewAbsoluteHeight = newAbsoluteWidth / targetRatio\n\t\t} else {\n\t\t\t// Preserve height, calculate width based on target ratio\n\t\t\tnewAbsoluteHeight = longestCurrentDimension\n\t\t\tnewAbsoluteWidth = newAbsoluteHeight * targetRatio\n\t\t}\n\n\t\t// Convert back to relative coordinates\n\t\tnewRelativeWidth = newAbsoluteWidth / uncroppedW\n\t\tnewRelativeHeight = newAbsoluteHeight / uncroppedH\n\n\t\t// Clamp to image bounds and adjust if necessary\n\t\tif (newRelativeWidth > 1) {\n\t\t\t// Width exceeds bounds, clamp and recalculate height\n\t\t\tnewRelativeWidth = 1\n\t\t\tnewRelativeHeight = imageAspectRatio / targetRatio\n\t\t}\n\t\tif (newRelativeHeight > 1) {\n\t\t\t// Height exceeds bounds, clamp and recalculate width\n\t\t\tnewRelativeHeight = 1\n\t\t\tnewRelativeWidth = targetRatio / imageAspectRatio\n\t\t}\n\n\t\t// Final clamp to ensure we stay within bounds\n\t\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\t\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\t}\n\n\tconst newCropZoom = Math.min(1 / newRelativeWidth, 1 / newRelativeHeight)\n\t// Adjust the new crop dimensions to match the current crop zoom\n\tnewRelativeWidth *= newCropZoom / currentCropZoom\n\tnewRelativeHeight *= newCropZoom / currentCropZoom\n\n\t// Ensure dimensions are within [0, 1] bounds after adjustment\n\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\n\t// Create the new crop object, centered around the CURRENT crop's center\n\tconst newCrop = createCropAroundCenter(\n\t\tcurrentCropCenter.x,\n\t\tcurrentCropCenter.y,\n\t\tnewRelativeWidth,\n\t\tnewRelativeHeight,\n\t\tisCircle\n\t)\n\n\t// Get the actual relative dimensions from the new crop (after potential clamping)\n\tconst finalRelativeWidth = newCrop.bottomRight.x - newCrop.topLeft.x\n\tconst finalRelativeHeight = newCrop.bottomRight.y - newCrop.topLeft.y\n\n\t// Calculate the base dimensions (as if applying the new crop to the uncropped image at scale 1)\n\tconst baseW = finalRelativeWidth * uncroppedW\n\tconst baseH = finalRelativeHeight * uncroppedH\n\n\t// Determine the current effective scale of the shape\n\t// This preserves the visual size when the crop changes\n\tlet currentScale = 1.0\n\tif (cropW > 0) {\n\t\tcurrentScale = imageShape.props.w / (cropW * uncroppedW)\n\t} else if (cropH > 0) {\n\t\t// Fallback to height if width relative dimension is zero\n\t\tcurrentScale = imageShape.props.h / (cropH * uncroppedH)\n\t}\n\n\t// Apply the current scale to the base dimensions to get the final dimensions\n\tconst newW = baseW * currentScale\n\tconst newH = baseH * currentScale\n\n\t// Calculate the new top-left position (x, y) for the shape\n\t// to keep the visual center of the cropped area fixed on the page.\n\tconst currentCenterXPage = imageShape.x + imageShape.props.w / 2\n\tconst currentCenterYPage = imageShape.y + imageShape.props.h / 2\n\tconst newX = currentCenterXPage - newW / 2\n\tconst newY = currentCenterYPage - newH / 2\n\n\treturn {\n\t\tcrop: newCrop,\n\t\tw: newW,\n\t\th: newH,\n\t\tx: newX,\n\t\ty: newY,\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\n\tBox,\n\tShapeWithCrop,\n\tTLCropInfo,\n\tTLImageShape,\n\tTLShapeCrop,\n\tTLShapeId,\n\tVec,\n\tclamp,\n\tisEqual,\n} from '@tldraw/editor'\n\n/** @internal */\nexport const MIN_CROP_SIZE = 8\n\n/** @public */\nexport interface CropBoxOptions {\n\tminWidth?: number\n\tminHeight?: number\n}\n\n/** @public */\nexport function getDefaultCrop(): TLShapeCrop {\n\treturn {\n\t\ttopLeft: { x: 0, y: 0 },\n\t\tbottomRight: { x: 1, y: 1 },\n\t}\n}\n\n/** @public */\nexport type ASPECT_RATIO_OPTION =\n\t| 'original'\n\t| 'square'\n\t| 'circle'\n\t| 'landscape'\n\t| 'portrait'\n\t| 'wide'\n\n/** @public */\nexport const ASPECT_RATIO_OPTIONS: ASPECT_RATIO_OPTION[] = [\n\t'original',\n\t'square',\n\t'circle',\n\t'landscape',\n\t'portrait',\n\t'wide',\n]\n\n/** @public */\nexport const ASPECT_RATIO_TO_VALUE: Record<ASPECT_RATIO_OPTION, number> = {\n\toriginal: 0,\n\tsquare: 1,\n\tcircle: 1,\n\tlandscape: 4 / 3,\n\tportrait: 3 / 4,\n\twide: 16 / 9,\n}\n\n/**\n * Original (uncropped) width and height of shape.\n *\n * @public\n */\nexport function getUncroppedSize(\n\tshapeSize: { w: number; h: number },\n\tcrop: TLShapeCrop | null\n): { w: number; h: number } {\n\tif (!crop) return { w: shapeSize.w, h: shapeSize.h }\n\tconst w = shapeSize.w / (crop.bottomRight.x - crop.topLeft.x)\n\tconst h = shapeSize.h / (crop.bottomRight.y - crop.topLeft.y)\n\treturn { w, h }\n}\n\n// Utility function to get crop dimensions\nfunction getCropDimensions(crop: TLShapeCrop) {\n\treturn {\n\t\twidth: crop.bottomRight.x - crop.topLeft.x,\n\t\theight: crop.bottomRight.y - crop.topLeft.y,\n\t}\n}\n\n// Utility function to get crop center\nfunction getCropCenter(crop: TLShapeCrop) {\n\tconst { width, height } = getCropDimensions(crop)\n\treturn {\n\t\tx: crop.topLeft.x + width / 2,\n\t\ty: crop.topLeft.y + height / 2,\n\t}\n}\n\n// Utility function to create crop with specified dimensions centered on given point\nfunction createCropAroundCenter(\n\tcenterX: number,\n\tcenterY: number,\n\twidth: number,\n\theight: number,\n\tisCircle?: boolean\n) {\n\tconst topLeftX = Math.max(0, Math.min(1 - width, centerX - width / 2))\n\tconst topLeftY = Math.max(0, Math.min(1 - height, centerY - height / 2))\n\n\treturn {\n\t\ttopLeft: { x: topLeftX, y: topLeftY },\n\t\tbottomRight: { x: topLeftX + width, y: topLeftY + height },\n\t\tisCircle,\n\t}\n}\n\n/** @public */\nexport function getCropBox<T extends ShapeWithCrop>(\n\tshape: T,\n\tinfo: TLCropInfo<T>,\n\topts = {} as CropBoxOptions\n):\n\t| {\n\t\t\tid: TLShapeId\n\t\t\ttype: T['type']\n\t\t\tx: number\n\t\t\ty: number\n\t\t\tprops: ShapeWithCrop['props']\n\t }\n\t| undefined {\n\tconst { handle, change, crop, aspectRatioLocked, isResizingFromCenter } = info\n\tconst { w, h } = info.uncroppedSize\n\tconst { minWidth = MIN_CROP_SIZE, minHeight = MIN_CROP_SIZE } = opts\n\n\tif (w < minWidth || h < minHeight || (change.x === 0 && change.y === 0)) {\n\t\treturn\n\t}\n\n\t// Lets get a box here in pixel space. For simplicity, we'll do all the math in\n\t// pixel space, then convert to normalized space at the end.\n\tconst prevCropBox = new Box(\n\t\tcrop.topLeft.x * w,\n\t\tcrop.topLeft.y * h,\n\t\t(crop.bottomRight.x - crop.topLeft.x) * w,\n\t\t(crop.bottomRight.y - crop.topLeft.y) * h\n\t)\n\n\tconst targetRatio = prevCropBox.aspectRatio\n\tconst tempBox = prevCropBox.clone()\n\n\t// Which axes does the dragged handle move?\n\tconst movesLeft = handle === 'top_left' || handle === 'bottom_left' || handle === 'left'\n\tconst movesRight = handle === 'top_right' || handle === 'bottom_right' || handle === 'right'\n\tconst movesTop = handle === 'top_left' || handle === 'top_right' || handle === 'top'\n\tconst movesBottom = handle === 'bottom_left' || handle === 'bottom_right' || handle === 'bottom'\n\n\tif (isResizingFromCenter) {\n\t\t// Resizing from the center (alt/option): the crop center stays fixed and the\n\t\t// opposite edge mirrors the dragged edge, so the crop grows/shrinks symmetrically.\n\t\tconst cx = prevCropBox.midX\n\t\tconst cy = prevCropBox.midY\n\n\t\t// Desired half-extents from the dragged edge. Undragged axes keep their size.\n\t\tlet halfW = prevCropBox.w / 2\n\t\tlet halfH = prevCropBox.h / 2\n\t\tif (movesLeft) halfW = cx - (prevCropBox.x + change.x)\n\t\telse if (movesRight) halfW = prevCropBox.maxX + change.x - cx\n\t\tif (movesTop) halfH = cy - (prevCropBox.y + change.y)\n\t\telse if (movesBottom) halfH = prevCropBox.maxY + change.y - cy\n\n\t\tif (aspectRatioLocked) {\n\t\t\t// Keep the aspect ratio while staying centered. Pick the driving dimension,\n\t\t\t// derive the other from the ratio, then clamp both at once so the ratio holds.\n\t\t\tconst isCorner = (movesLeft || movesRight) && (movesTop || movesBottom)\n\t\t\tlet drivingHalfW: number\n\t\t\tif (isCorner) {\n\t\t\t\tdrivingHalfW = halfW / halfH > targetRatio ? halfW : halfH * targetRatio\n\t\t\t} else if (movesLeft || movesRight) {\n\t\t\t\tdrivingHalfW = halfW\n\t\t\t} else {\n\t\t\t\tdrivingHalfW = halfH * targetRatio\n\t\t\t}\n\n\t\t\tconst minHalfW = Math.max(minWidth / 2, (minHeight / 2) * targetRatio)\n\t\t\tconst maxHalfW = Math.min(cx, w - cx, cy * targetRatio, (h - cy) * targetRatio)\n\t\t\tif (maxHalfW < minHalfW) return\n\n\t\t\thalfW = clamp(drivingHalfW, minHalfW, maxHalfW)\n\t\t\thalfH = halfW / targetRatio\n\t\t} else {\n\t\t\t// Clamp each dragged axis independently against the image bounds and min size.\n\t\t\tif (movesLeft || movesRight) {\n\t\t\t\tconst maxHalfW = Math.min(cx, w - cx)\n\t\t\t\tif (maxHalfW < minWidth / 2) return\n\t\t\t\thalfW = clamp(halfW, minWidth / 2, maxHalfW)\n\t\t\t}\n\t\t\tif (movesTop || movesBottom) {\n\t\t\t\tconst maxHalfH = Math.min(cy, h - cy)\n\t\t\t\tif (maxHalfH < minHeight / 2) return\n\t\t\t\thalfH = clamp(halfH, minHeight / 2, maxHalfH)\n\t\t\t}\n\t\t}\n\n\t\ttempBox.x = cx - halfW\n\t\ttempBox.y = cy - halfH\n\t\ttempBox.w = halfW * 2\n\t\ttempBox.h = halfH * 2\n\t} else {\n\t\t// Basic resizing logic based on the handles\n\n\t\tif (movesLeft) {\n\t\t\ttempBox.x = clamp(tempBox.x + change.x, 0, prevCropBox.maxX - minWidth)\n\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t} else if (movesRight) {\n\t\t\tconst tempRight = clamp(tempBox.maxX + change.x, prevCropBox.x + minWidth, w)\n\t\t\ttempBox.w = tempRight - tempBox.x\n\t\t}\n\n\t\tif (movesTop) {\n\t\t\ttempBox.y = clamp(tempBox.y + change.y, 0, prevCropBox.maxY - minHeight)\n\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t} else if (movesBottom) {\n\t\t\tconst tempBottom = clamp(tempBox.maxY + change.y, prevCropBox.y + minHeight, h)\n\t\t\ttempBox.h = tempBottom - tempBox.y\n\t\t}\n\t}\n\n\t// Aspect ratio locked resizing logic\n\n\tif (aspectRatioLocked && !isResizingFromCenter) {\n\t\tconst isXLimiting = tempBox.aspectRatio > targetRatio\n\n\t\tif (isXLimiting) {\n\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t} else {\n\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t}\n\n\t\tswitch (handle) {\n\t\t\tcase 'top_left': {\n\t\t\t\t// preserve the bottom right corner\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top_right': {\n\t\t\t\t// preserve the bottom left corner\n\t\t\t\ttempBox.x = prevCropBox.x\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\ttempBox.w = w - prevCropBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom_left': {\n\t\t\t\t// preserve the top right corner\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\ttempBox.y = prevCropBox.y\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\ttempBox.h = h - prevCropBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom_right': {\n\t\t\t\t// preserve the top left corner\n\t\t\t\ttempBox.x = prevCropBox.x\n\t\t\t\ttempBox.y = prevCropBox.y\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\ttempBox.w = w - prevCropBox.x\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\ttempBox.h = h - prevCropBox.y\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'top': {\n\t\t\t\t// preserve the bottom edge center\n\t\t\t\ttempBox.h = prevCropBox.maxY - tempBox.y\n\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\ttempBox.x -= (tempBox.w - prevCropBox.w) / 2\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\tconst leftSide = prevCropBox.midX\n\t\t\t\t\ttempBox.w = leftSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\tconst rightSide = w - prevCropBox.midX\n\t\t\t\t\ttempBox.w = rightSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = w - tempBox.w\n\t\t\t\t}\n\n\t\t\t\ttempBox.y = prevCropBox.maxY - tempBox.h\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'right': {\n\t\t\t\t// preserve the left edge center\n\t\t\t\ttempBox.w = tempBox.maxX - prevCropBox.x\n\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\ttempBox.y -= (tempBox.h - prevCropBox.h) / 2\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\tconst topSide = prevCropBox.midY\n\t\t\t\t\ttempBox.h = topSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\tconst bottomSide = h - prevCropBox.midY\n\t\t\t\t\ttempBox.h = bottomSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = h - tempBox.h\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'bottom': {\n\t\t\t\t// preserve the top edge center\n\t\t\t\ttempBox.h = tempBox.maxY - prevCropBox.y\n\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\ttempBox.x -= (tempBox.w - prevCropBox.w) / 2\n\n\t\t\t\tif (tempBox.x <= 0) {\n\t\t\t\t\tconst leftSide = prevCropBox.midX\n\t\t\t\t\ttempBox.w = leftSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxX >= w) {\n\t\t\t\t\tconst rightSide = w - prevCropBox.midX\n\t\t\t\t\ttempBox.w = rightSide * 2\n\t\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\t\ttempBox.x = w - tempBox.w\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'left': {\n\t\t\t\t// preserve the right edge center\n\t\t\t\ttempBox.w = prevCropBox.maxX - tempBox.x\n\t\t\t\ttempBox.h = tempBox.w / targetRatio\n\t\t\t\ttempBox.y -= (tempBox.h - prevCropBox.h) / 2\n\n\t\t\t\tif (tempBox.y <= 0) {\n\t\t\t\t\tconst topSide = prevCropBox.midY\n\t\t\t\t\ttempBox.h = topSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = 0\n\t\t\t\t}\n\n\t\t\t\tif (tempBox.maxY >= h) {\n\t\t\t\t\tconst bottomSide = h - prevCropBox.midY\n\t\t\t\t\ttempBox.h = bottomSide * 2\n\t\t\t\t\ttempBox.w = tempBox.h * targetRatio\n\t\t\t\t\ttempBox.y = h - tempBox.h\n\t\t\t\t}\n\n\t\t\t\ttempBox.x = prevCropBox.maxX - tempBox.w\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// Convert the box back to normalized space\n\tconst newCrop: TLShapeCrop = {\n\t\ttopLeft: { x: tempBox.x / w, y: tempBox.y / h },\n\t\tbottomRight: { x: tempBox.maxX / w, y: tempBox.maxY / h },\n\t}\n\tif (crop.isCircle != null) {\n\t\tnewCrop.isCircle = crop.isCircle\n\t}\n\n\t// If the crop hasn't changed, we can return early\n\tif (\n\t\tnewCrop.topLeft.x === crop.topLeft.x &&\n\t\tnewCrop.topLeft.y === crop.topLeft.y &&\n\t\tnewCrop.bottomRight.x === crop.bottomRight.x &&\n\t\tnewCrop.bottomRight.y === crop.bottomRight.y\n\t) {\n\t\treturn\n\t}\n\n\t// Adjust the shape's position to keep the crop's absolute coordinates correct\n\tconst newPoint = new Vec(tempBox.x - crop.topLeft.x * w, tempBox.y - crop.topLeft.y * h)\n\t\t.rot(shape.rotation)\n\t\t.add(shape)\n\n\treturn {\n\t\tid: shape.id,\n\t\ttype: shape.type,\n\t\tx: newPoint.x,\n\t\ty: newPoint.y,\n\t\tprops: {\n\t\t\t...shape.props,\n\t\t\tw: tempBox.w,\n\t\t\th: tempBox.h,\n\t\t\tcrop: newCrop,\n\t\t},\n\t}\n}\n\ninterface CropChange {\n\tcrop: {\n\t\ttopLeft: { x: number; y: number }\n\t\tbottomRight: { x: number; y: number }\n\t\tisCircle?: boolean\n\t}\n\tw: number\n\th: number\n\tx: number\n\ty: number\n}\n\n/**\n * Top-left position that keeps the image's visual centre where it is after its display size\n * changes. The half-size delta is in the shape's own space, so it has to be rotated by the\n * shape's rotation; a plain `center - newSize / 2` only holds for unrotated images.\n */\nfunction getPositionKeepingCenter(imageShape: TLImageShape, newW: number, newH: number) {\n\tconst delta = new Vec((imageShape.props.w - newW) / 2, (imageShape.props.h - newH) / 2).rot(\n\t\timageShape.rotation\n\t)\n\treturn { x: imageShape.x + delta.x, y: imageShape.y + delta.y }\n}\n\nfunction calculateCropChange(\n\timageShape: TLImageShape,\n\tnewCropWidth: number,\n\tnewCropHeight: number,\n\tcenterOnCurrentCrop: boolean = true,\n\tisCircle: boolean = false\n): CropChange {\n\tconst { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop())\n\tconst currentCrop = imageShape.props.crop || getDefaultCrop()\n\n\tlet cropCenterX, cropCenterY\n\tif (centerOnCurrentCrop) {\n\t\tconst { x, y } = getCropCenter(currentCrop)\n\t\tcropCenterX = x\n\t\tcropCenterY = y\n\t} else {\n\t\tcropCenterX = 0.5\n\t\tcropCenterY = 0.5\n\t}\n\n\t// Create new crop\n\tconst newCrop = createCropAroundCenter(\n\t\tcropCenterX,\n\t\tcropCenterY,\n\t\tnewCropWidth,\n\t\tnewCropHeight,\n\t\tisCircle\n\t)\n\n\t// Calculate new dimensions\n\tconst croppedW = newCropWidth * w\n\tconst croppedH = newCropHeight * h\n\n\treturn {\n\t\tcrop: newCrop,\n\t\tw: croppedW,\n\t\th: croppedH,\n\t\t...getPositionKeepingCenter(imageShape, croppedW, croppedH),\n\t}\n}\n\n/** @internal */\nexport const MAX_ZOOM = 3\n\n/**\n * Calculate new crop dimensions and position when zooming\n */\nexport function getCroppedImageDataWhenZooming(\n\tzoom: number,\n\timageShape: TLImageShape,\n\tmaxZoom?: number\n): CropChange {\n\tconst oldCrop = imageShape.props.crop || getDefaultCrop()\n\tconst { width: oldWidth, height: oldHeight } = getCropDimensions(oldCrop)\n\tconst aspectRatio = oldWidth / oldHeight\n\n\t// Calculate new crop size with zoom scale\n\tconst derivedMaxZoom = maxZoom ? 1 / (1 - maxZoom) : MAX_ZOOM\n\tconst zoomScale = 1 + zoom * (derivedMaxZoom - 1)\n\tlet newWidth, newHeight\n\n\tif (aspectRatio > 1) {\n\t\tnewWidth = Math.min(1, 1 / zoomScale)\n\t\tnewHeight = newWidth / aspectRatio\n\t} else {\n\t\tnewHeight = Math.min(1, 1 / zoomScale)\n\t\tnewWidth = newHeight * aspectRatio\n\t}\n\n\t// Calculate result with base function\n\tconst result = calculateCropChange(imageShape, newWidth, newHeight, true, oldCrop.isCircle)\n\n\t// Apply zoom factor to display dimensions\n\tconst scaleFactor = Math.min(MAX_ZOOM, oldWidth / newWidth)\n\tresult.w *= scaleFactor\n\tresult.h *= scaleFactor\n\n\t// Recenter\n\tconst { x, y } = getPositionKeepingCenter(imageShape, result.w, result.h)\n\tresult.x = x\n\tresult.y = y\n\n\treturn result\n}\n\n/**\n * Calculate new crop dimensions and position when replacing an image\n */\nexport function getCroppedImageDataForReplacedImage(\n\timageShape: TLImageShape,\n\tnewImageWidth: number,\n\tnewImageHeight: number\n): CropChange {\n\tconst defaultCrop = getDefaultCrop()\n\tconst currentCrop = imageShape.props.crop || defaultCrop\n\tconst origDisplayW = imageShape.props.w\n\tconst origDisplayH = imageShape.props.h\n\tconst newImageAspectRatio = newImageWidth / newImageHeight\n\n\tlet crop = defaultCrop\n\tlet newDisplayW = origDisplayW\n\tlet newDisplayH = origDisplayH\n\t// Compare the bounds, not the whole object: an explicit `isCircle: false` must still count\n\t// as the original crop.\n\tconst isOriginalCrop =\n\t\t!!imageShape.props.crop &&\n\t\tisEqual(imageShape.props.crop.topLeft, defaultCrop.topLeft) &&\n\t\tisEqual(imageShape.props.crop.bottomRight, defaultCrop.bottomRight) &&\n\t\t!imageShape.props.crop.isCircle\n\n\tif (isOriginalCrop) {\n\t\tnewDisplayW = origDisplayW\n\t\tnewDisplayH = (origDisplayW * newImageHeight) / newImageWidth\n\t} else {\n\t\tconst { w: uncroppedW, h: uncroppedH } = getUncroppedSize(\n\t\t\timageShape.props,\n\t\t\timageShape.props.crop || getDefaultCrop() // Use the ACTUAL current crop to correctly infer uncropped size\n\t\t)\n\t\tconst { width: cropW, height: cropH } = getCropDimensions(currentCrop)\n\t\tconst targetRatio = cropW / cropH\n\t\tconst oldImageAspectRatio = uncroppedW / uncroppedH\n\t\tlet newRelativeWidth: number\n\t\tlet newRelativeHeight: number\n\n\t\tconst currentCropCenter = getCropCenter(currentCrop)\n\n\t\t// Adjust the new crop dimensions to match the current crop zoom\n\t\tnewRelativeWidth = cropW\n\t\tconst ratioConversion = newImageAspectRatio / oldImageAspectRatio / targetRatio\n\t\tnewRelativeHeight = newRelativeWidth * ratioConversion\n\n\t\t// Check that our new crop dimensions are within the MAX_ZOOM bounds\n\t\tconst maxRatioConversion = MAX_ZOOM / (MAX_ZOOM - 1)\n\t\tif (ratioConversion > maxRatioConversion) {\n\t\t\tconst minDimension = 1 / MAX_ZOOM\n\t\t\tif (1 / newRelativeHeight < 1 / newRelativeWidth) {\n\t\t\t\tconst scale = newRelativeHeight / minDimension\n\t\t\t\tnewRelativeHeight = newRelativeHeight / scale\n\t\t\t\tnewRelativeWidth = newRelativeWidth / scale\n\t\t\t} else {\n\t\t\t\tconst scale = newRelativeWidth / minDimension\n\t\t\t\tnewRelativeWidth = newRelativeWidth / scale\n\t\t\t\tnewRelativeHeight = newRelativeHeight / scale\n\t\t\t}\n\t\t}\n\n\t\t// Ensure dimensions are within [0, 1] bounds after adjustment\n\t\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\t\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\n\t\t// Create the new crop object, centered around the CURRENT crop's center\n\t\tcrop = createCropAroundCenter(\n\t\t\tcurrentCropCenter.x,\n\t\t\tcurrentCropCenter.y,\n\t\t\tnewRelativeWidth,\n\t\t\tnewRelativeHeight,\n\t\t\tcurrentCrop.isCircle\n\t\t)\n\t}\n\n\treturn {\n\t\tcrop,\n\t\tw: newDisplayW,\n\t\th: newDisplayH,\n\t\t...getPositionKeepingCenter(imageShape, newDisplayW, newDisplayH),\n\t}\n}\n\n/**\n * Calculate new crop dimensions and position when changing aspect ratio\n */\nexport function getCroppedImageDataForAspectRatio(\n\taspectRatioOption: ASPECT_RATIO_OPTION,\n\timageShape: TLImageShape\n): CropChange {\n\t// If original aspect ratio is requested, use default crop\n\tif (aspectRatioOption === 'original') {\n\t\tconst { w, h } = getUncroppedSize(imageShape.props, imageShape.props.crop ?? getDefaultCrop())\n\n\t\treturn {\n\t\t\tcrop: getDefaultCrop(),\n\t\t\tw,\n\t\t\th,\n\t\t\t...getPositionKeepingCenter(imageShape, w, h),\n\t\t}\n\t}\n\n\t// Get target ratio and uncropped image properties\n\tconst targetRatio = ASPECT_RATIO_TO_VALUE[aspectRatioOption] // Assume valid option\n\tconst isCircle = aspectRatioOption === 'circle'\n\t// Use default crop to get uncropped size relative to the *original* image bounds\n\tconst { w: uncroppedW, h: uncroppedH } = getUncroppedSize(\n\t\timageShape.props,\n\t\timageShape.props.crop || getDefaultCrop() // Use the ACTUAL current crop to correctly infer uncropped size\n\t)\n\t// Calculate the original image aspect ratio\n\tconst imageAspectRatio = uncroppedW / uncroppedH\n\n\t// Get the current crop and its relative dimensions\n\tconst currentCrop = imageShape.props.crop || getDefaultCrop()\n\tconst { width: cropW, height: cropH } = getCropDimensions(currentCrop)\n\tconst currentCropCenter = getCropCenter(currentCrop)\n\n\t// Calculate the current crop zoom level\n\tconst currentCropZoom = Math.min(1 / cropW, 1 / cropH)\n\n\t// Calculate the relative width and height of the crop rectangle (0-1 scale)\n\t// Try to preserve the longest dimension of the current crop when changing aspect ratios\n\tlet newRelativeWidth: number\n\tlet newRelativeHeight: number\n\n\tif (imageAspectRatio === 0 || !Number.isFinite(imageAspectRatio) || targetRatio === 0) {\n\t\t// Avoid division by zero or NaN issues if image dimensions are invalid or target ratio is 0\n\t\tnewRelativeWidth = 1\n\t\tnewRelativeHeight = 1\n\t} else {\n\t\t// Get current crop dimensions in absolute units\n\t\tconst currentAbsoluteWidth = cropW * uncroppedW\n\t\tconst currentAbsoluteHeight = cropH * uncroppedH\n\n\t\t// Find the longest current dimension to preserve\n\t\tconst longestCurrentDimension = Math.max(currentAbsoluteWidth, currentAbsoluteHeight)\n\t\tconst isWidthLongest = currentAbsoluteWidth >= currentAbsoluteHeight\n\n\t\t// Calculate new dimensions preserving the longest dimension\n\t\tlet newAbsoluteWidth: number\n\t\tlet newAbsoluteHeight: number\n\n\t\tif (isWidthLongest) {\n\t\t\t// Preserve width, calculate height based on target ratio\n\t\t\tnewAbsoluteWidth = longestCurrentDimension\n\t\t\tnewAbsoluteHeight = newAbsoluteWidth / targetRatio\n\t\t} else {\n\t\t\t// Preserve height, calculate width based on target ratio\n\t\t\tnewAbsoluteHeight = longestCurrentDimension\n\t\t\tnewAbsoluteWidth = newAbsoluteHeight * targetRatio\n\t\t}\n\n\t\t// Convert back to relative coordinates\n\t\tnewRelativeWidth = newAbsoluteWidth / uncroppedW\n\t\tnewRelativeHeight = newAbsoluteHeight / uncroppedH\n\n\t\t// Clamp to image bounds and adjust if necessary\n\t\tif (newRelativeWidth > 1) {\n\t\t\t// Width exceeds bounds, clamp and recalculate height\n\t\t\tnewRelativeWidth = 1\n\t\t\tnewRelativeHeight = imageAspectRatio / targetRatio\n\t\t}\n\t\tif (newRelativeHeight > 1) {\n\t\t\t// Height exceeds bounds, clamp and recalculate width\n\t\t\tnewRelativeHeight = 1\n\t\t\tnewRelativeWidth = targetRatio / imageAspectRatio\n\t\t}\n\n\t\t// Final clamp to ensure we stay within bounds\n\t\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\t\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\t}\n\n\tconst newCropZoom = Math.min(1 / newRelativeWidth, 1 / newRelativeHeight)\n\t// Adjust the new crop dimensions to match the current crop zoom\n\tnewRelativeWidth *= newCropZoom / currentCropZoom\n\tnewRelativeHeight *= newCropZoom / currentCropZoom\n\n\t// Ensure dimensions are within [0, 1] bounds after adjustment\n\tnewRelativeWidth = Math.max(0, Math.min(1, newRelativeWidth))\n\tnewRelativeHeight = Math.max(0, Math.min(1, newRelativeHeight))\n\n\t// Create the new crop object, centered around the CURRENT crop's center\n\tconst newCrop = createCropAroundCenter(\n\t\tcurrentCropCenter.x,\n\t\tcurrentCropCenter.y,\n\t\tnewRelativeWidth,\n\t\tnewRelativeHeight,\n\t\tisCircle\n\t)\n\n\t// Get the actual relative dimensions from the new crop (after potential clamping)\n\tconst finalRelativeWidth = newCrop.bottomRight.x - newCrop.topLeft.x\n\tconst finalRelativeHeight = newCrop.bottomRight.y - newCrop.topLeft.y\n\n\t// Calculate the base dimensions (as if applying the new crop to the uncropped image at scale 1)\n\tconst baseW = finalRelativeWidth * uncroppedW\n\tconst baseH = finalRelativeHeight * uncroppedH\n\n\t// Determine the current effective scale of the shape\n\t// This preserves the visual size when the crop changes\n\tlet currentScale = 1.0\n\tif (cropW > 0) {\n\t\tcurrentScale = imageShape.props.w / (cropW * uncroppedW)\n\t} else if (cropH > 0) {\n\t\t// Fallback to height if width relative dimension is zero\n\t\tcurrentScale = imageShape.props.h / (cropH * uncroppedH)\n\t}\n\n\t// Apply the current scale to the base dimensions to get the final dimensions\n\tconst newW = baseW * currentScale\n\tconst newH = baseH * currentScale\n\n\treturn {\n\t\tcrop: newCrop,\n\t\tw: newW,\n\t\th: newH,\n\t\t...getPositionKeepingCenter(imageShape, newW, newH),\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAUO;AAGA,MAAM,gBAAgB;AAStB,SAAS,iBAA8B;AAC7C,SAAO;AAAA,IACN,SAAS,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,IACtB,aAAa,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,EAC3B;AACD;AAYO,MAAM,uBAA8C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAGO,MAAM,wBAA6D;AAAA,EACzE,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW,IAAI;AAAA,EACf,UAAU,IAAI;AAAA,EACd,MAAM,KAAK;AACZ;AAOO,SAAS,iBACf,WACA,MAC2B;AAC3B,MAAI,CAAC,KAAM,QAAO,EAAE,GAAG,UAAU,GAAG,GAAG,UAAU,EAAE;AACnD,QAAM,IAAI,UAAU,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ;AAC3D,QAAM,IAAI,UAAU,KAAK,KAAK,YAAY,IAAI,KAAK,QAAQ;AAC3D,SAAO,EAAE,GAAG,EAAE;AACf;AAGA,SAAS,kBAAkB,MAAmB;AAC7C,SAAO;AAAA,IACN,OAAO,KAAK,YAAY,IAAI,KAAK,QAAQ;AAAA,IACzC,QAAQ,KAAK,YAAY,IAAI,KAAK,QAAQ;AAAA,EAC3C;AACD;AAGA,SAAS,cAAc,MAAmB;AACzC,QAAM,EAAE,OAAO,OAAO,IAAI,kBAAkB,IAAI;AAChD,SAAO;AAAA,IACN,GAAG,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAC5B,GAAG,KAAK,QAAQ,IAAI,SAAS;AAAA,EAC9B;AACD;AAGA,SAAS,uBACR,SACA,SACA,OACA,QACA,UACC;AACD,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,UAAU,QAAQ,CAAC,CAAC;AACrE,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,UAAU,SAAS,CAAC,CAAC;AAEvE,SAAO;AAAA,IACN,SAAS,EAAE,GAAG,UAAU,GAAG,SAAS;AAAA,IACpC,aAAa,EAAE,GAAG,WAAW,OAAO,GAAG,WAAW,OAAO;AAAA,IACzD;AAAA,EACD;AACD;AAGO,SAAS,WACf,OACA,MACA,OAAO,CAAC,GASI;AACZ,QAAM,EAAE,QAAQ,QAAQ,MAAM,mBAAmB,qBAAqB,IAAI;AAC1E,QAAM,EAAE,GAAG,EAAE,IAAI,KAAK;AACtB,QAAM,EAAE,WAAW,eAAe,YAAY,cAAc,IAAI;AAEhE,MAAI,IAAI,YAAY,IAAI,aAAc,OAAO,MAAM,KAAK,OAAO,MAAM,GAAI;AACxE;AAAA,EACD;AAIA,QAAM,cAAc,IAAI;AAAA,IACvB,KAAK,QAAQ,IAAI;AAAA,IACjB,KAAK,QAAQ,IAAI;AAAA,KAChB,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAK;AAAA,KACvC,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAK;AAAA,EACzC;AAEA,QAAM,cAAc,YAAY;AAChC,QAAM,UAAU,YAAY,MAAM;AAGlC,QAAM,YAAY,WAAW,cAAc,WAAW,iBAAiB,WAAW;AAClF,QAAM,aAAa,WAAW,eAAe,WAAW,kBAAkB,WAAW;AACrF,QAAM,WAAW,WAAW,cAAc,WAAW,eAAe,WAAW;AAC/E,QAAM,cAAc,WAAW,iBAAiB,WAAW,kBAAkB,WAAW;AAExF,MAAI,sBAAsB;AAGzB,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,YAAY;AAGvB,QAAI,QAAQ,YAAY,IAAI;AAC5B,QAAI,QAAQ,YAAY,IAAI;AAC5B,QAAI,UAAW,SAAQ,MAAM,YAAY,IAAI,OAAO;AAAA,aAC3C,WAAY,SAAQ,YAAY,OAAO,OAAO,IAAI;AAC3D,QAAI,SAAU,SAAQ,MAAM,YAAY,IAAI,OAAO;AAAA,aAC1C,YAAa,SAAQ,YAAY,OAAO,OAAO,IAAI;AAE5D,QAAI,mBAAmB;AAGtB,YAAM,YAAY,aAAa,gBAAgB,YAAY;AAC3D,UAAI;AACJ,UAAI,UAAU;AACb,uBAAe,QAAQ,QAAQ,cAAc,QAAQ,QAAQ;AAAA,MAC9D,WAAW,aAAa,YAAY;AACnC,uBAAe;AAAA,MAChB,OAAO;AACN,uBAAe,QAAQ;AAAA,MACxB;AAEA,YAAM,WAAW,KAAK,IAAI,WAAW,GAAI,YAAY,IAAK,WAAW;AACrE,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI,IAAI,KAAK,cAAc,IAAI,MAAM,WAAW;AAC9E,UAAI,WAAW,SAAU;AAEzB,kBAAQ,qBAAM,cAAc,UAAU,QAAQ;AAC9C,cAAQ,QAAQ;AAAA,IACjB,OAAO;AAEN,UAAI,aAAa,YAAY;AAC5B,cAAM,WAAW,KAAK,IAAI,IAAI,IAAI,EAAE;AACpC,YAAI,WAAW,WAAW,EAAG;AAC7B,oBAAQ,qBAAM,OAAO,WAAW,GAAG,QAAQ;AAAA,MAC5C;AACA,UAAI,YAAY,aAAa;AAC5B,cAAM,WAAW,KAAK,IAAI,IAAI,IAAI,EAAE;AACpC,YAAI,WAAW,YAAY,EAAG;AAC9B,oBAAQ,qBAAM,OAAO,YAAY,GAAG,QAAQ;AAAA,MAC7C;AAAA,IACD;AAEA,YAAQ,IAAI,KAAK;AACjB,YAAQ,IAAI,KAAK;AACjB,YAAQ,IAAI,QAAQ;AACpB,YAAQ,IAAI,QAAQ;AAAA,EACrB,OAAO;AAGN,QAAI,WAAW;AACd,cAAQ,QAAI,qBAAM,QAAQ,IAAI,OAAO,GAAG,GAAG,YAAY,OAAO,QAAQ;AACtE,cAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,IACxC,WAAW,YAAY;AACtB,YAAM,gBAAY,qBAAM,QAAQ,OAAO,OAAO,GAAG,YAAY,IAAI,UAAU,CAAC;AAC5E,cAAQ,IAAI,YAAY,QAAQ;AAAA,IACjC;AAEA,QAAI,UAAU;AACb,cAAQ,QAAI,qBAAM,QAAQ,IAAI,OAAO,GAAG,GAAG,YAAY,OAAO,SAAS;AACvE,cAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,IACxC,WAAW,aAAa;AACvB,YAAM,iBAAa,qBAAM,QAAQ,OAAO,OAAO,GAAG,YAAY,IAAI,WAAW,CAAC;AAC9E,cAAQ,IAAI,aAAa,QAAQ;AAAA,IAClC;AAAA,EACD;AAIA,MAAI,qBAAqB,CAAC,sBAAsB;AAC/C,UAAM,cAAc,QAAQ,cAAc;AAE1C,QAAI,aAAa;AAChB,cAAQ,IAAI,QAAQ,IAAI;AAAA,IACzB,OAAO;AACN,cAAQ,IAAI,QAAQ,IAAI;AAAA,IACzB;AAEA,YAAQ,QAAQ;AAAA,MACf,KAAK,YAAY;AAEhB,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AAEvC,YAAI,QAAQ,KAAK,GAAG;AACnB,kBAAQ,IAAI;AACZ,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,QACxC;AAEA,YAAI,QAAQ,KAAK,GAAG;AACnB,kBAAQ,IAAI;AACZ,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,QACxC;AACA;AAAA,MACD;AAAA,MACA,KAAK,aAAa;AAEjB,gBAAQ,IAAI,YAAY;AACxB,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AAEvC,YAAI,QAAQ,QAAQ,GAAG;AACtB,kBAAQ,IAAI,IAAI,YAAY;AAC5B,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,QACxC;AAEA,YAAI,QAAQ,KAAK,GAAG;AACnB,kBAAQ,IAAI;AACZ,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,kBAAQ,IAAI,QAAQ,IAAI;AAAA,QACzB;AACA;AAAA,MACD;AAAA,MACA,KAAK,eAAe;AAEnB,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,gBAAQ,IAAI,YAAY;AAExB,YAAI,QAAQ,KAAK,GAAG;AACnB,kBAAQ,IAAI;AACZ,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,kBAAQ,IAAI,QAAQ,IAAI;AAAA,QACzB;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,kBAAQ,IAAI,IAAI,YAAY;AAC5B,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,YAAY,OAAO,QAAQ;AAAA,QACxC;AACA;AAAA,MACD;AAAA,MACA,KAAK,gBAAgB;AAEpB,gBAAQ,IAAI,YAAY;AACxB,gBAAQ,IAAI,YAAY;AAExB,YAAI,QAAQ,QAAQ,GAAG;AACtB,kBAAQ,IAAI,IAAI,YAAY;AAC5B,kBAAQ,IAAI,QAAQ,IAAI;AAAA,QACzB;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,kBAAQ,IAAI,IAAI,YAAY;AAC5B,kBAAQ,IAAI,QAAQ,IAAI;AAAA,QACzB;AACA;AAAA,MACD;AAAA,MACA,KAAK,OAAO;AAEX,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,gBAAQ,IAAI,QAAQ,IAAI;AACxB,gBAAQ,MAAM,QAAQ,IAAI,YAAY,KAAK;AAE3C,YAAI,QAAQ,KAAK,GAAG;AACnB,gBAAM,WAAW,YAAY;AAC7B,kBAAQ,IAAI,WAAW;AACvB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI;AAAA,QACb;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,gBAAM,YAAY,IAAI,YAAY;AAClC,kBAAQ,IAAI,YAAY;AACxB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,IAAI,QAAQ;AAAA,QACzB;AAEA,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC;AAAA,MACD;AAAA,MACA,KAAK,SAAS;AAEb,gBAAQ,IAAI,QAAQ,OAAO,YAAY;AACvC,gBAAQ,IAAI,QAAQ,IAAI;AACxB,gBAAQ,MAAM,QAAQ,IAAI,YAAY,KAAK;AAE3C,YAAI,QAAQ,KAAK,GAAG;AACnB,gBAAM,UAAU,YAAY;AAC5B,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI;AAAA,QACb;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,gBAAM,aAAa,IAAI,YAAY;AACnC,kBAAQ,IAAI,aAAa;AACzB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,IAAI,QAAQ;AAAA,QACzB;AACA;AAAA,MACD;AAAA,MACA,KAAK,UAAU;AAEd,gBAAQ,IAAI,QAAQ,OAAO,YAAY;AACvC,gBAAQ,IAAI,QAAQ,IAAI;AACxB,gBAAQ,MAAM,QAAQ,IAAI,YAAY,KAAK;AAE3C,YAAI,QAAQ,KAAK,GAAG;AACnB,gBAAM,WAAW,YAAY;AAC7B,kBAAQ,IAAI,WAAW;AACvB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI;AAAA,QACb;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,gBAAM,YAAY,IAAI,YAAY;AAClC,kBAAQ,IAAI,YAAY;AACxB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,IAAI,QAAQ;AAAA,QACzB;AACA;AAAA,MACD;AAAA,MACA,KAAK,QAAQ;AAEZ,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC,gBAAQ,IAAI,QAAQ,IAAI;AACxB,gBAAQ,MAAM,QAAQ,IAAI,YAAY,KAAK;AAE3C,YAAI,QAAQ,KAAK,GAAG;AACnB,gBAAM,UAAU,YAAY;AAC5B,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI;AAAA,QACb;AAEA,YAAI,QAAQ,QAAQ,GAAG;AACtB,gBAAM,aAAa,IAAI,YAAY;AACnC,kBAAQ,IAAI,aAAa;AACzB,kBAAQ,IAAI,QAAQ,IAAI;AACxB,kBAAQ,IAAI,IAAI,QAAQ;AAAA,QACzB;AAEA,gBAAQ,IAAI,YAAY,OAAO,QAAQ;AACvC;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAGA,QAAM,UAAuB;AAAA,IAC5B,SAAS,EAAE,GAAG,QAAQ,IAAI,GAAG,GAAG,QAAQ,IAAI,EAAE;AAAA,IAC9C,aAAa,EAAE,GAAG,QAAQ,OAAO,GAAG,GAAG,QAAQ,OAAO,EAAE;AAAA,EACzD;AACA,MAAI,KAAK,YAAY,MAAM;AAC1B,YAAQ,WAAW,KAAK;AAAA,EACzB;AAGA,MACC,QAAQ,QAAQ,MAAM,KAAK,QAAQ,KACnC,QAAQ,QAAQ,MAAM,KAAK,QAAQ,KACnC,QAAQ,YAAY,MAAM,KAAK,YAAY,KAC3C,QAAQ,YAAY,MAAM,KAAK,YAAY,GAC1C;AACD;AAAA,EACD;AAGA,QAAM,WAAW,IAAI,kBAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG,QAAQ,IAAI,KAAK,QAAQ,IAAI,CAAC,EACrF,IAAI,MAAM,QAAQ,EAClB,IAAI,KAAK;AAEX,SAAO;AAAA,IACN,IAAI,MAAM;AAAA,IACV,MAAM,MAAM;AAAA,IACZ,GAAG,SAAS;AAAA,IACZ,GAAG,SAAS;AAAA,IACZ,OAAO;AAAA,MACN,GAAG,MAAM;AAAA,MACT,GAAG,QAAQ;AAAA,MACX,GAAG,QAAQ;AAAA,MACX,MAAM;AAAA,IACP;AAAA,EACD;AACD;AAmBA,SAAS,yBAAyB,YAA0B,MAAc,MAAc;AACvF,QAAM,QAAQ,IAAI,mBAAK,WAAW,MAAM,IAAI,QAAQ,IAAI,WAAW,MAAM,IAAI,QAAQ,CAAC,EAAE;AAAA,IACvF,WAAW;AAAA,EACZ;AACA,SAAO,EAAE,GAAG,WAAW,IAAI,MAAM,GAAG,GAAG,WAAW,IAAI,MAAM,EAAE;AAC/D;AAEA,SAAS,oBACR,YACA,cACA,eACA,sBAA+B,MAC/B,WAAoB,OACP;AACb,QAAM,EAAE,GAAG,EAAE,IAAI,iBAAiB,WAAW,OAAO,WAAW,MAAM,QAAQ,eAAe,CAAC;AAC7F,QAAM,cAAc,WAAW,MAAM,QAAQ,eAAe;AAE5D,MAAI,aAAa;AACjB,MAAI,qBAAqB;AACxB,UAAM,EAAE,GAAG,EAAE,IAAI,cAAc,WAAW;AAC1C,kBAAc;AACd,kBAAc;AAAA,EACf,OAAO;AACN,kBAAc;AACd,kBAAc;AAAA,EACf;AAGA,QAAM,UAAU;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAGA,QAAM,WAAW,eAAe;AAChC,QAAM,WAAW,gBAAgB;AAEjC,SAAO;AAAA,IACN,MAAM;AAAA,IACN,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG,yBAAyB,YAAY,UAAU,QAAQ;AAAA,EAC3D;AACD;AAGO,MAAM,WAAW;AAKjB,SAAS,+BACf,MACA,YACA,SACa;AACb,QAAM,UAAU,WAAW,MAAM,QAAQ,eAAe;AACxD,QAAM,EAAE,OAAO,UAAU,QAAQ,UAAU,IAAI,kBAAkB,OAAO;AACxE,QAAM,cAAc,WAAW;AAG/B,QAAM,iBAAiB,UAAU,KAAK,IAAI,WAAW;AACrD,QAAM,YAAY,IAAI,QAAQ,iBAAiB;AAC/C,MAAI,UAAU;AAEd,MAAI,cAAc,GAAG;AACpB,eAAW,KAAK,IAAI,GAAG,IAAI,SAAS;AACpC,gBAAY,WAAW;AAAA,EACxB,OAAO;AACN,gBAAY,KAAK,IAAI,GAAG,IAAI,SAAS;AACrC,eAAW,YAAY;AAAA,EACxB;AAGA,QAAM,SAAS,oBAAoB,YAAY,UAAU,WAAW,MAAM,QAAQ,QAAQ;AAG1F,QAAM,cAAc,KAAK,IAAI,UAAU,WAAW,QAAQ;AAC1D,SAAO,KAAK;AACZ,SAAO,KAAK;AAGZ,QAAM,EAAE,GAAG,EAAE,IAAI,yBAAyB,YAAY,OAAO,GAAG,OAAO,CAAC;AACxE,SAAO,IAAI;AACX,SAAO,IAAI;AAEX,SAAO;AACR;AAKO,SAAS,oCACf,YACA,eACA,gBACa;AACb,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,WAAW,MAAM,QAAQ;AAC7C,QAAM,eAAe,WAAW,MAAM;AACtC,QAAM,eAAe,WAAW,MAAM;AACtC,QAAM,sBAAsB,gBAAgB;AAE5C,MAAI,OAAO;AACX,MAAI,cAAc;AAClB,MAAI,cAAc;AAGlB,QAAM,iBACL,CAAC,CAAC,WAAW,MAAM,YACnB,uBAAQ,WAAW,MAAM,KAAK,SAAS,YAAY,OAAO,SAC1D,uBAAQ,WAAW,MAAM,KAAK,aAAa,YAAY,WAAW,KAClE,CAAC,WAAW,MAAM,KAAK;AAExB,MAAI,gBAAgB;AACnB,kBAAc;AACd,kBAAe,eAAe,iBAAkB;AAAA,EACjD,OAAO;AACN,UAAM,EAAE,GAAG,YAAY,GAAG,WAAW,IAAI;AAAA,MACxC,WAAW;AAAA,MACX,WAAW,MAAM,QAAQ,eAAe;AAAA;AAAA,IACzC;AACA,UAAM,EAAE,OAAO,OAAO,QAAQ,MAAM,IAAI,kBAAkB,WAAW;AACrE,UAAM,cAAc,QAAQ;AAC5B,UAAM,sBAAsB,aAAa;AACzC,QAAI;AACJ,QAAI;AAEJ,UAAM,oBAAoB,cAAc,WAAW;AAGnD,uBAAmB;AACnB,UAAM,kBAAkB,sBAAsB,sBAAsB;AACpE,wBAAoB,mBAAmB;AAGvC,UAAM,qBAAqB,YAAY,WAAW;AAClD,QAAI,kBAAkB,oBAAoB;AACzC,YAAM,eAAe,IAAI;AACzB,UAAI,IAAI,oBAAoB,IAAI,kBAAkB;AACjD,cAAM,QAAQ,oBAAoB;AAClC,4BAAoB,oBAAoB;AACxC,2BAAmB,mBAAmB;AAAA,MACvC,OAAO;AACN,cAAM,QAAQ,mBAAmB;AACjC,2BAAmB,mBAAmB;AACtC,4BAAoB,oBAAoB;AAAA,MACzC;AAAA,IACD;AAGA,uBAAmB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,gBAAgB,CAAC;AAC5D,wBAAoB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,iBAAiB,CAAC;AAG9D,WAAO;AAAA,MACN,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACb;AAAA,EACD;AAEA,SAAO;AAAA,IACN;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG,yBAAyB,YAAY,aAAa,WAAW;AAAA,EACjE;AACD;AAKO,SAAS,kCACf,mBACA,YACa;AAEb,MAAI,sBAAsB,YAAY;AACrC,UAAM,EAAE,GAAG,EAAE,IAAI,iBAAiB,WAAW,OAAO,WAAW,MAAM,QAAQ,eAAe,CAAC;AAE7F,WAAO;AAAA,MACN,MAAM,eAAe;AAAA,MACrB;AAAA,MACA;AAAA,MACA,GAAG,yBAAyB,YAAY,GAAG,CAAC;AAAA,IAC7C;AAAA,EACD;AAGA,QAAM,cAAc,sBAAsB,iBAAiB;AAC3D,QAAM,WAAW,sBAAsB;AAEvC,QAAM,EAAE,GAAG,YAAY,GAAG,WAAW,IAAI;AAAA,IACxC,WAAW;AAAA,IACX,WAAW,MAAM,QAAQ,eAAe;AAAA;AAAA,EACzC;AAEA,QAAM,mBAAmB,aAAa;AAGtC,QAAM,cAAc,WAAW,MAAM,QAAQ,eAAe;AAC5D,QAAM,EAAE,OAAO,OAAO,QAAQ,MAAM,IAAI,kBAAkB,WAAW;AACrE,QAAM,oBAAoB,cAAc,WAAW;AAGnD,QAAM,kBAAkB,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK;AAIrD,MAAI;AACJ,MAAI;AAEJ,MAAI,qBAAqB,KAAK,CAAC,OAAO,SAAS,gBAAgB,KAAK,gBAAgB,GAAG;AAEtF,uBAAmB;AACnB,wBAAoB;AAAA,EACrB,OAAO;AAEN,UAAM,uBAAuB,QAAQ;AACrC,UAAM,wBAAwB,QAAQ;AAGtC,UAAM,0BAA0B,KAAK,IAAI,sBAAsB,qBAAqB;AACpF,UAAM,iBAAiB,wBAAwB;AAG/C,QAAI;AACJ,QAAI;AAEJ,QAAI,gBAAgB;AAEnB,yBAAmB;AACnB,0BAAoB,mBAAmB;AAAA,IACxC,OAAO;AAEN,0BAAoB;AACpB,yBAAmB,oBAAoB;AAAA,IACxC;AAGA,uBAAmB,mBAAmB;AACtC,wBAAoB,oBAAoB;AAGxC,QAAI,mBAAmB,GAAG;AAEzB,yBAAmB;AACnB,0BAAoB,mBAAmB;AAAA,IACxC;AACA,QAAI,oBAAoB,GAAG;AAE1B,0BAAoB;AACpB,yBAAmB,cAAc;AAAA,IAClC;AAGA,uBAAmB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,gBAAgB,CAAC;AAC5D,wBAAoB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,iBAAiB,CAAC;AAAA,EAC/D;AAEA,QAAM,cAAc,KAAK,IAAI,IAAI,kBAAkB,IAAI,iBAAiB;AAExE,sBAAoB,cAAc;AAClC,uBAAqB,cAAc;AAGnC,qBAAmB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,gBAAgB,CAAC;AAC5D,sBAAoB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,iBAAiB,CAAC;AAG9D,QAAM,UAAU;AAAA,IACf,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAGA,QAAM,qBAAqB,QAAQ,YAAY,IAAI,QAAQ,QAAQ;AACnE,QAAM,sBAAsB,QAAQ,YAAY,IAAI,QAAQ,QAAQ;AAGpE,QAAM,QAAQ,qBAAqB;AACnC,QAAM,QAAQ,sBAAsB;AAIpC,MAAI,eAAe;AACnB,MAAI,QAAQ,GAAG;AACd,mBAAe,WAAW,MAAM,KAAK,QAAQ;AAAA,EAC9C,WAAW,QAAQ,GAAG;AAErB,mBAAe,WAAW,MAAM,KAAK,QAAQ;AAAA,EAC9C;AAGA,QAAM,OAAO,QAAQ;AACrB,QAAM,OAAO,QAAQ;AAErB,SAAO;AAAA,IACN,MAAM;AAAA,IACN,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG,yBAAyB,YAAY,MAAM,IAAI;AAAA,EACnD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "5.
|
|
25
|
+
const version = "5.5.0-next.6d9e51e9f3f7";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2026-05-06T16:28:18.473Z",
|
|
28
|
-
minor: "2026-09-02T11:
|
|
29
|
-
patch: "2026-09-02T11:
|
|
28
|
+
minor: "2026-09-02T11:26:03.383Z",
|
|
29
|
+
patch: "2026-09-02T11:26:03.383Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
|
@@ -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 = '5.
|
|
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 = '5.5.0-next.6d9e51e9f3f7'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-09-02T11:26:03.383Z',\n\tpatch: '2026-09-02T11:26:03.383Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -54,7 +54,7 @@ const globlikeRegExp = (input) => {
|
|
|
54
54
|
};
|
|
55
55
|
const checkHostnames = (hostnames, targetHostname) => {
|
|
56
56
|
return !!hostnames.find((hostname) => {
|
|
57
|
-
const re = new RegExp(globlikeRegExp(hostname));
|
|
57
|
+
const re = new RegExp(`^(?:.+\\.)?${globlikeRegExp(hostname)}$`);
|
|
58
58
|
return targetHostname.match(re);
|
|
59
59
|
});
|
|
60
60
|
};
|