Skip to content

Conversation

@NicholasKissel
Copy link
Member

Describe what your pull request does. If you can, add GIFs or images showing the before and after of your change.

Change type

  • bugfix
  • improvement
  • feature
  • api
  • other

Test plan

  1. Create a shape...
  • Unit tests
  • End to end tests

Release notes

  • Fixed a bug with…

NathanFlurry and others added 30 commits October 22, 2025 16:02
This PR updates the i18n strings.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds new i18n key `95d2109dc8` ("Build with tldraw SDK") across all
locales and compiled locale files.
> 
> - **Internationalization**:
> - Add new translation key `95d2109dc8` for "Build with tldraw SDK" in
`apps/dotcom/client/public/tla/locales/*` and compiled
`locales-compiled/*` across all supported languages.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bc1373b. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
@steveruizok oh i see - yeah this is a regression from last week's
change tldraw#6924

this app will just hotfix by itself when i land this PR

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Spread user properties in PostHog `identify` call instead of nesting
them under `properties`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
76ea674. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
We have disparate origin checks that are varying in domains, and we want
to tighten up asset-upload as well.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Moves origin checks into shared utilities and applies unified
CORS/origin middleware across workers, updating the request
post-processing hook signature.
> 
> - **Shared (worker-shared)**:
> - **Origins utilities**: Add `isAllowedOrigin` and
`blockUnknownOrigins` in `packages/worker-shared/src/origins.ts` and
export from `index.ts`.
> - **Request handling**: Change `handleApiRequest` `after` hook
signature to `(response, request)` to support CORS helpers that need the
request.
> - **Workers**:
> - **asset-upload-worker**: Switch CORS `origin` to `isAllowedOrigin`;
add `blockUnknownOrigins` middleware; update to `corsify` via new hook
signature.
> - **sync-worker**: Replace local origin logic with shared
`blockUnknownOrigins`/`isAllowedOrigin`; pass `request` to `corsify`;
remove duplicated origin helpers.
> - **image-resize-worker**: Reuse shared `isAllowedOrigin` for
`isValidOrigin`; tidy imports.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
60477d1. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds `https://analytics.google.com` to CSP `connect-src` allowlist.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8e97fc5. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Reorganized `packages/worker-shared/package.json` to correctly
classify development and build-time dependencies
- Moved `@cloudflare/workers-types`, `typescript`, and `lazyrepo` to
`devDependencies`

## Rationale
These dependencies are only needed during development and build time,
not at runtime. This change:
- Clarifies the actual runtime dependencies of the package
- Follows best practices for dependency classification
- May reduce bundle size in environments that distinguish between
production and development dependencies

## Changes
**Moved to devDependencies:**
- `@cloudflare/workers-types` - TypeScript type definitions
- `typescript` - Build-time compiler
- `lazyrepo` - Build system tool

**Remaining in dependencies:**
- `@tldraw/utils`
- `@tldraw/validate`
- `cloudflare-workers-unfurl`
- `itty-router`
- `toucan-js`

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Reclassifies build-only packages from dependencies to devDependencies
in packages/worker-shared/package.json.
> 
> - **package.json (worker-shared)**:
>   - **Dependency classification**:
> - Moved `@cloudflare/workers-types`, `typescript`, and `lazyrepo` to
`devDependencies`.
> - Kept runtime deps: `@tldraw/utils`, `@tldraw/validate`,
`cloudflare-workers-unfurl`, `itty-router`, `toucan-js`.
>   - **Dev tooling**:
>     - Ensured `vitest` remains in `devDependencies`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cf8887d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude <noreply@anthropic.com>
Add 15-minute timeout to dotcom hotfix script to prevent action
timeouts. Now throws an error (sent to Discord) if PR checks don't
complete in time, leaving 5 minutes buffer before the 20-minute GitHub
Action timeout.

### Change type

- [x] `improvement`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a 15-minute timeout to the dotcom hotfix script while waiting for
PR checks, throwing an error if not ready in time.
> 
> - **Scripts**:
>   - `internal/scripts/trigger-dotcom-hotfix.ts`
> - Add 15-minute maximum wait with elapsed-time tracking when polling
PR `mergeable_state`.
>     - On timeout, log and throw an error with PR link.
>     - Retains initial 5-minute delay and 15s polling cadence.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c3dfe88. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#6964)

This is a PR to improve our analytics banner. As part of the onboarding
project, I had to get my head into the analytics work and figured I'd do
a detail pass while I was here.

As noted in greater detail by the bots below, the biggest change is
**removing react** dependencies and just making the banner with DOM
APIs. The reason for this is that we were shipping react / react-dom as
part of the analytics script bundle, which seemed like a waste for two
components.

I also moved a lot of the **services** (like posthog, hubspot, etc) into
their own files and into a common interface. If we want to add or remove
services in the future, or if we want to adjust the functionality we're
using for any of the services, then we can do that easily.

We also add a new **analytics worker** that checks whether we need to
show the cookie banner for a user, or whether we can opt them into
analytics automatically. We're not using any ad trackers or selling
people's medical data or any other sneaky stuff, so we're fine with
providing an opt-out to users in California etc. The worker itself is
very simple and uses cf-headers to identify the location of the IP
request.

Finally, I aligned the **design** of the components. The privacy
preferences in particular needed some love, so love it got. We now use
dark mode banners on light mode and light mode banners on dark mode for
increased contrast. The next step of forced engagement would be
immediate centered cookie wall, which tbh might be better than a sneaky
thing crawling up the screen later. We'll iterate.

None of the behavior should have changed materially, though a few things
here and there will have been tightened up. I'd done a lot of work while
still working in React (ie, using an external store for the consent) and
confirmed things are doing what they should be.

Reminder that this banner is currently only used on tldraw computer and
tldraw.dev. We'll need to do similar work on tldraw.com (or perhaps
migrate to using this analytics app instead).


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces React-based analytics with a modular vanilla JS
implementation and introduces a Cloudflare worker to determine consent
by geo, updating build, tests, and CI/deploy.
> 
> - **Analytics app (vanilla, modular architecture)**:
> - Replace React/Radix implementation with DOM-based components and a
unified `Analytics` class (`apps/analytics/src/index.ts`, `components/`,
`state/`, `types.ts`).
> - Add modular services with a common interface: `PostHog`, `GA4`,
`HubSpot`, `Reo` (`src/analytics-services/*`).
> - Introduce cookie consent and theme state managers (`src/state/*`),
consent banner and privacy dialog (`src/components/*`).
> - Geo-based consent check utility calling worker
(`src/utils/consent-check.ts`).
>   - Update styles to new tokens and compact UI (`src/styles.css`).
> - Remove React build setup; adjust Vite/TS/Vitest configs and deps
(`vite.config.ts`, `tsconfig.json`, `vitest.config.ts`, `package.json`).
> - Add preview `index.html` and expose global API
(`window.tlanalytics`).
> - **Cloudflare analytics worker** (`apps/analytics-worker`):
> - New worker to determine if explicit consent is required via
`CF-IPCountry` with CORS and caching (`src/worker.ts`, `wrangler.toml`).
>   - Package, TS/Vitest configs, and docs (`CONTEXT.md`).
> - **CI/Deploy**:
> - New workflow to deploy analytics worker
(`.github/workflows/deploy-analytics.yml`).
> - Deployment script for worker and preview env handling
(`internal/scripts/deploy-analytics.ts`, update
`internal/scripts/lib/deploy.ts`).
> - **Misc**:
>   - Add ignore rules for analytics public assets (`.prettierignore`).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e4db4db. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Enhanced the ExternalLink component to support an optional eventName
prop for analytics tracking. Updated the TlaSidebarDotDevLink to track
clicks and dismissals using the trackEvent utility.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Add optional analytics tracking to `ExternalLink` and wire up tracking
for sidebar dotdev link and feedback dialog links.
> 
> - **Analytics**:
> - Enhance `ExternalLink`
(`apps/dotcom/client/src/tla/components/ExternalLink/ExternalLink.tsx`):
> - Accepts optional `eventName` prop; fires `trackEvent(eventName, {
link })` on click and preserves existing `onClick`.
> - Sidebar
(`apps/dotcom/client/src/tla/components/TlaSidebar/components/TlaSidebarDotDevLink.tsx`):
>     - Track link click via `eventName="sidebar-dotdev-link-clicked"`.
> - Track dismiss button via
`trackEvent('sidebar-dotdev-link-dismissed')`.
> - Feedback dialog
(`apps/dotcom/client/src/tla/components/dialogs/SubmitFeedbackDialog.tsx`):
> - Add `eventName` to Discord and GitHub `ExternalLink`s to track
clicks.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f91ccbd. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Added an example that showcases how to create a bezier curve shape,
useful for vector editing.

## Demo
![2025-10-10 at 9 43 08 - Teal
Rattlesnake](https://github.com/user-attachments/assets/92b32847-0dd9-45e7-b18a-531ee2678b3c)

### Change type

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

### Release notes

- Added a new bezier curve example

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a cubic bezier curve shape example with custom handles, snapping,
editing/translation interactions, and undo/redo while editing.
> 
> - **Examples**:
> - Add cubic bezier curve example in
`apps/examples/src/examples/cubic-bezier-shape/` with entry `README.md`
and `CubicBezierShapeExample.tsx`.
> - **Shape Util** (`CubicBezierShape.tsx`):
> - Implement `bezier-curve` shape (`BezierCurveShapeUtil`) with
`CubicBezier2d` geometry, SVG rendering, bounds/handle snapping, and
resize behavior.
> - Provide four handles (`start`, `end`, `cp1`, `cp2`), auto-collapse
control points near endpoints, and meta-key gestures for handle drag
behaviors and curve bending during translate.
> - Show dashed control lines when selected during edit/drag/translate.
> - **Editor State Overrides** (`CubicBezierShapeExample.tsx`):
> - Customize select tool states to: collapse cp1/cp2 on meta-click,
remain in editing after handle drag, and allow translating while in
editing.
> - Register the custom shape and mount an initial curve at viewport
center.
> - **UI/Handles** (`CustomHandles.tsx`):
> - Custom `Handles` component to display handles for `bezier-curve`
during edit/drag/translate and default behavior for other shapes.
> - **Undo/Redo** (`SneakyUndoRedoWhileEditing.tsx`):
> - Add keybinding hook to perform undo/redo while preserving editing
state.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
16a4811. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This PR makes the analytics on tldraw.com use the new analytics worker
to check whether consent is required.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Switch consent checks to `consent.tldraw.xyz`, return explicit consent
status, auto-opt-in where allowed, and wire up routes/CSP/deploy for the
new domain.
> 
> - **Consent infrastructure**:
> - **Cloudflare Worker**: Add production route with custom domain
`consent.tldraw.xyz` in `apps/analytics-worker/wrangler.toml`.
> - **Deploy**: For previews, set `customDomain` to
`${previewId}-consent.tldraw.xyz` in
`internal/scripts/deploy-analytics.ts`.
> - **Analytics library (`apps/analytics`)**:
> - Update `shouldRequireConsent` to return `'requires-consent' |
'no-consent-needed'` and fetch from `https://consent.tldraw.xyz`.
> - Initialize consent using the new result enum; adjust logic to set
`'unknown'` only when `'requires-consent'`.
>   - Move `window.tlanalytics` setup earlier in `src/index.ts`.
> - **Dotcom UI**:
> - `TlaCookieConsent`: perform consent check on mount, delay banner
until resolved, auto opt-in when `'no-consent-needed'`, and hide banner
(via opacity) when manage dialog is open.
> - **Security**:
> - Add `https://consent.tldraw.xyz` to `connect-src` in
`apps/dotcom/client/src/utils/csp.ts`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
06edc47. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds `https://tldraw.dev` to CORS allowlists and mocks the consent
service in e2e tests for deterministic banner behavior.
> 
> - **Workers/CORS**:
> - Allow exact origin `https://tldraw.dev` in
`apps/analytics-worker/src/worker.ts` and
`packages/worker-shared/src/origins.ts`.
> - **Tests**:
> - In `apps/dotcom/client/e2e/tests/cookie-consent.spec.ts`, mock
`https://consent.tldraw.xyz` to always return `{ requires_consent: true,
country_code: 'TEST' }` to ensure the banner displays during tests.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b8b83c4. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@MitjaBezensek and I did some frontend work and testing on staging and
found a couple of things that need to be fixed before we push the groups
data model to production tomorrow.

1. We should throw an error if old User DOs try to connect to the new
replicator (cloudflare will kill them and restart them with the new
worker script very soon after this happens so it's no biggie to throw an
error)
2. We needed to propagate the user's name to their home group name so
that the shared file name triggers work correctly for files shared from
a user's home group.
3. We improved a check for the groups_backend flag.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Reject legacy clients by erroring on guestFileIds in registerUser; add
triggers to sync/initialize home group names from user names; update
migration flag check to groups_backend.
> 
> - **Backend (replicator)**:
> - `TLPostgresReplicator.registerUser`: remove support for
`guestFileIds` and throw if provided to block legacy clients.
> - **Database (migrations)**:
> - Add `update_home_group_name` trigger/function to sync a home group’s
`name` when the corresponding `user.name` changes.
> - Add `initialize_home_group_name` trigger/function to set a home
group’s `name` from the corresponding `user.name` on insert.
> - Update migration check to use `flags LIKE '%groups_backend%'`
instead of `'%groups%'`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
b333e97. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Invoke PostHog opt_in_capturing when consent is granted and
opt_out_capturing when revoked within configurePosthog.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f9b7d1c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
we are seeing slowness when loading rooms in production, this should
help us narrow down exactly which operation(s) is causing it.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds granular timing analytics around room load, auth, rate limiting,
group checks, R2/Supabase I/O, and create-from-source to pinpoint
slowness.
> 
> - **Backend (sync-worker TLDrawDurableObject)**
> - **Timing instrumentation**: Add `timer()` helper and emit metrics
across key paths.
>     - `getAppFileRecord`: measure success/error durations.
> - `onRequest`: measure auth, rate-limit checks, group membership
query, `getRoom`, and total request time.
> - `handleFileCreateFromSource`: measure await-persist, R2 fetch/put,
and total fetch time.
> - `loadFromDatabase`: measure R2 fetch, create-from-source, Supabase
fetch, total time, and error path.
> - **Minor**: avoid double JSON parsing in R2 load by reusing
`snapshot` var.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bd09db0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This reverts commit 2e5f9f2.

### API changes
- Revert the 120 fps changes.

### Change type

- [x] `bugfix`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Standardizes throttling to 60fps (removing custom/120fps logic),
updates TLSyncClient scheduling, simplifies FPS debug display, and
adjusts the utils API.
> 
> - **Utils**:
> - Reverts `fpsThrottle` to fixed 60fps; removes custom FPS support and
related state.
> - Updates docs/comments and `throttleToNextFrame` to reference 60fps.
>   - API change: `fpsThrottle(fn)` no longer accepts `getTargetFps`.
> - **Sync Core** (`packages/sync-core/src/lib/TLSyncClient.ts`):
> - Removes dynamic sync FPS logic (`SOLO_MODE_FPS`,
`COLLABORATIVE_MODE_FPS`, `getSyncFps`).
> - `flushPendingPushRequests` and `scheduleRebase` now use
`fpsThrottle()` without FPS getter.
> - **UI**
(`packages/tldraw/src/lib/ui/components/DefaultDebugPanel.tsx`):
> - Simplifies FPS output to `FPS <current>` (removes max FPS display).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c9995c0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This should reduce the amount of traffic going through the replicator.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces per-event file_state updates with a single throttled updater
that batches session, edit, and visit info every 10s, removing old
handlers.
> 
> - **Editor**:
> - Add `FileStateUpdater` to coalesce `lastSessionState`, `lastEditAt`,
and `lastVisitAt` updates, throttled to 10,000ms.
> - Replace ad-hoc session-state listener and remove
`SneakyFileUpdateHandler` usage.
> - **App**:
> - Remove `onFileEdit` and `onFileSessionStateUpdate` methods and
related usage.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
98ed362. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
We changed some env variable, using this to trigger another deploy to
staging.

### Change type

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> No code changes; this PR is a no-op to trigger a redeploy.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
60920d1. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
adds `url` so that we can discover which sites are using tldraw

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds the current page URL to the watermark/evaluation tracking fetch
in `LicenseManager.maybeTrack`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3343b8c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…raw#6998)

- Bumps the client protocol version to 3.
- Removes feature flag logic for `tldraw_groups_init`
- Defaults all new users to the new groups initialization flow

In a few days we'll bump the backend and make the clients update.

### Change type

- [x] `improvement`

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Bumps protocol to v3 and always use groups-based `init` for new users,
removing the feature flag and legacy user insert path.
> 
> - **Protocol**:
> - Bump `Z_PROTOCOL_VERSION` and `MIN_Z_PROTOCOL_VERSION` to `3` in
`packages/dotcom-shared/src/types.ts`.
> - **App initialization**
(`apps/dotcom/client/src/tla/app/TldrawApp.ts`):
> - Remove `localStorage` feature flag `tldraw_groups_init` and legacy
`user.insert` path.
>   - Always call `z.mutate.init({ user, time })` for new user creation.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
575dc07. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Updated openUrl and runtime.openWindow to accept an allowReferrer
parameter, enabling control over whether the referrer is sent when
opening external links. Refactored related components and hooks to
support this option for improved flexibility and privacy handling.

### Change type

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

### API changes
- Adds an optional parameter to `openWindow` and `runtime.openWindow` to
allow referrers on links that open in new tabs.

### Release notes

- Adds the option to include referrer on links that open in new tabs.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds an optional allowReferrer flag to window-opening utilities and
updates editor/dotcom components to use it for selected external links.
> 
> - **API / Core**:
> - Extend `packages/editor` public API: `openWindow(url, target?,
allowReferrer?)` and `runtime.openWindow(url, target, allowReferrer?)`.
>   - Add wrapper support in `window-open.ts` and update API report.
> - **Editor components**:
> - `Watermark`: pass `allowReferrer: true` for unlicensed CTA; keep
default for licensed link.
> - `RichTextLabel` and `Toolbar/LinkEditor`: replace direct
`window.open` calls with `openWindow(...)` and wire `allowReferrer` as
needed.
> - **Dotcom client**:
> - `utils/url.openUrl(url, allowReferrer?)` and `useOpenUrlAndTrack`
accept/forward the flag.
> - Update menu items (`Links.tsx`, `tla/.../menu-items.tsx`) to opt-in
`allowReferrer` for specific `tldraw.dev` links.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
773aefc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- allow `QueryExpression` to handle nested object properties and extend
`executeQuery` to filter nested criteria
- expose helpers on `StoreQueries` for retrieving ids and records to
support the new query shape
- expand store query tests with nested shape data and assertions for
nested matching

## Testing
- yarn vitest run packages/store/src/lib/executeQuery.test.ts

### API Changes
- Store queries now support comparisons on nested properties.

------
https://chatgpt.com/codex/tasks/task_b_68f734d0a7748321b36b12cdd3de4351


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Enable querying and indexing of nested record properties (via
backslash-delimited paths), update types/helpers, and expand tests for
nested/reactive scenarios.
> 
> - **Store Queries / Indexing**:
> - Support nested property indexes using backslash-delimited `path`
(e.g., `metadata\sessionId`) in `StoreQueries.index` and
`__uncached_createIndex`.
> - Indexing skips undefined nested values and handles
adds/updates/removes incrementally.
> - Generalize `RSIndex`, `RSIndexMap`, `RSIndexDiff` to use `any` key
types.
> - **Query Language**:
>   - Extend `QueryExpression` to recursively support nested objects.
>   - `objectMatchesQuery` now matches nested criteria.
> - `executeQuery` flattens nested matchers to paths and queries
corresponding indexes; intersects results.
> - **StoreQueries helpers**:
> - Add internal `getAllIdsForType` and `getRecordById` to support
efficient queries.
>   - `ids()` uses `getAllIdsForType` for empty queries.
> - **Tests**:
> - Add extensive tests for nested queries, deep nesting, operators
(`eq`, `neq`, `gt`) on nested fields, reactivity, batch ops, and mixed
criteria.
> - **API Report**:
> - Reflect new nested `QueryExpression`, path-based index signatures,
new internal helpers, and generalized `RSIndex*` types.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5dc7bab. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Adds batch migration functionality to migrate all users from the legacy
file_state model to the new groups backend. Includes real-time progress
tracking, ability to stop migrations, and a user count endpoint.

### Change type

- [x] `improvement`



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds an admin UI and SSE backend to batch-migrate users to the groups
backend with configurable batching, live progress, and unmigrated count.
> 
> - **Admin UI (client)**:
> - **Batch Migration section** in
`apps/dotcom/client/src/pages/admin.tsx` with controls for `batchSize`,
`batchSleepMs`, and optional `maxUsers`, plus start/stop.
> - Real-time progress log and stats (total, completed, succeeded,
failed, percent), and a button to fetch unmigrated user count.
> - New styles in `apps/dotcom/client/src/pages/admin.module.css` for
stats, counts, config inputs, and logs.
> - **Admin API (worker)**:
> - `GET /api/app/admin/unmigrated_users_count` returns number of users
without `groups_backend`.
> - `GET /api/app/admin/migrate_users_batch` streams Server-Sent Events
with progress, supports `batchSize`, `batchSleepMs`, `maxUsers`, and
stop-on-client-cancel.
> - Helpers: `getUnmigratedUsers` and `performBatchUserMigration` to
query and migrate users in batches with throttling and detailed
progress.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ab8c45c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
deletions were going through but we were also triggering an error due to
a 'on file exit' behaviour where a file_state update was triggered and
that was failing due to the fact that the file had been deleted.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Prevents updates on deleted files and replaces onFileExit with a
direct lastVisitAt update to avoid deletion-related errors.
> 
> - **App logic (`TldrawApp.ts`)**:
> - Add guards in `updateFileState` and `updateFile` to skip updates
when the file is missing or `isDeleted`.
>   - Remove `onFileExit` method.
> - **Editor (`TlaEditor.tsx`)**:
> - In unmount cleanup, replace `app.onFileExit(fileId)` with
`app.updateFileState(fileId, { lastVisitAt })`, preserving the existing
error guard.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ba0e318. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Hopefully this will make it more obvious what is happening.

### Change type

- [x] `improvement`
Fixes two issues with our production deployment Discord messages:

  1. Fixed step count for dotcom deployments
2. Added deployment prefixes - Analytics and dotcom deployments now post
with [ANALYTICS] and [DOTCOM] prefixes respectively, making it easier to
distinguish which deployment posted each message when they run in
parallel. An alternative approach would be for all steps to use the same
message, which might be better? That said we would loose the timestamps
so we wouldn't know how long the steps took 🤷‍♂️

### Before (dotcom and analytics worker deploy messages are
interleaving)

```
--- production dotcom deploy pre-flight ---
setting up deploy ✅ 
--- production analytics worker deploy pre-flight ---
cloudflare deploy dry run ✅ 
building dotcom app ✅ 
--- pre-flight complete, starting real analytics worker deploy ---
deploying analytics worker to cloudflare ✅ 
Deploy complete!
cloudflare deploy dry run ✅ 
--- pre-flight complete, starting real dotcom deploy ---
deploying asset uploader to cloudflare ✅ 
deploying multiplayer worker to cloudflare ✅ 
deploying image resizer to cloudflare ✅ 
deploying health worker to cloudflare ✅ 
deploying fairy worker to cloudflare ✅ 
deploying dotcom app to vercel ✅ 
Deploy complete!
```

### After
```
  [DOTCOM] --- production dotcom deploy pre-flight ---
  [DOTCOM] setting up deploy ✅
  [ANALYTICS] --- production analytics worker deploy pre-flight ---
  [ANALYTICS] cloudflare deploy dry run ✅
  [DOTCOM] building dotcom app ✅
  [ANALYTICS] --- pre-flight complete, starting real analytics worker deploy ---
  [ANALYTICS] deploying analytics worker to cloudflare ✅
  [ANALYTICS] Deploy complete!
  [DOTCOM] cloudflare deploy dry run ✅
  [DOTCOM] --- pre-flight complete, starting real dotcom deploy ---
  [DOTCOM] deploying asset uploader to cloudflare ✅
  [DOTCOM] deploying multiplayer worker to cloudflare ✅
  [DOTCOM] deploying image resizer to cloudflare ✅
  [DOTCOM] deploying health worker to cloudflare ✅
  [DOTCOM] deploying fairy worker to cloudflare ✅
  [DOTCOM] deploying dotcom app to vercel ✅
  [DOTCOM] Deploy complete!
```


### Change type

- [x] `improvement`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds message prefixes to Discord deploy notifications for
analytics/dotcom and fixes dotcom total step count; introduces
`messagePrefix` support in the Discord helper.
> 
> - **Deploy Scripts**
>   - `internal/scripts/deploy-dotcom.ts`:
>     - Set Discord `totalSteps` to `previewId ? 10 : 9`.
>     - Add `messagePrefix: "[DOTCOM]"` to Discord notifications.
>   - `internal/scripts/deploy-analytics.ts`:
>     - Add `messagePrefix: "[ANALYTICS]"` to Discord notifications.
> - **Library**
>   - `internal/scripts/lib/discord.ts`:
>     - Extend `Discord` constructor to accept optional `messagePrefix`.
> - Prefix all sent and edited messages with `messagePrefix` when
provided.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0c9aa6e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This PR updates the i18n strings.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Updates Spanish `tool.rich-text-bold` from "Atrevido" to "Negrita".
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bdec887. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
reverts part of tldraw#6626 to fix
https://discord.com/channels/859816885297741824/1429837310920495229

### Change type

- [x] `bugfix`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Moves `tl-canvas__in-front` outside the canvas and applies modal
overlay styling to it to match `.tl-canvas`.
> 
> - **Editor**:
> - Moves `tl-canvas__in-front` container outside `.tl-canvas` in
`packages/editor/src/lib/components/default-components/DefaultCanvas.tsx`.
> - **Styles**:
> - Extends modal overlay styling to ` .tl-canvas__in-front` alongside
`.tl-canvas` in `templates/chat/src/app/styles.css` (same radius,
shadow, and insets).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7494a2d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
If you close tab or refresh, your latest viewport position might not be
saved. Adding this beforeunload callback seems to work locally (_most_
of the time?)

anyway it can't hurt.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Flushes pending file state updates on tab close/refresh to persist the
latest session/viewport state.
> 
> - **Editor**:
> - Add `beforeunload` listener in `TlaEditor.tsx` `FileStateUpdater` to
`flush` throttled `update`, ensuring latest `session state` and
timestamps are saved when the page is closed or refreshed.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5e8b0c2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
mimecuvalo and others added 26 commits January 18, 2026 12:20
…gs (tldraw#7128)

we usually get translations same day but the login stuff went out before
translations were ready so we didn't notice this issue until now.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Ensure untranslated strings fall back to English by merging fetched
locale messages with the English bundle.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
59298bc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
cc @derekcicerone

### Change type

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

### Release notes

- cross-realm: adapt `useCanvasEvents`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Scopes the canvas pointermove listener to the editor container’s
ownerDocument to support cross-realm contexts.
> 
> - **Editor**:
>   - `useCanvasEvents`:
> - Derives `ownerDocument` from `editor.getContainer().ownerDocument`.
> - Attaches/removes `pointermove` listener on `ownerDocument.body`
instead of `document.body`.
>     - Updates effect dependencies to include `ownerDocument`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bccc226. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
made it hard to do rich text for custom shapes downstream

### Change type

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

### Release notes

- add export for `DefaultLabelColorStyle`, used with `labelColor` which
is necessary for rich text in custom shapes

### API changes

- add export for `DefaultLabelColorStyle`, used with `labelColor` which
is necessary for rich text in custom shapes

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Export `DefaultLabelColorStyle` from `@tldraw/tlschema` and update the
API report accordingly.
> 
> - **Exports**:
> - Add `DefaultLabelColorStyle` to `packages/tlschema/src/index.ts`
from `styles/TLColorStyle`.
> - **API**:
> - Include `DefaultLabelColorStyle` in
`packages/tlschema/api-report.api.md`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4689e57. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
this is work that was in the fairy branch, landing this parent tweak
ahead of the SDK release

### Change type

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

### Release notes

- examples: dynamic tools

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a new example that dynamically adds/removes a custom Heart tool
and updates the toolbar using setTool/removeTool.
> 
> - **Examples**:
> - **New example**
`apps/examples/src/examples/dynamic-tools/DynamicToolsExample.tsx`:
> - Implements `HeartTool` (`StateNode`) creating a heart emoji on
click.
> - Uses `editor.setTool` / `editor.removeTool` to dynamically
register/unregister the tool.
> - Adds UI overrides to expose `tools.heart` with custom icon and
toolbar item.
> - Integrates a toggle button (in `InFrontOfTheCanvas`) to add/remove
the tool at runtime.
>     - Supplies `customAssetUrls` for `heart-icon`.
> - **Docs**:
>   - Adds `README.md` describing dynamic tool addition/removal.
> - **Styles**:
>   - Adds `dynamic-tools.css` for the toggle button positioning.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3e218db. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This PR updates the i18n strings.

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Refreshes i18n across many locales, adding translations for
groups/invites, file‑linking, auth/terms/privacy prompts, and related
UI; removes/renames a few deprecated keys.
> 
> - **i18n/localization**:
> - Add new strings across
`apps/dotcom/client/public/tla/locales*/**.json` for: `groups`
(create/delete/leave, settings, members/roles), `invites` (copy,
accept/join, invalid link), `file links` (add/paste URL), `auth` flows
(continue with email, Google sign-in, verification code, guest),
`legal/cookies` (accept analytics, terms of use, privacy policy), and
various UI prompts (save, show more/less, danger zone, room limits).
> - Adjust/rename some keys (e.g., pin/unpin/upload strings removed or
replaced) and add richer status/tooltip texts (rate limiting, errors,
read‑only, publish/unpublish).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
dd016a8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: huppy-bot[bot] <128400622+huppy-bot[bot]@users.noreply.github.com>
Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com>
This PR improves fairy collaboration 2.0.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds project orchestration with awaitable tasks, splits working mode
into drone/solo, introduces personal todo list and viewport-bound parts,
and updates UI/debug for projects and in-canvas tasks.
> 
> - **Agent/Modes**:
> - Split `working` into `working-drone` and `working-solo`; add mode
hooks `onEnter/onExit/onRequestComplete` and mode transition
notifications.
> - New wait/notify system: agents can `waitFor` conditions; notify on
`task-completed` and `agent-mode-transition`.
> - **Actions**:
>   - New `await-tasks-completion` action.
> - Rename/adjust: `create-solo-task` → `create-task`;
`update-todo-list` → `update-shared-todo-list`.
> - Separate task completion: `mark-my-task-done` (drone) and
`mark-task-done` (solo).
> - Improve `start-task`, `direct-to-start-project-task`,
`fly-to-bounds`, `sleep`, `update-personal-todo-list` behaviors and
interruptions.
> - **Prompt/Parts**:
> - Split `viewportBounds` into `userViewportBounds` and
`agentViewportBounds` parts.
> - Add `personalTodoList` and augment `currentProject` with `role` and
`plan`; update system prompt for orchestration.
> - **Schema/Types**:
> - Extend `FairyProject` with `plan`; add `FairyWaitCondition` types;
export new parts/actions.
> - **UI**:
> - Group chat → project creation flow with `onStartProject`; HUD header
shows "Create project" for multi-select.
> - In-canvas task list: bounds overlay (debug flag), component rename
to `InCanvasTaskList`.
> - Debug dialog: separate Home/Fairy options; global `$fairyDebugFlags`
with `showTaskBounds`; project inspector shows `plan`.
> - **Styling/Assets**:
> - Add styles for debug/options and task bounds; new `propellor` hat
variant.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
82641ab. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Max Drake <maxdrake46@gmail.com>
Add some basic group e2e tests. There's a ton more we could add, but
went for the most basic ones for now as they do tend to be on the slow
side.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds end-to-end tests and fixtures for groups (CRUD, reorder, file
ops, sharing) with supporting test utilities and minor test-id/DO route
adjustments.
> 
> - **E2E Tests**:
> - Add `tests/groups.spec.ts` covering group create/rename/delete,
expand/collapse persistence, reordering via drag, file
create/move/drag/pin/unpin/duplicate/delete, move-to-home, and
sharing/invitation flows with cross-user propagation.
> - **Fixtures/Helpers**:
> - New `GroupInviteDialog` fixture; register in `tla-test.ts` and
`helpers.ts`.
> - Extend `Sidebar` fixture with group operations, file operations
within groups, drag-and-drop utilities, and invite-link copy methods;
add `createGroupButton` selector.
> - `Database` fixture: add `enableGroupsFrontend()` and simplify
cleanup to hit `prepare-for-test` without legacy header.
>   - Remove init-mode code from `HomePage`.
> - **App UI**:
> - Change sidebar create-group button test id to `tla-create-group` in
`TlaSidebar.tsx`.
> - **Sync Worker (test utilities)**:
> - Simplify `__test__prepareForTest`: always set `groups_backend`,
clear all user groups, and recreate home group; update
`/app/__test__/user/:userId/prepare-for-test` route accordingly.
> - **Cleanup**:
>   - Remove `run-both-modes.sh` and `init-mode-verification.spec.ts`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
18c142a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
see title

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Stops including <!-- CURSOR_SUMMARY --> sections when generating the
draft changelog.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fbfa8d7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This adds a variant of the sign in modal which automatically accepts the
group invite.

<img width="1134" height="1312" alt="CleanShot 2025-11-18 at 13 41
01@2x"
src="https://github.com/user-attachments/assets/aa29dd03-21d9-4a06-97c7-52abd6bf897c"
/>


### Change type

- [x] `other`

### Test plan

1. Create group invite link.
2. Accept it in another browser that is logged out.
3. You should see the new sign in dialog variant and after logging in
you should see the group you were invited to.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds an invite-aware sign-in flow that auto-accepts group invites,
updates invite/sign-in dialogs and routing, and refreshes related i18n
strings.
> 
> - **Auth/Invites**:
> - Add invite-aware `TlaSignInDialog` that shows group info, supports
Google/email sign-in, and triggers auto-accept via `onInviteAccepted`
with `?accept=true`.
> - Simplify `TlaInviteDialog`: remove sign-in branch; always show
“Accept and join group” (no HTML in copy).
> - **Routing/URLs**:
> - Use `routes.tlaInvite(...)` for invite links in
`GroupSettingsDialog` and Google OAuth redirect.
> - `local.tsx`: when logged out, show `TlaSignInDialog` for invites and
navigate to invite URL with `accept=true` on completion.
>   - `file.tsx`: only show `TlaInviteDialog` when user is signed in.
> - `useInviteDetails`: fetch invite by `inviteSecret` from location
state and clear it via `navigate(replace)`.
> - **i18n**:
> - Replace invite title string with plain text (`You have been invited
to join group:`) and add `Sign in or create an account to accept the
invitation.`; remove older HTML-based keys.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1d8e53d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
An example showing how you could use a callback with `onInteractionEnd`

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a new example demonstrating `onInteractionEnd` to control
behavior after translating a newly created shape.
> 
> - **Examples**:
> - Add
`apps/examples/src/examples/interaction-end-callback/InteractionEndExample.tsx`
showcasing a custom `QuickShapeTool` that:
> - Creates a `geo` shape on pointer down, switches to
`select.translating`, and uses `onInteractionEnd` to update the shape's
`fill` to `pattern` and return to `quick-shape`.
> - Add `apps/examples/src/examples/interaction-end-callback/README.md`
documenting usage of `onInteractionEnd` for translate/resize/rotate
interactions.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ecafafb. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Add an example to demonstrate the new `snapReferenceHandleId` property.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a new example showcasing `snapReferenceHandleId` via a custom
Y-shaped shape and accompanying README.
> 
> - **Examples**:
> - **New example**
`apps/examples/src/examples/custom-relative-snapping/CustomRelativeSnappingExample.tsx`.
> - Defines `YShape` and `YShapeUtil` with four handles; arms use
`snapReferenceHandleId: 'center'` for shift-angle snapping.
> - Renders three lines from center to arms; updates arm positions on
handle drag.
>     - On mount, creates and selects a `y-shape` at viewport center.
> - **Docs**:
> - Adds `README.md` with metadata and brief explanation of
`snapReferenceHandleId` for control-point angle snapping.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2dd9aaf. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This pull request updates the English localization and sign-in dialog
messaging to unify and simplify the description of tldraw as a product.
Several older, redundant, or less concise messages have been removed and
replaced with a single, clearer message about tldraw being a free online
whiteboard and the benefits of creating an account. This ensures
consistency across the UI and improves the onboarding experience for new
users.

**Localization and Messaging Updates:**

* Replaced multiple older messages (e.g., "tldraw is a free and instant
virtual whiteboard.", "Create a free account to save your work,
collaborate in real-time, and more.", and "Sign in or create an account
to accept the invitation.") with a new, unified message: "tldraw is a
free online whiteboard. Create an account to save your files and work
with your friends." in both `en.json` and `locales-compiled/en.json`.
[[1]](diffhunk://#diff-00aff287a2ba64df0ac85108a2a353c57dbd6ff2fcf0af506a371533b0ae27ecL26-L37)
[[2]](diffhunk://#diff-00aff287a2ba64df0ac85108a2a353c57dbd6ff2fcf0af506a371533b0ae27ecR260-R262)
[[3]](diffhunk://#diff-00aff287a2ba64df0ac85108a2a353c57dbd6ff2fcf0af506a371533b0ae27ecL450-L452)
[[4]](diffhunk://#diff-172557233305d8563de0969a2f73486aaafa7f44e657aebd4664797a492ac730L54-L59)
[[5]](diffhunk://#diff-172557233305d8563de0969a2f73486aaafa7f44e657aebd4664797a492ac730L72-L77)
[[6]](diffhunk://#diff-172557233305d8563de0969a2f73486aaafa7f44e657aebd4664797a492ac730R560-R565)
[[7]](diffhunk://#diff-172557233305d8563de0969a2f73486aaafa7f44e657aebd4664797a492ac730L1048-L1053)

* Updated the `TlaSignInDialog.tsx` component to use the new unified
message in both the invitation and default sign-in flows, removing
references to the older, now-deleted messages.
### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Unifies sign-in dialog copy by replacing multiple older strings with a
single message and updating the dialog to use it in both invite and
default flows.
> 
> - **Localization (en)**
> - Remove redundant strings from
`apps/dotcom/client/public/tla/locales/en.json` and compiled
`locales-compiled/en.json`.
> - Add unified message `"76dea3c9ca"`: `"tldraw is a free online
whiteboard. Create an account to save your files and work with your
friends."`.
> - **UI**
> - Update `src/tla/components/dialogs/TlaSignInDialog.tsx` to display
the new unified message for both invite and default sign-in cases;
remove references to deleted strings.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3bfc213. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Expands i18n across the Fairy UI (hat names, HUD toggle labels),
includes `src/fairy` in extraction, updates locales, and converts Fairy
Debug View to plain strings.
> 
> - **i18n**:
>   - Include `src/fairy` in `i18n:extract`.
> - Add numerous Fairy-related messages to `public/tla/locales*/en.json`
(hat types, HUD toggles, group chat, task list, etc.).
> - `src/fairy/FairyConfigDialog.tsx`: map hat variants to translated
names.
> - `src/fairy/FairyHUD.tsx`: use i18n for toggle labels; plumb labels
through header props.
> - `src/fairy/FairyGroupChat.tsx`: minor label tweaks to use translated
"Name"/"Personality".
> - **Debug**:
> - `src/fairy/FairyDebugDialog.tsx`: replace i18n components with plain
strings for titles, labels, and headers.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d6798b5. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
we were pushing the new branch using the tag as a ref before the tag had
been pushed, this command pushes the tag in addition to the branch

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Updates `internal/scripts/publish-new.ts` to push the release branch
and tag in one command using `--follow-tags`.
> 
> - **Release script (`internal/scripts/publish-new.ts`)**:
> - Replace two separate `git push` calls with a single `git push origin
${gitTag}:${branchName} --follow-tags` to push both the release branch
and annotated tag.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
dd29e6e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
we forgot to put this pin icon behind the feature flag.

<img width="261" height="175" alt="image"
src="https://github.com/user-attachments/assets/8cc45169-02ab-4bf9-a4c3-dd993fac3b4d"
/>


### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Show the sidebar inline pin icon only when `groups_frontend` flag is
enabled (in file link and rename input).
> 
> - **Sidebar**:
> - `TlaSidebarFileLinkInner` and `TlaSidebarInlineInput` now render
`pinIcon` only when `isPinned` and `useHasFlag('groups_frontend')` are
true.
>   - Added `useHasFlag` usage to both components.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
8bcb01d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
argh so this wasn't creating a branch, chatgpt lied to me 😭

### Change type


- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Corrects git push to create/update the release branch by pushing HEAD
to `refs/heads/<branch>` with tags.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
67c4515. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This PR adds an example that demonstrates how to use tldraw's various
"easter egg" styles programmatically.

It shows how to use:

- White
- Fill
- Lined fill
- Label color
- Scale

### Change type

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

### Test plan

1. Open the easter egg example.
2. Check you can see the five demo shapes, and that they have the
correct styles.

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

### Release notes

- Added an example showing how to use tldraw's hidden easter egg shape
styles programmatically.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> <sup>[Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) is
generating a summary for commit
518f80e. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Removed trailing periods from 'Open tldraw.' and 'Back to tldraw.'
messages in ErrorPage, and clarified the rate limit error message in
TlaFileError to 'Too many requests. Please slow down.' for improved user
understanding.

### Change type

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Standardizes error copy (removes trailing periods and clarifies rate
limit text) and updates English locale entries accordingly.
> 
> - **UI copy**:
> - Update `ErrorPage` go-back link text to `Open tldraw` (in iframe)
and `Back to tldraw` (no trailing periods).
> - In `TlaFileError`, change rate limit message to `Too many requests.
Please slow down.`
> - **i18n (en)**:
> - Add/update keys for `Back to tldraw`, `Open tldraw`, and the new
rate limit message in `apps/dotcom/client/public/tla/locales*.json`.
> - Remove old variants with trailing periods and the previous `Please
slow down.` string.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
43a1b2a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

We're not using PostHog surveys, so disable the surveys module to
prevent loading an unnecessary 30.2 kB script at runtime.

This adds `disable_surveys: true` to the PostHog configuration to
prevent the surveys module from being dynamically loaded.

## References

- PostHog PR adding the `disable_surveys` option:
PostHog/posthog-js#1123

## Test plan

- [ ] Verify that `surveys.js` is no longer requested in the network tab
when loading tldraw.com
- [ ] Verify that analytics still work as expected (event tracking, page
views, etc.)

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Disable PostHog surveys by setting `disable_surveys: true` in the
analytics config to avoid loading the surveys module.
> 
> - **Analytics**:
> - Update `apps/dotcom/client/src/utils/analytics.tsx` PostHog config
to set `disable_surveys: true`, preventing the surveys module from
loading.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
451912a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude <noreply@anthropic.com>
Duplicate tracking removed:
- Code copy now only fires `click_copy_code` via `trackCopyCode()`,
removed duplicate `docs.copy.code-block` PostHog event
- Both button clicks and manual copy (Cmd+C) tracked separately, no
duplication

  Consent tracking fixed:
  - Renamed `consent_changed` → `consent_update`
  - Disabled GTM-specific consent events in enable/disable
- Now fires single `consent_update` with proper structured consent
object:

```ts
  {
    event: 'consent_update',
    consent: {
      ad_user_data: 'granted',
      ad_personalization: 'granted',
      ad_storage: 'granted',
      analytics_storage: 'granted'
    }
  }
```

  Cleanup:
  - Removed unused name param from CopyButton

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Centralizes consent_update emission in analytics core and removes
duplicate docs copy tracking by standardizing on trackCopyCode and
simplifying CopyButton.
> 
> - **Analytics**:
> - Centralizes consent tracking in `apps/analytics/src/index.ts`,
emitting `track('consent_update', { consent: ... })` with detailed
grant/deny state.
> - `apps/analytics/src/analytics-services/gtm.ts`: stops pushing
consent updates on `enable/disable`; retains identify/event/pageview
flows.
> - **Docs**:
> - Standardizes code-copy telemetry via
`window.tlanalytics.trackCopyCode` only; removes custom
`track('docs.copy.code-block', ...)`.
> - Simplifies `CopyButton` API (removes `name` prop) and updates call
sites in `pre.tsx` and `code-files.tsx`.
>   - Keeps Google Ads conversion firing on copy in `app/analytics.tsx`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
eacfd14. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Make sure we correctly send the changes (after services are enabled and
before they are disabled).

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Reorders consent tracking so `consent_update` fires after enabling on
opt-in and before disabling on opt-out, ensuring the event is captured.
> 
> - **Analytics consent flow (`apps/analytics/src/index.ts`)**:
> - Reorders `consent_update` tracking relative to service state
changes:
>     - On `opted-in`: enable services, then track `consent_update`.
> - On opt-out/unknown: track `consent_update`, then disable services.
> - Centralizes `consentState` construction for granted/denied fields
used in the event payload.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cd54b1a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
continues the work done in tldraw#6739

### Change type

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

### Test plan

1. Tested manually in
https://github.com/Andarist/tldraw-augmentation-test
2. The most complex `TLIndexedShapes` also tested manually in [TS
playground](https://www.typescriptlang.org/play/?ts=6.0.0-dev.20251027#code/JYOwLgpgTgZghgYwgAgCoBkBCcDOEDKAFnAA4QA8qAnmQDTIAKUA9iTgHzIDeAUMsmBoQAXGiEBuPshIs2opqxySAvjx6CyadAEEoLAO5FSKALxbseI2XIAiOHub6b9LsgAmwKKJxgooAObIyuySGigYmMwAHlam5rgExNY2AEbRztzIUaIgAK4AtinQ4shUOQVFUEEh6kJaAML2brHIZhEJsbYITRmuCMwANsxeyD5+IIHBoXUYAOIsuSQtbVgdSRQ2-gskvcjAbt6+AdXTmhgAavsQzMvxluu2AG5XzLtgwPkiyHmFxdIDcCoAVEaUGEDgIBOajCWgAIhB4LkBmBllIAD5aXQGWLo8zRHH8DEYRpQZrrXFzbYE5BE9CXNzXWKSHgQKIkYZgPbgaDwJBaWZDFJwAaxBRsACypG4qhZbI5XMgsEQ4XQAuYQpF6zFOElJG4Um6pPKAwGJQA9GbkKyEANcgzkPpgGBCMh4AMcFQDbkfMx8qJXEKAF4BOCHcaTc2W1mQEBuB1Ol1wZD9fIkAYQSADKjJ71gX2jcn8Z4M5j+gQfL4-SolNOA4HIUHpiElYVgVDRsPHZSRq1RGNxhmI5EF4xSLbMRbG03IC17MAAchwyCT-SgKBw63oTuTEPnnKK7mAODgKXTbjNzEe0D8DJAPBlrPZUE5MIwAElY6yIGTjEuzLx+FnAB1FAnWgOBIGQS9oGXZBchAYBmEhZgYGXXJ-E+bk4wAawgKglwhAcETgJFOQ3YwBCEHAADopFnVBCCPZcTUcJdvQEZguRtO0UAACmGZMkI8d4kOFLNe24+0BOgvQrgASgEQgUEHEjh3Isgl1AKCQBQfxmFowCzToy0P0UlAAANcHM5MAW9FB9BQewUBgYBkWgY4J05Dxj1Pb8Rw0gzkAAbQAaS5KRCWQXCqBQ-lBWFUVZB1UgIppOFiNI2IgpsMIbAAXWXJcwujCBYyXDB4SHFF1my3K8tSgB+GdLVXdd1gItdmP0QECMeOBXJPdNl1jHcQBAZh9xQGSb1vUZOIc5AACtcy4217WdCB8lS-hir7Uq3CXTZthsbbkCakLTtEXb+yXaLYrmeLNWMbVdVOprZ2AVDtxKsrYLyE0jxdPrbRQDa4LwKpiCXbzBr850mPU0G6hwebnNcxU5yg3JOUdZ1vggK8oFO-gHvVBKtSS3VQoKn6Du+JEBjS+DB1Ab9ibO-HCfZq7LuQC7+H4K68qu3sbuQI6Jx2RrmsEzrEY6xyBm6-Dlz6gbfOGuNujGiaGymwmZtKuaHRQZafFWnizK2gWBYAUT7KBEDASh0Eq1TquMFxKLIEXglSkXafK12MuRLKcqEfLpY+r6FwI9wQ7I9ZvfslAdLhziECUhBsL2GPFz1o3pv2W9Tuu-bbrw+7VUexLFFem3+Hey1PsxqGC+Qg2rkhDbITssycx9fJ-OUhFQCdRC7wbgX2nuYxyBC+hSY1WuJVIan2F56PW9wb5daKQvO9mnvkDXPvWSPd4JmT5jgASJcYAEoowAxmFvNrT0p-4e3fCdl23cyzcmQwi+w3jbUQs4+5gwQLmfMiN44uQQiJSeDcZ6JDngvOKZMnpkBemvMKAAyKKldUJL3Js9SmpA8o1G7GoWca4UYDCvHGd8n4ojfliDgYQPALTcMtK+dAH4GRsJ-BpVo+pZw22gYPUQqDOg2CkXmfIuxZwKNgeSCRn8gwhk7BMSQGj+BTF4Q3Ys1wZGrFnskExrwvazgAFS02-LY5AVijGf3LJ8covwoB6MtG49+9ZGzghAD4txzE2wdlGEcXRriBaGP0cuBw+gzFYkcEyGWrUEkGBifwNI2Q8QxHWD2DJuSjGqFnLOWEAB5W2+AABy85UDIGIFeZA9ioiSUcTuUkPAgA)
(previous iteration can be found in: [TS
playground](https://www.typescriptlang.org/play/?ts=6.0.0-dev.20251027#code/JYOwLgpgTgZghgYwgAgCoBkBCcDOEDKAFnAA4QA8qAnmQDTIAKUA9iTgHzIDeAUMsmBoQAXGiEBuPshIs2opqxySAvjx6CyadAEEoLAO4K2yALzdkAE2BRROMFFABzZKo0oMug0VIozGbHjeZOQA5HB6zPoh9B4RhrIc6kJamMwAHkY4puZpoiAArgC2AEbQ4shUeUWlUC5Jmv7pQb4puATEwSHF6dEp6Zns9e7oAMLhFpnZXMgIzAA2zDbIdg4gzq7JGGNQFs3Z-m3NoQjjvVvjA0NaAGrAFhDMk2bTYMCFIsgFJWXSc3BUTlE3XmEDgIDqbhudweez8WEOHQoIQAbtDmGd0Ld7o8EoMrhgACIQeD5OZgWFaTyRPYAHz6aVpWm2u0RyDpGCxMMRanuCD+UBQsxAdgEcxGi3aPlE+RAwAAjvkUDgqCV5jwIGkSIswAJkuKBUdUJw-GzdWRmDBRfrJZoAPS2mYS5aIrInEAgZg60rIZjI6AOCz3EA8Xn8wXMYU6sBzIkksnNaWyhVKlXdObqzXas0oADyfqg+gcYDgxTmEFjcFJ5MRlGNaFNBTmc1NMvuMFAEAsprcFtFFarzTUoEgsEQw378cRmQAsqRuFJwgZRHnoIXgMXS+XiZXJz5KDo4gNJPwTjtl-m1xuyxPq3vzjsj1JurlkNbDVh+rjj8hUdjz6uixLa9twHGsOTRR9VAzLUoB1YdoHgJAtAAcQWYo4DmZoZznDVIBACwskJEDdzIbCSG4KCNRguDwAQscULQjCsISWdyN4E9xiqJtyntZANT5fJ7mQfR10IZB4DmZUpAQfI7GYQpRGmdCAC8nDgWx7CcFweIdXCIHw4TROQOBHUKEgy0gOYqBmWSwHk50fCkX8HkUgQ3g+L4anKcz-kBZBgTLMFygwsBUFwjTVnWHS+LSPCu1AYzTJIOBXk3YT-h4SjM1g5B4NHJCMFQ5h0MwqcWLned+F4xdIlc-RiEgfMIq05RkF40BZjMlLgDSvThWACMZgFFKICyME+IiWpgEtKhmHyZAZU695wFysBMrUKis0hDAAEl8I1TtmiyZ4eAASHa8EwEIFAAANcBumY-lklB9BesEo2YcTgDJaAfXyHUrBwIDOwcsgcDOgBtABpXLwQAawgWbLUKxjSp8MjjKyGG+oI5AEaRrQb2YxRWLO06AH42odWYBVB0bjNpjD9H+MbkTgb7geMgy3Q9L0UF9f07iDZZPte5AACtbNhgShKuiBCjJ06e0taM31ZHHCPQImypJ0hoYAXUVymocV0R2stdcYrisbPlJOZgBwMS2bmRUBGuha8FqYgskB4Guyuh26ezEXhJQdsfqmnU5p1ESrs+CB80V06UeKpidbYViDat-TccbZs6VbYkOwsJPKZABPoCT0QTdO07q-1s3qadR2fDGxm5mZqhWfZv40rBLsec9fz+fzAMgzO7HYpzrJ8d7aZoYAJlhvHEd7Ii41vUjyvI3Bs17VWJT2DXCeIzeIDIxf9eQY2l7yCuoAbk+N+JjO9ahher9UWvKYAUViqBEBgH3NrHw9AXhCGri4QYtdRBVQdNNN2KZVTNkDnYb6zY5YCnoJbd4YIshyw9r9b2nwh4CwLEWPCq1Fa8RujDAAZCvAm68dxnzIg9TSCA4a5SyM9Ls11aZ2T4sKfIAjCCB2KAAkACAxIRissZEg5lgD00EQQtsLCg5uBwLQahDpPT8JEngGyclCg+nLj7Iu5cuz2wRsgG6JBQCKWUA9fQc05hdmSjgfBYjXTXU4YrZWfZT5HynvhTWRUSovxwJnehjC15ayCenKJpBDa1wpvwKmHstI3QOIEGsUN6B-xlhQcJad0bbwNvQPOLZ9rtksd2IQ+8YwJJ8OwdgzjXFdhcVALh-CIBJ14sUf6AhPqFDgDYyEgMfJd2QIUJ0QoEAOwgPQMWz1EGOj0KNLU+EtJtmLsHDCwA2jg1SadP+9hAHAOaXQcwbhIHKGgTA9JvFakYSssslAw1pFrJksYumWRen0FUQ7P2fzQ7t2GhYay4cRwg2jv5ayBC7o4AenySseAk45JtBQfJyAAByEZcV22BvuEpaMt66xIAbVp60eC8QFDgeYfouy7X2mkQ6LphC0ttFy4OLL7hspZK3KYPL0lGLsgpVouS9wACIfniulWAjJcr7ItzICK0V6SVJqWamsSQvFRX3L1Q6UVzlmCiExUcaVpqFXmF4gAKhxp2O1P40Tqo1W5d4VRvhQCNe69JUy-IBVBCAX1frjJkjCrFHVjhQ3pMNW6hmS5KRxGaNFGmKAar6ATc+c1H4GSIjTU6Z8PLVC8V4gSHMP98C4pCKgZAxA-TIAdWkIpFhnWngsEAA))

### Description

The main goal of this PR is to allow users to provide users to strictly
type all their custom shapes' props.

Previously people would have to resort to using an unknown shape,
casting, or "casting" through generics (where tldraw types would just
accept their type arguments happily). With the new system in place users
can augment a builtin tldraw interface like this:
```ts
const CIRCLE_CLIP_TYPE = 'circle-clip'

declare module 'tldraw' {
	export interface TLGlobalShapePropsMap {
		[CIRCLE_CLIP_TYPE]: {
			w: number
			h: number
		}
	}
}

export type CircleClipShape = TLShape<typeof CIRCLE_CLIP_TYPE>
```

Once this is done, users can now use the shape type like this:
```ts
editor.updateShape({
	id: shapeId,
	type: 'circle-clip',
	props: {
		w: 100,
		h: 100,
	},
})
```

Thanks to the power of discriminated unions, this will now error if the
user tries to update the shape with the wrong props.

That said, for the time-being, I kept generic parameters in the editor
methods to:
- maintain backwards compatibility
- keep this documented pattern working:
https://tldraw.dev/docs/shapes#Meta-information

⚠️ this allows users to bypass the created augmentation system
and locally call methods with shapes having *more* properties than
prescribed by the augmentation system. Given the TS structural type
system... this is something that can always happen when arguments are
not passed inline so it's largely fine as the users should start using
the new system anyway. This should allow them to do it gradually though.

This PR consists of:
- the mentioned augmentation system. Related changes are mostly local to
[`TLShape`
file](https://github.com/tldraw/tldraw/pull/7091/files#diff-74a7534c2bdfff2f6ef9e068496c69dca2a7b09d05c1ff482485fd30480694d3)
- using the new system in the
[examples](https://github.com/tldraw/tldraw/pull/7091/files#diff-5f23c6b955fcdcf8a402446d96178e12241ca0cb01af4e73535cc1ca261f81f0),
[templates](https://github.com/tldraw/tldraw/pull/7091/files#diff-5845112b17d3eb903a41e90e834858e5b8ab71b6350348811d183137824e4f07)
and tests
- updating the docs to reflect the new system
[here](https://github.com/tldraw/tldraw/pull/7091/files#diff-1b0ad2aab7ced6fc26f40f9929d2d16269b8fa505bdf4b18b5b746facc5bcefc)
and in the "guides" in the examples
- followup code changes required by the new system, they can be found
all over the place.
- the same kind of changes related to the bindings (this PR includes the
work done in tldraw#7133 )

### API changes

Numerous typings changes! make @ds300 compile a list

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces module-augmentation-driven typing for custom shapes and
bindings, updates core editor APIs and utilities to support it, and
migrates docs, examples, templates, and tests accordingly.
> 
> - **Types & Schema**:
> - Add `TLGlobalShapePropsMap`/`TLGlobalBindingPropsMap`,
`TLIndexedShapes`/`TLIndexedBindings`, `ExtractShapeByProps`, and
`TLCreateShapePartial`.
> - Refactor `TLBaseShape`/`TLBaseBinding` and shape/binding record
validators to work with augmented types.
> - **Editor API**:
> - Overload `getShapeUtil`, `isShapeOfType`, and `getBindings*` for
typed shape/binding keys.
> - Update `createShape(s)`/`updateShape(s)` generics and accept typed
partials.
> - Tighten types across selection, snapping, export, reparenting, etc.
> - **Bindings**:
> - Generic `BindingUtil`/callbacks now use typed `TLBinding`;
connection/arrow binding utilities updated.
> - **Docs & Guides**:
> - Rewrite shape/binding guides to use module augmentation
(`TLShape<typeof TYPE>`), update API docs.
> - **Examples & Templates**:
> - Migrate all examples and app templates to declare module
augmentations and use typed shapes; remove legacy generic casts.
> - **Tests**:
> - Update tests to new typing model and API overloads; add tests for
type narrowing and create/update generics.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ac324a6. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Our claude config has a hook set to run prettier on
`Edit|MultiEdit|Write`. This causes issues with imports as it always
does the import first, then writes (which runs prettier and removes the
unused import), only then it adds the usage.

This PR uses an alternative. What if we only trigger prettier on `Stop`
event, which triggers when claude is done. Think it wont trigger if
claude gets interrupted by the user.

Worth a try, can always revert.

### Change type

- [x] `other`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Switches the Claude hook to run Prettier on Stop, formatting only
changed files detected by git diff.
> 
> - **Config (`.claude/settings.json`)**:
>   - Switch hook from `PostToolUse` to `Stop`.
> - Update Prettier command to format only changed files (`git diff
--name-only --diff-filter=ACMR | grep -E '\.(ts|tsx|js|jsx|json|md)$' |
xargs -r yarn run -T prettier --write`).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2ff3d46. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This PR lands the completed FaeOS. There is still further UI and backend
work to do.

### Change type

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

### Release notes

- improve 2-fairy experience
- add memory gates for fairies
- finalize model choice improve support for various models
 

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduce duo-project orchestration and model selection, add
memory-level gating, refactor tasks (string IDs, titles) and
persistence, plus prompt/worker updates and UI/debug improvements.
> 
> - **Fairy Orchestration & Modes**:
> - Add duo orchestration flow (`duo-orchestrating-*`,
`working-orchestrator`) with new actions:
create/start/direct/await/complete duo tasks and end duo projects.
> - Update group chat to start duo projects and set roles;
sidebar/orchestrator indicators support `duo-orchestrator`.
> - Mode chart gains waiting states, prompt start/end/cancel hooks, and
response-time logging.
> - **Tasks & Persistence**:
> - Refactor tasks to string IDs with `title`; drag tool, list UI, and
actions updated accordingly.
> - Rename persisted `sharedTodoList` → `fairyTaskList`; agent state
uses `personalTodoList`.
> - **Memory & Chat History**:
> - Introduce memory levels (`fairy`|`project`|`task`) and
`memory-transition` items; filter chat history by current mode.
> - **Model Selection & Usage**:
> - Add selectable model (`gemini-3-pro-preview`, `claude-4.5-sonnet`,
`gpt-5.1`) via debug UI; include `modelName` prompt part.
> - Track cumulative usage and cost per-model (incl. cached input);
worker selects provider by model and adds Anthropic cache breakpoints.
> - **Prompt/Schema Updates**:
> - Expand agent action schemas and prompt parts for duo/project
context; system prompt updated for solo/orchestrator/duo flows.
> - **UI/UX**:
> - Debug view: model dropdown, response-time flag; project/task
inspectors; "Clear task list" → "Disband projects".
>   - HUD spinner optimization and minor label tweaks.
> - **i18n**:
> - Add strings for model label, response-time logging, and disband
projects; remove obsolete "Clear task list".
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
231d734. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Max Drake <maxdrake46@gmail.com>
Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@NathanFlurry NathanFlurry deleted the feature/sync-rivet branch January 20, 2026 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.