Skip to content

[pull] main from tldraw:main#553

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

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

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 20, 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 : )

steveruizok and others added 6 commits May 20, 2026 09:15
In order to keep the next release notes current with `main`, this PR
updates `apps/docs/content/releases/next.mdx` with PRs that have landed
since v5.0.1. It bumps `last_version` from v5.0.0 to v5.0.1 (no archive
needed — v5.0.0 is already archived and v5.0.1 is a patch) and adds new
entries for the page menu redesign, the `selectLockedShapes` option, the
perpetual-license expired warning fix, the video toolbar tooltip fix,
and the page/zoom menu open-state hint fix. These are preliminary —
source is `main`, so some entries may be pruned later if they don't make
it onto production.

### Change type

- [x] `other`

### Test plan

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

### Code changes

| Section       | LOC change |
| ------------- | ---------- |
| Documentation | +19 / -1   |
In order to make pattern fills export correctly under `darkMode: true`,
this PR fixes `HashPatternForExport` to read the color mode from the SVG
export context instead of the editor. Before this fix, when the editor's
color mode differed from the requested export color mode (e.g. light
editor + `darkMode: true`), the `<pattern>` was emitted with the
editor's color-mode id while the shape's `fill="url(#...)"` referenced
the export's color-mode id, leaving the pattern broken.

Resolves #8854.

### Why the ids drifted

SVG export wraps the rendered shape tree in `SvgExportContextProvider`
(see `getSvgJsx.tsx`). That context carries the export's effective
`colorMode`, derived from `opts.darkMode ?? editor.getColorMode()`. The
`useColorMode()` hook is wired to honor this:

```ts
export function useColorMode(): 'light' | 'dark' {
  const editor = useEditor()
  const exportContext = useSvgExportContext()
  return useValue(
    'colorMode',
    () => {
      if (exportContext) return exportContext.colorMode
      return editor.getColorMode()
    },
    [exportContext, editor]
  )
}
```

So inside an export render, `useColorMode()` returns the *export's*
mode, while `editor.getColorMode()` keeps returning the editor's own
mode. `PatternFill` already used `useColorMode()` to build the
`fill="url(#...)"` reference, but `HashPatternForExport` was reading
`editor.getColorMode()` directly to emit the `<pattern>` def, which is
how the two sides could disagree.

The bug only surfaces in production-style setups because in local dev
the editor often happens to match the requested mode (e.g. dev
environment in dark mode); in headless/CLI exports the editor is left in
light mode while `darkMode: true` is passed.

### Change type

- [x] `bugfix`

### Test plan

1. Create a geo shape with `fill: 'pattern'`.
2. Call `editor.toImage([id], { format: 'svg', darkMode: true })` from a
light-mode editor.
3. The exported SVG should render the hash pattern (not a missing fill).

- [x] Unit tests

### Release notes

- Fix pattern fill exports broken in dark mode when the editor was in
light mode (and vice versa).
This PR updates the i18n strings.

### Change type
- [x] `other`

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
In order to silence noisy unhandled promise warnings from the icon
preload effect, this PR catches rejections from `image.decode()` in
`AssetUrlsProvider`. The preload is best-effort — any real load failure
will still surface where the icon is actually used.

`image.decode()` returns a promise that can reject (e.g. with
`EncodingError` when the browser cannot decode the image, or
`AbortError` when the source changes). Without a `.catch()` handler,
these rejections become uncaught promise errors in the console.

### Change type

- [x] `bugfix`

### Test plan

1. Open the editor in a browser
2. Confirm no `Uncaught (in promise) EncodingError` warnings appear in
the console from the asset URLs provider

### Release notes

- Silently catch `image.decode()` rejections from the icon preload
effect.

### Code changes

| Section   | LOC change |
| --------- | ---------- |
| Core code | +7 / -2    |
In order to clean up editor manager state across editor lifecycles, this
PR adds disposal hooks for the FontManager and overlay manager
lifecycle. It clears FontManager font state and pending font requests,
releases its store-backed cache references, and disposes registered
overlay utils from editor disposal. Closes #8885. Closes #8891.

### Change type

- [x] `api`

### Test plan

1. `cd packages/editor && yarn test run
src/lib/editor/managers/FontManager/FontManager.test.ts`
2. `cd packages/tldraw && yarn test run
src/test/overlays/OverlayManager.test.ts`
3. `yarn typecheck`
4. `yarn api-check`
5. `yarn lint-current`

- [x] Unit tests

### Release notes

- Add disposal lifecycle hooks for font and overlay managers.

### API changes

- Added `FontManager.dispose()` for clearing font manager state and
cache references.
- Added `OverlayManager.dispose()` for disposing registered overlay
utils.
- Added `OverlayUtil.dispose()` as a default no-op for custom overlay
cleanup.

### Code changes

| Section         | LOC change |
| --------------- | ---------- |
| Core code       | +33 / -2   |
| Tests           | +71 / -2   |
| Automated files | +5 / -0    |
In order to stop the dotcom, examples, and perf e2e workflows from
running on PRs that don't touch their dependencies (for example,
docs-only PRs or markdown-only changes inside `packages/`), this PR
splits each workflow's relevance check into two `dorny/paths-filter`
steps and ANDs the results in the job's `outputs:` expression.

Closes #8878

### Why the previous filter misbehaved

`dorny/paths-filter@v3` defaults to `predicate-quantifier: some`, so the
filter returns `true` if **any** listed pattern matches. Under that
quantifier, a negation like `!**/*.md` matches every file that isn't a
`.md` — including `.mdx` files. A docs-only change to
`apps/docs/content/releases/next.mdx` therefore matched `!**/*.md` and
tripped the filter, even though no positive include matched it.

### Why one filter isn't enough

`dorny/paths-filter` only supports `predicate-quantifier: some | every`
(it's a per-step option, not per-filter). Under `every`, every listed
pattern must match for a file to count, which doesn't combine with
multiple positive includes like `packages/**` **or** `apps/dotcom/**` —
no single file is in both. So we use two steps and combine.

### How the new check works

Per workflow:

- `includes` (default `some`): true if any changed file is in a relevant
path (`packages/**`, `apps/dotcom/**` or `apps/examples/**`, the
workflow file itself, `.github/actions/**`, `yarn.lock`).
- `nondocs` (`predicate-quantifier: 'every'` with `['**', '!**/*.md',
'!**/*.mdx']`): a file is included only if it matches `**` and is not
`.md` and not `.mdx`, so the filter is true iff at least one
non-markdown file changed.

`relevant = includes && nondocs`, evaluated in the job's `outputs:`
expression. Behavior:

| PR contents | `includes` | `nondocs` | `relevant` |
| --- | --- | --- | --- |
| `apps/docs/content/foo.mdx` only | false | false | **false** (skip) |
| `packages/foo/README.md` only | true | false | **false** (skip) |
| `packages/foo/src/index.ts` only | true | true | **true** (run) |
| `packages/foo/src/index.ts` + `apps/docs/foo.mdx` | true | true |
**true** (run, code change wins) |

### Change type

- [x] `other`

### Test plan

1. Open a PR that only changes `apps/docs/content/releases/next.mdx`.
Confirm each of `End to end tests (dotcom)`, `End to end tests
(examples)`, and `End to end tests (perf)` reports both filter steps and
resolves `relevant = false`, and the matching `build` job is skipped.
2. Open a PR that only changes a `packages/**/README.md`. Confirm the
same three workflows skip the `build` job (`includes = true`, `nondocs =
false`).
3. Open a PR that changes a relevant code file (e.g. anything under
`packages/<x>/src/`, `apps/dotcom/`, `apps/examples/`, the workflow
file, `.github/actions/`, or `yarn.lock`). Confirm the corresponding e2e
workflow still runs.
4. Open a PR that mixes a code change with a docs/markdown change.
Confirm the workflow still runs (the code change satisfies `nondocs`).

### Release notes

- Internal CI fix; no user-facing changes.
@pull pull Bot locked and limited conversation to collaborators May 20, 2026
@pull pull Bot added the ⤵️ pull label May 20, 2026
@pull pull Bot merged commit d7ec5d9 into code:main May 20, 2026
@pull pull Bot had a problem deploying to bemo-canary May 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to deploy-staging May 20, 2026 15:13 Error
@pull pull Bot had a problem deploying to deploy-production May 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to vsce publish May 20, 2026 15:13 Error
@pull pull Bot had a problem deploying to deploy-staging May 20, 2026 15:13 Error
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