From 420fad89c51993a12cb4bb76f1b8d0ab626740a5 Mon Sep 17 00:00:00 2001 From: Guillaume Richard Date: Tue, 5 May 2026 16:09:04 +0100 Subject: [PATCH 1/3] feature(docs): process image url through an asset url helper (#8761) Following dotdev imminent release, we need to make sure all assets URLs in doc can be prefixed with the right url so we don't have issues when visitors are coming from dotdev urls ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan - [ ] visit staging docs - [ ] images are working --- apps/docs/app/layout.tsx | 15 +++---- apps/docs/components/content/image.tsx | 7 +++- apps/docs/utils/asset-url.test.ts | 55 ++++++++++++++++++++++++++ apps/docs/utils/asset-url.ts | 6 +++ 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 apps/docs/utils/asset-url.test.ts create mode 100644 apps/docs/utils/asset-url.ts diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 1531ba3f60db..b7ee5df5fa57 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -5,6 +5,7 @@ import localFont from 'next/font/local' import AutoRefresh from '@/components/common/autorefresh' import { Footer } from '@/components/navigation/footer' import { Header } from '@/components/navigation/header' +import { assetUrl } from '@/utils/asset-url' import { cn } from '@/utils/cn' import Analytics from './analytics' import './github-dark.css' @@ -37,13 +38,13 @@ export const metadata: Metadata = { statusBarStyle: 'black', }, icons: [ - { rel: 'shortcut icon', url: '/favicon.svg' }, - { rel: 'icon', url: '/favicon-32x32.svg', sizes: '32x32' }, - { rel: 'icon', url: '/favicon-16x16.svg', sizes: '16x16' }, - { rel: 'apple-touch-icon', url: '/apple-touch-icon.png' }, - { rel: 'apple-touch-icon', url: '/apple-touch-icon-152x152.svg', sizes: '152x152' }, - { rel: 'apple-touch-icon', url: '/apple-touch-icon-180x180.svg', sizes: '180x180' }, - { rel: 'apple-touch-icon', url: '/apple-touch-icon-167x167.svg', sizes: '167x167' }, + { rel: 'shortcut icon', url: assetUrl('/favicon.svg') }, + { rel: 'icon', url: assetUrl('/favicon-32x32.svg'), sizes: '32x32' }, + { rel: 'icon', url: assetUrl('/favicon-16x16.svg'), sizes: '16x16' }, + { rel: 'apple-touch-icon', url: assetUrl('/apple-touch-icon.png') }, + { rel: 'apple-touch-icon', url: assetUrl('/apple-touch-icon-152x152.svg'), sizes: '152x152' }, + { rel: 'apple-touch-icon', url: assetUrl('/apple-touch-icon-180x180.svg'), sizes: '180x180' }, + { rel: 'apple-touch-icon', url: assetUrl('/apple-touch-icon-167x167.svg'), sizes: '167x167' }, ], } diff --git a/apps/docs/components/content/image.tsx b/apps/docs/components/content/image.tsx index ba65f711b583..e2687ef356c8 100644 --- a/apps/docs/components/content/image.tsx +++ b/apps/docs/components/content/image.tsx @@ -1,13 +1,16 @@ +import { assetUrl } from '@/utils/asset-url' import { TldrawLink } from '../common/tldraw-link' export function Image(props: any) { + const src = typeof props.src === 'string' ? assetUrl(props.src) : props.src + const href = props.href ?? src return ( - {props.title} + {props.title} {props.caption && ( diff --git a/apps/docs/utils/asset-url.test.ts b/apps/docs/utils/asset-url.test.ts new file mode 100644 index 000000000000..9d80817e0576 --- /dev/null +++ b/apps/docs/utils/asset-url.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { assetUrl } from './asset-url' + +describe('assetUrl', () => { + it('returns the original path when ASSET_PREFIX is unset', () => { + const prev = process.env.ASSET_PREFIX + try { + delete process.env.ASSET_PREFIX + expect(assetUrl('/favicon.svg')).toBe('/favicon.svg') + } finally { + process.env.ASSET_PREFIX = prev + } + }) + + it('returns the original path when ASSET_PREFIX is empty/whitespace', () => { + const prev = process.env.ASSET_PREFIX + try { + process.env.ASSET_PREFIX = ' ' + expect(assetUrl('/favicon.svg')).toBe('/favicon.svg') + } finally { + process.env.ASSET_PREFIX = prev + } + }) + + it('prefixes root-relative paths', () => { + const prev = process.env.ASSET_PREFIX + try { + process.env.ASSET_PREFIX = 'https://docs.example.com' + expect(assetUrl('/images/foo.png')).toBe('https://docs.example.com/images/foo.png') + } finally { + process.env.ASSET_PREFIX = prev + } + }) + + it('strips a trailing slash from the prefix', () => { + const prev = process.env.ASSET_PREFIX + try { + process.env.ASSET_PREFIX = 'https://docs.example.com/' + expect(assetUrl('/images/foo.png')).toBe('https://docs.example.com/images/foo.png') + } finally { + process.env.ASSET_PREFIX = prev + } + }) + + it('does not change non-root-relative paths', () => { + const prev = process.env.ASSET_PREFIX + try { + process.env.ASSET_PREFIX = 'https://docs.example.com' + expect(assetUrl('https://cdn.example.com/a.png')).toBe('https://cdn.example.com/a.png') + expect(assetUrl('relative.png')).toBe('relative.png') + } finally { + process.env.ASSET_PREFIX = prev + } + }) +}) diff --git a/apps/docs/utils/asset-url.ts b/apps/docs/utils/asset-url.ts new file mode 100644 index 000000000000..2b7bc8b2139f --- /dev/null +++ b/apps/docs/utils/asset-url.ts @@ -0,0 +1,6 @@ +export function assetUrl(path: string): string { + if (!path.startsWith('/')) return path + const prefix = process.env.ASSET_PREFIX?.trim() + if (!prefix) return path + return `${prefix.replace(/\/$/, '')}${path}` +} From 0167b2616f8f350cf0a6c97c321a29ea887297cc Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Tue, 5 May 2026 17:16:01 +0100 Subject: [PATCH 2/3] chore: add tldraw-migrate skill (#8765) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to help projects upgrade to newer tldraw SDK versions, this PR adds a `tldraw-migrate` skill that an agent can invoke as `/tldraw-migrate` (or `/tldraw-migrate ` to override version detection). The skill auto-detects the previous tldraw version from git history, fetches and filters the public release notes between that version and the latest, downloads the full SDK docs for ad-hoc lookup, and walks through a structured migration: 1. Inspect the project (package manager, tldraw packages installed, import style, typecheck/build/lint setup). 2. Upgrade tldraw packages. 3. Categorize TypeScript errors by code, file, and pattern. 4. Fix in order of impact: React types → custom shape and binding registration → API renames → TipTap → remaining errors. 5. Address `@deprecated` symbols. 6. Verify with typecheck and build, plus a sanity check that no new `as` casts crept in. The skill is marked `disable-model-invocation: true` and `user-invocable: true`, so it only runs when the user types `/tldraw-migrate`. ### Bundled helpers - `detect-versions.mjs` — resolves the "from" version. Order: explicit CLI arg → `git show main:package.json` (only when it differs from the working tree, so on-main migrations don't return the new version) → `HEAD~1` → working-tree fallback. Probes 11 tldraw packages so projects that don't depend on the umbrella `tldraw` package are still detected. - `filter-changelog.mjs` — filters `https://tldraw.dev/llms-releases.txt` to versions between `from` and `to` (inclusive of `to`). The downloaded references live under `skills/tldraw-migrate/references/` and are gitignored. The full docs (~1.5MB) are cached across invocations; the changelog is always re-fetched since it's small and version-dependent. ### Change type - [x] \`other\` ### Test plan 1. In a tldraw-using project where the working tree has bumped tldraw versions but `main` (or `HEAD~1`) still has the old ones, invoke \`/tldraw-migrate\`. 2. Confirm the skill prints the detected previous version, writes the filtered changelog to \`references/tldraw-releases.txt\`, downloads the full docs to \`references/tldraw-full-docs.txt\`, and re-uses the cached docs on a second invocation. 3. Override detection with \`/tldraw-migrate 4.0.0\` and confirm the explicit version is used. ### Code changes | Section | LOC change | | -------------- | ---------- | | Config/tooling | +339 / -0 | --- skills/tldraw-migrate/.gitignore | 1 + skills/tldraw-migrate/SKILL.md | 155 +++++++++++++++++++++ skills/tldraw-migrate/detect-versions.mjs | 104 ++++++++++++++ skills/tldraw-migrate/filter-changelog.mjs | 79 +++++++++++ 4 files changed, 339 insertions(+) create mode 100644 skills/tldraw-migrate/.gitignore create mode 100644 skills/tldraw-migrate/SKILL.md create mode 100644 skills/tldraw-migrate/detect-versions.mjs create mode 100644 skills/tldraw-migrate/filter-changelog.mjs diff --git a/skills/tldraw-migrate/.gitignore b/skills/tldraw-migrate/.gitignore new file mode 100644 index 000000000000..6f49e841fe98 --- /dev/null +++ b/skills/tldraw-migrate/.gitignore @@ -0,0 +1 @@ +references/ diff --git a/skills/tldraw-migrate/SKILL.md b/skills/tldraw-migrate/SKILL.md new file mode 100644 index 000000000000..307fd4562682 --- /dev/null +++ b/skills/tldraw-migrate/SKILL.md @@ -0,0 +1,155 @@ +--- +name: tldraw-migrate +description: Migrate a project to a newer version of the tldraw SDK. Use when upgrading tldraw packages, fixing TypeScript errors after a tldraw upgrade, or when the user mentions tldraw migration. +argument-hint: '[previous-version]' +disable-model-invocation: true +user-invocable: true +--- + +# tldraw migration assistant + +You are helping migrate a project to a newer version of the tldraw SDK. Follow this process carefully. + +Previous version (auto-detected from git history; pass an explicit version as `/tldraw-migrate ` to override): !`node ${CLAUDE_SKILL_DIR}/detect-versions.mjs $ARGUMENTS` + +## Resources (auto-fetched on invocation) + +!`mkdir -p ${CLAUDE_SKILL_DIR}/references && CHANGELOG=${CLAUDE_SKILL_DIR}/references/tldraw-releases.txt && PREV=$(node ${CLAUDE_SKILL_DIR}/detect-versions.mjs $ARGUMENTS) && curl --fail -sS https://tldraw.dev/llms-releases.txt | node ${CLAUDE_SKILL_DIR}/filter-changelog.mjs "$PREV" > "$CHANGELOG" && echo "Saved changelog (from $PREV) to $CHANGELOG ($(wc -l < "$CHANGELOG") lines)"` + +!`DOCS=${CLAUDE_SKILL_DIR}/references/tldraw-full-docs.txt && if [ -s "$DOCS" ]; then echo "Using cached full docs at $DOCS ($(wc -l < "$DOCS") lines) — delete the file to refresh"; else curl --fail -sS https://tldraw.dev/llms-full.txt -o "$DOCS" && echo "Saved full docs to $DOCS ($(wc -l < "$DOCS") lines)"; fi` + +- **[Filtered changelog](references/tldraw-releases.txt)** — release notes for versions between the previous version and now. Read this first to understand what changed. +- **[Full docs](references/tldraw-full-docs.txt)** — complete tldraw SDK docs (~1.5MB). Do NOT read this upfront. Use Grep or Read with line ranges to search for specific topics as needed (e.g., custom shapes, TLTextOptions). + +## Step 1: Understand the environment + +Before making any changes, scan the project to understand what you're working with. Run these in parallel: + +- **Package manager**: Check for lock files (`yarn.lock`, `pnpm-lock.yaml`, `bun.lockb`, `package-lock.json`). Use the corresponding tool throughout. +- **tldraw packages**: `grep -E "tldraw|@tldraw" package.json` — which packages are installed, and at what versions? Note: not every project uses all tldraw packages. +- **Import style**: `grep -r "from '@tldraw/" src/ --include="*.ts" --include="*.tsx" -l | head -5` — does the project import from `'tldraw'`, `'@tldraw/editor'`, or both? This affects module augmentation targets. +- **TypeScript**: Check `package.json` for a `typecheck` or `tsc` script. Also check the TypeScript version — is it a direct dependency or does the project rely on a global install? +- **Build tool**: Check `package.json` scripts for the build command (vite, next, webpack, esbuild, etc.) +- **Linter**: Check for eslint/biome config files (`.eslintrc*`, `eslint.config.*`, `biome.json`). A linter may help catch deprecations later. +- **Monorepo**: Is `package.json` at the working directory root, or is this a nested package? Check for workspaces config. + +## Step 2: Upgrade packages + +Using the detected package manager, upgrade all tldraw packages that are already in the project's dependencies to the latest version. Don't add new packages the project doesn't already use. + +## Step 3: Identify all TypeScript errors + +Run the project's typecheck command (from Step 1) and categorize the errors: + +1. **Count errors by TS code** (e.g., TS2344, TS2786) to understand the distribution +2. **List unique files with errors** to understand the scope +3. **Identify error patterns** — common categories in tldraw upgrades: + - React types mismatch (TS2786 "cannot be used as JSX component") — usually means `@types/react` version doesn't match what tldraw bundles. To find the bundled version: with npm or yarn classic, check `node_modules/@tldraw/editor/node_modules/@types/react/package.json`; with pnpm or yarn berry, run the package manager's "why" command (e.g. `pnpm why @types/react`, `yarn why @types/react`) to see the resolved range. + - Custom shape type registration (TS2344 "does not satisfy constraint TLShape/TLBaseBoxShape") — tldraw v4.3+ requires module augmentation of `TLGlobalShapePropsMap` + - Custom binding type registration — same pattern with `TLGlobalBindingPropsMap` + - `BaseBoxShapeTool.shapeType` type mismatch (TS2416) — needs `as const` + - API renames/removals (TS2305, TS2724) — check changelog for specific migrations + - `createShapes`/`updateShapes` type widening (TS2345) — mapped arrays need `TLShapePartial` or `TLCreateShapePartial` casts + +## Step 4: Fix errors in order of impact + +Fix in this order (each fix eliminates many downstream errors): + +### 4a. Fix React types + +If you see TS2786 "bigint not assignable to ReactNode" errors, upgrade `@types/react` and `@types/react-dom` to match tldraw's bundled version. + +### 4b. Register custom shapes and bindings + +For every `TLBaseShape<'name', Props>` in the codebase, add module augmentation. Use the import style detected in Step 1 as the module target: + +```ts +declare module 'tldraw' { + interface TLGlobalShapePropsMap { + 'shape-name': { + /* props */ + } + } +} +type MyShape = TLShape<'shape-name'> +``` + +Same pattern for bindings with `TLGlobalBindingPropsMap` and `TLBinding<'name'>`. + +**IMPORTANT**: If multiple files use the same shape type name with different props, rename them to be unique — they all share the global type registry. + +Add `as const` to all `static override shapeType = '...'` properties. + +### 4c. Fix API renames and removals + +Cross-reference each TS2305/TS2724/TS2339 error with the changelog. Common patterns: + +- Removed exports: find replacement by grepping the tldraw type definitions +- Renamed properties: check changelog for the new name +- Changed method signatures: check the current type definitions + +To find tldraw type definitions, check which paths exist — the location varies by version: + +- `node_modules/tldraw/dist-cjs/index.d.ts` +- `node_modules/tldraw/dist/index.d.ts` +- `node_modules/@tldraw/editor/dist-cjs/index.d.ts` +- `node_modules/@tldraw/tlschema/dist-cjs/index.d.ts` + +### 4d. Fix TipTap imports if needed + +If the project uses TipTap (`@tiptap/*` in dependencies or imports), the tldraw upgrade may require upgrading TipTap as well. Starting with tldraw v4.2, tldraw bundles TipTap v3 as a transitive dependency. If the project pins TipTap v2 packages directly, this will cause version conflicts and broken imports. **Upgrade the project's direct `@tiptap/*` dependencies to v3** to match what tldraw expects — leaving them on v2 will not work. + +After upgrading, fix any breaking TipTap v2 → v3 changes: + +- **Default → named exports**: If a default import fails, switch to a named import: `import StarterKit from '@tiptap/starter-kit'` → `import { StarterKit } from '@tiptap/starter-kit'` +- **Renames**: If a named import fails, check the package's actual exports: `grep 'export' node_modules/@tiptap//dist/index.js | head` — some extensions were renamed in TipTap v3 (e.g., `TextStyle` → `TextStyleKit`). +- **Transaction handler types**: If TipTap event handler types break, import the proper types: `import { EditorEvents } from '@tiptap/core'` and type handlers as `(props: EditorEvents['transaction']) => void` rather than inline type annotations. + +### 4e. Fix remaining type errors + +- `createShapes`/`updateShapes` with `.map()`: The proper fix is to add `as const` to the `type` field in the mapped object literal so TypeScript narrows it to the string literal type. If that's not sufficient, use a `satisfies` annotation on the return value. Only as a last resort, cast the result to `TLCreateShapePartial` or `TLShapePartial`. +- TipTap extension commands not on `ChainedCommands`: use `declare module '@tiptap/core'` augmentation to register custom commands. +- **General rule**: every `as` cast you add is tech debt. Before adding one, exhaust these alternatives in order: + 1. `as const` on object literals to narrow string literal types + 2. `satisfies` annotations to check types without widening + 3. Proper generic type parameters on the call site + 4. Module augmentation to teach TypeScript about your types + 5. Only then, a targeted `as` cast with a comment explaining why it's needed + +## Step 5: Fix deprecations + +After all type errors are resolved, find and fix deprecated API usage. These still compile but should be migrated. + +1. **Find deprecated symbols** — grep the tldraw type definitions for `@deprecated` annotations. Also search the changelog for "deprecated" in `${CLAUDE_SKILL_DIR}/references/tldraw-releases.txt`. + +2. **Check if a linter is configured** — look for eslint, biome, or other linting config in the project. If available, run the linter — it may flag deprecated usage automatically (e.g., eslint's `deprecation/deprecation` rule). If no linter is configured, skip to step 3. + +3. **Search the project source** for each deprecated symbol found in step 1. Replace with the recommended alternative from the `@deprecated` JSDoc comment or changelog. + +## Step 6: Verify + +Run the project's typecheck and build commands (as discovered in Step 1). + +If errors remain, repeat the categorize-and-fix cycle. + +### Post-migration sanity check + +After all errors are resolved, do a quick audit: + +1. **Count `as` casts before vs after**: Run `grep -rn ' as ' --include='*.ts' --include='*.tsx' src/` and compare against the same grep on the pre-migration code (use `git stash` or `git show HEAD:path`). **The migration should add no more than a small handful of new casts** (ideally zero). If you added more than ~5 new `as` casts across the entire migration, go back and fix them — you are almost certainly using the new API incorrectly. +2. **Review every cast you added**: For each new `as` cast, verify it's truly necessary by checking whether `as const`, `satisfies`, generic type parameters, or module augmentation could replace it. Remove or replace any that have a cleaner alternative. +3. **Verify no stubs or dead code**: If you stubbed out removed APIs (e.g., replaced a removed function with a no-op), make sure the calling code doesn't depend on the return value. If it does, find the proper replacement in the changelog or docs. + +## Quality checks + +- **Type safety is paramount.** The goal is a migration that is as type-safe as the original code. Do NOT add `as any`, `as unknown`, or broad type casts. Do NOT add `@ts-ignore` or `@ts-expect-error`. These are never acceptable — if you can't make the types work, you don't understand the new API yet. Stop and read the changelog and type definitions before continuing. +- **Prefer TypeScript's narrowing features over casts.** Use `as const` for literal types, `satisfies` for type-checking without widening, generic parameters for call sites, and module augmentation for extending interfaces. These are the right tools for a migration — `as` casts are not. +- Use parallel agents for fixing large batches of files with the same pattern. +- **Don't just make errors go away — understand the new API.** When a method signature changes (e.g., new parameters added, property renamed to a richer type), read the changelog AND the current type definitions to understand _why_ it changed. A fix that compiles but passes hardcoded/dummy values where the new API expects real data is worse than a type error — it silently degrades behavior. For example, if a function gains new required parameters, check what shape props or editor state should feed those parameters rather than passing `0` or `1`. +- **When unsure about an API pattern**, grep the full docs (`${CLAUDE_SKILL_DIR}/references/tldraw-full-docs.txt`) for usage examples of that specific API. The docs contain code samples that show the canonical way to use each API. + +## Tips + +- Read `${CLAUDE_SKILL_DIR}/references/tldraw-releases.txt` for the filtered changelog — this is the primary source for what changed +- Grep `${CLAUDE_SKILL_DIR}/references/tldraw-full-docs.txt` when you need docs on a specific API +- When searching tldraw types, try multiple paths — the dist directory structure varies by version and package manager diff --git a/skills/tldraw-migrate/detect-versions.mjs b/skills/tldraw-migrate/detect-versions.mjs new file mode 100644 index 000000000000..a5b60c27a775 --- /dev/null +++ b/skills/tldraw-migrate/detect-versions.mjs @@ -0,0 +1,104 @@ +#!/usr/bin/env node +/** + * Detects the previous tldraw version and writes it to stdout. + * + * Resolution order: + * 1. An explicit version passed as argv[2] (e.g., "/tldraw-migrate 4.0.0"). + * 2. The version of any tldraw package on the main/master branch — but only + * if it differs from the working tree (otherwise the user is migrating + * directly on main and main already reflects the new version). + * 3. The version in HEAD~1. + * 4. The version in the current working tree (last resort: assumes the user + * hasn't bumped yet, so the working tree IS the previous version). + * + * Outputs just the version string (no trailing newline) so the SKILL.md can + * interpolate it into other commands. + */ + +import { execSync } from 'child_process' +import { readFileSync } from 'fs' + +function exec(cmd) { + try { + return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim() + } catch { + return null + } +} + +const TLDRAW_PACKAGES = [ + 'tldraw', + '@tldraw/editor', + '@tldraw/tldraw', + '@tldraw/store', + '@tldraw/tlschema', + '@tldraw/state', + '@tldraw/state-react', + '@tldraw/sync', + '@tldraw/sync-core', + '@tldraw/utils', + '@tldraw/validate', +] + +function extractTldrawVersion(packageJsonStr) { + try { + const pkg = JSON.parse(packageJsonStr) + const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) } + for (const name of TLDRAW_PACKAGES) { + const raw = deps[name] + if (!raw) continue + const cleaned = String(raw).replace(/[\^~>=<\s]/g, '') + const match = cleaned.match(/(\d+\.\d+\.\d+)/) + if (match) return match[1] + } + return null + } catch { + return null + } +} + +function readWorkingTreeVersion() { + try { + return extractTldrawVersion(readFileSync('package.json', 'utf8')) + } catch { + return null + } +} + +function emit(version) { + process.stdout.write(version) + process.exit(0) +} + +const explicit = process.argv[2]?.trim() +if (explicit) { + const cleaned = explicit.replace(/^v/, '') + const match = cleaned.match(/^(\d+\.\d+\.\d+)/) + if (match) emit(match[1]) +} + +const workingTreeVersion = readWorkingTreeVersion() + +for (const branch of ['main', 'master']) { + const content = exec(`git show ${branch}:package.json`) + if (!content) continue + const ver = extractTldrawVersion(content) + if (!ver) continue + // If the branch version matches the working tree, the user likely bumped + // directly on this branch — main isn't a useful "before" reference. + if (workingTreeVersion && ver === workingTreeVersion) continue + emit(ver) +} + +const parent = exec('git show HEAD~1:package.json') +if (parent) { + const ver = extractTldrawVersion(parent) + if (ver && ver !== workingTreeVersion) emit(ver) +} + +if (workingTreeVersion) emit(workingTreeVersion) + +console.error( + 'Could not detect previous tldraw version. Pass it as an argument: /tldraw-migrate 4.0.0' +) +process.exit(1) diff --git a/skills/tldraw-migrate/filter-changelog.mjs b/skills/tldraw-migrate/filter-changelog.mjs new file mode 100644 index 000000000000..fb2e2cdb832f --- /dev/null +++ b/skills/tldraw-migrate/filter-changelog.mjs @@ -0,0 +1,79 @@ +#!/usr/bin/env node +/** + * Filters tldraw changelog to only versions between `from` and `to` (inclusive of `to`). + * Usage: node filter-changelog.mjs [to-version] + * + * If to-version is omitted, includes everything newer than from-version. + * Versions should be semver like "4.0.0" or "v4.0.0" (the "v" prefix is optional). + * + * Reads changelog from stdin, writes filtered output to stdout. + */ + +const [, , fromArg, toArg] = process.argv + +if (!fromArg) { + console.error('Usage: node filter-changelog.mjs [to-version]') + console.error( + ' e.g.: curl -s https://tldraw.dev/llms-releases.txt | node filter-changelog.mjs 4.0.0 4.6.0' + ) + process.exit(1) +} + +function parseVersion(str) { + const m = str.replace(/^v/, '').match(/^(\d+)\.(\d+)\.(\d+)/) + if (!m) return null + return [+m[1], +m[2], +m[3]] +} + +function compareVersions(a, b) { + for (let i = 0; i < 3; i++) { + if (a[i] !== b[i]) return a[i] - b[i] + } + return 0 +} + +const fromVer = parseVersion(fromArg) +const toVer = toArg ? parseVersion(toArg) : null + +if (!fromVer) { + console.error(`Invalid from-version: ${fromArg}`) + process.exit(1) +} +if (toArg && !toVer) { + console.error(`Invalid to-version: ${toArg}`) + process.exit(1) +} + +let input = '' +process.stdin.setEncoding('utf8') +process.stdin.on('data', (chunk) => (input += chunk)) +process.stdin.on('end', () => { + // Split into sections by top-level headings (# vX.Y.Z) + const sections = [] + let current = null + + for (const line of input.split('\n')) { + const headingMatch = line.match(/^# (v?\d+\.\d+\.\d+)/) + if (headingMatch) { + if (current) sections.push(current) + current = { version: parseVersion(headingMatch[1]), lines: [line] } + } else if (current) { + current.lines.push(line) + } + } + if (current) sections.push(current) + + // Filter: include versions where from < version <= to + const filtered = sections.filter((s) => { + const afterFrom = compareVersions(s.version, fromVer) > 0 + const beforeTo = toVer ? compareVersions(s.version, toVer) <= 0 : true + return afterFrom && beforeTo + }) + + if (filtered.length === 0) { + console.error(`No versions found between ${fromArg} and ${toArg || 'latest'}`) + process.exit(0) + } + + process.stdout.write(filtered.map((s) => s.lines.join('\n')).join('\n\n')) +}) From 6ed7b78abbea8c0a6e59ddb3f335c4431a42f3b3 Mon Sep 17 00:00:00 2001 From: Guillaume Richard Date: Tue, 5 May 2026 17:22:10 +0100 Subject: [PATCH 3/3] fix(docs): ASSET_PREFIX for future rewrites from tldraw.dev (#8766) Before https://github.com/tldraw/tldraw/pull/8665 we need to test the ASSET_PREFIX env variable, this would not break the docs in case the SDK / new docs are released before ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan 1. Go to staging.tldraw.dev 2. Nothing's broken XD --- apps/docs/next.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/next.config.js b/apps/docs/next.config.js index be9b4065c2d7..2c462df7a33a 100644 --- a/apps/docs/next.config.js +++ b/apps/docs/next.config.js @@ -5,6 +5,7 @@ const REWRITE_DOMAIN = 'tldrawdotdev.framer.website' const nextConfig = { reactStrictMode: true, + assetPrefix: process.env.ASSET_PREFIX || undefined, experimental: { scrollRestoration: true, },