Skip to content

[pull] main from tldraw:main#606

Merged
pull[bot] merged 6 commits into
code:mainfrom
tldraw:main
Jun 19, 2026
Merged

[pull] main from tldraw:main#606
pull[bot] merged 6 commits into
code:mainfrom
tldraw:main

Conversation

@pull

@pull pull Bot commented Jun 19, 2026

Copy link
Copy Markdown

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 6 commits June 19, 2026 09:39
In order to keep the public MCP worker (`tldraw-mcp-app`) on the same
commit and SDK build as production dotcom without a manual step, this PR
makes `deploy-mcp-app.yml` auto-deploy on every push to the `production`
branch, mirroring how the VS Code extension auto-publishes via its
`push` trigger.

Previously the workflow was `workflow_dispatch`-only, so it never ran
automatically (despite #8587's description, the auto-trigger was never
wired up). This adds a `push` trigger scoped to `production` and keeps
the manual `workflow_dispatch`. `TARGET` now falls back to
`github.ref_name` so the checkout ref resolves for both event types.

Production only: the MCP app is a single Cloudflare worker with no
staging environment, so a push to `main` must not overwrite the shared
`tldraw-mcp-app` worker. (This is why it does not also trigger on `main`
like the VS Code extension, which has a separate staging pre-release
channel.)

### Change type

- [x] `other`

### Test plan

1. Confirm `MCP_APP_TLDRAW_LICENSE_KEY` and the Cloudflare secrets are
available to the `deploy-production` environment.
2. After merge, push to `production` (or wait for a production release)
and confirm the **Deploy MCP app** workflow runs automatically and the
worker serves the new bundle.
3. Confirm the manual **Deploy MCP app** workflow_dispatch still works.

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Config/tooling | +8 / -1    |
### The problem

The workspace sidebar can crash the whole app on the proper-Zero
backend:

```
TypeError: Cannot read properties of undefined (reading 'owningGroupId')
  at getWorkspaceFilesSorted (TldrawApp.ts)
```

`getWorkspaceFilesSorted` reads `f.file.owningGroupId` for every
`group_file` row, assuming the `file` is always there. The
`workspaceMemberships` query joins `file` as a Zero `.one()`
relationship, which resolves to `undefined` when the referenced `file`
row isn't in the client's synced data. A `group_file` row can be present
without its `file` when:

- the file was deleted, leaving a dangling join row,
- the join row synced before its file row, or
- the file is filtered out server-side because the user can no longer
read it.

Any of these throws inside the sidebar's reactive derive and takes down
the component tree. The declared type `groupFiles: Array<TlaGroupFile &
{ file: TlaFile }>` asserted `file` was always present, so the compiler
never flagged the access.

This only affects the proper-Zero path. The legacy `ZeroPolyfill`
already drops orphan rows at the source (`compact()` + `if (!file)
return null`), so the guard was effectively lost when proper Zero took
over the same shape. Introduced in #9270.

### The change

- Make the type honest: `file: TlaFile | undefined`.
- Drop orphan rows in `getWorkspaceFilesSorted` via a type-guard
predicate, so the non-undefined narrowing flows into `pinned`/`unpinned`
and the rest of the function with no scattered `!` assertions.
- Guard the `canCreateNewFile` file-limit count the same way.

### Testing

Added `getWorkspaceFilesSorted.test.ts`, which drives the real method
against a membership whose first row is an orphan (`file: undefined`).
Without the guard it reproduces the exact production crash; with it, the
orphan is excluded and the owned file is returned. `yarn typecheck`
passes (the honest type surfaced two more access sites, fixed by the
type guard).
In order to keep dotcom styling aligned with the SDK token set, this PR
replaces near-match app-shell CSS variable references with existing
`--tl-*` package variables and removes now-dead dotcom token metadata.
It folds the dotcom `--tla-color-overlay` token onto the package
`--tl-color-overlay`, drops the radius and shadow-highlight leftovers
from `tla.css`, and deletes the `TLA_COLOR_KEYS_TO_OMIT` set in
`ui-themes.ts` (and its filter), which had become a no-op once the color
migration landed — none of the keys it listed appear in any theme block
anymore.

### Change type

- [x] `other`

### Test plan

1. Run `git diff --check`.
2. Confirm no removed `--tla-*` token reads remain in
`apps/dotcom/client/src/tla` (every `var(--tla-color-*)` still read
resolves to a definition in `tla.css`).
3. Confirm no undefined `--color-warn`, `--tl-color-hover`, or
`--tla-shadow-primary-highlight` reads remain.
4. Run `yarn format-current` and `yarn lint-current`.
5. Run `yarn dev-app` and visually inspect the dotcom app shell,
sidebar, dialogs, share menu, buttons, drag/drop zones, and admin page
in both themes.

Note: the collapsed-sidebar scrim now uses `--tl-color-overlay`, which
is lighter than the old `--tla-color-overlay` in dark mode (50% vs 81%
black). Worth confirming the dark-mode scrim still reads acceptably on a
narrow viewport.

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

### Code changes

| Section | LOC change |
| ------- | ---------- |
| Apps | +17 / -90 |
)

In order to keep the CI/build runtime current with the [June 2026
Node.js security
releases](https://nodejs.org/en/blog/vulnerability/june-2026-security-releases),
this bumps the Node version used by the shared `setup` action from
`24.13.1` to `24.17.0` — the patched 24.x release (dated 2026-06-17,
flagged `security`) — and updates the two cache-key references that pin
the version alongside it.

Scope notes:

- **Build/CI runtime only.** The published `engines` field is unaffected
(that's a separate concern — see #9098, which sets it to the
`require(esm)` feature floor). `engines` is advisory and isn't the right
place to chase security patch levels.
- **Not a fix for an exposure in tldraw.** The CVEs in this release are
in Node's TLS/hostname-verification, HTTP/HTTP2/proxy, WebCrypto, and
`--permission` subsystems. The SDK is browser code, tldraw.com's server
side runs on Cloudflare workerd (not Node), and Node only runs at
build/CI time here — so we don't believe tldraw is impacted. This is
just keeping the build runtime patched.

### Change type

- [x] `other`

### Test plan

- CI runs green on Node 24.17.0.

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

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Config/tooling | +3 / -3    |
## Problem
After the pattern-tile cache (#9320), profiling a tldraw.com file open
on the iOS Simulator showed the next-biggest cost was
`findOrCreateFontFace` — ~385 ms, attributed to `next@native` iterating
the document's `FontFaceSet`. Crucially this was on a *warm* open (the
pattern `toBlob` was already ~0), so it's a per-mount cost, not
first-load.

Root cause: `findOrCreateFontFace` deduplicates font faces with an O(n)
scan of `document.fonts` on every lookup. It runs for each font on every
editor mount, and a fresh editor (e.g. switching documents on
tldraw.com, which remounts the editor) gets a fresh `FontManager` with
no memory of the previous one — so the scan re-runs every mount.

The trace ruled out recreation: the `FontFace` constructor was ~11 ms,
`load()` ~0 ms, and there were zero font-file network requests — faces
are *reused*, just found via a slow scan.

## Fix
Cache resolved font faces in a per-document `Map` (keyed by family +
descriptors), shared across `FontManager` instances, so repeated lookups
and remounts are O(1). On a cache miss the existing `document.fonts`
scan still runs once, so faces added outside the manager (preloaded
fonts) are still reused rather than duplicated. Keyed per document for
cross-window embedding; faces are never removed from `document.fonts`,
so the cache stays valid for the document's lifetime.

## Tests
`FontManager.test.ts`: a fresh manager on the same document reuses the
existing face instead of recreating it (the remount regression), and
fonts with different descriptors get distinct faces. The cross-document
cache has a test-only reset. All 22 tests pass; typecheck clean; no
public-API change (the reset helper is `@internal` and not re-exported).

## Verification
Re-trace a file open on the simulator: `findOrCreateFontFace`'s scan
cost should be present on the first open and ~0 on subsequent opens.

## Context
This is a sibling to #9320 (pattern-tile cache) — same symptom
(per-mount cost on tldraw.com file switches), different root cause:
patterns had the wrong lifetime (per-instance state); fonts had the
wrong data structure (O(n) scan vs O(1) lookup).
…loy (#9327)

In order to make deployed tldraw.com builds debuggable in browser
devtools and stop the source-map 404s, this PR stops stripping `.js.map`
files from the client deploy and serves them publicly. Closes #9322.

The client build generated source maps, uploaded them to Sentry, then
deleted the `.js.map` files from the deployed assets — while leaving the
`//# sourceMappingURL` comments in the shipped JS. That produced a 404
for every map and left browser devtools unable to symbolicate deployed
builds. The usual reason to hide source maps is to keep source private,
which does not apply here since the client source is public in this
repo. Sentry upload is unaffected: it reads from `dist/assets` before
the strip step.

### Change type

- [x] `bugfix`

### Test plan

1. From the preview deploy, open devtools → Network, reload, and confirm
`*.js.map` requests return 200 rather than 404.
2. Open a minified stack frame in devtools and confirm it symbolicates
to real names and lines.

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Config/tooling | +7 / -3    |
@pull pull Bot locked and limited conversation to collaborators Jun 19, 2026
@pull pull Bot added the ⤵️ pull label Jun 19, 2026
@pull
pull Bot had a problem deploying to bemo-canary June 19, 2026 15:13 Failure
@pull
pull Bot had a problem deploying to deploy-staging June 19, 2026 15:13 Failure
@pull
pull Bot merged commit a59e668 into code:main Jun 19, 2026
4 of 8 checks passed
@pull
pull Bot had a problem deploying to deploy-production June 19, 2026 15:13 Failure
@pull
pull Bot had a problem deploying to deploy-staging June 19, 2026 15:13 Error
@pull
pull Bot had a problem deploying to vsce publish June 19, 2026 15:13 Failure
@pull
pull Bot had a problem deploying to bemo-canary June 19, 2026 15:13 Failure
@pull
pull Bot had a problem deploying to deploy-staging June 19, 2026 15:13 Failure
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