[pull] main from tldraw:main#573
Merged
Merged
Conversation
In order to stop the fill dropdown trigger from showing a misleading tooltip, this PR makes the trigger describe what the dropdown opens instead of the currently selected value. Closes #8955. The fill style panel shows the main fills (`none`, `semi`, `solid`) as buttons and the extra fills (`pattern`, `lined fill`, `fill`) behind a dropdown trigger. The trigger's tooltip was always built from the current selection, so hovering it with a main fill selected showed something like "Fill — Solid" — a value that isn't even in that dropdown. Now the tooltip only names the selected value when that value is actually one of the dropdown's items; otherwise it just shows the style name ("Fill"). This mirrors the existing icon logic, which already falls back to the first item when the current value isn't in the dropdown. Standalone dropdown pickers (geo, spline, arrowhead kind) always hold the current value, so their tooltips are unchanged. ### Change type - [x] `bugfix` ### Test plan 1. Select a shape with a fill (or set the fill to `none`, `semi`, or `solid`). 2. Hover the fill dropdown trigger (the last item in the fill row). 3. The tooltip reads "Fill", not the name of the currently selected fill. 4. Open the dropdown and select an extra fill (e.g. pattern); the trigger tooltip then reads "Fill — Pattern". - [x] End to end tests ### Release notes - Fix the fill style dropdown trigger showing a misleading tooltip for the currently selected fill.
Lets group owners demote themselves to admin from the group settings dialog, but only when the group has another owner. The role dropdown now appears on your own member row when `ownersCount > 1`; with a single owner you still see read-only role text. The backend mutator `setGroupMemberRole` already permits self-targeting and blocks demoting the last owner, so this is a client-only change to surface the dropdown in the valid case. ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan 1. Create a group with two owners. 2. Open group settings as one of the owners. 3. On your own row, change your role from Owner to Admin — succeeds. 4. With only one owner, confirm your own row shows read-only role text (no dropdown). - [ ] Unit tests - [ ] End to end tests ### Release notes - Group owners can now demote themselves to admin, as long as another owner remains.
In order to stop the Zero Cloud promo banner from printing on every local zero-cache startup, this PR sets `ZERO_ENABLE_STARTUP_MESSAGE=0` in the dev `.env`, alongside the existing `DO_NOT_TRACK` opt-out. ### Change type - [x] `other` ### Test plan 1. Run the zero-cache dev server (`yarn dev` in `apps/dotcom/zero-cache`). 2. Confirm the "Cloud Zero is now available" startup message no longer prints.
This simplifies tldraw's multi-click handling. Previously `ClickManager` tracked double, triple, and quadruple clicks, each emitting its own event with `down`, `up`, and `settle` phases. In practice only double-click and the hand tool's zoom shortcuts used the higher counts, and the `settle` phase couldn't tell a multi-click that finished with the pointer held apart from one that finished with it released. This PR: - Removes `triple_click` and `quadruple_click` entirely. `TLCLickEventName` is now just `'double_click'`, and the `onTripleClick` / `onQuadrupleClick` handlers are gone from `TLEventHandlers` and `StateNode`. - Splits the `settle` phase into `settle-down` and `settle-up`. `ClickManager` tracks whether the pointer is pressed when the click timer fires and emits the matching phase. - Fires double-click-to-edit on the `down` phase. Select tool child states (`Idle`, `PointingShape`, `PointingCanvas`, `PointingHandle`, `PointingSelection`, and the crop states) now act on the second pointer-down instead of waiting for pointer-up, so editing and handle behavior trigger immediately. - Updates `HandTool` to keep only double-click zoom-in (on `settle-up`); triple-click zoom-out and quadruple-click reset/zoom-to-fit are removed. ### API changes - Breaking! `TLCLickEventName` no longer includes `'triple_click'` or `'quadruple_click'`; only `'double_click'` remains. - Breaking! `onTripleClick` and `onQuadrupleClick` are removed from `TLEventHandlers` and `StateNode`. - Breaking! `TLClickEventInfo.phase` replaces `'settle'` with `'settle-down'` and `'settle-up'`. Consumers that checked `phase === 'settle'` should switch to `'settle-up'` (or branch on both if they care whether the pointer is still pressed). - `TLClickState` drops `'pendingTriple'` and `'pendingQuadruple'`. ### Change type - [x] `api` ### Test plan 1. With the hand tool, double-click the canvas and release — should zoom in. 2. With the select tool, double-click a shape — should enter editing immediately on the second press. 3. Confirm triple- and quadruple-clicks no longer zoom out or reset zoom with the hand tool. - [x] Unit tests - [x] End to end tests ### Release notes - Simplified multi-click handling: triple- and quadruple-click events are removed, and the `settle` click phase is split into `settle-down` and `settle-up` so tools can tell whether a multi-click finished with the pointer held or released. ### Code changes | Section | LOC change | | ---------------- | ------------ | | Core code | +157 / -112 | | API report | +3 / -15 | | Tests and infra | +107 / -141 | | Documentation | +55 / -63 | --------- Co-authored-by: Max Drake <maxdrake46@gmail.com>
…flame chart (#8312) In order to make the Chrome Performance flame chart more readable when profiling the examples app, this PR disables React's `console.createTask` instrumentation by setting it to `undefined` before the app script loads. React uses `console.createTask` to wrap every component render in a "Run console task" entry. While this provides async stack traces in DevTools, it adds significant visual noise to the Performance panel's flame chart, making it harder to identify actual performance bottlenecks. ### Change type - [x] `improvement` ### Test plan 1. Open the examples app in Chrome 2. Record a Performance trace 3. Verify the flame chart no longer shows "Run console task" wrappers around component renders ### Release notes - N/A (internal development tooling change) ### Code changes | Section | LOC change | | --------------- | ---------- | | Apps | +6 / -0 |
In order to run tests against a modern jsdom — and specifically to get a native `PointerEvent` constructor (jsdom 25 has none, which forces hand-rolled mocks) — this PR bumps `jsdom` from v25.0.1 to v29.1.1, fixes the test fallout the upgrade surfaces, and then removes the test polyfills jsdom now implements natively. It's split into two commits so the upgrade and the polyfill cleanup review independently. Relates to #9006, where a new test had to polyfill `PointerEvent` because of the old jsdom. ### What changed between jsdom v25 and v29 Newly available behaviours (relied on or cleaned up here): - **`PointerEvent` constructor** (v27) — implemented (without jsdom ever firing pointer events itself); preserves `pointerId`, `pointerType`, `clientX/Y`, etc. - **`TouchEvent` constructor** (v27) — implemented; preserves `touches`. - **`movementX`/`movementY` on `MouseEvent`** (v27). - **`TextEncoder`/`TextDecoder`** on jsdom `Window`s (v27.4) — and exposed on the vitest global, so the hand-rolled polyfills are now redundant. - Other event constructors (v27): `BeforeUnloadEvent`, `BlobEvent`, `DeviceMotionEvent`, `DeviceOrientationEvent`, `PromiseRejectionEvent`, `TransitionEvent`. - New CSS selector engine (v27), CSSOM overhaul (v29), `blob.text()/arrayBuffer()/bytes()` (v28.1), `getComputedStyle()` specificity and `!important` fixes (v28.1). Breaking changes handled: - **`element.click()` now fires a `PointerEvent` instead of a `MouseEvent`** (v27). No fallout — our suites drive clicks through the editor, not via `element.click()` with `MouseEvent` assertions. - **jsdom now fires real pointer events**, so React's canvas handlers reach the pointer-capture API, which jsdom still doesn't implement. Added no-op `setPointerCapture`/`releasePointerCapture`/`hasPointerCapture` stubs to the shared vitest setup. - **CSSOM overhaul (v29)** changed SVG-export style serialization. Updated the `getSvgString` snapshot — it now serializes the shapes' real styles instead of `getComputedStyle` polyfill placeholders (a fidelity improvement). - **jsdom 28+ ships a global `WebSocket`** (its bundled undici) whose events fail the Node `EventTarget` realm check in the jsdom test environment. sync-core tests now point the global `WebSocket` at the `ws` package, matching the `WebSocketServer` they connect to. - **Minimum Node** rose to v20.19.0+ / v22.13.0+ / v24.0.0+ (v27.0.1, v29.0.0). CI runs Node 24.13.1, so this is fine. ### Polyfills removed vs kept (commit 2) Each shim was removed and the affected suites (editor 809, tldraw 2468, sync-core 608) re-run to confirm they stay green. Removed (jsdom 29 / the vitest jsdom env provides them natively): - `PointerEvent` and `TouchEvent` constructor mocks (jsdom added these in v27) - the `DOMRect` polyfill (jsdom's native `DOMRect` is complete and spec-accurate) - the `TextEncoder`/`TextDecoder` polyfills — not just redundant but counterproductive: the shared `setup.ts` did `import { TextEncoder } from 'util'`, whose broken ESM interop on the CJS builtin bound to `undefined` and *clobbered* the native global, which the per-package `require()` calls then merely restored. - the `requestAnimationFrame`/`cancelAnimationFrame` polyfill (guarded; jsdom provides both) - the `delete global.crypto; global.crypto = require('crypto')` reassignment in `setup.ts` (jsdom's `window.crypto` already exposes `subtle`; the `node:crypto` module does not). The `@peculiar/webcrypto` fallback for crypto-less environments is kept. - the guarded `CSSStyleDeclaration` `getPropertyValue`/`setProperty`/`removeProperty` fallbacks (jsdom implements all three) Kept (jsdom 29 still doesn't provide these, or the shim does more than the native API): - the `DragEvent` mock (jsdom has no `DragEvent` constructor) - `ResizeObserver`, `FontFace`, `document.fonts`, `window.matchMedia`, `URL.createObjectURL`, `HTMLImageElement.prototype.decode`, `CSS.supports`, `Path2D.prototype.roundRect`, `fake-indexeddb` - the `@peculiar/webcrypto` crypto fallback, the sync-core `WebSocket` override, and the tldraw `getComputedStyle` override ### Change type - [x] `other` ### Test plan - jsdom-based suites pass: `packages/editor` (809), `packages/tldraw` (2468), `packages/sync-core` (608), `packages/mermaid` (53), `apps/docs` (18), `apps/dotcom/client` (133). - Each polyfill removal was independently verified by removing it and re-running the affected suites; ones that broke (e.g. the native `WebSocket`, `DragEvent`) were kept. - `yarn typecheck` passes; no public API changes (api-extractor reports unchanged). - [x] Unit tests - [ ] End to end tests ### Code changes | Section | LOC change | | --------------- | ----------- | | Automated files | +4 / -6 | | Config/tooling | +244 / -257 |
In order to remove an upward dependency from the editor package on the tldraw package's tool tree, this PR changes `FocusManager` to stop checking the select tool's `'select.editing_shape'` state path. As reported in #8888, the editor package referenced a tldraw-package tool state held together only by a string, which would break silently if the select tool were renamed, restructured, or swapped out. Instead, `FocusManager` now uses the editor's own first-class, tool-agnostic editing state via `editor.getEditingShapeId()`. This is the same state that `select.editing_shape` sets on enter and clears on exit, so the focus-ring behavior is equivalent — but it no longer couples the editor to a specific tool, and it correctly suppresses the focus ring whenever any tool is editing a shape. The blur point from #8888 ("`editor.blur({ blurContainer: false })` bypasses the manager") was already resolved separately by #8895, which centralized the blur contract on `FocusManager`. ### Change type - [x] `improvement` ### Test plan 1. Double-click a shape to start editing its text label. 2. Press Tab or an arrow key — the container focus ring should stay suppressed while editing. 3. Tab into the contextual toolbar — the focus ring should still be allowed there. - [x] Unit tests ### Code changes | Section | LOC change | | --------- | ---------- | | Core code | +1 / -2 | | Tests | +4 / -4 | Closes #8888
In order to render the agent template's context and viewport-bounds highlights through tldraw's first-class overlay system instead of bespoke React/SVG components mounted via \`OnTheCanvas\`, this PR replaces the \`AgentViewportBoundsHighlights\`, \`ContextHighlights\`, \`AreaHighlight\`, and \`PointHighlight\` components with a single \`AgentHighlightOverlayUtil\` registered through the new \`overlayUtils\` prop. The overlay util enumerates active agents, derives area/point overlays from each agent's active request and selected context items, and draws them on both the main canvas and the minimap with the existing dashed/animated styling. This branch is a clean replay of [\`max/port-agent-starter-highlight-to-overlays\`](main...max/port-agent-starter-highlight-to-overlays) split into reviewable commits. The second commit is supporting work that the overlay port surfaced: \`FocusedFontSize\` now derives the focused font size from the text shape util's display values (via \`getDisplayValues\`) instead of a static multiplier table and a hard-coded 16px base, so the conversion stays in sync with whatever the editor actually renders. The forward conversion now also forwards the text shape's \`font\` prop so the nearest size is chosen per-font. ### Change type - [x] \`improvement\` ### Test plan 1. \`yarn dev-template agent\` and open the demo. 2. Send a prompt so the agent runs; confirm the dashed viewport-bounds highlight and the agent-side context area highlights render on the canvas while generating, including the \"Reviewing\" / \"Agent ...'s view\" labels. 3. Pick context (shape, multiple shapes, area, point) before sending; confirm each highlight kind renders correctly with the selection-stroke colour. 4. Open the minimap and confirm filled rounded-rect highlights pulse while generating and stay solid otherwise. 5. Edit a text shape, change its \`size\`/\`scale\`/\`font\` and confirm focused font size round-trips correctly across a request. - [ ] Unit tests - [ ] End to end tests ### Release notes - Agent template now renders highlights through an \`OverlayUtil\`, including minimap support. ### Code changes | Section | LOC change | | --------- | ------------ | | Templates | +434 / -460 |
) Thinks this works better than preserving the center, wdyt @steveruizok? In order to stop the viewport from shifting when a zoom gesture overshoots the zoom limits, this PR makes `getConstrainedCamera` preserve the caller's focal point (e.g. the cursor) when it clamps the zoom, instead of snapping back to the viewport center. Closes #8942. When you wheel/pinch zoom out past the minimum (or in past the maximum), the gesture computes a camera that keeps the point under the cursor fixed at the *requested* zoom. The old code then clamped the zoom and re-derived `x`/`y` to keep the viewport *center* fixed, discarding the cursor anchor — so the canvas visibly jumped. Now the clamped camera keeps the same focal point fixed. When that focal point happens to be the viewport center (e.g. the zoom-out button), the math reduces to the previous center-preserving behaviour, so nothing else changes. `zoomIn`/`zoomOut` are unaffected because they snap exactly onto zoom steps and never overshoot. ### Before https://github.com/user-attachments/assets/0cc13c03-2ea5-4642-bcf4-2c5f62e57e4d ### After https://github.com/user-attachments/assets/b800a301-b101-4e98-8303-3e5801b7bbeb ### Change type - [x] `bugfix` ### Test plan 1. Open the editor at any zoom level with the pointer away from the screen center. 2. Wheel- or pinch-zoom out until the zoom clamps at the minimum. 3. The point under the cursor should stay put as the zoom hits the limit; further zoom-out gestures should not move the canvas. 4. Repeat zooming in past the maximum. - [x] Unit tests ### Release notes - Fix the viewport shifting slightly when zooming past the minimum or maximum zoom level. ### Code changes | Section | LOC change | | --------- | ---------- | | Core code | +20 / -14 | | Tests | +97 / -1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 : )