This code has started to bitrot a bit and this freshens it up a bit.
- there's a double request happening for every bookmark paste at the
moment, yikes! One request originates from the paste logic, and the
other originates from the `onBeforeCreate` in `BookmarkShapeUtil`. They
both see that an asset is missing and race to make the request at the
same time. It _seems_ like we don't need the `onBeforeCreate` anymore.
But, if I'm mistaken on some edge case here lemme know and we can
address this in a different way.
- the extractor is really crusty (the grabity code is from 5 yrs ago and
hasn't been updated) and we don't have control over it. i've worked on
unfurling stuff before with Paper and my other projects and this reworks
things to use Cheerio, which is a more robust library.
- this adds `favicon` to the response request which should usually
default to the apple-touch-icon. this helps with some better bookmark
displays (e.g. like Wikipedia if an image is empty)
In general, this'll start to make this more maintainable and improvable
on our end.
Double request:
<img width="1496" alt="Screenshot 2024-05-31 at 17 54 49"
src="https://github.com/tldraw/tldraw/assets/469604/22033170-caaa-4fd2-854f-f19b61611978">
Before:
<img width="355" alt="Screenshot 2024-05-31 at 17 55 02"
src="https://github.com/tldraw/tldraw/assets/469604/fd272669-ee52-4cc7-bed7-72a8ed8d53a0">
After:
<img width="351" alt="Screenshot 2024-05-31 at 17 55 44"
src="https://github.com/tldraw/tldraw/assets/469604/87d27342-0d49-4cfc-a811-356370562d19">
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [ ] `sdk` — Changes the tldraw SDK
- [x] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [x] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Test pasting links in, and pasting again.
### Release Notes
- Bookmarks: fix up double request and rework extractor code.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Lots of people are having a bad time with loading/restoring snapshots
and there's a few reasons for that:
- It's not clear how to preserve UI state independently of document
state.
- Loading a snapshot wipes the instance state, which means we almost
always need to
- update the viewport page bounds
- refocus the editor
- preserver some other sneaky properties of the `instance` record
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
This PR adds a heart geo shape. ❤️
It also:
- adds `toSvgPathData` to geometry2d
- uses geometry2d in places where previously we recalculated things like
perimeter of ellipse
- flattens geo shape util components
- [x] Calculate the path length for the DashStyleHeart
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `feature` — New feature
### Release Notes
- Adds a heart shape to the geo shape set.
This PR reworks the `canBind` callback to work with customizable
bindings. It now accepts an object with a the shape, the other shape
(optional - it may not exist yet), the direction, and the type of the
binding. Devs can use this to create shapes that only participate in
certain binding types, can have bindings from but not to them, etc.
If you're implementing a binding, you can see if binding two shapes is
allowed using `editor.canBindShapes(fromShape, toShape, 'my binding
type')`
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `improvement` — Improving existing features
### Release Notes
#### Breaking changes
The `canBind` flag now accepts an options object instead of just the
shape in question. If you're relying on its arguments, you need to
change from `canBind(shape) {}` to `canBind({shape}) {}`.
Typescript's type aliases (`type X = thing`) can refer to basically
anything, which makes it hard to write an automatic document formatter
for them. Interfaces on the other hand are only object, so they play
much nicer with docs. Currently, object-flavoured type aliases don't
really get expanded at all on our docs site, which means we have a bunch
of docs content that's not shown on the site.
This diff introduces a lint rule that forces `interface X {foo: bar}`s
instead of `type X = {foo: bar}` where possible, as it results in a much
better documentation experience:
Before:
<img width="437" alt="Screenshot 2024-05-22 at 15 24 13"
src="https://github.com/tldraw/tldraw/assets/1489520/32606fd1-6832-4a1e-aa5f-f0534d160c92">
After:
<img width="431" alt="Screenshot 2024-05-22 at 15 33 01"
src="https://github.com/tldraw/tldraw/assets/1489520/4e0d59ee-c38e-4056-b9fd-6a7f15d28f0f">
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `docs` — Changes to the documentation, examples, or templates.
- [x] `improvement` — Improving existing features
First draft of the new bindings API. We'll follow this up with some API
refinements, tests, documentation, and examples.
Bindings are a new record type for establishing relationships between
two shapes so they can update at the same time.
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `feature` — New feature
### Release Notes
#### Breaking changes
- The `start` and `end` properties on `TLArrowShape` no longer have
`type: point | binding`. Instead, they're always a point, which may be
out of date if a binding exists. To check for & retrieve arrow bindings,
use `getArrowBindings(editor, shape)` instead.
- `getArrowTerminalsInArrowSpace` must be passed a `TLArrowBindings` as
a third argument: `getArrowTerminalsInArrowSpace(editor, shape,
getArrowBindings(editor, shape))`
- The following types have been renamed:
- `ShapeProps` -> `RecordProps`
- `ShapePropsType` -> `RecordPropsType`
- `TLShapePropsMigrations` -> `TLPropsMigrations`
- `SchemaShapeInfo` -> `SchemaPropsInfo`
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
This PR implements a camera options API.
- [x] Initial PR
- [x] Updated unit tests
- [x] Feedback / review
- [x] New unit tests
- [x] Update use-case examples
- [x] Ship?
## Public API
A user can provide camera options to the `Tldraw` component via the
`cameraOptions` prop. The prop is also available on the `TldrawEditor`
component and the constructor parameters of the `Editor` class.
```tsx
export default function CameraOptionsExample() {
return (
<div className="tldraw__editor">
<Tldraw cameraOptions={CAMERA_OPTIONS} />
</div>
)
}
```
At runtime, a user can:
- get the current camera options with `Editor.getCameraOptions`
- update the camera options with `Editor.setCameraOptions`
Setting the camera options automatically applies them to the current
camera.
```ts
editor.setCameraOptions({...editor.getCameraOptions(), isLocked: true })
```
A user can get the "camera fit zoom" via `editor.getCameraFitZoom()`.
# Interface
The camera options themselves can look a few different ways depending on
the `type` provided.
```tsx
export type TLCameraOptions = {
/** Whether the camera is locked. */
isLocked: boolean
/** The speed of a scroll wheel / trackpad pan. Default is 1. */
panSpeed: number
/** The speed of a scroll wheel / trackpad zoom. Default is 1. */
zoomSpeed: number
/** The steps that a user can zoom between with zoom in / zoom out. The first and last value will determine the min and max zoom. */
zoomSteps: number[]
/** Controls whether the wheel pans or zooms.
*
* - `zoom`: The wheel will zoom in and out.
* - `pan`: The wheel will pan the camera.
* - `none`: The wheel will do nothing.
*/
wheelBehavior: 'zoom' | 'pan' | 'none'
/** The camera constraints. */
constraints?: {
/** The bounds (in page space) of the constrained space */
bounds: BoxModel
/** The padding inside of the viewport (in screen space) */
padding: VecLike
/** The origin for placement. Used to position the bounds within the viewport when an axis is fixed or contained and zoom is below the axis fit. */
origin: VecLike
/** The camera's initial zoom, used also when the camera is reset.
*
* - `default`: Sets the initial zoom to 100%.
* - `fit-x`: The x axis will completely fill the viewport bounds.
* - `fit-y`: The y axis will completely fill the viewport bounds.
* - `fit-min`: The smaller axis will completely fill the viewport bounds.
* - `fit-max`: The larger axis will completely fill the viewport bounds.
* - `fit-x-100`: The x axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-y-100`: The y axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-min-100`: The smaller axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-max-100`: The larger axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
*/
initialZoom:
| 'fit-min'
| 'fit-max'
| 'fit-x'
| 'fit-y'
| 'fit-min-100'
| 'fit-max-100'
| 'fit-x-100'
| 'fit-y-100'
| 'default'
/** The camera's base for its zoom steps.
*
* - `default`: Sets the initial zoom to 100%.
* - `fit-x`: The x axis will completely fill the viewport bounds.
* - `fit-y`: The y axis will completely fill the viewport bounds.
* - `fit-min`: The smaller axis will completely fill the viewport bounds.
* - `fit-max`: The larger axis will completely fill the viewport bounds.
* - `fit-x-100`: The x axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-y-100`: The y axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-min-100`: The smaller axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
* - `fit-max-100`: The larger axis will completely fill the viewport bounds, or 100% zoom, whichever is smaller.
*/
baseZoom:
| 'fit-min'
| 'fit-max'
| 'fit-x'
| 'fit-y'
| 'fit-min-100'
| 'fit-max-100'
| 'fit-x-100'
| 'fit-y-100'
| 'default'
/** The behavior for the constraints for both axes or each axis individually.
*
* - `free`: The bounds are ignored when moving the camera.
* - 'fixed': The bounds will be positioned within the viewport based on the origin
* - `contain`: The 'fixed' behavior will be used when the zoom is below the zoom level at which the bounds would fill the viewport; and when above this zoom, the bounds will use the 'inside' behavior.
* - `inside`: The bounds will stay completely within the viewport.
* - `outside`: The bounds will stay touching the viewport.
*/
behavior:
| 'free'
| 'fixed'
| 'inside'
| 'outside'
| 'contain'
| {
x: 'free' | 'fixed' | 'inside' | 'outside' | 'contain'
y: 'free' | 'fixed' | 'inside' | 'outside' | 'contain'
}
}
}
```
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `feature` — New feature
### Test Plan
These features combine in different ways, so we'll want to write some
more tests to find surprises.
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
### Release Notes
- SDK: Adds camera options.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
I added a new embed type, for desmos graphing calculator
(https://www.desmos.com/calculator) that uses their supported embed URL.
I added an icon, the new embed shape, and created tests for it.
https://github.com/tldraw/tldraw/assets/111339712/acc1a6b0-2551-4f25-8f85-20e6f829930e
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [ ] `sdk` — Changes the tldraw SDK
- [x] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [x] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. Add links for desmos graphing calculator (e.g.
https://www.desmos.com/calculator/4wa2im6u31) by either pasting or using
the insert embed menu.
### Release Notes
- (feature) add desmos embed
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Previously, we weren't exporting migrations & validators for our default
shapes. This meant that it wasn't possible to make your own tlschema
with both our default shapes and some of your own (e.g. for custom
multiplayer). This fixes that by exposing all the migrations,
validators, and versions from tlschema.
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `bugfix` — Bug fix
We're missing the export for `createShapePropsMigrationIds`, so lets add
it. This also fixes some other bits that were used in examples but not
exported properly from tldraw.
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `bugfix` — Bug fix
### Release Notes
- Expose `createShapePropsMigrationIds`, `defaultEditorAssetUrls`,
`PORTRAIT_BREAKPOINT`, `useDefaultColorTheme`, & `getPerfectDashProps`
Describe what your pull request does. If appropriate, add GIFs or images
showing the before and after.
### Change Type
- [x] `sdk` — Changes the tldraw SDK
- [x] `galaxy brain` — Architectural changes
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
#### BREAKING CHANGES
- The `Migrations` type is now called `LegacyMigrations`.
- The serialized schema format (e.g. returned by
`StoreSchema.serialize()` and `Store.getSnapshot()`) has changed. You
don't need to do anything about it unless you were reading data directly
from the schema for some reason. In which case it'd be best to avoid
that in the future! We have no plans to change the schema format again
(this time was traumatic enough) but you never know.
- `compareRecordVersions` and the `RecordVersion` type have both
disappeared. There is no replacement. These were public by mistake
anyway, so hopefully nobody had been using it.
- `compareSchemas` is a bit less useful now. Our migrations system has
become a little fuzzy to allow for simpler UX when adding/removing
custom extensions and 3rd party dependencies, and as a result we can no
longer compare serialized schemas in any rigorous manner. You can rely
on this function to return `0` if the schemas are the same. Otherwise it
will return `-1` if the schema on the right _seems_ to be newer than the
schema on the left, but it cannot guarantee that in situations where
migration sequences have been removed over time (e.g. if you remove one
of the builtin tldraw shapes).
Generally speaking, the best way to check schema compatibility now is to
call `store.schema.getMigrationsSince(persistedSchema)`. This will throw
an error if there is no upgrade path from the `persistedSchema` to the
current version.
- `defineMigrations` has been deprecated and will be removed in a future
release. For upgrade instructions see
https://tldraw.dev/docs/persistence#Updating-legacy-shape-migrations-defineMigrations
- `migrate` has been removed. Nobody should have been using this but if
you were you'll need to find an alternative. For migrating tldraw data,
you should stick to using `schema.migrateStoreSnapshot` and, if you are
building a nuanced sync engine that supports some amount of backwards
compatibility, also feel free to use `schema.migratePersistedRecord`.
- the `Migration` type has changed. If you need the old one for some
reason it has been renamed to `LegacyMigration`. It will be removed in a
future release.
- the `Migrations` type has been renamed to `LegacyMigrations` and will
be removed in a future release.
- the `SerializedSchema` type has been augmented. If you need the old
version specifically you can use `SerializedSchemaV1`
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Comparing different culling optimizations:
https://github.com/tldraw/tldraw/assets/2523721/0b3b8b42-ed70-45b7-bf83-41023c36a563
I think we should go with the `display: none` + showing the skeleteon.
The way it works is:
- We now add a sibling to the shape wrapper div which serves as the
skeleton for the culled shapes.
- Only one of the two divs (shape wrapper and skeleton div) is
displayed. The other one is using `display: none` to improve
performance.
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [ ] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [x] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
- Improve performance of culled shapes by using `display: none`.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
I'm bringing the sockets example up to date and ran into some issues
that were tricky to resolve in userland but trivial to resolve in
packageland.
Gonna collect them here.
### Change Type
- [x] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
This PR fixes a check on whether the dot com multiplayer editor has been
loaded in an iframe.
It tries to keep it working on tldraw.com itself.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Load me in an iframe
In #2856, we moved changed line handles into an array of points. This
introduced an issue where some concurrent operations wouldn't work
because they array indexes change. We need some sort of stable way of
referring to these points. Our existing fractional indexing system is a
good fit.
In this version, instead of making the points be a map from index to
x/y, we make the points be a map from id (the index) to
x/y/index/id(also index). This is "kinda silly" (steve's words) but
might be more familiar to devs who are expecting maps to be keyed on IDs
rather than anything else.
### Change Type
- [x] `major` — Breaking change
This PR adds a validation mode whereby previous known-to-be-valid values
can be used to speed up the validation process itself. At the same time
it enables us to do fine-grained equality checking on records much more
quickly than by using something like lodash isEqual, and using that we
can prevent triggering effects for record updates that don't actually
alter any values in the store.
Here's some preliminary perf testing of average time spent in
`store.put()` during some common interactions
| task | before (ms) | after (ms) |
| ---- | ---- | ---- |
| drawing lines | 0.0403 | 0.0214 |
| drawing boxes | 0.0408 | 0.0348 |
| translating lines | 0.0352 | 0.0042 |
| translating boxes | 0.0051 | 0.0032 |
| rotating lines | 0.0312 | 0.0065 |
| rotating boxes | 0.0053 | 0.0035 |
| brush selecting boxes | 0.0200 | 0.0232 |
| traversal with shapes | 0.0130 | 0.0108 |
| traversal without shapes | 0.0201 | 0.0173 |
**traversal** means moving the camera and pointer around the canvas
#### Discussion
At the scale of hundredths of a millisecond these .put operations are so
fast that even if they became literally instantaneous the change would
not be human perceptible. That said, there is an overall marked
improvement here. Especially for dealing with draw shapes.
These figures are also mostly in line with expectations, aside from a
couple of things:
- I don't understand why the `brush selecting boxes` task got slower
after the change.
- I don't understand why the `traversal` tasks are slower than the
`translating boxes` task, both before and after. I would expect that
.putting shape records would be much slower than .putting pointer/camera
records (since the latter have fewer and simpler properties)
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
This PR replaces the line shape's `handles` prop with `points`, an array
of `VecModel`s.
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
- [ ] End to end tests
Currently, only the end handles of the line tool snap. It should be all
of them.
Line handles work kind of weirdly at the moment: instead of just storing
the positions, we store full `TLHandle` objects complete with IDs,
`canSnap`/`canBind` properties, etc. Currently, all the handles get
written to the store with `canSnap: false`, when really it should be up
to the shape util to decide which handles are snappable.
This diff replaces the current handles map (from arbitrary ID to
`TLHandle`) with just the data we need: a map from index to x/y. The
extra information that the `Editor` needs for `TLHandle` is hydrated at
runtime (with `canSnap` set to `true` this time!)
Fixes TLD-2200
This PR is part of a series - please don't merge it until the things
before it have landed!
1. #2827
2. #2831 (you are here)
3. #2793
4. #2841
5. #2845
### Change Type
- [x] `major` — Breaking change
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Create a funky line shape on tldraw.com
2. Paste it into staging and make sure it comes across ok
3. Make some funky line shape in staging - make sure you use dragging,
mid-point creation, and shift-clicking
- [x] Unit Tests
### Release Notes
- Simplify the contents of `TLLineShape.props.handles`
Currently, we type our fractional index keys as `string` and don't have
any validation for them. I'm touching some of this code for my work on
line handles and wanted to change that:
- fractional indexes are now `IndexKey`s, not `string`s. `IndexKey`s
have a brand property so can't be used interchangeably with strings
(like our IDs)
- There's a new `T.indexKey` validator which we can use in our
validations to make sure we don't end up with nonsense keys.
This PR is part of a series - please don't merge it until the things
before it have landed!
1. #2827 (you are here)
2. #2831
3. #2793
4. #2841
5. #2845
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Mostly relying on unit & end to end tests here - no user facing
changes.
- [x] Unit Tests
Adds Slovenian localization.
### Change Type
- [ ] `patch` — Bug fix
- [x] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
This is a followup on the arrows work.
- allow labels to go to the ends if no arrowhead is present
- avoid using / overloading TLHandle and use a new PointingLabel state
to specifically address label movement
- removes the feature flag to launch this feature!
### Change Type
- [x] `patch` — Bug fix
### Release Notes
- Arrow labels: provide more polish on label placement
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Improves duplication. It will now remember the offset and shapes of your
`alt + drag` duplication and use that if you use the duplicate action
immediately after this.
![duplicate](https://github.com/tldraw/tldraw/assets/2523721/6fbfd455-d179-4eae-8aba-399b0781f633)
Fixes https://github.com/tldraw/tldraw/issues/2264
### Change Type
- [ ] `patch` — Bug fix
- [x] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
---------
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This adds the ability to drag the label on an arrow to a different
location within the line segment/arc.
https://github.com/tldraw/tldraw/assets/469604/dbd2ee35-bebc-48d6-b8ee-fcf12ce91fa5
- A lot of the complexity lay in ensuring a fixed distance from the ends
of the arrowheads.
- I added a new type of handle `text-adjust` that makes the text box the
very handle itself.
- I added a `ARROW_HANDLES` enum - we should use more enums!
- The bulk of the changes are in ArrowShapeUtil — check that out in
particular obviously :)
Along the way, I tried to improve a couple spots as I touched them:
- added some more documentation to Vec.ts because some of the functions
in there were obscure/new to me. (at least the naming, hah)
- added `getPointOnCircle` which was being done in a couple places
independently and refactored those places.
### Questions
- the `getPointOnCircle` API changed. Is this considered breaking and/or
should I leave the signature the same? Wasn't sure if it was a big deal
or not.
- I made `labelPosition` in the schema always but I guess it could have
been optional? Lemme know if there's a preference.
- Any feedback on tests? Happy to expand those if necessary.
### Change Type
- [ ] `patch` — Bug fix
- [x] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. For arrow in [straightArrow, curvedArrow] test the following:
a. Label in the middle
b. Label at both ends of the arrow
c. Test arrows in different directions
d. Rotating the endpoints and seeing that the label stays at the end of
the arrow at a fixed width.
e. Test different stroke widths.
f. Test with different arrowheads.
2. Also, test arcs that are more circle like than arc-like.
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
- Adds ability to change label position on arrows.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Co-authored-by: alex <alex@dytry.ch>
This is a followup to PR #2347 which was addressing #2349.
This makes sure that vertical resizing is disabled still for the text
shapes because they get in the way of rotation.
Fixes#2455
### Change Type
- [x] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Add a brief release note for your PR here.
Describe what your pull request does. If appropriate, add GIFs or images
showing the before and after.
### Change Type
- [x] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Release Notes
- Changed the label for the Simplified Chinese language from `Chinese -
Simplified` to `简体中文`, following the convention of other languages.
- Updated the API and relevant documentation through build scripts.
This PR:
- shrinks the distance for edge scrolling and insets the distance for
coarse pointers
- adds edge inset tracking
## Scroll distances
Rather than increasing the distance, we move the "zero" in from the
edges, so that the middle of a honkin' fat finger would be at "zero"
when the edge of the finger is touching the edge of the screen. This is
a bit more reliable than looking at just the component size.
## Inset tracking
We now track whether a shape's edges are identical to the edges of the
document body. When an edge is inset, we extend the edge scrolling
distance outside of the component, so that dragging PAST the edge of the
component will scroll. When an edge is NOT inset, we bring that distance
into the component's bounds, so that dragging NEAR TO the edge will
begin to scroll.
![image](https://github.com/tldraw/tldraw/assets/23072548/bb216c98-3dd0-4e2e-a635-4c4f339d5117)
![image](https://github.com/tldraw/tldraw/assets/23072548/75e83c81-1ca9-40a9-8edc-72851d3b1411)
![image](https://github.com/tldraw/tldraw/assets/23072548/6cda7bda-2935-4ded-821c-e7bf78833a1c)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Use edge scrolling on mobile
2. Use edge scrolling on desktop
3. Use edge scrolling in the "scrolling example"
- [x] Unit Tests
### Release Notes
- Add `instanceState.insets` to track which edges of the component are
inset from the edges of the document body.
- Improve behavior around edge scrolling
This pull request was initiated by Lokalise (user Lu) at 2023-12-19
10:48:13
## Release Notes
Added Czech translations.
Updated translations for German, Korean, Russian, Ukrainian, Traditional
Chinese.
This PR makes several improvements to the behavior of arrows as they
relate to precision and container relationships.
- an arrow's terminals are always "true" and are never snapped to { x:
.5, y: .5 } as they were previously when not precise
- instead, a new `isPrecise` boolean is added to the arrow terminal
- when an arrow terminal renders "imprecisely" it will be placed to the
center of the bound shape
- when an arrow terminal renders "precisely" it will be placed at the
normalized location within the bound shape
![Kapture 2023-11-29 at 23 12
12](https://github.com/tldraw/tldraw/assets/23072548/e94e1594-75fa-4c94-86f3-7d911bf25f7f)
The logic now is...
- if the user has indicated precision by "pausing" while drawing the
arrow, it will be precise
- otherwise...
- if both of an arrow's terminals are bound to the same shape, both will
be precise
- if a terminal is bound to a shape that contains the shape that its
opposite terminal is bound to, it will be precise
- if a terminal is bound to a shape that contains the shape that its
opposite terminal is bound to, it will be precise
- or else it will be imprecise
If the spatial relationships change, the precision may change as well.
Fixes https://github.com/tldraw/tldraw/issues/2204
Note: a previous version of this PR was based around ancestry but that's
not actually important.
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Draw an arrow between a frame and its descendant
2. Draw an arrow inside of a shape to another shape contained within the
bounds of the big shape
3. Vis versa
4. Vis versa
- [x] Unit Tests
### Release Notes
- Improves the logic about when to draw "precise" arrows between the
center of bound shapes.
This PR adds support for multiple scribbles at the same time. It
prevents the sudden disappearance of existing scribbles when new ones
are added. It simplifies the management of scribbles by moving the
scribble manager to the editor.
![Kapture 2023-10-29 at 10 17
48](https://github.com/tldraw/tldraw/assets/23072548/23089047-6247-4714-bb79-c4972370140f)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Use the eraser, scribble select, and laser pointer tools
- [x] Unit Tests
### Release Notes
- [feature] multi scribbles
Before the geometry change, we'd rely on the browser to tell us which
element was hovered, which meant that when the pointer left the canvas
we'd automatically clear the hovered shape.
Currently, we don't know whether the pointer is over the canvas or not -
so we keep showing the hover indicator for the last shape you had your
pointer over.
This diff adds an `isHoveringCanvas` prop to the instance state (true,
false, or null if the current pointer doesn't support hovering) that we
can use to track this and disable the hover indicator appropriately.
![Kapture 2023-10-05 at 12 00
00](https://github.com/tldraw/tldraw/assets/1489520/236b9459-878b-47e2-bcaa-10d245581347)
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Create some shapes that go below the UI
2. Move the mouse from the shape to the UI
3. Hover indicator should disappear
This PR:
- adds `canSnap` as a property to handle and ignores snapping when
dragging a handle that does not have `canSnap` set to true. Arrows no
longer snap.
- adds `isLabel` to Geometry2d
- fixes selection on empty text labels
- fixes vertices / snapping for empty text labels
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
Add `Store.migrateSnapshot`, another surface API alongside getSnapshot
and loadSnapshot.
### Change Type
- [x] `minor` — New feature
### Release Notes
- [editor] add `Store.migrateSnapshot`
This PR:
- fixes switching between an embed and a link (it's moved to the edit
menu for now, so a bit buried).
- fixes the tldraw embed
### Change Type
- [x] `patch` — Bug fix
### Test Plan
1. Try to embed a tldraw url into a room.
### Release Notes
- [fix] tldraw embeds
Reverts tldraw/tldraw#1778.
Fuzz testing picked up errors related to deleting pages and undo/redo
which may doom this PR.
### Change Type
- [x] `major` — Breaking change
This PR shrinks the commands API surface and adds a manager
(`CleanupManager`) for side effects.
### Change Type
- [x] `major` — Breaking change
### Test Plan
Use the app! Especially undo and redo. Our tests are passing but I've
found more cases where our coverage fails to catch issues.
### Release Notes
- tbd
This PR adds val town to tldraw's collection of embeddable things.
### Change Type
- [x] `minor` — New feature
### Test Plan
1. Add links to val town, e.g.
https://www.val.town/v/steveruizok.mathFact
### Release Notes
- (feature) val town
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
This PR fixes a bug where dark mode would not immediately cause shapes
to update their colors. Previously, we got the current theme during
render but not in a way that hooked into the change. In this update, we
hook into the change. We also pass the change down to shape fills as
props rather than getting the theme from deeper down.
### Change Type
- [x] `patch`
### Test Plan
1. Use dark mode.
2. Switch colors
### Release Notes
- [fix] dark mode colors not updating
This PR is another grab bag:
- renames `readOnly` to `readonly` throughout editor
- fixes a regression related to focus and keyboard shortcuts
- adds a small outline for focused editors
### Change Type
- [x] `major`
### Test Plan
- [x] End to end tests
This PR removes several extraneous computed values from the editor. It
adds some silly instance state onto the instance state record and
unifies a few methods which were inconsistent. This is fit and finish
work 🧽
## Computed Values
In general, where once we had a getter and setter for `isBlahMode`,
which really masked either an `_isBlahMode` atom on the editor or
`instanceState.isBlahMode`, these are merged into `instanceState`; they
can be accessed / updated via `editor.instanceState` /
`editor.updateInstanceState`.
## tldraw select tool specific things
This PR also removes some tldraw specific state checks and creates new
component overrides to allow us to include them in tldraw/tldraw.
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [tldraw] rename `useReadonly` to `useReadOnly`
- [editor] remove `Editor.isDarkMode`
- [editor] remove `Editor.isChangingStyle`
- [editor] remove `Editor.isCoarsePointer`
- [editor] remove `Editor.isDarkMode`
- [editor] remove `Editor.isFocused`
- [editor] remove `Editor.isGridMode`
- [editor] remove `Editor.isPenMode`
- [editor] remove `Editor.isReadOnly`
- [editor] remove `Editor.isSnapMode`
- [editor] remove `Editor.isToolLocked`
- [editor] remove `Editor.locale`
- [editor] rename `Editor.pageState` to `Editor.currentPageState`
- [editor] add `Editor.pageStates`
- [editor] add `Editor.setErasingIds`
- [editor] add `Editor.setEditingId`
- [editor] add several new component overrides
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>