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 fixes a failing end to end test that snuck in.
### Change Type
- [x] `tests` — Changes to any test code only[^2]
### 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>
This PR adds support for custom shapes (`shapes`) to the y.js example. A
user should pass the same data to the `useYjsStore` hook as they pass to
the `<Tldraw>` component.
### Change Type
- [x] `internal`
![Kapture 2023-07-04 at 16 36
31](https://github.com/tldraw/tldraw/assets/1242537/bcb19959-ac66-46fa-92ea-50fe4692a96c)
### Change Type
- [x] `minor` — New feature
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
### Test Plan
1. Make some cloud shapes, try different sizes, colors, fills.
2. Export cloud shapes to images.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- Adds a cloud shape.
We'd like to make the @tldraw/editor layer more independent of specific
shapes. Unfortunately there are many places where shape types and
certain shape behavior is deeply embedded in the Editor. This PR begins
to refactor out dependencies between the editor library and shape utils.
It does this in two ways:
- removing shape utils from the arguments of `isShapeOfType`, replacing
with a generic
- removing shape utils from the arguments of `getShapeUtil`, replacing
with a generic
- moving custom arrow info cache out of the util and into the editor
class
- changing the a tool's `shapeType` to be a string instead of a shape
util
We're here trading type safety based on inferred types—"hey editor, give
me your instance of this shape util class"—for knowledge at the point of
call—"hey editor, give me a shape util class of this type; and trust me
it'll be an instance this shape util class". Likewise for shapes.
### A note on style
We haven't really established our conventions or style when it comes to
types, but I'm increasingly of the opinion that we should defer to the
point of call to narrow a type based on generics (keeping the types in
typescript land) rather than using arguments, which blur into JavaScript
land.
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
### Release Notes
- removes shape utils from the arguments of `isShapeOfType`, replacing
with a generic
- removes shape utils from the arguments of `getShapeUtil`, replacing
with a generic
- moves custom arrow info cache out of the util and into the editor
class
- changes the a tool's `shapeType` to be a string instead of a shape
util
This PR removes the `tool` parameter from the `defineShape` function.
It's an opinionated change that I think we should at the very least
consider.
## What's the context?
Currently, you can add **tools** (aka state nodes) to your state chart
in two different ways:
1. Passing them to the `<Tldraw>` component with the `tools` attribute.
2. As part of a shape definition's `tool` property, which you then pass
to the `<Tldraw>` component with the `shapes` attribute.
This is what (1) looks like:
```jsx
import { MyTool } from "./MyTool"
function Example() {
return <Tldraw tools={[MyTool]} />
}
```
This is what (2) looks like:
```jsx
import { MyTool } from "./MyTool"
import { MyShapeUtil, myShapeProps } from "./MyShape"
const MyShapeDefinition = defineShape("my-shape", {
util: MyShapeUtil,
props: myShapeProps,
tool: MyTool,
})
function Example() {
return <Tldraw shapes={[MyShapeDefinition]} />
}
```
Clearly, (1) is better for when you want to add *just a tool*, that
doesn't have an associated shape.
And (2) is better for when you want to add *both* a tool and an
associated shape.
## Why change it?
I think we should remove method (2). Because I think that it adds a few
complications.
#### Does it help?
I don't think that it helps to streamline the process of coupling shapes
and tools. You still need to remember to add your tool.
Seeing as `tool` is optional on the shape definition (rightly so), it
doesn't prompt you to do it.
#### What's easier to explain?
I think it's easier to just have to explain _one method_. It would take
longer to explain two methods, and it complicates the concepts involved.
Seeing as there's not a big benefit to one method over the other, the
added explanation wouldn't be a good trade-off.
#### What happens if I use both?
It's unclear to the user what would happen if they use both methods. Do
we know what the intended behaviour of this would be? I think this will
happen often.
```jsx
import { MyTool } from "./MyTool"
import { MyShapeUtil, myShapeProps } from "./MyShape"
const MyShapeDefinition = defineShape("my-shape", {
util: MyShapeUtil,
props: myShapeProps,
tool: MyTool,
})
function Example() {
return <Tldraw tools={[MyTool]} shapes={[MyShapeDefinition]} />
}
```
#### Does it fit my shape/tool?
Many shapes are coupled closely with one tool. But some shapes would
involve multiple tools. And some tools would involve multiple shapes.
For example, you might first add a tool and a shape that go nicely
together, so you use method (2). But two months later, you decide that
you want another tool to be able to make this shape too. Now you've
inserted your related tools in two different places, unless you
refactor.
Alternatively, you might want to add some more functionality to your
tool, so that it can make multiple types of shapes. Instead of
refactoring the existing shape, you want to create an entirely new
shape, to keep your new code separate. Should you add the `tool`
property to the new shape as well? What would happen if you did/didn't?
What happens if you later disable the original shape? Would you need to
move the `tool` property from there to the newer shape?
It would be a lot simpler to just have the tool in your list of tools,
instead of having them tangled up with shapes.
#### Plugins?
We've been considering moving towards some sort of 'plugins' system in
the future, that could collect together shapes, tools, and other stuff.
I think that a more complete concept of a 'plugin' would be the best
place to collect together shapes, and tools — not on the shape itself.
### 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. Try using all of the app's tools, making sure they still work.
- [ ] Unit Tests
- [ ] End to end tests
### Release Notes
- [dev] Removed the `tool` property from `defineShape`
This PR fixes the shortcut for lock shapes, which may be blocked by
browser defaults. We now use Shift+L instead of Cmd+Shift+L. Closes
https://github.com/tldraw/tldraw/issues/1676.
### Change Type
- [x] `patch`
### Test Plan
1. Try the lock tool shortcuts.
- [x] End to end tests
### Release Notes
- [@tldraw/editor] Fix lock tool shortcut
This PR adds a `meta` property to shapes and other records.
It adds it to:
- asset
- camera
- document
- instance
- instancePageState
- instancePresence
- page
- pointer
- rootShape
## Setting meta
This data can generally be added wherever you would normally update the
corresponding record.
An exception exists for shapes, which can be updated using a partial of
the `meta` in the same way that we update shapes with a partial of
`props`.
```ts
this.updateShapes([{
id: myShape.id,
type: "geo",
meta: {
nemesis: "steve",
special: true
}
])
```
## `Editor.getInitialMetaForShape`
The `Editor.getInitialMetaForShape` method is kind of a hack to set the
initial meta property for newly created shapes. You can set it
externally. Escape hatch!
### Change Type
- [x] `minor` — New feature
### Test Plan
todo
- [ ] Unit Tests (todo)
### Release Notes
- todo
This PR fixes an import in the custom shapes example. It also tweaks the
example to show how buttons and other interactive content should work.
### Change Type
- [x] `documentation`
Adds some basic API docs for the new styles API.
### Change Type
- [x] `documentation` — Changes to the documentation only[^2]
### Test Plan
--
### Release Notes
--
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
tldraw-zero themed follow-ups to the styles API added in #1580.
- Removed style related helpers from `ShapeUtil`
- `editor.css` no longer includes the tldraw default color palette.
Instead, a global `DefaultColorPalette` is defined as part of the color
style. If developers wish to cusomise the colors, they can mutate that
global.
- `ShapeUtil.toSvg` no longer takes font/color. Instead, it takes an
"svg export context" that can be used to add `<defs>` to the exported
SVG element. Converting e.g. fonts to inlined data urls is now the
responsibility of the shapes that use them rather than the Editor.
- `usePattern` is not longer a core part of the editor. Instead,
`ShapeUtil` has a `getCanvasSvgDefs` method for returning react
components representing anything a shape needs included in `<defs>` for
the canvas.
- The shape-specific cleanup logic in `setStyle` has been deleted. It
turned out that none of that logic has been running anyway, and instead
the relevant logic lives in shape `onBeforeChange` callbacks already.
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
--
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This diff:
- tweaks how playwright runs in CI to have it go a bit faster
- uploads nice browsable reports to S3 for looking at playwright
failures and traces
- adds visual regression testing to playwright
### Change Type
- [x] `tests` — Changes to any test code only[^2]
### Test Plan
- [ ] Unit Tests
- [x] End to end tests
### Release Notes
--
---------
Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
This PR fixes a few components that were updating too often. It changes
the format of our error boundaries in order to avoid re-rendering them
as changed props.
### Change Type
- [x] `major` — Breaking change
It tried to get out but we're dragging it back in.
This PR brings [signia](https://github.com/tldraw/signia) back into
tldraw as @tldraw/state.
### Change Type
- [x] major
---------
Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
This PR improves the ergonomics of `ShapeUtil` classes.
### Cached methods
First, I've remove the cached methods (such as `bounds`) from the
`ShapeUtil` class and lifted this to the `Editor` class.
Previously, calling `ShapeUtil.getBounds` would return the un-cached
bounds of a shape, while calling `ShapeUtil.bounds` would return the
cached bounds of a shape. We also had `Editor.getBounds`, which would
call `ShapeUtil.bounds`. It was confusing. The cached methods like
`outline` were also marked with "please don't override", which suggested
the architecture was just wrong.
The only weirdness from this is that utils sometimes reach out to the
editor for cached versions of data rather than calling their own cached
methods. It's still an easier story to tell than what we had before.
### More defaults
We now have three and only three `abstract` methods for a `ShapeUtil`:
- `getDefaultProps` (renamed from `defaultProps`)
- `getBounds`,
- `component`
- `indicator`
Previously, we also had `getCenter` as an abstract method, though this
was usually just the middle of the bounds anyway.
### Editing bounds
This PR removes the concept of editingBounds. The viewport will no
longer animate to editing shapes.
### Active area manager
This PR also removes the active area manager, which was not being used
in the way we expected it to be.
### Dpr manager
This PR removes the dpr manager and uses a hook instead to update it
from React. This is one less runtime browser dependency in the app, one
less thing to document.
### Moving things around
This PR also continues to try to organize related methods and properties
in the editor.
### Change Type
- [x] `major` — Breaking change
### Release Notes
- [editor] renames `defaultProps` to `getDefaultProps`
- [editor] removes `outline`, `outlineSegments`, `handles`, `bounds`
- [editor] renames `renderBackground` to `backgroundComponent`
This reverts commit b6716a3750.
Not sure why, but this introduced a `/// <references />` comment in the
tldraw/tldraw api-report.md file that doesn't show up when you build
from brivate. Reverting for now.
### Change Type
- [x] `dependencies` — Changes to package dependencies[^1]
### Test Plan
-
### Release Notes
-
This PR introduces `@tldraw/tldraw/tldraw.css`, an exported CSS file
that replaces the `editor.css` and `ui.css` that were previously copied
from the editor and ui packages. Instead, these files are combined into
the `tldraw.css` file, simplifying the import story when using
`@tldraw/tldraw`.
### Change Type
- [x] `major` — Breaking change
### Release Notes
- [tldraw] Removes `editor.css` and `ui.css` exports, replaces with
`tldraw.css`
This PR:
- updates the yjs example to include user presence
- tweaks the `createPresenceStateDerivation` API
- fix a "double update" bug caused by re-syncing local changes
- fix connection bugs
### Change Type
- [x] `minor` — New feature
Removes `propsForNextShape` and replaces it with the new styles API.
Changes in here:
- New custom style example
- `setProp` is now `setStyle` and takes a `StyleProp` instead of a
string
- `Editor.props` and `Editor.opacity` are now `Editor.sharedStyles` and
`Editor.sharedOpacity`
- They return an object that flags mixed vs shared types instead of
using null to signal mixed types
- `Editor.styles` returns a `SharedStyleMap` - keyed on `StyleProp`
instead of `string`
- `StateNode.shapeType` is now the shape util rather than just a string.
This lets us pull the styles from the shape type directly.
- `color` is no longer a core part of the editor set on the shape
parent. Individual child shapes have to use color directly.
- `propsForNextShape` is now `stylesForNextShape`
- `InstanceRecordType` is created at runtime in the same way
`ShapeRecordType` is. This is so it can pull style validators out of
shape defs for `stylesForNextShape`
- Shape type are now defined by their props rather than having separate
validators & type defs
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Big time regression testing around styles!
2. Check UI works as intended for all shape/style/tool combos
- [x] Unit Tests
- [ ] End to end tests
### Release Notes
-
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR adds support for seeing **another user**'s chat messages.
It's part 1 of two PRs relating to Cursor Chat.
And it's needed for the much bigger part 2:
https://github.com/tldraw/brivate/pull/1981
# Presence
You can see another person's chat messages!
![2023-06-02 at 17 42 33 - Blush
Capybara](https://github.com/tldraw/tldraw/assets/15892272/8f3efb5f-9c05-459c-aa7e-24842be75e58)
If they have a name, it gets popped on top.
![2023-06-02 at 17 45 34 - Sapphire
Meerkat](https://github.com/tldraw/tldraw/assets/15892272/749bd924-c1f5-419b-a028-1fafe1b61292)
That's it!
With this PR, there's no way of actually *typing* your chat messages.
That comes with the [next
one](https://github.com/tldraw/brivate/pull/1981)!
# Admin
### To-do
- [x] Store chat message
- [x] Allow overflowing chat
- [x] Presence for chat message
- [x] Display chat message to others
### Change Type
- [x] `minor` — New Feature
### Test Plan
To test this, I recommend checking out both `lu/cursor-chat` branches,
and opening two browser sessions in the same shared project.
1. In one session, type some cursor chat by pressing the Enter key while
on the canvas (and typing).
2. On the other session, check that you can see the chat message appear.
3. Repeat this while being both named, and unnamed.
I recommend just focusing on the visible presense in this PR.
The [other PR](https://github.com/tldraw/brivate/pull/1981) is where we
can focus about how we _input_ the cursor chat.
### Release Notes
- [dev] Added support for cursor chat presence.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR updates exports related to user presence, in order to enable
external sync solutions that use user presence.
### Change Type
- [x] `patch` — Bug fix
[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
This PR does a first-pass of adding tsdocs to the methods of the Editor
class.
It's a minimal start — just descriptions of them, and their parameters.
It makes the Editor docs page a lot more fleshed out though, and easier
to quickly scan.
There's still a lot more to do!
### Change Type
- [x] `documentation` — Changes to the documentation only[^2]
### Release Notes
- [dev] Added initial documentation for the Editor class.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR adds a generic that we can use with `updateShapes` and
`createShapes` in order to type the partials being passed into those
methods. By default, the partials are typed as `TLUnknownShape`, which
accepts any props.
### Change Type
- [x] `minor` — New feature
### Test Plan
- [x] Unit Tests
### Release Notes
- [editor] adds an optional shape generic to `updateShapes` and
`createShapes`
Describe what your pull request does. If appropriate, add GIFs or images
showing the before and after.
### Change Type
This pr updates yjs example to remove awareness when a peer disconnects
<!-- 💡 Indicate the type of change your pull request is. -->
<!-- 🤷♀️ If you're not sure, don't select anything -->
<!-- ✂️ Feel free to delete unselected options -->
<!-- To select one, put an x in the box: [x] -->
- [ ] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [ ] `major` — Breaking Change
- [ ] `dependencies` — Dependency Update (publishes a `patch` release,
for devDependencies use `internal`)
- [x] `documentation` — Changes to the documentation only (will not
publish a new version)
- [ ] `tests` — Changes to any testing-related code only (will not
publish a new version)
- [ ] `internal` — Any other changes that don't affect the published
package (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
- [ ] Webdriver tests
### Release Notes
- Add a brief release note for your PR here.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This diff adds a new property to `defineShape`: `tool`.
The tool prop allows shapes to bring a tool along with them as part of
their definition. E.g. the draw shape isn't much use without the draw
tool, so adding the draw shape to your app gives you the draw tool tool.
As part of this, i renamed the `shapeutils` folder to just `shapes`, and
moved a bunch of shape-specific tools from the tools folder into the
shapes folder. This more closely reflects how things will be once we
move our default shapes out of core for tldraw-zero.
### Change Type
- [x] `patch` — Bug fix
### Test Plan
Tested locally
### Release Notes
n/a
Based on #1549, but with a lot of code-structure related changes backed
out. Shape schemas are still defined in tlschemas with this diff.
Couple differences between this and #1549:
- This tightens up the relationship between store schemas and editor
schemas a bit
- Reduces the number of places we need to remember to include core
shapes
- Only `<TLdrawEditor />` sets default shapes by default. If you're
doing something funky with lower-level APIs, you need to specify
`defaultShapes` manually
- Replaces `validator` with `props` for shapes
### Change Type
- [x] `major` — Breaking Change
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [x] Unit Tests
- [ ] Webdriver tests
### Release Notes
[dev-facing, notes to come]
This PR adds a yjs example to the examples app.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package (will not publish a new version)
### Release Notes
- [editor] Adds yjs example project
This change hoists opacity out of props and changes it to a number
instead of an enum.
The change to a number is to make tldraw more flexible for library
consumers who might want more expressivity with opacity than our 5
possible values allow. the tldraw editor will now happily respect any
opacity between 0 and 1. The limit to our supported values is enforced
only in the UI. I think this is limited enough that it's a reasonable
tradeoff between in-app simplicity and giving external developers the
flexibility they need.
There's a new `opacityForNextShape` property on the instance. This works
exactly the same way as propsForNextShape does, except... it's just for
opacity. With this, there should be no user-facing changes to how
opacity works in tldraw. There are also new `opacity`/`setOpacity` APIs
in the editor that work with it/selections similar to how props do.
@ds300 do you mind reviewing the migrations here?
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
- [ ] Webdriver tests
### Release Notes
[internal only for now]
This PR
- Removes UserDocumentRecordType
- moving isSnapMode to user preferences
- moving isGridMode and isPenMode to InstanceRecordType
- deleting the other properties which are no longer needed.
- Creates a separate pipeline for persisting instance state.
Previously the instance state records were stored alongside the document
state records, and in order to load the state for a particular instance
(in our case, a particular tab) you needed to pass the 'instanceId'
prop. This prop ended up totally pervading the public API and people ran
into all kinds of issues with it, e.g. using the same instance id in
multiple editor instances.
There was also an issue whereby it was hard for us to clean up old
instance state so the idb table ended up bloating over time.
This PR makes it so that rather than passing an instanceId, you load the
instance state yourself while creating the store. It provides tools to
make that easy.
- Undoes the assumption that we might have more than one instance's
state in the store.
- Like `document`, `instance` now has a singleton id
`instance:instance`.
- Page state ids and camera ids are no longer random, but rather derive
from the page they belong to. This is like having a foreign primary key
in SQL databases. It's something i'd love to support fully as part of
the RecordType/Store api.
Tests to do
- [x] Test Migrations
- [x] Test Store.listen filtering
- [x] Make type sets in Store public and readonly
- [x] Test RecordType.createId
- [x] Test Instance state snapshot loading/exporting
- [x] Manual test File I/O
- [x] Manual test Vscode extension with multiple tabs
- [x] Audit usages of store.query
- [x] Audit usages of changed types: InstanceRecordType, 'instance',
InstancePageStateRecordType, 'instance_page_state', 'user_document',
'camera', CameraRecordType, InstancePresenceRecordType,
'instance_presence'
- [x] Test user preferences
- [x] Manual test isSnapMode and isGridMode and isPenMode
- [ ] Test indexedDb functions
- [x] Add instanceId stuff back
### Change Type
- [x] `major` — Breaking Change
### Test Plan
1. Add a step-by-step description of how to test your PR here.
2.
- [ ] Unit Tests
- [ ] Webdriver tests
### Release Notes
- Add a brief release note for your PR here.
This PR renames all exported types to include the `TL` prefix. It also
removes the `TL` prefix from things that are not types, including:
- shape utils (e.g. `TLArrowUtil` becomes `ArrowShapeUtil`)
- tools (e.g. `TLArrowTool` becomes `ArrowShapeTool`, `TLSelectTool`
becomes `SelectTool`)
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- Renaming of types, shape utils, tools
This PR cleans up the file names and imports for @tldraw/tlschema.
It also:
- renames some erroneously named validators / migrators (e.g.
`pageTypeValidator` -> `pageValidator`)
- removes the duplicated `languages.ts` and makes `tlschema` the source
of truth for languages
- renames ID to RecordId
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- [editor] Remove `app.createShapeId`
- [tlschema] Cleans up exports
This PR cleans up exports from TldrawUi, unifying types under `TLUi` and
removing many items from exports / marking others as internal.
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- [editor] clean up / unify types
This PR renames `App`, `app` and all appy names to `Editor`, `editor`,
and editorry names.
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- Rename `App` to `Editor` and many other things that reference `app` to
`editor`.
This PR adds some things that we need for the Project Name feature on
tldraw.com.
It should be reviewed alongside
https://github.com/tldraw/tldraw-lite/pull/1814
## Name Property
This PR adds a `name` property to `TLDocument`. We use this to store a
project's name.
<img width="454" alt="Screenshot 2023-05-09 at 15 47 26"
src="https://github.com/tldraw/tldraw/assets/15892272/f3be438e-aa0f-4dec-8f51-8dfd9f9d0ced">
## Top Zone
This PR adds a `topZone` area of the UI that we can add stuff to,
similar to how `shareZone` works.
It also adds an example to show where the `topZone` and `shareZone` are:
<img width="1511" alt="Screenshot 2023-05-12 at 10 57 40"
src="https://github.com/tldraw/tldraw/assets/15892272/f5e1cd33-017e-4aaf-bfee-4d85119e2974">
## Breakpoints
This PR change's the UI's breakpoints a little bit.
It moves the action bar to the bottom a little bit earlier.
(This gives us more space at the top for the project name).
![2023-05-12 at 11 08 26 - Fuchsia
Bison](https://github.com/tldraw/tldraw/assets/15892272/34563cea-b1d1-47be-ac5e-5650ee0ba02d)
![2023-05-12 at 13 45 04 - Tan
Mole](https://github.com/tldraw/tldraw/assets/15892272/ab190bd3-51d4-4a8b-88de-c72ab14bcba6)
## Input Blur
This PR adds an `onBlur` parameter to `Input`.
This was needed because 'clicking off' the input wasn't firing
`onComplete` or `onCancel`.
<img width="620" alt="Screenshot 2023-05-09 at 16 12 58"
src="https://github.com/tldraw/tldraw/assets/15892272/3b28da74-0a74-4063-8053-e59e47027caf">
## Create Project Name
This PR adds an internal `createProjectName` property to
`TldrawEditorConfig`.
Similar to `derivePresenceState`, you can pass a custom function to it.
It lets you control what gets used as the default project name. We use
it to set different names in our local projects compared to shared
projects.
In the future, when we add more advanced project features, we could
handle this better within the UI.
<img width="454" alt="Screenshot 2023-05-09 at 15 47 26"
src="https://github.com/tldraw/tldraw/assets/15892272/da9a4699-ac32-40d9-a97c-6c682acfac41">
### Test Plan
1. Gradually reduce the width of the browser window.
2. Check that the actions menu jumps to the bottom before the style
panel moves to the bottom.
---
1. In the examples app, open the `/zones` example.
2. Check that there's a 'top zone' at the top.
- [ ] Unit Tests
- [ ] Webdriver tests
### Release Note
- [dev] Added a `topZone` area where you can put stuff.
- [dev] Added a `name` property to `TLDocument` - and `app` methods for
it.
- [dev] Added an internal `createProjectName` config property for
controlling the default project name.
- [dev] Added an `onBlur` parameter to `Input`.
- Moved the actions bar to the bottom on medium-sized screens.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR replaces our `console.log` with `nicelog` so that I can more
easily grep for errant console.logs.
### Change Type
- [x] `internal` — Any other changes that don't affect the published
package (will not publish a new version)
This PR updates our user-facing APIs for the Tldraw and TldrawEditor
components, as well as the Editor (App). It mainly incorporates surface
changes from #1450 without any changes to validators or migrators,
incorporating feedback / discussion with @SomeHats and @ds300.
Here we:
- remove the TldrawEditorConfig
- bring back a loose version of shape definitions
- make a separation between "core" shapes and "default" shapes
- do not allow custom shapes, migrators or validators to overwrite core
shapes
- but _do_ allow new shapes
## `<Tldraw>` component
In this PR, the `Tldraw` component wraps both the `TldrawEditor`
component and our `TldrawUi` component. It accepts a union of props for
both components. Previously, this component also added local syncing via
a `useLocalSyncClient` hook call, however that has been pushed down to
the `TldrawEditor` component.
## `<TldrawEditor>` component
The `TldrawEditor` component now more neatly wraps up the different ways
that the editor can be configured.
## The store prop (`TldrawEditorProps.store`)
There are three main ways for the `TldrawEditor` component to be run:
1. with an externally defined store
2. with an externally defined syncing store (local or remote)
3. with an internally defined store
4. with an internally defined locally syncing store
The `store` prop allows for these configurations.
If the `store` prop is defined, it may be defined either as a `TLStore`
or as a `SyncedStore`. If the store is a `TLStore`, then the Editor will
assume that the store is ready to go; if it is defined as a SyncedStore,
then the component will display the loading / error screens as needed,
or the final editor once the store's status is "synced".
When the store is left undefined, then the `TldrawEditor` will create
its own internal store using the optional `instanceId`, `initialData`,
or `shapes` props to define the store / store schema.
If the `persistenceKey` prop is left undefined, then the store will not
be synced. If the `persistenceKey` is defined, then the store will be
synced locally. In the future, we may also here accept the API key /
roomId / etc for creating a remotely synced store.
The `SyncedStore` type has been expanded to also include types used for
remote syncing, e.g. with `ConnectionStatus`.
## Tools
By default, the App has two "baked-in" tools: the select tool and the
zoom tool. These cannot (for now) be replaced or removed. The default
tools are used by default, but may be replaced by other tools if
provided.
## Shapes
By default, the App has a set of "core" shapes:
- group
- embed
- bookmark
- image
- video
- text
That cannot by overwritten because they're created by the app at
different moments, such as when double clicking on the canvas or via a
copy and paste event. In follow up PRs, we'll split these out so that
users can replace parts of the code where these shapes are created.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
- [x] Unit Tests
This PR adds `getSnapshot` and `loadSnapshot` to the `Store`, sanding
down a rough corner that existed when persisting and loading data.
Avoids learning about stores vs schemas vs migrations until a little
later.
### Change Type
- [x] `minor` — New Feature
### Test Plan
- [x] Unit Tests
### Release Notes
- [tlstore] adds `getSnapshot` and `loadSnapshot`
This PR replaces our webdriver end to end tests with playwright tests.
It:
- replaces our webdriver workflow with a new e2e workflow based on
playwright
- removes the webdriver project
- adds e2e tests to our examples app
- replaces all `data-wd` attributes with `data-testid`
### Coverage
Most of the tests from our previous e2e tests are reproduced here,
though there are some related to our gestures that will need to be done
in a different way—or not at all. I've also added a handful of new
tests, too.
### Where are they
The tests are now part of our examples app rather than being in its own
different app. This should help us test our different examples too. As
far as I can tell there are no downsides here in terms of the regular
developer experience, though they might complicate any CodeSandbox
projects that are hooked into the examples app.
### Change Type
- [x] `tests` — Changes to any testing-related code only (will not
publish a new version)
This diff tweaks our `debugFlags` framework to support setting different
default value for different environments, makes it easier to define
feature flags, and makes feature flags show up in the debug menu by
default. With this change, feature flags will default to being enabled
in dev and preview environments, but disabled in production.
Specify a feature flag like this:
```ts
const featureFlags = {
myCoolNewFeature: createFeatureFlag('myCoolNewFeature')
}
```
optionally, pass a second value to control its defaults:
```ts
const featureFlags = {
featureEnabledInProduction: createFeatureFlag('someFeature', { all: true }),
customEnabled: createFeatureFlag('otherFeature', {development: true, staging: false, production: false}),
}
```
In code, the value can be read using `featureFlags.myFeature.value`.
Remember to wrap reading it in a reactive context!
### Change Type
- [x] `patch` — Bug Fix
### Test Plan
-
### Release Notes
[internal only change]
This PR renames our record types to avoid a type collision with the type
that they are based on. For example `TLCamera` is both a type and a
record; after this PR, we use `CameraRecordType` for the camera's record
type.
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- [editor] rename record types
- Remove TLUser, TLUserPresence
- Add first-class support for user preferences that persists across
rooms and tabs
### Change Type
<!-- 💡 Indicate the type of change your pull request is. -->
<!-- 🤷♀️ If you're not sure, don't select anything -->
<!-- ✂️ Feel free to delete unselected options -->
<!-- To select one, put an x in the box: [x] -->
- [ ] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [x] `major` — Breaking Change
- [ ] `dependencies` — Dependency Update (publishes a `patch` release,
for devDependencies use `internal`)
- [ ] `documentation` — Changes to the documentation only (will not
publish a new version)
- [ ] `tests` — Changes to any testing-related code only (will not
publish a new version)
- [ ] `internal` — Any other changes that don't affect the published
package (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
- [ ] Webdriver tests
### Release Notes
- Add a brief release note for your PR here.
This PR restores `createTLSchema`.
It also:
- removes `TldrawEditorConfig.default`
- makes `config` a required property of `<TldrawEditor>`, though it's
created automatically in `<Tldraw>`.
- makes `config` a required property of `App`
- removes `TLShapeType` and replaces the rare usage with
`TLShape["type"]`
- adds `TLDefaultShape` for a union of our default shapes
- makes `TLShape` a union of `TLDefaultShape` and `TLUnknownShape`
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- [editor] Simplifies custom shape definition
- [tldraw] Updates props for <TldrawEditor> component to require a
`TldrawEditorConfig`.
This PR removes `TLShapeDef` and associated helpers / references.
It purposely loosens the configuration and typings to better support
customization.
### Change Type
- [x] `major` — Breaking Change
### Test Plan
1. Use the app!
### Release Notes
- [tlschema] Update props of `createTLSchema`
- [editor] Update props of `TldrawEditorConfig`
- [editor] Remove `App.getShapeUtilByType`
- [editor] Update `App.getShapeUtil` to take a type rather than a shape
---------
Co-authored-by: alex <alex@dytry.ch>
This PR fixes several issues with the way that SVG overlays were
rendered.
- fixes editing embed shape on firefox (weird SVG pointer events bug)
- fixes layering of overlays
- collaborator cursors are offset
### Change Type
- [x] `patch` — change to unshipped changes
### Test Plan
1. Try editing an embed shape on Firefox
2. Confirm that cursor hints are no longer spinning
3. Confirm that cursors are displayed correctly over other shapes
This PR:
- renames `onEvent` to `onUiEvent`
- adds docs for `onUiEvent` to the docs site
- splits the `EventsExample` into `UiEventsExample` and
`StoreEventsExample`
### Change Type
- [x] `major` — Breaking Change
### Release Notes
- [docs] Adds docs for ui events
- [tldraw] Renames `onEvent` to `onUiEvent`
The assets package now only exports esm-formatted .js files. There's one
for each strategy - import-based, and meta.url-based. These are directly
generated as .js and .d.ts files rather than generated as .ts and
converted to js/dts through other means.
As this package depends on esm-specific stuff to function, we don't
publish a cjs version any more.
### Change Type
<!-- 💡 Indicate the type of change your pull request is. -->
<!-- 🤷♀️ If you're not sure, don't select anything -->
<!-- ✂️ Feel free to delete unselected options -->
<!-- To select one, put an x in the box: [x] -->
- [ ] `patch` — Bug Fix
- [ ] `minor` — New Feature
- [x] `major` — Breaking Change
- [ ] `dependencies` — Dependency Update (publishes a `patch` release,
for devDependencies use `internal`)
- [ ] `documentation` — Changes to the documentation only (will not
publish a new version)
- [ ] `tests` — Changes to any testing-related code only (will not
publish a new version)
- [ ] `internal` — Any other changes that don't affect the published
package (will not publish a new version)
### Release Notes
- [dev] If you're using the `@tldraw/assets` package, you need to update
your code to `import { getAssetUrlsByImport } from
'@tldraw/assets/imports'` instead of `import { getBundlerAssetUrls }
from '@tldraw/assets`
This PR has been hijacked! 🗑️🦝🦝🦝
The <Canvas> component was previously split into an <SVGLayer> and an
<HTMLLayer>, mainly due to the complexity around translating SVGs.
However, this was done before we learned that SVGs can have overflow:
visible, so it turns out that we don't really need the SVGLayer at all.
This PR now refactors away SVG Layer.
It also updates the class name prefix in editor from `rs-` to `tl-` and
does a few other small changes.
---------
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
Right now this examples app looks exactly the same as our old examples
app, but there are a couple of tiny differences:
- We use `vite` instead of our own esbuild setup for development and
bundling
- We use `@tldraw/assets` for smart asset hashing instead of copying the
assets to a public folder
You can use `@tldraw/assets` with vite with a bunch of extra config, but
it (plus a bunch of other bundlers) also support a special syntax for
specifying asset urls: `new URL('./my/asset.svg',
import.meta.url).href`. This approach is more standards-complient, but
doesn't work with every bundler just yet. This diff also adds a
url-based version of `@tldraw/assets`, although I'd like to tweak the
entry point - right now you need to import from
`@tldraw/assets/lib/urls`, but i'd like to find a way to get this to
`@tldraw/assets/urls` or something at some point.
There are a couple other extra fixes in here:
- vscode builds were broken, they're fixed now!
- there's also a little tweak to the `getBundlerAssetUrls` API to allow
passing in a function instead of an object for URL formatting
- there are new internal-only functions for injecting asset urls
globally instead of passing them in via react props. this means we can
get the benefits of cacheable URLs without having to clutter our
examples by passing them in
In this PR I'm adding new versions of the `LiveCollaborators` and
`Collaborators` components for the ephemeral state work. They are behind
a feature flag for now.