Skip to content

Commit 17b19dc

Browse files
steveruizokCopilotclaude
authored
feat(editor): introduce canvas OverlayUtil system (tldraw#8633)
In order to replace tldraw's growing tangle of per-feature canvas React components (selection foreground, shape handles, brush rectangles, snap indicators, scribbles, collaborator cursors, arrow hints, and shape indicators) with a single declarative API that renders to a shared canvas layer, this PR introduces the **OverlayUtil** system. OverlayUtils subclass a common base class that declares when an overlay is active, produces overlay instances from editor state, optionally contributes hit-test geometry + cursor behavior, and draws into a 2D context. https://github.com/user-attachments/assets/c7174d07-b3a8-44f1-b504-f35a38faade4 Built as a clean, narrative-quality reimplementation of tldraw#8626 (same end state, commits reordered and regrouped so a reviewer can read the story end-to-end). The original branch is [`ui-widgets-clean`](https://github.com/tldraw/tldraw/tree/ui-widgets-clean). ### Concepts | Term | Type | Meaning | | ---- | ---- | ------- | | `OverlayUtil` | abstract class | Subclass to describe a kind of canvas overlay (predicate, overlay generator, hit test, cursor, render) | | `TLOverlay` | interface | A single live overlay instance — `{ id, type, props }`, where `id` is globally unique across utils | | `OverlayManager` | class | Lives on `editor.overlays`; registers utils, resolves active overlays, hit-tests, tracks `hoveredOverlayId` | | `TLAnyOverlayUtilConstructor` | type | What you pass in `overlayUtils` on `<Tldraw>` / the Editor | | `defaultOverlayUtils` | const | The tldraw package's built-in set (shape indicator, selection foreground, handles, brush, zoom brush, snaps, scribble, collaborator cursor / brush / hint / scribble, arrow hints / binding hints) | ### Editor API | Method / property | Description | | ----------------- | ----------- | | `editor.overlays` | The `OverlayManager` instance | | `editor.overlays.getActiveOverlayEntries()` | All currently-active overlays, grouped by util, for render / hit test | | `editor.overlays.getOverlayAtPoint(point)` | Hit-test in draw order with vertex-handle priority | | `editor.overlays.hoveredOverlayId` | Atom tracking which overlay the pointer is currently over | | `editor.getVisibleCollaborators()` | Replaces the old `useActivePeerIds$` hook with a reactive, editor-owned version | | `editor.getVisibleCollaboratorsOnCurrentPage()` | Same, scoped to current page; used by the minimap and collab overlays | | `TLPointerEventTarget` | Gains an `{ target: 'overlay', overlay }` variant so tools can dispatch on overlay hits | | `TLTheme.colors` | New keys: `snap`, `selectionStroke`, `selectionFill`, `brushFill`, `brushStroke`, `selectedContrast`, `laser` | ### Example Register a custom overlay by subclassing `OverlayUtil` and passing it in `overlayUtils`: ```ts class PointerRingOverlayUtil extends OverlayUtil<{ type: 'pointer_ring'; id: string; props: {} }> { static override type = 'pointer_ring' override isActive() { return true } override getOverlays() { return [{ id: 'pointer_ring:main', type: 'pointer_ring', props: {} }] } override render(ctx: CanvasRenderingContext2D) { const { x, y } = this.editor.inputs.currentScreenPoint ctx.strokeStyle = this.editor.getTheme().selectionStroke ctx.beginPath(); ctx.arc(x, y, 20, 0, Math.PI * 2); ctx.stroke() } } <Tldraw overlayUtils={[PointerRingOverlayUtil]} /> ``` Replace a built-in by matching its `type`: ```ts class DashedBrushOverlayUtil extends BrushOverlayUtil { override render(ctx: CanvasRenderingContext2D, overlay: TLBrushOverlay) { ctx.setLineDash([4, 4]) super.render(ctx, overlay) } } <Tldraw overlayUtils={[DashedBrushOverlayUtil]} /> ``` ### New examples - `editor-api/custom-overlay` — minimal new OverlayUtil (pointer ring). - `editor-api/hovered-overlay` — reading `editor.overlays.hoveredOverlayId` from React. - `editor-api/replace-brush-overlay` — swapping a built-in util by `type`. - `ui/overlay-theme-colors` — the new TLTheme overlay color keys in action. ### Change type - [x] `api` ### Test plan 1. Run `yarn dev` and confirm selection handles, brush, scribbles, snap lines, collaborator cursors, arrow hints, and shape indicators render and respond identically to main. 2. Enter crop mode — crop handles should work via the overlay path. 3. Drag a line vertex and a nearby virtual handle — vertex wins. 4. Open a multiplayer room; remote cursors, selections, and scribbles should fade in/out via the shared visibility clock. 5. Toggle dark mode — all overlay colors update via TLTheme. - [x] Unit tests - [x] End to end tests ### Release notes - Introduce the `OverlayUtil` system for canvas UI (selection foreground, handles, brush, snaps, scribbles, collaborator overlays, shape indicators, arrow hints, arrow binding hints). Pass `overlayUtils` on `<Tldraw>` / `TldrawEditor` to customize. - Breaking! The following legacy `EditorComponents` slots are removed: `Brush`, `ZoomBrush`, `Scribble`, `SnapIndicator`, `Handle`, `Handles`, `SelectionForeground`, `ShapeIndicator`, `ShapeIndicators`, `CollaboratorBrush`, `CollaboratorCursor`, `CollaboratorHint`, `CollaboratorScribble`, `CollaboratorShapeIndicator`, `Cursor`, `Overlays`, `ShapeIndicatorErrorFallback`. Migrate by subclassing the equivalent `OverlayUtil`. - Breaking! The `options.useCanvasIndicators` flag is removed — shape indicators are always canvas. - Breaking! `ShapeUtil.useLegacyIndicator()` is removed. - Add `Editor.getVisibleCollaborators()` / `getVisibleCollaboratorsOnCurrentPage()` with a shared visibility clock; `usePeerIds` now reads from them. Replaces `useActivePeerIds$`. - Add seven `TLTheme` color keys (`snap`, `selectionStroke`, `selectionFill`, `brushFill`, `brushStroke`, `selectedContrast`, `laser`). - Rewrite the minimap in Canvas 2D (dropped the WebGL pipeline). - Add selectable UI color themes on tldraw.com under user settings. ### API changes - Added `OverlayUtil`, `OverlayManager`, `TLOverlay`, `TLOverlayEntry`, `TLOverlayUtilConstructor`, `TLAnyOverlayUtilConstructor`, `ShapeIndicatorOverlayUtil`. - Added tldraw overlay utils: `ArrowBindingHintOverlayUtil`, `ArrowHintOverlayUtil`, `BrushOverlayUtil`, `CollaboratorBrushOverlayUtil`, `CollaboratorCursorOverlayUtil`, `CollaboratorHintOverlayUtil`, `CollaboratorScribbleOverlayUtil`, `ScribbleOverlayUtil`, `SelectionForegroundOverlayUtil`, `ShapeHandleOverlayUtil`, `SnapIndicatorOverlayUtil`, `ZoomBrushOverlayUtil`, plus `defaultOverlayUtils`. - Added `Editor.overlays`, `Editor.getVisibleCollaborators`, `Editor.getVisibleCollaboratorsOnCurrentPage`; added `overlayUtils` option on `TLEditorOptions` / `TldrawEditorBaseProps` / `<Tldraw>`. - Added `TLPointerEventTarget` variant `{ target: 'overlay', overlay }`. - Added `TLTheme.colors.snap` / `.selectionStroke` / `.selectionFill` / `.brushFill` / `.brushStroke` / `.selectedContrast` / `.laser`. - Breaking! Removed legacy canvas components: `DefaultBrush`, `DefaultCollaboratorHint`, `DefaultCursor` (slot removed; file kept internal), `DefaultHandle`, `DefaultHandles`, `DefaultScribble`, `DefaultSelectionForeground`, `DefaultShapeIndicator`, `DefaultShapeIndicators`, `DefaultShapeIndicatorErrorFallback`, `DefaultSnapIndicator`, `CanvasShapeIndicators`, `LiveCollaborators`, and tldraw's `TldrawCropHandles`, `TldrawHandles`, `TldrawOverlays`, `TldrawArrowHints`, `TldrawScribble`, `TldrawSelectionForeground`, `TldrawShapeIndicators`. - Breaking! Removed `EditorComponents` slots listed in Release notes. - Breaking! Removed `ShapeUtil.useLegacyIndicator` and `TldrawOptions.useCanvasIndicators`. - Breaking! Removed `useActivePeerIds$` (use `editor.getVisibleCollaborators` instead). ### Code changes | Section | LOC change | | --------------- | ---------- | | Core code | +3507 / -3567 | | Tests | +2420 / -526 | | Automated files | +531 / -286 | | Documentation | +931 / -601 | | Apps | +2117 / -105 | | Templates | +503 / -430 | | Config/tooling | +72 / -13 | Relates to tldraw#8626 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: steveruizok <23072548+steveruizok@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 67d0d18 commit 17b19dc

237 files changed

Lines changed: 10441 additions & 5642 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,4 @@ opencode.json
132132
.claude/skills/pr-walkthrough/video/dist
133133
.claude/skills/pr-walkthrough/video/public/audio-*
134134
.claude/skills/pr-walkthrough/video/public/manifest.json
135+
todo.md

apps/docs/content/docs/collaboration.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Collaboration involves three concerns:
5858

5959
- **Synchronizing data** — Getting document changes in and out of the [store](/sdk-features/store). See [Persistence](/docs/persistence) for the basics, or [Collaboration](/sdk-features/collaboration) for building custom sync.
6060
- **User presence** — Sharing cursor positions, selections, and viewports with other users. See [Collaboration](/sdk-features/collaboration) for presence APIs and [Cursors](/sdk-features/cursors) for customizing how collaborator cursors appear.
61-
- **Collaboration UI** — The visual elements that show other users on the canvas. See [UI components](/sdk-features/ui-components) for overriding components like `CollaboratorCursor`, `CollaboratorBrush`, and `SharePanel`.
61+
- **Collaboration UI** — The visual elements that show other users on the canvas. See [UI components](/sdk-features/ui-components) for overriding components like `CollaboratorCursor` and `SharePanel`. Collaborator brushes, scribbles, and hints are rendered via the [OverlayUtil](?) system.
6262

6363
## Related
6464

apps/docs/content/docs/handles.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,9 @@ class SpeechBubbleUtil extends ShapeUtil<SpeechBubbleShape> {
236236
)
237237
}
238238

239-
indicator(shape: SpeechBubbleShape) {
239+
getIndicatorPath(shape: SpeechBubbleShape) {
240240
const geometry = this.getGeometry(shape)
241-
return <path d={geometry.getSvgPathData()} />
241+
return new Path2D(geometry.getSvgPathData())
242242
}
243243
}
244244
```

apps/docs/content/docs/indicators.mdx

Lines changed: 11 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Indicators are the outlines that appear around shapes when they're selected or h
1818

1919
When you select a shape in tldraw, an indicator appears as a stroke around the shape's geometry. Indicators are separate from the shape's visual appearance so they can be styled consistently across all shape types.
2020

21-
Each [ShapeUtil](?) defines how its indicator should be drawn by implementing the required `indicator` method:
21+
Each [ShapeUtil](?) defines how its indicator should be drawn by implementing the required `getIndicatorPath` method:
2222

2323
```tsx
2424
import { HTMLContainer, Rectangle2d, ShapeUtil } from 'tldraw'
@@ -42,13 +42,15 @@ class CardShapeUtil extends ShapeUtil<CardShape> {
4242
return <HTMLContainer>Hello</HTMLContainer>
4343
}
4444

45-
indicator(shape: CardShape) {
46-
return <rect width={shape.props.w} height={shape.props.h} />
45+
getIndicatorPath(shape: CardShape) {
46+
const path = new Path2D()
47+
path.rect(0, 0, shape.props.w, shape.props.h)
48+
return path
4749
}
4850
}
4951
```
5052

51-
The `indicator` method returns SVG elements that describe the shape's outline. The editor automatically positions and styles these elements based on selection state.
53+
The `getIndicatorPath` method returns paths in the shape's local coordinate space. The editor automatically positions and styles these paths based on selection state.
5254

5355
## When indicators appear
5456

@@ -62,48 +64,21 @@ By default, indicators appear in these situations:
6264

6365
Indicators are hidden during certain interactions, like when changing styles or during tool operations that would make indicators distracting.
6466

65-
## Rendering approaches
66-
67-
tldraw supports two ways to render indicators: SVG-based and canvas-based.
68-
69-
### SVG indicators (default)
70-
71-
The default approach uses React to render indicators as SVG elements. This is simpler to implement. It works well for most shapes:
72-
73-
```tsx
74-
class MyShapeUtil extends ShapeUtil<MyShape> {
75-
indicator(shape: MyShape) {
76-
return <rect width={shape.props.w} height={shape.props.h} rx={5} />
77-
}
78-
}
79-
```
80-
81-
SVG indicators support any valid SVG elements. The editor wraps them in a styled `<g>` element that applies the indicator color.
82-
83-
### Canvas indicators
67+
## Defining paths
8468

85-
Canvas-based rendering improves performance for complex shapes. Override `useLegacyIndicator` to return `false` and implement `getIndicatorPath`:
69+
For most shapes, return a `Path2D`:
8670

8771
```tsx
8872
class MyShapeUtil extends ShapeUtil<MyShape> {
89-
override useLegacyIndicator() {
90-
return false
91-
}
92-
9373
override getIndicatorPath(shape: MyShape): Path2D | undefined {
9474
const path = new Path2D()
9575
path.roundRect(0, 0, shape.props.w, shape.props.h, 5)
9676
return path
9777
}
98-
99-
indicator(shape: MyShape) {
100-
// Required method. Used only when useLegacyIndicator returns true (the default).
101-
return <rect width={shape.props.w} height={shape.props.h} rx={5} />
102-
}
10378
}
10479
```
10580

106-
Canvas indicators are drawn on a single canvas layer. This is more efficient when many shapes are selected. Most built-in shapes use canvas indicators.
81+
Indicators are drawn on a single canvas layer, which is efficient when many shapes are selected.
10782

10883
### Complex canvas indicators
10984

@@ -125,109 +100,12 @@ override getIndicatorPath(shape: MyShape): TLIndicatorPath | undefined {
125100

126101
return {
127102
path: bodyPath,
128-
clipPath: labelClipPath, // Areas to exclude from the main path
129-
additionalPaths: [arrowheadPath] // Extra paths to stroke
103+
clipPath: labelClipPath, // Areas to exclude from the main path
104+
additionalPaths: [arrowheadPath], // Extra paths to stroke
130105
}
131106
}
132107
```
133108

134-
## Customizing indicator display
135-
136-
Override the indicator components to control when and how indicators appear.
137-
138-
### Show all indicators
139-
140-
To show indicators for every shape on the canvas:
141-
142-
```tsx
143-
import { DefaultShapeIndicators, TLComponents, Tldraw } from 'tldraw'
144-
145-
const components: TLComponents = {
146-
ShapeIndicators: () => {
147-
return <DefaultShapeIndicators showAll />
148-
},
149-
}
150-
151-
export default function App() {
152-
return <Tldraw components={components} />
153-
}
154-
```
155-
156-
### Hide all indicators
157-
158-
To hide indicators entirely:
159-
160-
```tsx
161-
const components: TLComponents = {
162-
ShapeIndicators: () => {
163-
return <DefaultShapeIndicators hideAll />
164-
},
165-
}
166-
```
167-
168-
### Custom indicator logic
169-
170-
For complete control over which shapes show indicators, use the `OnTheCanvas` component:
171-
172-
```tsx
173-
import { TLComponents, Tldraw, useEditor, useEditorComponents, useValue } from 'tldraw'
174-
175-
const components: TLComponents = {
176-
OnTheCanvas: () => {
177-
const editor = useEditor()
178-
179-
const renderingShapes = useValue(
180-
'rendering shapes',
181-
() =>
182-
editor.getRenderingShapes().filter((info) => {
183-
// Custom filter logic here
184-
const shape = editor.getShape(info.id)
185-
return shape?.type === 'geo' // Only show indicators for geo shapes
186-
}),
187-
[editor]
188-
)
189-
190-
const { ShapeIndicator } = useEditorComponents()
191-
if (!ShapeIndicator) return null
192-
193-
return (
194-
<div style={{ position: 'absolute', top: 0, left: 0, zIndex: 9999 }}>
195-
{renderingShapes.map(({ id }) => (
196-
<ShapeIndicator key={id + '_indicator'} shapeId={id} />
197-
))}
198-
</div>
199-
)
200-
},
201-
}
202-
```
203-
204-
### Custom indicator styling
205-
206-
To change the color or appearance of indicators:
207-
208-
```tsx
209-
import { DefaultShapeIndicator, TLComponents, TLShapeIndicatorProps } from 'tldraw'
210-
211-
const CustomShapeIndicator = (props: TLShapeIndicatorProps) => {
212-
return <DefaultShapeIndicator {...props} color="red" opacity={0.5} />
213-
}
214-
215-
const components: TLComponents = {
216-
ShapeIndicator: CustomShapeIndicator,
217-
}
218-
```
219-
220-
The [DefaultShapeIndicator](?) component accepts these props:
221-
222-
| Prop | Type | Description |
223-
| --------- | --------- | -------------------------------------------------- |
224-
| shapeId | TLShapeId | The shape to show an indicator for |
225-
| userId | string | The collaborator's user ID (for multiplayer) |
226-
| color | string | Stroke color (default: `var(--tl-color-selected)`) |
227-
| opacity | number | Opacity from 0 to 1 |
228-
| className | string | Additional CSS class |
229-
| hidden | boolean | Whether to hide this indicator |
230-
231109
## Collaborator indicators
232110

233111
In multiplayer sessions, indicators show other users' selections. These appear with the collaborator's assigned color and slightly reduced opacity. The canvas indicator system handles collaborator indicators automatically.

apps/docs/content/docs/shapes.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ class CardShapeUtil extends ShapeUtil<CardShape> {
7171
return <HTMLContainer>Hello</HTMLContainer>
7272
}
7373

74-
indicator(shape: CardShape) {
75-
return <rect width={shape.props.w} height={shape.props.h} />
74+
getIndicatorPath(shape: CardShape) {
75+
const path = new Path2D()
76+
path.rect(0, 0, shape.props.w, shape.props.h)
77+
return path
7678
}
7779
}
7880
```

apps/docs/content/getting-started/installation.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,7 @@ const components: TLComponents = {
192192
Background: YourCustomBackground,
193193
Cursor: YourCustomCursor,
194194
Grid: YourCustomGrid,
195-
SelectionForeground: YourCustomSelectionForeground,
196-
SnapIndicator: YourCustomSnapIndicator,
195+
SelectionBackground: YourCustomSelectionBackground,
197196

198197
// UI components
199198
Toolbar: YourCustomToolbar,
@@ -205,6 +204,8 @@ const components: TLComponents = {
205204
<Tldraw components={components} />
206205
```
207206

207+
Canvas overlay elements like the selection foreground, brush, snap indicators, and scribble are rendered via the [OverlayUtil](?) system rather than React components. You can customize them by extending the built-in overlay utils and passing them via the `overlayUtils` prop.
208+
208209
See the [TLEditorComponents](?) and [TLUiComponents](?) type definitions for the complete list of customizable components.
209210

210211
## Versioning

apps/docs/content/releases/next.mdx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ status: published
88
last_version: v4.5.10
99
---
1010

11-
This release adds a first-class theme system with display values for customizing default shapes, extensible asset types via a new `AssetUtil` system, shape attribution with a new `TLUserStore` provider and extensible user records, clipboard hooks for intercepting copy, cut, and paste, custom record types to the store, a new `@tldraw/mermaid` package for converting Mermaid diagrams to native shapes, WebSocket hibernation support for tlsync, a new `@tldraw/editor-controller` package for scripting and automation, RTL language support in the UI, cross-window embedding support, arbitrary iframe embed pasting, a paste-as-plain-text keyboard shortcut, and smarter export trimming. It also includes various other improvements and bug fixes.
11+
This release adds a first-class theme system with display values for customizing default shapes, a new canvas overlay system that replaces React component overrides with Canvas 2D rendering, extensible asset types via a new `AssetUtil` system, shape attribution with a new `TLUserStore` provider and extensible user records, clipboard hooks for intercepting copy, cut, and paste, custom record types to the store, a new `@tldraw/mermaid` package for converting Mermaid diagrams to native shapes, WebSocket hibernation support for tlsync, a new `@tldraw/editor-controller` package for scripting and automation, RTL language support in the UI, cross-window embedding support, arbitrary iframe embed pasting, a paste-as-plain-text keyboard shortcut, and smarter export trimming. It also includes various other improvements and bug fixes.
1212

1313
## What's new
1414

@@ -229,6 +229,45 @@ A new `Cmd+Shift+V` / `Ctrl+Shift+V` shortcut pastes clipboard content as plain
229229

230230
Note: `Cmd+Shift+V` previously toggled paste-at-cursor positioning. Since the "Paste at cursor" preference now covers that use case, this shortcut has been repurposed for plain text paste. Users who relied on `Cmd+Shift+V` for paste-at-cursor should use the preference toggle instead.
231231

232+
### 💥 Canvas overlay system ([overlay utils](/sdk-features/overlay-utils))
233+
234+
Canvas overlay UI — the selection foreground, brush rectangles, snap indicators, shape indicators, shape handles, scribbles, arrow hints, and collaborator presence visuals — is now rendered via a new `OverlayUtil` system instead of React components. Overlays draw directly to a `<canvas>` element using the Canvas 2D API, with optional hit-test geometry for pointer interactions.
235+
236+
Pass custom overlay utils through the `overlayUtils` prop on `<Tldraw>` or `<TldrawEditor>`. Custom utils are merged with the defaults; if your util has the same `type` as a built-in, it replaces it:
237+
238+
```tsx
239+
import { Tldraw, BrushOverlayUtil, type TLBrushOverlay } from 'tldraw'
240+
241+
class BlueBrushOverlayUtil extends BrushOverlayUtil {
242+
override render(ctx: CanvasRenderingContext2D, overlays: TLBrushOverlay[]): void {
243+
const overlay = overlays[0]
244+
if (!overlay) return
245+
const { x, y, w, h } = overlay.props
246+
ctx.fillStyle = 'rgba(0, 0, 255, 0.1)'
247+
ctx.fillRect(x, y, w, h)
248+
}
249+
}
250+
251+
;<Tldraw overlayUtils={[BlueBrushOverlayUtil]} />
252+
```
253+
254+
<details>
255+
<summary>Migration guide</summary>
256+
257+
The following `TLEditorComponents` entries have been removed: `Brush`, `CollaboratorBrush`, `CollaboratorCursor`, `CollaboratorHint`, `CollaboratorScribble`, `CollaboratorShapeIndicator`, `Cursor`, `Handle`, `Handles`, `Overlays`, `Scribble`, `SelectionForeground`, `ShapeIndicator`, `ShapeIndicatorErrorFallback`, `ShapeIndicators`, `SnapIndicator`, and `ZoomBrush`. Their corresponding default implementations (`DefaultBrush`, `DefaultCollaboratorHint`, `DefaultCursor`, `DefaultHandle`, `DefaultHandles`, `DefaultScribble`, `DefaultSelectionForeground`, `DefaultShapeIndicator`, `DefaultShapeIndicators`, `DefaultSnapIndicator`) and prop types (`TLBrushProps`, `TLCollaboratorHintProps`, `TLCursorProps`, `TLHandleProps`, `TLHandlesProps`, `TLScribbleProps`, `TLSelectionForegroundProps`, `TLShapeIndicatorProps`, `TLShapeIndicatorErrorFallbackComponent`, `TLShapeIndicatorsProps`, `TLSnapIndicatorProps`) have also been removed.
258+
259+
If you were overriding these via the `components` prop, create a custom `OverlayUtil` subclass instead and pass it via `overlayUtils`. See the [overlay utils documentation](/sdk-features/overlay-utils) for details.
260+
261+
The following `tldraw` canvas helpers have also been removed: `TldrawArrowHints`, `TldrawCropHandles`, `TldrawCropHandlesProps`, `TldrawHandles`, `TldrawOverlays`, `TldrawScribble`, `TldrawSelectionForeground`, and `TldrawShapeIndicators`.
262+
263+
Custom shape utils must now implement `getIndicatorPath()`. `indicator()` is deprecated and no longer rendered, and `useLegacyIndicator()` has been removed. Return a `Path2D` (or `TLIndicatorPath` for clipped / multi-path indicators) from `getIndicatorPath()` instead.
264+
265+
`TldrawOptions.useCanvasIndicators` has been removed. Shape indicators now always render through `ShapeIndicatorOverlayUtil`; replace that overlay util if you need custom indicator visibility or styling.
266+
267+
Custom themes must include the overlay UI colors `snap`, `selectionStroke`, `selectionFill`, `brushFill`, `brushStroke`, `selectedContrast`, and `laser`.
268+
269+
</details>
270+
232271
### Cross-window embedding support ([#8196](https://github.com/tldraw/tldraw/pull/8196))
233272

234273
Tldraw now works correctly when embedded in iframes, Electron pop-out windows, and Obsidian plugins where the global `document` and `window` differ from the ones tldraw is mounted in. All bare `document` and `window` references have been replaced with container-aware alternatives.
@@ -267,6 +306,13 @@ New helpers `getOwnerDocument()` and `getOwnerWindow()` are exported from `@tldr
267306
- Add `TLUserStore` interface with `getCurrentUser()` and `resolve()` for connecting tldraw to auth systems. Add unified `TLUser` record type, `UserRecordType`, `createUserId`, `isUserId`, `userIdValidator`, and `createUserRecordType()` for extensible user schemas. Add `user` parameter to `createTLSchema()`. Add `Editor.getAttributionUser()`, `Editor.getAttributionUserId()`, and `Editor.getAttributionDisplayName()`. Add `textFirstEditedBy` prop to `TLNoteShapeProps`. ([#8147](https://github.com/tldraw/tldraw/pull/8147))
268307
- Add `AssetUtil` base class with `configure()`, `getDefaultProps()`, `getSupportedMimeTypes()`, `getAssetFromFile()`, and `createShape()`. Add `assetUtils` prop to `<Tldraw>`. Add `Editor.getAssetUtil()`, `Editor.hasAssetUtil()`, and `Editor.getAssetUtilForMimeType()`. Add `TLGlobalAssetPropsMap` for type-safe custom asset registration. Add `createAssetRecordType()`, `defaultAssetSchemas`, and `assets` parameter to `createTLSchema()`. ([#8031](https://github.com/tldraw/tldraw/pull/8031))
269308
- Add `Cmd+Shift+V` / `Ctrl+Shift+V` shortcut to paste clipboard content as plain text. `Cmd+Shift+V` no longer toggles paste-at-cursor positioning. ([#8347](https://github.com/tldraw/tldraw/pull/8347))
309+
- 💥 Remove `Brush`, `CollaboratorBrush`, `CollaboratorCursor`, `CollaboratorHint`, `CollaboratorScribble`, `CollaboratorShapeIndicator`, `Cursor`, `Handle`, `Handles`, `Overlays`, `Scribble`, `SelectionForeground`, `ShapeIndicator`, `ShapeIndicatorErrorFallback`, `ShapeIndicators`, `SnapIndicator`, and `ZoomBrush` from `TLEditorComponents`. Remove their default implementations and prop types. These are now rendered via the `OverlayUtil` system.
310+
- 💥 Remove `TldrawArrowHints`, `TldrawCropHandles`, `TldrawCropHandlesProps`, `TldrawHandles`, `TldrawOverlays`, `TldrawScribble`, `TldrawSelectionForeground`, and `TldrawShapeIndicators` from `tldraw`.
311+
- 💥 Change shape indicators from SVG to canvas paths: `ShapeUtil.getIndicatorPath()` is now abstract, `ShapeUtil.indicator()` is deprecated and no longer rendered, and `ShapeUtil.useLegacyIndicator()` has been removed.
312+
- 💥 Remove `TldrawOptions.useCanvasIndicators`.
313+
- 💥 Add `snap`, `selectionStroke`, `selectionFill`, `brushFill`, `brushStroke`, `selectedContrast`, and `laser` to the required `TLThemeDefaultColors` / `TLThemeUiColorKeys` UI color keys.
314+
- Add `OverlayUtil` base class, `OverlayManager`, `TLOverlay`, `TLOverlayEntry`, `TLOverlayUtilConstructor`, and `TLAnyOverlayUtilConstructor` to `@tldraw/editor`. Add `overlayUtils` prop to `<Tldraw>` and `<TldrawEditor>`. Add `Editor.overlays` (`OverlayManager`) with `getOverlayUtil()`, `getOverlayUtilsInZOrder()`, `getActiveOverlayEntries()`, `getCurrentOverlays()`, `getOverlayGeometry()`, `getOverlayAtPoint()`, `getHoveredOverlayId()`, `getHoveredOverlay()`, and `setHoveredOverlay()`.
315+
- Add `ShapeIndicatorOverlayUtil`, `BrushOverlayUtil`, `ZoomBrushOverlayUtil`, `ScribbleOverlayUtil`, `CollaboratorBrushOverlayUtil`, `CollaboratorCursorOverlayUtil`, `CollaboratorScribbleOverlayUtil`, `CollaboratorHintOverlayUtil`, `ShapeHandleOverlayUtil`, `SelectionForegroundOverlayUtil`, `SnapIndicatorOverlayUtil`, `ArrowHintOverlayUtil`, `ArrowBindingHintOverlayUtil`, their overlay types, and `defaultOverlayUtils` to `tldraw`.
270316
- Add `Vec.IsFinite()` static method for checking whether a vector has finite coordinates. ([#8176](https://github.com/tldraw/tldraw/pull/8176))
271317

272318
## Improvements

apps/docs/content/sdk-features/culling.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ class MyShapeUtil extends ShapeUtil<MyShape> {
9797
return <div style={{ width: shape.props.w, height: shape.props.h }} />
9898
}
9999

100-
indicator(shape: MyShape) {
101-
return <rect width={shape.props.w} height={shape.props.h} />
100+
getIndicatorPath(shape: MyShape) {
101+
const path = new Path2D()
102+
path.rect(0, 0, shape.props.w, shape.props.h)
103+
return path
102104
}
103105
}
104106
```

apps/docs/content/sdk-features/cursors.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ editor.user.updateUserPreferences({ color: '#FF802B' })
152152

153153
### Rendering collaborator cursors
154154

155-
Remote cursors render through the `CollaboratorCursor` component. The default implementation ([DefaultCursor](?)) displays:
155+
Remote cursors render through the `CollaboratorCursor` component. The default implementation (`DefaultCursor`) displays:
156156

157157
- The cursor icon in the user's color
158158
- The user's name as a label below the cursor

apps/docs/content/sdk-features/drag-and-drop.mdx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,10 @@ class SlotContainerShapeUtil extends ShapeUtil<SlotContainerShape> {
259259
)
260260
}
261261

262-
indicator(shape: SlotContainerShape) {
263-
return <rect width={shape.props.slots * 100} height={100} />
262+
getIndicatorPath(shape: SlotContainerShape) {
263+
const path = new Path2D()
264+
path.rect(0, 0, shape.props.slots * 100, 100)
265+
return path
264266
}
265267
}
266268
```

0 commit comments

Comments
 (0)