Skip to content

[pull] main from tldraw:main#438

Merged
pull[bot] merged 9 commits intocode:mainfrom
tldraw:main
Mar 10, 2026
Merged

[pull] main from tldraw:main#438
pull[bot] merged 9 commits intocode:mainfrom
tldraw:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Mar 10, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

max-drake and others added 9 commits March 10, 2026 16:17
In order to demonstrate the full range of reactive inputs available in
the editor's InputsManager (closes #7713), this PR adds modifier key
indicators to the reactive inputs example and moves the panel into the
canvas UI as a TopPanel component.

It also fixes a bug where modifier key state (particularly Meta/Cmd on
macOS) could get stuck after key release, because on macOS the keyup
event for Cmd can report `e.metaKey = true`. The fix force-clears a
modifier key's state when its own explicit keyup event is received.

### Changes

- Add shift, alt, meta, and accel key indicators using `useValue` with
the InputsManager's reactive getter methods
- Move the panel from a side panel layout to a `TopPanel` component
inside the canvas UI, fixing the issue where the panel was below the
fold and hidden
- Fix modifier key state getting stuck on macOS by immediately clearing
state on explicit keyup events for Shift, Alt, and Meta keys
- Remove the `Ctrl` badge since `getCtrlKey()` is normalized to
`e.metaKey || e.ctrlKey` (redundant with Accel)

### Change type

- [x] `other`

### Test plan

1. Run `yarn dev` and open the "Reactive inputs" example
2. Verify the inputs panel renders in the top zone of the canvas
3. Press modifier keys (Shift, Alt, Cmd/Meta) and verify they light up
4. Release modifier keys and verify they immediately clear (no sticking)
5. Verify the "Accel" badge follows Cmd on Mac and Ctrl elsewhere

### Release notes

- Add modifier key indicators (Shift, Alt, Meta, Accel) to the reactive
inputs example.
- Fix modifier key state getting stuck after key release on macOS.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Example-only UI changes that rewire how the panel is rendered and add
a few new reactive subscriptions; no production logic or data handling
is affected.
> 
> **Overview**
> Updates the Reactive Inputs example to render its panel inside the
canvas UI by wiring `ReactiveInputsPanel` as a `TopPanel` via
`components`, switching from storing the `editor` in local state to
using `useEditor()`.
> 
> Extends the panel to display reactive modifier key state (`Shift`,
`Ctrl`, `Alt`, `Meta`, `Accel`) using `useValue`, and
simplifies/compacts the coordinate and velocity readouts with a shared
`formatNum` helper.
> 
> Restyles the panel from a fixed side layout to a compact,
wrap-friendly top bar and adds new styles for modifier key badges
(including active-state styling).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6760056. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Closes #5613

When a custom shape has an `onClick` handler and is not yet selected,
`PointingShape.onEnter` defers selection to pointer up (so that
`onClick` can fire). However, if the user drags instead of clicking, the
`Translating` state requires a selection to operate and immediately
bails back to idle—making it impossible to drag unselected shapes that
have an `onClick` handler.

This PR fixes the issue by selecting the shape in `startTranslating`
when the shape wasn't selected on enter and there is no existing
selection. This ensures that dragging works correctly while preserving
the existing behavior for shapes that are already selected or part of a
multi-selection.


https://github.com/user-attachments/assets/ff74b3c3-1c57-4810-8b9f-cf39213940bf

### Change type

- [x] `bugfix`

### Test plan

1. Create a custom shape with an `onClick` handler
2. Make sure the shape is not selected
3. Attempt to drag the shape — it should translate as expected
4. Click (without dragging) — the `onClick` handler should still fire
5. Multi-select shapes and drag — existing behavior should be preserved

- [x] Unit tests

### Release notes

- Fix dragging unselected shapes that have an `onClick` handler.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches the `SelectTool` state machine to change when selection is
applied during a drag, which could subtly affect selection/translation
behavior in edge cases (e.g. grouped or multi-selected shapes). Covered
by a new unit test, but still impacts core interaction logic.
> 
> **Overview**
> Fixes a selection/translation edge case where shapes with a
`ShapeUtil.onClick` handler could not be dragged while unselected.
> 
> `PointingShape.startTranslating` now ensures the hit shape is selected
(only when it wasn’t selected on enter and there’s no existing
selection) before transitioning to translating, preserving
click-to-run-`onClick` behavior.
> 
> Adds a regression unit test for dragging shapes with `onClick`, and
introduces a new examples page (`shape-with-onClick`) demonstrating a
clickable custom shape that increments a counter via
`ShapeUtil.onClick`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c7f6206. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
omg

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: only adds a lint suppression for an intentionally deprecated
API call and adjusts comment formatting, with no runtime behavior
changes expected.
> 
> **Overview**
> Keeps backward compatibility for the deprecated `embeds` prop by
explicitly suppressing the `@typescript-eslint/no-deprecated` warning
before calling `EmbedShapeUtil.setEmbedDefinitions(embeds)`.
> 
> Also applies a tiny formatting cleanup in the `embeds` prop JSDoc
comment.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
71d460a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…mode (#7933)

In order to fix a false positive "multiple instances" warning that fires
in Next.js dev mode, this PR deduplicates registrations in
`registerTldrawLibraryVersion()`.

When Next.js re-evaluates modules on page refresh, each tldraw package
re-registers itself via `registerTldrawLibraryVersion()`. Since
`globalThis.__TLDRAW_LIBRARY_VERSIONS__` persists across module
re-evaluations, duplicate entries accumulate and trigger the "multiple
instances" warning — even though only one copy of each library is loaded
(all entries show the same version and same module type).

The fix skips registration when an identical entry (same name, version,
and module type) already exists. Genuine conflicts (different versions
or mixed ESM/CJS) are still detected correctly.

Closes #4614

### Change type

- [x] `bugfix`

### Test plan

1. Use tldraw in a Next.js app with dev mode
2. Refresh the page — the false "multiple instances" warning should no
longer appear
3. Genuine version mismatches or ESM/CJS conflicts should still produce
warnings

- [x] Unit tests

### Release notes

- Fixed false positive "multiple instances" warning in Next.js dev mode
caused by module re-evaluation

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small, well-scoped change to warning/dedup logic gated to Next.js dev
mode with unit test coverage; main risk is inadvertently suppressing a
real duplicate only under those conditions.
> 
> **Overview**
> Prevents false "multiple instances" warnings in Next.js *dev mode* by
skipping re-registration when `registerTldrawLibraryVersion` sees an
**exact duplicate** entry (same package, version, and module type)
during Fast Refresh.
> 
> Adds `isNextjsDev()` gating (checks `NODE_ENV === 'development'` and
presence of `__NEXT_DATA__`) so duplicate tracking and warnings still
occur in non-Next.js environments, and updates tests to cover both the
Next.js dev dedupe behavior and the unchanged non-dev duplicate warning.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cc920c0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
update mcp app well-known token

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: only changes the static response body for the
unauthenticated `/.well-known/openai-apps-challenge` endpoint, with no
impact on auth or request handling logic.
> 
> **Overview**
> Updates the static token returned by the unauthenticated
`/.well-known/openai-apps-challenge` route in
`apps/mcp-app/src/worker.ts` for OpenAI app/domain verification.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5c6da77. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- After 30 continuous seconds in debug mode, shows a toast prompting
dotcom users to check out the tldraw SDK
- Toast appears once (persisted via localStorage), dismisses
automatically when debug mode is toggled off
- Works for both signed-in and signed-out users

## Test plan
- [ ] Sign out, enable debug mode, wait 30s — toast appears
- [ ] Click "Get started" — opens tldraw.dev with UTM params
- [ ] Toggle debug mode off — toast dismisses
- [ ] Re-enable debug mode — toast does NOT reappear (localStorage flag
set)
- [ ] Clear `showDebugSdkToast` from localStorage, re-enable — toast
reappears
- [ ] Sign in, repeat above — works the same

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only changes (toast + menu item wiring) with minimal side effects
beyond localStorage persistence and an external link/analytics event.
> 
> **Overview**
> Adds `SneakyDebugModeToast`, which watches `isDebugMode` and after a
delay displays a persistent toast promoting the SDK; the toast click is
tracked and opens `tldraw.dev` with UTM params, is dismissed when debug
mode is turned off, and is shown only once via `localStorage`.
> 
> Mounts this behavior in both the signed-in editor (`TlaEditor`) and
the signed-out local editor page (`local.tsx`). Separately updates
`DefaultMainMenuContent` / e2e menu fixture to remove the dedicated
`ToggleDebugModeItem` from Preferences and instead render
`toggle-debug-mode` via `TldrawUiMenuActionItem`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3aaa731. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Closes #8148

## Summary

- `npm create tldraw` now always creates a subdirectory from the project
name instead of installing into the current directory
- If a directory with that name already exists, appends `-1`, `-2`, etc.
— never overwrites
- Removes the `--overwrite` flag and "directory is not empty" prompt,
since they're no longer needed

This one change fixes both bugs from the issue:
1. **Files in wrong directory** — the name now determines the directory
2. **Ctrl+C writing files** — the prompt where this happened no longer
exists

## Test plan

- [ ] Run `cli.cjs` from a non-empty directory, type a name → verify
files go into a new subdirectory
- [ ] Run it again with the same name → verify it creates `<name>-1`
instead of overwriting
- [ ] Press Ctrl+C at any prompt → verify no files are created

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [x] `feature`
- [ ] `api`
- [ ] `other`

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Behavioral change to CLI output location and directory selection could
affect existing user workflows/scripts, but it’s limited to project
scaffolding and reduces risk of accidental overwrites.
> 
> **Overview**
> `create-tldraw` no longer scaffolds into the current directory or
overwrites an existing target. It now derives a project directory from
the provided/entered app name, and if that directory already exists it
automatically picks the next available `-1`, `-2`, etc. suffix.
> 
> This removes the `--overwrite` flag and the interactive “directory not
empty” prompt/cleanup path, replaces it with `findAvailableDir`, and
hardens `isDirEmpty` to treat non-directories as non-empty (with new
unit tests covering file paths and `.git`-only dirs).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
846d76e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…8042)

In order to make shapes dragged from the toolbar respect dynamic sizing
(matching the behavior of click-to-create), this PR applies the same
`scale` calculation to each toolbar `onDragStart` callback and
centralizes the scale factor logic into a new
`editor.getResizeScaleFactor()` method.

When dynamic size mode is enabled, shapes created by clicking with a
tool respect the zoom level via the `scale` prop, but shapes dragged out
of the toolbar did not. This adds the same scaling to toolbar
drag-create for geo, arrow, line, frame, text, and note tools, and
refactors all Pointing states to use the shared helper.

Closes #7998

### Change type

- [x] `bugfix`

### Test plan

1. Enable dynamic size mode in user preferences
2. Zoom in/out to a non-1x zoom level
3. Drag each shape type (geo, arrow, line, frame, text, note) out of the
toolbar
4. Verify the created shapes appear at a consistent screen size
regardless of zoom, matching the behavior of click-to-create

- [ ] Unit tests
- [ ] End to end tests

### API changes

- Added `Editor.getResizeScaleFactor()` — returns `1 / zoomLevel` when
dynamic sizing is enabled, `1` otherwise

### Release notes

- Fix shapes dragged from the toolbar not respecting dynamic size mode

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
In order to provide a clear reference for z-order manipulation in the
editor API, this PR adds a new example demonstrating all four reordering
methods: `sendToBack`, `sendBackward`, `bringForward`, and
`bringToFront`.

The example creates overlapping colored shapes with button controls in a
TopPanel so users can select shapes and visually see stacking order
change. Includes comments covering relative order preservation, the
`considerAllShapes` option, and automatic index management.

Closes #7460

### Change type

- [x] `other`

### Test plan

1. Run `yarn dev`, navigate to the z-order example under editor-api
2. Verify four overlapping colored shapes are visible
3. Select a shape and test each of the four buttons — stacking order
should visibly change
4. Select multiple shapes and verify they move as a group with relative
order preserved

- [ ] Unit tests
- [ ] End to end tests

### Release notes

- Add z-order manipulation example to editor-api examples

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Adds a self-contained examples-only demo with no changes to core
editor behavior or data handling.
> 
> **Overview**
> Adds a new `editor-api/z-order` example that visually demonstrates
z-order manipulation via `editor.sendToBack`, `sendBackward`,
`bringForward`, and `bringToFront` on the current selection.
> 
> The example auto-creates overlapping colored shapes on mount, provides
TopPanel buttons to trigger each reorder action, and includes brief
documentation/CSS for discoverability and layout.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
736bdbe. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Mar 10, 2026
@pull pull Bot added the ⤵️ pull label Mar 10, 2026
@pull pull Bot merged commit 0fa9e2f into code:main Mar 10, 2026
@pull pull Bot had a problem deploying to deploy-production March 10, 2026 21:13 Failure
@pull pull Bot had a problem deploying to deploy-staging March 10, 2026 21:13 Error
@pull pull Bot had a problem deploying to deploy-staging March 10, 2026 21:13 Error
@pull pull Bot had a problem deploying to deploy-staging March 11, 2026 00:27 Failure
@pull pull Bot temporarily deployed to e2e-dotcom March 11, 2026 02:35 Inactive
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants