Skip to content

Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint - #1565

Merged
stipsan merged 16 commits into
mainfrom
cursor/plugin-kit-oxfmt-migration-99d1
Jul 15, 2026
Merged

stipsan merged 16 commits into
mainfrom
cursor/plugin-kit-oxfmt-migration-99d1

Conversation

@stipsan

@stipsan stipsan commented Jul 13, 2026 •

Copy link
Copy Markdown
Member

Description

Drops prettier and eslint support from @sanity/plugin-kit entirely, replacing them with oxfmt and oxlint, shipping the monorepo's own settings as reusable configs (inspired by @epic-web/config). plugin-kit also stops pushing tsc type-check runs (the shared oxlint config has type-aware rules and TypeScript type checking via oxlint-tsgolint enabled, with warnings denied), and the Sanity v2 imports check is removed entirely — v2-era imports are no longer linted for.

Shared configs shipped by plugin-kit (both authored in src/, typed, built to plain JS in dist/ so Node never type-strips under node_modules)

  • @sanity/plugin-kit/oxfmt: oxfmt preset with the exact settings previously in the root .oxfmtrc.json.
  • @sanity/plugin-kit/oxlint: oxlint config (typed OxlintConfig) matching the monorepo's previous .oxlintrc.json minus workspace-specific ignores/overrides — same plugins, typeAware/typeCheck/denyWarnings/reportUnusedDisableDirectives options, categories and rules (verified semantically identical, plus identical lint file lists and diagnostics before/after).
  • Both are consumed the same way, via TS config files (extends supports imported config objects in oxlint.config.ts):
    • root oxfmt.config.ts: export {default} from '@sanity/plugin-kit/oxfmt'
    • root oxlint.config.ts: defineConfig({extends: [sanityPluginKitOxlint], ignorePatterns: [...shared, ...workspace], overrides: [...workspace]})

Scaffolding (init / inject)

  • Scaffolds oxfmt.config.ts + oxlint.config.ts (both plain re-exports of the shared configs), oxfmt/oxlint/oxlint-tsgolint devDependencies and format/lint scripts. No prettier or eslint packages at all.
  • --no-prettier → --no-oxfmt, --no-eslint → --no-oxlint; the --no-typescript flag now actually controls TypeScript scaffolding (it previously piggybacked on the eslint flag).
  • The semver-workflow preset's lint-staged config runs oxfmt + oxlint instead of eslint + tsc --build.

verify-package / verify-studio

  • The post-check tsc --build run is gone (tsc check key removed) — oxlint type-checks as part of linting.
  • The Sanity v2 imports check (eslintImports) is removed entirely, along with the import-linter module; verify-studio now checks studio config and dependencies only.
  • New monorepo-aware oxfmt and oxlint checks: walk up for a workspace root (pnpm-workspace.yaml / workspaces field); the config is expected there (or next to the plugin's package.json outside monorepos) and must use the shared plugin-kit config. Leftover prettier/eslint configuration fails with migration instructions; .oxfmtrc.json / .oxlintrc.json files are flagged since JSON configs cannot import the shared configs.
  • Peer dependencies are now @sanity/pkg-utils, oxfmt and oxlint (the eslint and typescript peers are gone, and oxlint is not a runtime dependency of plugin-kit).

Housekeeping

  • dev/test-studio now pins typescript (catalog) so the sanity peer keeps resolving to the catalog version now that plugin-kit no longer contributes eslint/typescript peers to the graph.
  • Fixtures/tests migrated (obsolete invalid-eslint fixture removed; the init→verify→build integration test now runs type-aware oxlint in the scaffold instead of tsc --build), unit tests for both config-location checks, snapshots regenerated.
  • knip.jsonc, AGENTS.md, plugin-kit README updated; single major changeset.
  • Note: pnpm format and pnpm lint now require Node >=22.18 (^20.19 also works for oxfmt) to load the TypeScript configs. CI uses lts/*, so it is unaffected.

Testing

  • pnpm format, pnpm lint, pnpm knip, pnpm build, pnpm test run (169 files / 917 tests) all pass.
  • Config-equivalence proof: identical lint file lists and identical diagnostics on a multi-violation scratch file across the .oxlintrc.json → shared-config → oxlint.config.ts refactors; semantic JSON diff of rules/options/categories/overrides is empty.
  • End-to-end with a packed tarball, standalone and pnpm-workspace layouts: scaffold → npm run lint (proved typescript(TS2322) type errors and no-floating-promises fire through the published dist/oxlint.js re-export) → npm run format → verify-package (pass + legacy-eslint / JSON-config / multiple-config / missing-config failure modes) → npm run build; in the monorepo layout, proved root defineConfig({extends}) merges shared rules with workspace overrides.

Notes

  • Requires review from the prompter before marking ready for team review.
Open in Web Open in Cursor 

- Ship the monorepo's oxfmt settings as a @sanity/plugin-kit/oxfmt preset
- Re-export the preset from the root oxfmt.config.ts (replaces .oxfmtrc.json)
- init/inject scaffold oxfmt (config, devDependency, format script) instead
  of prettier; --no-prettier is now --no-oxfmt
- verify-package: replace the duplicate-prettier-config check with a
  monorepo-aware oxfmt check that expects the config at the workspace root
  (or next to package.json outside monorepos) and flags leftover prettier
  configuration with migration instructions
@changeset-bot

changeset-bot Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9f0868d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sanity/plugin-kit Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
plugins-studio Ready Ready Preview, Comment Jul 15, 2026 1:50pm

Request Review

- Ship the monorepo's oxlint settings as @sanity/plugin-kit/oxlint-config.json
  (type-aware rules, type checking via oxlint-tsgolint, warnings denied); the
  root .oxlintrc.json extends it and keeps only workspace-specific ignores and
  overrides
- init/inject scaffold oxlint (.oxlintrc.json, oxlint + oxlint-tsgolint
  devDependencies, lint script) instead of eslint; --no-eslint is now
  --no-oxlint, and the typescript flag now controls TypeScript scaffolding
  instead of piggybacking on the eslint flag
- verify-package: drop the tsc run (oxlint type-checks as part of linting) and
  the duplicate-eslint-config check; add a monorepo-aware oxlint config check
  with legacy eslint detection; the v2-imports check (eslintImports -> imports)
  now runs a bundled oxlint pass instead of the package's eslint
- pin typescript in dev/test-studio so the sanity peer keeps resolving to the
  catalog version now that plugin-kit no longer pulls eslint/typescript peers
@cursor cursor Bot changed the title Migrate plugin-kit and monorepo formatting from prettier to oxfmt Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint Jul 13, 2026
oxlint auto-switches to GitHub annotations format on CI; --format=agent keeps
the compact per-diagnostic output (including help: messages) everywhere
… pattern)

Replaces the oxlint-config.json + .oxlintrc.json extends-by-file-path setup
with a TS config module, mirroring the oxfmt preset: the shared config is
authored in src/oxlint.ts (typed OxlintConfig, built to plain JS in dist), the
root and scaffolded configs are oxlint.config.ts files that re-export or extend
it via defineConfig({extends: [...]})
v2-era imports are no longer linted for; with the bundled oxlint pass gone,
oxlint moves from a runtime dependency of plugin-kit to a dev/peer dependency
(it is only needed by consumers, which the scaffolding already installs)
@stipsan
stipsan marked this pull request as ready for review July 14, 2026 00:56
@stipsan
stipsan requested a review from a team as a code owner July 14, 2026 00:56
@stipsan
stipsan requested review from Copilot and juice49 and removed request for a team and Copilot July 14, 2026 00:56
@stipsan
stipsan enabled auto-merge (squash) July 14, 2026 00:56
…mt-migration-99d1

# Conflicts:
#	pnpm-lock.yaml
Copilot AI review requested due to automatic review settings July 14, 2026 02:01
@stipsan
stipsan removed the request for review from Copilot July 14, 2026 02:01
…figs detected

- init/inject with --no-oxfmt/--no-oxlint now write the matching
  sanityPlugin.verifyPackage opt-outs, so the scaffolded build script
  (which runs verify-package) works out of the box
- the oxfmt/oxlint checks now also flag leftover prettier/eslint configs at
  the detected workspace root, not only next to the plugin's package.json
Copilot AI review requested due to automatic review settings July 14, 2026 13:55
@stipsan
stipsan removed the request for review from Copilot July 14, 2026 13:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates @sanity/plugin-kit (and this monorepo’s scaffolding + verification workflows) from the Prettier/ESLint + tsc --build model to the Oxc toolchain (oxfmt + oxlint with type-aware/type-checking via oxlint-tsgolint). It also makes the monorepo’s formatter/linter settings reusable by publishing them as @sanity/plugin-kit/oxfmt and @sanity/plugin-kit/oxlint, then reusing those across scaffolds and monorepo configs.

Changes:

  • Add shared, published @sanity/plugin-kit/oxfmt preset and @sanity/plugin-kit/oxlint config; wire them into build/exports and scaffolded configs.
  • Replace verify-package/verify-studio checks that previously relied on Prettier/ESLint and a post-check tsc --build step with new monorepo-aware oxfmt/oxlint config validation and legacy-config detection.
  • Update fixtures/tests/snapshots and the semver-workflow preset templates to reflect the new formatting/linting toolchain and ESM config files.

Reviewed changes

Copilot reviewed 60 out of 61 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/@sanity/plugin-kit/tsdown.config.ts Adds build entrypoints for the new exported oxfmt/oxlint configs.
packages/@sanity/plugin-kit/test/verify-package.test.ts Updates verify-package tests for the new check set and removes the old invalid-eslint fixture test.
packages/@sanity/plugin-kit/test/verify-oxlint.test.ts Adds dedicated tests for oxlint config discovery/validation (standalone + monorepo).
packages/@sanity/plugin-kit/test/verify-oxfmt.test.ts Adds dedicated tests for oxfmt config discovery/validation (standalone + monorepo).
packages/@sanity/plugin-kit/test/inject.test.ts Verifies injected preset files align with ESM + oxlint constraints.
packages/@sanity/plugin-kit/test/init.test.ts Updates init scaffolding expectations to generate oxlint/oxfmt config and deps.
packages/@sanity/plugin-kit/test/init-verify-build.test.ts Updates integration flow from tsc --build to oxlint, including resolving local plugin-kit import.
packages/@sanity/plugin-kit/test/fixtures/verify-package/valid/package.json Updates fixture scripts/devDependencies to oxfmt/oxlint.
packages/@sanity/plugin-kit/test/fixtures/verify-package/valid/oxlint.config.ts Adds fixture oxlint config re-exporting the shared plugin-kit config.
packages/@sanity/plugin-kit/test/fixtures/verify-package/valid/.prettierrc.json Removes legacy prettier config fixture.
packages/@sanity/plugin-kit/test/fixtures/verify-package/valid/.eslintrc Removes legacy eslint config fixture.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/tsconfig.json Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/src/index.ts Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/README.md Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/package.json Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/LICENSE Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/.prettierrc.json Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/.gitignore Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/.eslintrc Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/verify-package/invalid-eslint/.editorconfig Removes obsolete invalid-eslint fixture content.
packages/@sanity/plugin-kit/test/fixtures/inject/valid/package.json Updates injected fixture to use oxfmt/oxlint scripts/deps.
packages/@sanity/plugin-kit/test/fixtures/inject/valid/oxlint.config.ts Adds injected fixture oxlint config re-export.
packages/@sanity/plugin-kit/test/fixtures/inject/valid/.prettierrc.json Removes injected legacy prettier config fixture.
packages/@sanity/plugin-kit/test/fixtures/inject/valid/.eslintrc Removes injected legacy eslint config fixture.
packages/@sanity/plugin-kit/test/snapshots/verify-package.test.ts.snap Updates snapshots for new oxfmt/oxlint + legacy-config error messaging and removed v2-import checks.
packages/@sanity/plugin-kit/src/util/command-parser.ts Removes helper previously used to run tsc --build.
packages/@sanity/plugin-kit/src/oxlint.ts Adds the shared published oxlint config used by monorepo + scaffolds.
packages/@sanity/plugin-kit/src/oxfmt.ts Adds the shared published oxfmt preset used by monorepo + scaffolds.
packages/@sanity/plugin-kit/src/npm/package.ts Updates scaffolding dependencies/scripts and adds verify-package opt-outs when oxfmt/oxlint are disabled.
packages/@sanity/plugin-kit/src/index.test.ts Updates export manifest test to include ./oxfmt and ./oxlint.
packages/@sanity/plugin-kit/src/dependencies/import-linter.ts Removes the ESLint-based v2 import linter module.
packages/@sanity/plugin-kit/src/constants.ts Removes the now-unused link to the legacy v2 import linter package.
packages/@sanity/plugin-kit/src/configs/prettier.ts Removes prettierignore templating (no longer scaffolded).
packages/@sanity/plugin-kit/src/configs/oxlint.ts Adds oxlint config templating for init/inject.
packages/@sanity/plugin-kit/src/configs/oxfmt.ts Adds oxfmt config templating for init/inject.
packages/@sanity/plugin-kit/src/configs/eslint.ts Removes eslint templating (no longer scaffolded).
packages/@sanity/plugin-kit/src/configs/default-source.ts Updates generated source to use oxlint-disable-next-line and avoids unused config param.
packages/@sanity/plugin-kit/src/cmds/inject.ts Renames CLI flags to --no-oxlint / --no-oxfmt in help/docs.
packages/@sanity/plugin-kit/src/cmds/init.ts Renames CLI flags to --no-oxlint / --no-oxfmt in help/docs.
packages/@sanity/plugin-kit/src/actions/verify/verify-common.ts Removes tsc and eslintImports check keys and introduces oxfmt/oxlint keys.
packages/@sanity/plugin-kit/src/actions/verify/validations.ts Implements monorepo-aware oxfmt/oxlint config validation and legacy prettier/eslint detection.
packages/@sanity/plugin-kit/src/actions/verify-studio.ts Removes eslint import checks and post-check tsc --build from verify-studio.
packages/@sanity/plugin-kit/src/actions/verify-package.ts Swaps duplicate-config and eslint import checks for new oxfmt/oxlint validations; removes post-check tsc --build.
packages/@sanity/plugin-kit/src/actions/inject.ts Updates injected assets to add oxfmt/oxlint configs and remove eslint/prettier templates.
packages/@sanity/plugin-kit/src/actions/init.ts Renames init flags to oxlint/oxfmt.
packages/@sanity/plugin-kit/README.md Documents new oxfmt/oxlint usage and updates verify-package docs to reflect new checks.
packages/@sanity/plugin-kit/package.json Adds new exports for ./oxfmt and ./oxlint, and replaces eslint/typescript peers with oxfmt/oxlint peers.
packages/@sanity/plugin-kit/docs/semver-workflow.md Updates semver-workflow preset docs to describe oxfmt/oxlint behavior.
packages/@sanity/plugin-kit/assets/inject/semver-workflow/lint-staged.template.js Updates lint-staged template to run oxfmt/oxlint and switches to ESM export.
packages/@sanity/plugin-kit/assets/inject/semver-workflow/commitlint.template.js Switches commitlint template to ESM export.
packages/@sanity/plugin-kit/assets/inject/prettierrc.json Removes legacy prettier template asset.
package.json Adds workspace dependency on @sanity/plugin-kit so root configs can import the shared presets.
oxlint.config.ts Introduces root oxlint config extending @sanity/plugin-kit/oxlint with workspace-specific ignores/overrides.
oxfmt.config.ts Introduces root oxfmt config re-exporting @sanity/plugin-kit/oxfmt.
knip.jsonc Updates dependency ignore list to reflect removal of eslint/prettier runtime-resolved deps.
dev/test-studio/package.json Pins TypeScript (catalog) to stabilize dependency resolution after peer changes.
AGENTS.md Updates agent guide to reflect the new oxfmt/oxlint configs and Node requirements.
.oxlintrc.json Removes old JSON-based oxlint config (replaced by TS config + shared config).
.oxfmtrc.json Removes old JSON-based oxfmt config (replaced by TS config + shared preset).
.changeset/plugin-kit-oxfmt.md Adds a major changeset describing the migration and breaking changes.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@stipsan
stipsan requested a review from Copilot July 15, 2026 12:01
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 417dd6a. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 60 out of 61 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

plugin-kit's oxfmt/oxlint peers now reference the default catalog
(catalog:) rather than catalog:peer or explicit ranges: the shared
configs may rely on rules and features that ship in a new minor, so
the peer range must stay in lockstep with the version the monorepo
develops against.
Copilot AI review requested due to automatic review settings July 15, 2026 12:25
@stipsan
stipsan removed the request for review from Copilot July 15, 2026 12:25
Copilot AI review requested due to automatic review settings July 15, 2026 13:28
@stipsan
stipsan removed the request for review from Copilot July 15, 2026 13:28
Unlike @sanity/pkg-utils (a hard requirement, checked unconditionally
by verify-package), the oxfmt/oxlint verify-package checks are
individually opt-outable via sanityPlugin.verifyPackage.oxfmt/oxlint:
false, and plugin-kit's own code only ever imports their types. So a
consumer can legitimately use plugin-kit without oxfmt or oxlint
installed; peerDependenciesMeta now reflects that.
Copilot AI review requested due to automatic review settings July 15, 2026 13:49
@stipsan
stipsan removed the request for review from Copilot July 15, 2026 13:49
@stipsan
stipsan merged commit 67f4292 into main Jul 15, 2026
13 checks passed
@stipsan
stipsan deleted the cursor/plugin-kit-oxfmt-migration-99d1 branch July 15, 2026 20:51
@squiggler-app squiggler-app Bot mentioned this pull request Jul 15, 2026
cursor Bot pushed a commit that referenced this pull request Jul 21, 2026
Resolve the modify/delete conflict on .oxlintrc.json: main moved shared
lint rules into @sanity/plugin-kit/oxlint (#1565). Accept the deletion
and port the Component/PureComponent/createRef bans into
packages/@sanity/plugin-kit/src/oxlint.ts alongside the existing
createElement/forwardRef restrictions.

Also rewrite OrderableDocumentList to use React 19 ref-as-prop instead
of forwardRef so it complies with main's forwardRef ban, and update
skill docs that still pointed at .oxlintrc.json.
@cursor cursor Bot mentioned this pull request Jul 21, 2026
5 tasks done
cursor Bot pushed a commit that referenced this pull request Jul 28, 2026
Replace styled-components and inline style objects with colocated
vanilla-extract stylesheets, using assignInlineVars for dynamic
theme/color values. Builds on the function-component + useDrag
refactor from the ban-react-class-components branch.

fix: address review feedback on drag attach timing and imperative handle

- useDrag: attach the mousedown listener in useLayoutEffect so it exists
  before paint (matches the previous synchronous JSX onMouseDown wiring).
- OrderableDocumentList: give useImperativeHandle a dependency list so a
  new handle object is not installed on every render (the structure pane
  stores the handle in state, so per-render handles could loop).

refactor(color-input): extract shared useDrag hook for picker surfaces

Replace the per-component propsRef/AbortController plumbing in
Alpha/Hue/Saturation/EditableInput with a single useDrag hook. It wires a
native mousedown listener via useEffect and keeps callbacks fresh with
useEffectEvent, so drag events always read the latest props. mousemove and
mouseup listeners attach per-gesture and abort on mouseup or unmount.

fix(color-input): address review feedback on drag listeners

- Keep latest props in a ref so window mousemove listeners registered on
  mousedown see current values (matches class `this.props` behavior).
- Normalize EditableInput value comparisons to avoid number/string mismatch.
- Correct orderable-document-list changeset wording (ref-as-prop, not forwardRef).

Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve color-input conflicts from #1673 (simplify components, remove
CustomPicker HOC). Main kept Alpha/Hue/Saturation/EditableInput as class
components pending a follow-up conversion; this PR is that follow-up:
- Accept ColorWrap.tsx deletion (HOC replaced by ColorInput state).
- Re-apply function-component conversions on top of main's AbortController
  listener cleanup, using useRef instead of createRef.

fix(color-input): simplify components, remove HOC (#1673)
Version Packages (#1668)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1671)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate sanity-plugin-workflow styling to vanilla-extract (#1450)

* Migrate sanity-plugin-workflow styling to vanilla-extract

* Keep styled-components catalog:devDependency for peer alignment

After the vanilla-extract migration, the peer can go, but the catalog
devDependency must remain so pnpm resolves the workspace
@sanity/styled-components override (same as google-maps-input).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve the modify/delete conflict on .oxlintrc.json: main moved shared
lint rules into @sanity/plugin-kit/oxlint (#1565). Accept the deletion
and port the Component/PureComponent/createRef bans into
packages/@sanity/plugin-kit/src/oxlint.ts alongside the existing
createElement/forwardRef restrictions.

Also rewrite OrderableDocumentList to use React 19 ref-as-prop instead
of forwardRef so it complies with main's forwardRef ban, and update
skill docs that still pointed at .oxlintrc.json.

ci: request workflows permission for typegen app token (#935)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling (#1588)

* feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling

Drop verify-studio and all verify-package checks that existed to migrate from
or stay compatible with Sanity Studio v2 (incompatible-plugin shim, part:/config:
imports, merged @sanity/* packages, sanity.json parts validation). Package
verification now focuses on current plugin-kit conventions.

* chore(@sanity/plugin-kit): drop unused json5/knip ignores and refresh snapshots

Remove the json5 dependency and readJson5File helper that only served v2
sanity.json parsing, prune stale eslint knip ignoreDependencies, and update
verify-package snapshots after removing v2-upgrade checks.

* fix(@sanity/plugin-kit): address review feedback on path validation and studio detection

Drop the dist-hardcoded path existence check in getPackage entirely: compiled
entry points legitimately don't exist pre-build regardless of outDir, and
verify-package's esmOnly check already bans main/module fields. Also detect
sanity.cli.(js|ts) in findStudioConfig so init/inject refuse cli-config-only
studio directories too.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1669)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency (#1667)

* fix(sanity-plugin-markdown): declare @sanity/client and rxjs as regular deps

Move @sanity/client and rxjs out of inlinedDependencies into dependencies
so they are resolved at runtime instead of bundled into the package.

* chore: update lockfile for sanity-plugin-markdown deps

* fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency

Move @sanity/client from devDependencies to dependencies so tsdown
externalizes it instead of writing it (and transitive rxjs) to
inlinedDependencies. rxjs is not imported by the plugin and is not
needed as a direct dependency once client is externalized.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(typegen): 🤖 ✨ (#1666)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.7 (#1664)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.7

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1665)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1663)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: add @sanity/block-insert-picker (#1601)

* feat(block-insert-picker): add @sanity/block-insert-picker

Slash-command block-insert picker and markdown input rules for Portable
Text fields, extracted from the sanity-io/www-sanity-io docs Studio
(apps/studio/src/plugins/pte, PR #4003 there parameterized the docs
knowledge out in preparation).

Public API: blockInsertPicker({arrayTypeName, items?, inputRules?,
onItemInserted?}) returning the component for an array type's
components.portableText.plugins, plus the codeFenceRule/blockquoteRule
config factories and fence-language helpers. Items derive from the host
array's members with metadata-driven curation (triggers, keywords,
grouping, rank); inserts resolve member initial values, land as one
undo step, and open for editing.

Ported against sanity 6.5.0 / @portabletext/editor 7.10.7 /
@portabletext/plugin-input-rule 6.0.4 (peer editor ^7.10.7 to match the
input-rule plugin's requirement; editor stays a peer, never bundled).
112 tests ported with the source; test-studio example wired under
Home > Block Insert Picker.

* refactor(block-insert-picker): zero-config detection and reviewed v1 API

Applies the pre-publication design review (DESIGN-REVIEW.md in the
pte-plugin-extraction research):

- blockInsertPicker() now works with zero config: the containing array and
  its members are detected via PortableTextMemberSchemaTypesContext
  (quarantined in memberSchemaTypes.ts); arrayTypeName becomes an escape
  hatch. Peer narrowed to sanity ^5.6 (the context export's floor).
- Text blocks are excluded by resolved type chain, fixing aliased
  {name, type: 'block'} members being offered as object inserts (picker and
  input-rule allow-list both).
- New options: labels (translatable chrome), shortcut, openOnInsert
  (global + per-item), presets (well-known-type triggers/keywords),
  filter (receives the bare query in both modes), resolveItems middleware
  with custom item actions (query cleanup included); onItemInserted renamed
  onInsert, events carry mode/query.
- Presentation matches core: icon fallback chain, upperFirst titles, and
  curated descriptions now win over schema descriptions.
- New exports: BlockInsertPicker, MarkdownInputRules, derivePickerItems,
  filterPickerItems, standardBlockPresets, wellKnownInputRules, PickerMode.
- Input rules resolve blockType through member type chains, so
  wellKnownInputRules serves aliased code-input members.
- Review fixes: host-callback errors surface as toasts instead of unhandled
  rejections; anchor-deletion mid-insert shows a warning toast; positional
  row indices tolerate duplicate item ids; replacement-pattern-safe label
  formatting; screen-reader position announcement is translatable.

Deliberate deviations from the review doc: presets carry no description
(avoids a three-way precedence chain).

* test(block-insert-picker): lift cold-start timeout on first picker popover test

* docs: add @sanity/block-insert-picker to Current Plugins table

* chore: trigger pkg.pr.new preview publish

* refactor(block-insert-picker): address Copilot review comments

- insertPickerItem: spread the resolved initialValue before `_key`/`_type`
  so the generated key and target type always win. Sanity's initial-value
  resolver sets `_type` on object values; the values coincide by
  construction here, but ordering keeps the insert correct regardless.
- openBlockOnInsert: correct the stale rationale comment. The package
  enables the React Compiler, so the old "not active" claim was wrong; the
  point is the empty-dep useCallback keeps a stable reference for consumers.
- blockInsertPicker: fix the comment naming the wrong mouse events. Rows
  select via onClick and highlight via onMouseMove (not onMouseDown /
  onMouseEnter), passing an explicit idx to avoid a not-yet-flushed
  highlightedIndex.

* fix(block-insert-picker): case-insensitive fence language table match; trim README install

- normalizeFenceLanguage now compares the typed token to language-table
  `value`/`aliases` case-insensitively, returning the canonical value
  (casing preserved). Previously a table with mixed-case entries never
  matched and the mapped filename was skipped (Cursor Bugbot, low severity).
  Built-in rules and code-input's lowercase table were unaffected; this
  hardens host-supplied custom tables. Adds a mixed-case-table test.
- README: collapse the three install commands to a single `npm install`
  (no --save), matching @sanity/code-input's install section.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(block-insert-picker): restore bracket notation for process.env access

The Copilot Autofix commit rewrote `process.env['NODE_ENV']` to dot
notation, which fails this repo's type-aware lint: `noPropertyAccessFrom
IndexSignature` (TS4111) requires index-signature properties like
`process.env.NODE_ENV` to be accessed with brackets. Restore the bracket
form so `pnpm lint` passes.

* fix(block-insert-picker): address Cursor Bugbot findings

- inputRules: preserve case for unknown fence tokens (`return token`, not the
  lowercased form) so the "pass through as typed" contract holds — e.g.
  `GraphQL` stays `GraphQL` for wellKnownInputRules' no-table path.
- inputRules: spread host `buildValue` before `_key`/`_type` in createInsertRule
  so a stray `_key`/`_type` can't hijack block identity (mirrors the
  insertPickerItem fix).
- blockInsertPicker: close the picker when a select bails on an in-flight
  insert, so a menu reopened during a slow initial-value resolution can't get
  stuck swallowing Enter.
- blockInsertPicker: key sections on the namespaced raw group instead of the
  display label, so a curated group named like `ungroupedSection` can't collide
  with the null-group section's React key.

Two other Bugbot findings need no change: the text-block exclusion
(`isTextBlockMember`) already mirrors Sanity's own `findBlockType` (walk the
type chain to root, match `name === 'block'`), and the initial-value spread in
insertPickerItem was already reordered in an earlier commit.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
feat(code-input): export SUPPORTED_LANGUAGES and LANGUAGE_ALIASES (#1600)

Downstream tooling that resolves external language tokens (markdown
fence infostrings, paste/import pipelines) onto code field values has
had to vendor this list and hand-sync it with the Language selector.
Exporting the canonical table (and the alias map) removes the drift.
Version Packages (#1662)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): note npm provenance re-enabled for sanity-plugin-utils (#1661)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1660)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): bump google maps input for css dts cleanup (#1659)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1658)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/client to ^7.24.0 (#1581)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1657)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1640)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): use turbo transit for build cache invalidation (#1655)

Replace empty dependsOn with a transit node so sanity build still skips
workspace package builds while Turbo invalidates when dependency source changes.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): bump pkg-utils, tsdown-config, and vanilla-extract plugins (#1656)

Update catalog entries to @sanity/pkg-utils ^11.0.13,
@sanity/tsdown-config ^0.19.6, and @sanity/vanilla-extract-vite-plugin
^0.2.5. Exclude lightningcss@1.33.0 (and platform packages) from
minimumReleaseAge so pnpm update/dedupe can resolve the new transitive
deps, and align turbo/generators tsdown with the workspace catalog.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1653)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: remove @typescript/native-preview (use TypeScript 7 native tsc) (#1650)

* chore: remove @typescript/native-preview now that TypeScript 7 ships native tsc

TypeScript 7.0.2 includes the native compiler as `tsc`, so the preview package
and its `tsgo` binary are no longer needed. VS Code settings now point the
TypeScript 7 extension at the workspace `typescript` install.

* Update settings.json

* Update settings.json

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): lock file maintenance (#1651)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): stop waiting on dependency builds (#1652)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Delete dependencies-GH-1642.md
Migrate to date-fns v4 and official @date-fns/tz, matching sanity core (#1569)
chore(deps): dedupe pnpm-lock.yaml (#1649)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.12 (#1647)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1645)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.20 (#1646)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): bump @sanity/vanilla-extract-vite-plugin to ^0.2.4

Drop the Bynder Styles.css.js Vite workaround from the test studio —
0.2.4 keeps the compiler alive under unstable_bundledDev, so plain
*.css.js modules no longer hang processVanillaFile.

fix(deps): update dependency tsdown to ^0.22.12 (#1642)
fix(deps): update dependency styled-components to ^6.4.4 (#1566)
chore(deps): dedupe pnpm-lock.yaml (#1643)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-virtuoso to ^4.18.11 (#1641)

* fix(deps): update dependency react-virtuoso to ^4.18.11

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(agents): catalog shared deps + enable pnpm catalogMode prefer (#1374)
chore(deps): dedupe pnpm-lock.yaml (#1637)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.82.0 (#1639)

* fix(deps): update dependency react-hook-form to ^7.82.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update davelosert/vitest-coverage-report-action action to v2.12.2 (#1638)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1612)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
ci: shard Vitest suite (#1584)

* ci: shard vitest suite

* ci: report sharded test coverage

* chore: ignore Vitest blob reports

* ci: require test shards to succeed in aggregate check

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1634)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(format): 🤖 ✨ (#1633)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1632)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.9 (#1622)

* fix(deps): update dependency tsdown to ^0.22.9

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(assist): keep field group selection while editing AI Assist inputs (#1467)

* fix(assist): keep field group selection while editing AI Assist inputs

The AI Assist instruction inspector renders a nested document form that
reused the host document's pane router. Sanity's document pane mirrors the
router `path` param to programmatic focus, and focus drives the selected
field group. Navigating the instruction form therefore overwrote the host
`path`, re-focused the host document, and reset its field group tab.

Isolate the nested pane's `path` navigation in local state while still
forwarding other params (e.g. selected instruction) to the host router.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* test(test-studio): add AI Assist + field groups fixture (SAPP-3970)

Adds an 'AI Assist: Field groups' document type with Content/SEO field
group tabs so the field-group-reset behavior with AI Assist can be
manually reproduced and verified.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* refactor(assist): use react-fast-compare for param equality

Addresses PR review: the hand-rolled shallowEqualParams returned a false
positive when two param objects had the same key count but different keys
whose values were undefined (e.g. {foo: undefined} vs {bar: undefined}),
because it never verified the key existed on the other object. Reuse
react-fast-compare (already a plugin dependency, used in _lib/useListeningQuery)
instead of a custom check, and add a regression test.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(bynder-input): restore Compact View modal visibility in Studio v6 (#1553)

* fix(bynder-input): restore Compact View modal visibility in Studio v6

Upgrade @bynder/compact-view from 5.1.2 to 5.4.0 and patch its shadow DOM
integration for Sanity Studio v6:

- disableCSSOMInjection on StyleSheetManager so styled-components injects
  CSS into the shadow root instead of leaving empty style tags
- position:fixed + z-index on CompactViewContainer so the portal host
  stacks above Studio UI layers
- widen React peer dependency range to support React 19

Also render BynderModalLayout outside the button flex row.

* fix: unmount bynder layout when not open

* fix(bynder-input): take @bynder/compact-view 5.4.0 as a regular pinned dependency

Implements review feedback on the patch-based approach:
- drop the pnpm patch and the bundling of @bynder/compact-view; pin it
  as a regular dependency instead
- style the Compact View modal host from the plugin with a
  vanilla-extract globalStyle instead of patching Modal.js
- document how to allow the stale react peer range (npm
  --legacy-peer-deps, pnpm peerDependencyRules.allowedVersions) in the
  README, and use the pnpm rule for the workspace itself

* fix(bynder-input): survive Compact View shadow-root re-attachment and fix dev-studio css.js crash

- append a <slot> fallback to Compact View's shadow root while the modal
  is mounted: its attachShadow effect has no cleanup, so when React
  reconnects passive effects on the same host (StrictMode, Activity),
  the second attachShadow() throws and Compact View falls back to the
  host's light DOM, which the slotless shadow tree kept invisible
- work around @bynder/compact-view's Styles.css.js (a plain JS module
  whose name matches vanilla-extract's cssFileFilter and deadlocks the
  compiler under sanity dev's bundled dev mode) in the test studio
- move the container globalStyle to BynderInput.css.ts so it loads with
  the input instead of the lazy modal chunk

* chore(bynder-input): bump changeset to major

Taking @bynder/compact-view as an unbundled regular dependency with a
stale react peer range can fail npm installs without --legacy-peer-deps,
so it's a breaking change.

* chore(bynder-input): widen @bynder/compact-view to ^5.4.0

The exact pin was only needed while the package was patched and
inlined; as a plain regular dependency a caret range is fine.

* chore: merge main and regenerate lockfile

The PR lockfile was generated against a main where
@sanity/vanilla-extract-vite-plugin resolved to 0.2.1; main has since
bumped it to 0.2.2, and the git-merged lockfile on the PR merge ref was
internally inconsistent (ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY in CI).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.19.3 (#1598)

* fix(deps): update dependency @sanity/tsdown-config to ^0.19.3

* chore: dedupe lockfile and add release-age excludes for tsdown transitive deps

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1631)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2 (#1614)

* fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2

* chore(deps): exempt yuku-parser from minimum release age for dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Move turbo/**/*.hbs oxfmt ignore to root config (#1604)

The turbo generator template ignore is monorepo-specific and should
not ship in the shared @sanity/plugin-kit/oxfmt preset consumed by
standalone plugins.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.10 (#1630)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(assist): fix README markdown formatting and document English-only image descriptions (#1628)

* fix(@sanity/assist): respect document language when generating image descriptions

Pass the configured translate.document.languageField as languagePath to the
generate-caption task (both the automatic generation triggered on image
change, and the manual 'Generate image description' field action), so
generated alt text/captions are written in the document's language instead
of always defaulting to English.

Fixes https://github.com/sanity-io/plugins/issues/1606

* docs(@sanity/assist): clarify caption generation language limitation

Testing against the live AI Assist backend confirms that the generate-caption
task currently always writes descriptions in English, regardless of the
languagePath sent from the client (this matches Sanity's own documentation,
which states the automatic image description feature is English-only today
and recommends the Transform Agent Action's image-description operation as a
workaround). Update the changeset and README to accurately describe the
change as forward-compatible plumbing rather than a full fix, and point users
at the documented workaround for non-English alt text today.

* refactor(assist): resolve languagePath inside useGenerateCaption

Compute languagePath from config.translate.document.languageField once
inside useGenerateCaption itself (via useAiAssistanceConfig), instead of
requiring every call site to read the config and thread it through as an
argument. This removes the duplicated config lookup from both
ImageContextProvider and generateCaptionsActions, and matches the cleaner
approach used in sanity-io/plugins#1627.

Also documents in translate/types.ts JSDoc that languageField is reused
for image description generation.

* docs(assist): fix README markdown formatting and add known limitations

The README relied on markdown block elements (headings, lists, code
fences, images) interrupting paragraphs without blank lines between
them. GitHub tolerates this, but stricter renderers (e.g. the plugin
listing on sanity.io) join everything into unreadable walls of text
with literal '### Heading' and '- list item' fragments inline.

- Separate all block elements with blank lines throughout
- Add a 'Known limitations' section documenting that image descriptions
  are always generated in English regardless of
  translate.document.languageField (backend limitation, see #1606),
  with Transform Agent Action and translate-after workarounds
- Fix broken links: empty-href heading, HOW-TO-USE pointing at the old
  standalone repo layout, LICENSE pointing at a non-existent file
- Fix broken bold markup in the 'Enabling the AI Assist API' steps and
  the useExampleFieldActions TOC anchor
- Add missing TOC entries (Known limitations, Caveats, Third party
  sub-processors)

Also reverts the languagePath client plumbing from this branch: the
backend ignores it, so the plugin change had no effect. This PR is now
docs-only.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1619)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11.13.1 (#1624)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.9 (#1625)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.5 (#1626)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.27.0

chore: fix css

fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1 (#1610)

* fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.59.0 (#1599)

* fix(deps): update dependency oxfmt to ^0.59.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.7 (#1594)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Fix TypeGen workflow failure by pinning `@sanity/vanilla-extract-vite-plugin` to 0.1.1 (#1616)

* Initial plan

* fix(typegen): pin vanilla-extract vite plugin to 0.1.1

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): bump tsdown and vanilla-extract

fix(deps): update dependency oxlint to ^1.74.0 (#1603)

* fix(deps): update dependency oxlint to ^1.74.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update zizmorcore/zizmor-action action to v0.6.0 (#1609)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1611)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin (#1590)

* chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin

Swap the vanilla-extract Vite plugin used by the test studio and
@sanity/google-maps-input's vitest config to @sanity/vanilla-extract-vite-plugin,
a Vite 8 rewrite with plugin hook filters and a caching compiler on Vite's
Environment API / ModuleRunner instead of the legacy vite-node. Bump tsdown to
^0.22.8 and @sanity/tsdown-config to ^0.17.0 alongside it.

google-maps-input's vitest.config.ts also needed a small stub plugin: its
dist/bundle.css.js Node/SSR shim matches the upstream cssFileFilter by
filename alone (*.css.js), which made the new plugin try to evaluate it as
real .css.ts source and throw ReferenceError: require is not defined via
ModuleRunner (vite-node tolerated this). The stub renames that module id away
from a .css.js shape before vanillaExtractPlugin() ever sees it.

Documented both the plugin swap and the shim workaround in the
sanity-plugin-best-practices skill and the new-plugin generator templates so
future vanilla-extract plugins get this for free.

* chore(deps): bump @sanity/tsdown-config to ^0.17.1, drop bundle.css.js shim workaround

@sanity/tsdown-config 0.17.1 (via @sanity/vanilla-extract-tsdown-plugin
0.2.1 / @sanity/vanilla-extract-rolldown-plugin 0.1.1) renames the
vanilla-extract Node/SSR compat shim from bundle.css.js to bundle-css.js so
it no longer matches vanillaExtractPlugin()'s cssFileFilter by filename
alone (sanity-io/pkg-utils#3043). That was the root cause of the
require-is-not-defined regression worked around in the previous commit, so
the stub-bundle-css-js-shim plugin is no longer needed.

Rebuilding @sanity/google-maps-input picked up the renamed dist/bundle-css.js
shim and auto-rewrote its package.json ./bundle.css export map (adding a
types condition too). Reverted the vitest.config.ts workaround and the
matching documentation/generator-template changes back to the plain
vanillaExtractPlugin() registration, now noted as a historical gotcha
instead of a live workaround.

* docs(test-studio): note that unstable_bundledDev breaks .css.ts HMR

Document the trade-off discovered while testing the vanilla-extract Vite
plugin swap: with unstable_bundledDev: true, editing a .css.ts file doesn't
hot-reload (a sanity dev restart is required), while plain .ts/.tsx HMR is
unaffected. Comment out the line for a session if fast CSS HMR is needed
(confirmed sub-10ms with it unset).

* docs(styling skill): trim verbose vanilla-extract setup section

The ./bundle.css export map (and its dist/bundle-css.js shim/types) is
generated and kept in sync automatically by @sanity/tsdown-config's
vanillaExtract option, so it doesn't need explaining or a JSON example to
copy. Simplify the vitest.config.ts guidance to why (@sanity/vanilla-extract-
vite-plugin is faster than upstream) without the ModuleRunner/hook-filter/
sanity-build internals. Drop the sanity.cli.ts snippet, since the test studio
already registers the plugin globally in this repo and doesn't need to be
touched per-plugin. Drop the bundle.css.js->bundle-css.js rename history
note, a one-off gotcha now fixed upstream rather than evergreen guidance.

* chore(deps): bump @sanity/tsdown-config to ^0.17.2, @sanity/vanilla-extract-vite-plugin to ^0.1.1

Both are latest-available patch releases (0.17.1 -> 0.17.2 pulls in
@sanity/vanilla-extract-tsdown-plugin 0.2.2 / @sanity/vanilla-extract-
rolldown-plugin 0.1.2; 0.1.0 -> 0.1.1 is the vite plugin's own patch).
tsdown stays at ^0.22.8, already the latest. Benchmarked in the next commit's
message to confirm this doesn't regress anything.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @changesets/cli to ^2.31.1 (#1607)

* fix(deps): update dependency @changesets/cli to ^2.31.1

* chore(deps): exempt @changesets/cli and refresh lockfile with dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.1 (#1593)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1591)
chore(deps): dedupe pnpm-lock.yaml (#1602)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(media): use useMediaIndex so Tags panel works on wide viewports (#1592)

The home-rolled useBreakpointIndex returned -1 above 1920px, which
force-hid the Tags panel. Switch Items to @sanity/ui's useMediaIndex,
which includes a min-width catch-all and matches Controls breakpoints.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency xstate to ^5.32.5 (#1597)

* fix(deps): update dependency xstate to ^5.32.5

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.8 (#1596)

* fix(deps): update dependency tsdown to ^0.22.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsconfig to ^2.2.1 (#1595)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/setup-node action to v7 (#1586)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11 (#1579)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency nanoid to v6 (#1577)

* fix(deps): update dependency nanoid to v6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency groq to ^6.5.0 (#1523)

* fix(deps): update dependency groq to ^6.5.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint (#1565)

* Migrate plugin-kit and monorepo formatting from prettier to oxfmt

- Ship the monorepo's oxfmt settings as a @sanity/plugin-kit/oxfmt preset
- Re-export the preset from the root oxfmt.config.ts (replaces .oxfmtrc.json)
- init/inject scaffold oxfmt (config, devDependency, format script) instead
  of prettier; --no-prettier is now --no-oxfmt
- verify-package: replace the duplicate-prettier-config check with a
  monorepo-aware oxfmt check that expects the config at the workspace root
  (or next to package.json outside monorepos) and flags leftover prettier
  configuration with migration instructions

* Migrate plugin-kit and monorepo linting from eslint to oxlint, drop tsc

- Ship the monorepo's oxlint settings as @sanity/plugin-kit/oxlint-config.json
  (type-aware rules, type checking via oxlint-tsgolint, warnings denied); the
  root .oxlintrc.json extends it and keeps only workspace-specific ignores and
  overrides
- init/inject scaffold oxlint (.oxlintrc.json, oxlint + oxlint-tsgolint
  devDependencies, lint script) instead of eslint; --no-eslint is now
  --no-oxlint, and the typescript flag now controls TypeScript scaffolding
  instead of piggybacking on the eslint flag
- verify-package: drop the tsc run (oxlint type-checks as part of linting) and
  the duplicate-eslint-config check; add a monorepo-aware oxlint config check
  with legacy eslint detection; the v2-imports check (eslintImports -> imports)
  now runs a bundled oxlint pass instead of the package's eslint
- pin typescript in dev/test-studio so the sanity peer keeps resolving to the
  catalog version now that plugin-kit no longer pulls eslint/typescript peers

* Pin oxlint imports-check output format so CI matches snapshots

oxlint auto-switches to GitHub annotations format on CI; --format=agent keeps
the compact per-diagnostic output (including help: messages) everywhere

* Ship the oxlint config as @sanity/plugin-kit/oxlint (oxlint.config.ts pattern)

Replaces the oxlint-config.json + .oxlintrc.json extends-by-file-path setup
with a TS config module, mirroring the oxfmt preset: the shared config is
authored in src/oxlint.ts (typed OxlintConfig, built to plain JS in dist), the
root and scaffolded configs are oxlint.config.ts files that re-export or extend
it via defineConfig({extends: [...]})

* Drop the Sanity v2 imports check from verify-package/verify-studio

v2-era imports are no longer linted for; with the bundled oxlint pass gone,
oxlint moves from a runtime dependency of plugin-kit to a dev/peer dependency
(it is only needed by consumers, which the scaffolding already installs)

* Address Bugbot review: opt-out scaffolds must verify, root legacy configs detected

- init/inject with --no-oxfmt/--no-oxlint now write the matching
  sanityPlugin.verifyPackage opt-outs, so the scaffolded build script
  (which runs verify-package) works out of the box
- the oxfmt/oxlint checks now also flag leftover prettier/eslint configs at
  the detected workspace root, not only next to the plugin's package.json

* Scaffold ESM semver-workflow configs so fresh presets pass oxlint

The preset's lint-staged.config.js and commitlint.config.js templates
still used module.exports, but scaffolded plugins are ESM
("type": "module") and the shared oxlint config bans CommonJS via
import/no-commonjs (the old .eslintignore that excluded these files is
gone). A fresh 'init --preset semver-workflow' failed 'oxlint' out of
the box; switch the templates to 'export default' and cover the preset
configs in the inject test.

* Validate package-local oxfmt/oxlint configs that override the workspace root

verify-package treated a valid workspace-root config as sufficient and only
looked at the plugin directory when the root check failed. But oxfmt/oxlint
discover nested configs, so a local oxfmt.config.ts / oxlint.config.ts
overrides the root for that package's files — verify could pass while the
package wasn't actually using the shared plugin-kit settings.

In a monorepo the validators now check the plugin directory first: when a
local config exists it must use the shared preset/config (the error notes it
overrides the root config), and only when no local config exists does the
workspace root config decide the result.

* Flag legacy prettier/eslintConfig keys in the workspace root package.json

The oxfmt/oxlint checks scanned the workspace root for leftover
prettier/eslint config files but only looked at the plugin's own
package.json for the prettier / eslintConfig keys. In monorepos those
keys often live in the root package.json, where they still configure
the whole workspace — verify-package now inspects the root manifest
too and reports them alongside the legacy config files.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Move oxfmt, oxlint and oxlint-tsgolint to the pnpm catalog

plugin-kit's oxfmt/oxlint peers now reference the default catalog
(catalog:) rather than catalog:peer or explicit ranges: the shared
configs may rely on rules and features that ship in a new minor, so
the peer range must stay in lockstep with the version the monorepo
develops against.

* Mark oxfmt/oxlint as optional peer dependencies of plugin-kit

Unlike @sanity/pkg-utils (a hard requirement, checked unconditionally
by verify-package), the oxfmt/oxlint verify-package checks are
individually opt-outable via sanityPlugin.verifyPackage.oxfmt/oxlint:
false, and plugin-kit's own code only ever imports their types. So a
consumer can legitimately use plugin-kit without oxfmt or oxlint
installed; peerDependenciesMeta now reflects that.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
chore(deps): bump sanity to 6.5.0 and enable unstable_bundledDev (#1589)

* chore(deps): bump sanity to 6.5.0 and enable bundledDev

Align @sanity/client (7.23.2) and @sanity/eventsource (5.0.4) so
unstable_bundledDev can be enabled without EventSource2 crashes in Structure.

* chore(markdown): sync inlined @sanity/client to 7.23.2

* chore(deps): drop client/eventsource overrides after lockfile bump

Keep @sanity/client@7.23.2 and @sanity/eventsource@5.0.4 via the
lockfile after pnpm dedupe; remove the temporary overrides and markdown
changeset.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1585)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.0 (#1578)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.3 (#1582)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Remove redundant `dts` option from tsdown configs (#1567)

* Remove redundant dts option from tsdown configs

Since typescript v7 (tsgo) landed, tsdown enables dts generation
automatically (packages declare types, or the resolved tsconfig sets
declaration: true) and rolldown-plugin-dts picks the tsgo generator
whenever the installed typescript major is >= 7. The explicit
dts: {tsgo: true} / dts: true options are therefore no-ops.

Verified: dist output across all 50 packages is byte-identical
before/after this change.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* Mention tsconfig declaration fallback in AGENTS.md dts note

Not all packages declare a types field (@repo/generators,
@sanity/form-toolkit); for those, tsdown enables dts generation via
declaration: true inherited from the @sanity/tsconfig presets.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.14.2 (#1583)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: simplify, rely on default options

fix(deps): update build tooling dependencies (#1580)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1572)
chore(deps): dedupe pnpm-lock.yaml (#1575)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.6 (#1573)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/icons to ^5.1.0 (#1574)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.19 (#1570)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Prevent Renovate from rewriting pnpm `peer` catalog ranges (#1568)
fix(deps): update tsdown to ^0.22.7 and @sanity/tsdown-config to ^0.14.0 (#1571)
Version Packages (#1563)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Add pnpm `peer` catalog for peer dependency ranges (#1562)

Centralize all external peerDependencies ranges in a named `peer` catalog
in pnpm-workspace.yaml, referenced as `catalog:peer`. Conflicting ranges
resolve to the most-used one: react/react-dom `^19.2` and
styled-components `^6.1` (aligning sanity-plugin-media and
sanity-plugin-mux-input with what Sanity Studio v5+ already requires).

Peers on other workspace packages keep `workspace:^` (or an explicit
range in @sanity/sfcc) so changesets can keep tracking dependents.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1558)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: enable React Compiler for media and mux-input (#1560)

* feat: enable React Compiler for media, mux-input and debug-preview-url-secret-plugin

Enables the reactCompiler tsdown option for the last three plugins that
were still built without it. For sanity-plugin-media and
sanity-plugin-mux-input the react peer dependency is tightened to ^19.2
(matching every other plugin) since the compiled output imports
react/compiler-runtime, which requires React 19. Their sanity peer
(^5 || ^6.0.0-0) already mandates React 19.2, so no supported setup is
dropped.

* chore: don't enable React Compiler for debug-preview-url-secret-plugin

This plugin has no React components, so running the build through React
Compiler is a no-op (its dist output is byte-identical with or without
it). Drop the reactCompiler flag, the babel-plugin-react-compiler devDep,
and the changeset; keep the change limited to the two plugins that
actually ship components (media and mux-input).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Handle TS7 `@typescript-eslint` config crashes in plugin-kit import validation (#1561)

* Initial plan

* fix(plugin-kit): fallback eslint import check for TS7 parser crash

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency typescript to v7 (#1531)

* fix(deps): update dependency typescript to v7

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
refactor: simplify how the PKG_VERSION is handled in mjux

Version Packages (#1556)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1557)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update sanity monorepo to ^6.4.0 (#1555)

Bumps the workspace catalog (@sanity/mutator, @sanity/schema, @sanity/util, @sanity/vision, sanity) and groq in sanity-plugin-media from ^6.3.0 to ^6.4.0.

- mux-input: replace the DocumentPreviewLink render-function workaround with IntentLink passed directly to PreviewCard's as prop, dropping the FIXME cast and the react-compiler lint suppression.
- orderable-document-list: keep using useDocumentVersionInfo (the useDocumentVersions replacement would need the unexported getDocumentVersionInfoFromVersions util) with an oxlint suppression for the new deprecation, and drop a ts-expect-error made obsolete by the 6.4.0 PreviewCard typings.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1527)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat!: support TypeScript 7 in plugin-kit, require typescript 6.x || 7.x (#1554)

* feat!: support TypeScript 7, require typescript 6.x || 7.x in plugin-kit

* fix: scaffold typescript ^6 in plugin-kit init to avoid @typescript-eslint peer conflict

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): ts doesn't need to be a dep to plugin packages

fix(deps): update turbo monorepo to v2.10.4 (#1543)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1552)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.13.1 (#1551)
fix(deps): update dependency vitest to ^4.1.10 (#1542)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @uiw/react-codemirror to ^4.25.11 (#1540)

* fix(deps): update dependency @uiw/react-codemirror to ^4.25.11

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.1 (#1550)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1549)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update linters (#1548)

* fix(deps): update linters

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency get-it to ^9.1.0 (#1546)

* fix(deps): update dependency get-it to ^9.1.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.81.0 (#1547)

* fix(deps): update dependency react-hook-form to ^7.81.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.26.0 (#1545)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @tsconfig/vite-react to ^8.1.1 (#1544)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency iso-639-1 to ^3.1.6 (#1541)

* fix(deps): update dependency iso-639-1 to ^3.1.6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @uiw/codemirror-themes to ^4.25.11 (#1539)

* fix(deps): update dependency @uiw/codemirror-themes to ^4.25.11

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @types/node to ^24.13.3 (#1538)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.5 (#1536)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.5

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1537)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-query to ^5.101.2 (#1535)

* fix(deps): update dependency @tanstack/react-query to ^5.101.2

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v10.34.5 (#1533)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @portabletext/block-tools to ^5.1.11 (#1534)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1532)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.4 (#1519)

* fix(deps): update dependency tsdown to ^0.22.5

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.13.0 (#1529)
fix(deps): update dependency @vanilla-extract/vite-plugin to ^5.2.4 (#1517)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to v11 (#1530)
fix(deps): update dependency react-virtuoso to ^4.18.10 (#1518)

* fix(deps): update dependency react-virtuoso to ^4.18.10

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.25.0 (#1520)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1526)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-icons to ^5.7.0 (#1486)

* fix(deps): update dependency react-icons to ^5.7.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^10.9.1 (#1515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1502)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Merge pull request #1524 from sanity-io/actions/format-main

chore(format): 🤖 ✨
fix(deps): update dependency @sanity/tsdown-config to ^0.12.0 (#1521)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.58.0 (#1522)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.3.5 (#1516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.18 (#1505)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1511)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: disable automatic issue triage (#1514)

* chore: disable automatic issue triage
Version Packages (#1504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Merge pull request #1513 from sanity-io/cursor/orderable-intent-repro-1506-bf83

fix(orderable-document-list): scope canHandleIntent to list's own type (#1506)
test(test-studio): reframe issue #1506 repro as regression fixture

Now that canHandleIntent is scoped to the list's own type, update labels
and comments to describe regression verification rather than active repro.

chore: remove repro step, add fix

test(test-studio): add repro case for orderable-document-list issue #1506

Add dedicated schema types and structure section that mirrors the issue
report: a regular document list alongside an orderable list at the same
level. Creating a Page via the global create menu incorrectly routes into
the Team Members orderable list due to canHandleIntent claiming all types.

Includes REPRO.md with step-by-step verification instructions.

Add orderable document list readme (#1508)
fix(deps): update dependency react-rx to ^4.2.3 (#1493)

* fix(deps): update dependency react-rx to ^4.2.3

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @codemirror/view to ^6.43.5 (#1500)

* fix(deps): update codemirror

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1496)
chore(deps): update linters (#1499)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(@sanity/assist): render instruction preview icons from @sanity/icons v5 (#1503)

The instruction schema's prepare() returns a rendered <Icon symbol> element
for instructions with a custom icon (since the icons v5 lazy-map migration),
but the preview component still rendered props.icon as a component, crashing
the instruction list with 'Element type is invalid ... got: <Icon />'.
Handle both the element and the component (SparklesIcon fallback) shapes.

Also add a README troubleshooting section for MISSING_EXPORT icon build
errors, which come from code importing icons from the @sanity/icons package
root - a pattern removed in v5 (the plugin itself already uses the per-icon
export paths).

Fixes #1495

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.3.1 (#1498)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.0.8 (#1492)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @vis.gl/react-google-maps to ^1.9.0 (#1501)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1497)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency xstate to ^5.32.4 (#1494)

* fix(deps): update dependency xstate to ^5.32.4

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Upgrade oxlint-tsgolint to ^0.24.0 and fix new lint findings (#1476)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1489)
refactor: move all plugins and packages to tsdown (#1491)
refactor: move dbeug-live-sync-tags to tsdown (#1490)
Version Packages (#1487)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1488)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Remove isolatedDeclarations and collapse to one tsconfig per plugin (#1460)

* Remove isolatedDeclarations and use a single tsconfig per plugin

Drop the @sanity/tsconfig/isolated-declarations preset from the shared
@repo/tsconfig/build.json (now removed) and @repo/package.config, and
delete every per-plugin tsconfig.build.json. @sanity/pkg-utils now builds
declarations from each plugin's single tsconfig.json; dts: 'rolldown' is
kept, so isolated declarations can be re-adopted later by re-adding the
preset.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* Scaffold a single tsconfig in the plugin generator

Remove the isolatedDeclarations prompt and the tsconfig.build.json
scaffolding action from both generators, and delete the now-unused
tsconfig.build.json.hbs template.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* Drop tsconfig.build.json from plugin-transfer docs

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* Add changesets for plugins with regenerated declarations

Patch bumps for the 8 published packages whose dist declaration output
(.d.ts and/or .d.ts.map) changed when dts generation moved off
isolatedDeclarations. The differences are cosmetic (quote style, JSDoc
indentation, union member order); runtime JS is unchanged.

* Remove @repo/tsconfig and extend @sanity/tsconfig/strictest directly

With a single tsconfig per plugin the @repo/tsconfig wrapper package no
longer earns its keep. Each plugin now extends @sanity/tsconfig/strictest
directly and sets customConditions: ['development'] inline (temporarily
needed until the planned move to tsdown, which handles dev conditions
differently). Swaps the @repo/tsconfig workspace devDependency for
@sanity/tsconfig (catalog) in all 48 packages and updates the generator
templates and knip config. Built dist output is byte-identical.

* Generate declarations with tsgo

Enable the tsgo pipeline (@typescript/native-preview) in
@repo/package.config, the same engine oxlint's type-check runs on.
tsgo requires inferred exported types to be portable (TS2883), so a few
sites gain explicit annotations: the xstate form machine in
sanity-plugin-dashboard-widget-vercel, the schema test fixtures in
sanity-naive-html-serializer, and the render helper in
sanity-plugin-media's tests. Documents the TS2883 failure mode in
AGENTS.md.

* Add changesets for tsgo-regenerated declarations

Unifies the earlier isolatedDeclarations changesets and adds patch bumps
for the 21 additional published packages whose dist declaration output
changed when dts generation switched to tsgo (cosmetic quote-style and
union-ordering differences only).

* Relax minimumReleaseAge to the pnpm v11 default

Drops the release-age gate from 3 days to pnpm v11's default of 1440
minutes (1 day), unblocking resolution of @vitejs/devtools releases the
test studio tracks closely.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
Version Packages (#1484)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(@sanity/assist): open icon picker reliably with lazy-loaded icons (#1485)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): update dependency @vitejs/devtools to ^0.3.4 (#1479)

* chore(deps): update dependency @vitejs/devtools to ^0.3.4

* chore: dedupe lockfile with pnpm

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency motion to ^12.42.2 (#1480)

* fix(deps): update dependency motion to ^12.42.2

* chore(deps): changeset for dependency update

* chore: run pnpm dedupe for motion update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Version Packages (#1483)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @vanilla-extract/css to ^1.21.1 (#1477)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Add changesets for plugins using styledComponents tree-shaking upgrade (#1478) (#1481)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix: update icon lazy loading

fix(deps): update dependency @sanity/icons to v5 (#1482)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^10.9.0 (#1478)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1473)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency typescript to v6 (#1474)

* fix(deps): update dependency typescript to v6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(document-internationalization): enable creating language copies for documents in releases (#1475)

* fix(document-internationalization): enable translations for documents in releases

When …
cursor Bot pushed a commit that referenced this pull request Jul 28, 2026
Knip flagged 12 types re-exported from the react-color barrel that nothing
outside the module imports. Keep only the types consumers actually use.

Merge branch 'cursor/ban-react-class-components-ca2a' into cursor/color-input-vanilla-extract-35df

Keep vanilla-extract styling for Alpha/Hue/Saturation/EditableInput;
ban branch still uses styled-components/inline styles for those files.

fix(color-input): convert vendored react-color components to function components

Convert Alpha/Hue/Saturation/EditableInput and centralize drag wiring in a
shared useDrag hook: a native mousedown listener attached in useLayoutEffect,
useEffectEvent so events always read the latest props, and per-gesture
mousemove/mouseup listeners cleaned up via AbortController. EditableInput
clears its pending blur-sync value once the prop catches up with the
displayed value, so blurring can't revert a typed value.

fix(sanity-plugin-media): convert ReduxProvider to a function component

Initialize the redux store once with a lazy useState initializer.

fix(orderable-document-list): convert OrderableDocumentList to a function component

Expose the structure menu actionHandlers via useImperativeHandle and the
React 19 ref-as-prop pattern, with a dependency list so the handle is not
re-created on every render (the pane stores it in state).

feat(plugin-kit): ban Component, PureComponent and createRef imports from react

Class components are not allowed in this repo — only function components.
createRef is only useful in class components; use the useRef hook instead.
Also point skill docs at the shared @sanity/plugin-kit/oxlint config.

chore: drop color-input oxlint override

Leave shared oxlint rules untouched; the vanilla-extract migration does
not need a package-specific no-restricted-imports lock-in.

Merge branch 'cursor/ban-react-class-components-ca2a' into cursor/color-input-vanilla-extract-35df

Resolve stack conflicts: keep vanilla-extract exports (drop EditableInputStyles),
bump @vanilla-extract/css catalog to ^1.21.2 while retaining @vanilla-extract/dynamic,
and regenerate the lockfile. Preserve EditableInput blur-sync fix from the base branch.

Version Packages (#1735)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
refactor(color-input): migrate styling to vanilla-extract

Replace styled-components and inline style objects with colocated
vanilla-extract stylesheets, using assignInlineVars for dynamic
theme/color values. Builds on the function-component + useDrag
refactor from the ban-react-class-components branch.

Merge remote-tracking branch 'origin/main' into cursor/ban-react-class-components-ca2a

fix(color-input): clear stale blur-sync value when prop catches up

The prop-sync effect early-returned when the normalized prop matched the
displayed value, leaving a blurValue captured from an older prop in place;
blurring would then restore that older value. Clear it (in a transition to
satisfy the React Compiler EffectSetState rule).

fix(deps): bump @sanity/pkg-utils, @sanity/tsdown-config and vanilla-extract plugins (#1769)

* fix(deps): bump @sanity/pkg-utils, @sanity/tsdown-config and vanilla-extract plugins

- @sanity/pkg-utils ^11.0.16 -> ^11.0.17
- @sanity/tsdown-config ^0.21.1 -> ^0.21.2
- @sanity/vanilla-extract-vite-plugin ^0.2.7 -> ^0.2.8

tsdown (^0.22.14) and @vanilla-extract/css (^1.21.2) were already on the
latest release; refreshing the lockfile picks up the transitive
@sanity/vanilla-extract-{integration,rolldown-plugin,tsdown-plugin} updates.

* fix(deps): public-hoist @vanilla-extract/css so node_modules .css.js files resolve it

`@sanity/vanilla-extract-vite-plugin` filters its transform hook on
/\.css\.(js|cjs|mjs|jsx|ts|tsx)/ with no node_modules exclusion, so it also
compiles `@bynder/compact-view/Styles.css.js` — a plain module exporting a CSS
string for a shadow root, not a vanilla-extract module. That package declares no
dependencies, so the compile only finds `@vanilla-extract/css/adapter` when pnpm
happens to hoist it into the hidden .pnpm/node_modules directory. On Vercel it
did not, failing `sanity build` for dev/test-studio with
"Cannot find module '@vanilla-extract/css/adapter'".

Public-hoisting puts @vanilla-extract/css on the root node_modules resolution
path, which is deterministic regardless of the implicit hoist layout.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1766)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.16 (#1768)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(test-studio): add experimental @sanity/themer tool (#1738)

* feat(test-studio): add experimental @sanity/themer tool

Wire themerTool() into the Home workspace so theme presets and colors
can be previewed live against the plugins kitchen sink.

* chore(test-studio): bump @sanity/themer to 0.2.0

* chore(test-studio): use ^0.2.0 for @sanity/themer

Merge main and widen the themer dependency to a caret range.

* chore: dedupe lockfile to resolve @sanity/client type mismatch

pnpm dedupe collapses the 7.24.0 / 7.25.0 client split that broke
type-aware lint after merging main with themer.

* chore: handle lockfile

* chore(knip): ignore unused catalog entry reports in pnpm-workspace.yaml

The catalogued @sanity/types entry is referenced only by the overrides block
in pnpm-workspace.yaml, which knip does not read, so it was reported as an
unused catalog entry and failed `pnpm knip`.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: fix lock

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @vanilla-extract/css to ^1.21.2 (#1764)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.21 (#1763)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.5.0 (#1762)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1761)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
test(document-internationalization): expand Vitest + Playwright e2e coverage (#1712)
chore(deps): dedupe pnpm-lock.yaml (#1754)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @playwright/test to ^1.62.0 (#1760)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.7 (#1759)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency concurrently to ^10.0.4 (#1758)

* fix(deps): update dependency concurrently to ^10.0.4

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.10 (#1757)

* fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.10

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.8 (#1756)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.13 (#1755)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1749)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: fix lockfile

chore(deps): dedupe pnpm-lock.yaml (#1753)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/client to ^7.25.0 (#1747)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.5 (#1745)

* fix(deps): update dependency @sanity/ui to ^3.4.5

* fix(knip): remove unused react-is devDependency from media plugin

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @mux/mux-player to ^3.13.2 (#1751)

* fix(deps): update dependency @mux/mux-player to ^3.13.2

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1748)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.83.0 (#1750)

* fix(deps): update dependency react-hook-form to ^7.83.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @mux/mux-player-react to ^3.13.2 (#1752)

* fix(deps): update dependency @mux/mux-player-react to ^3.13.2

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.2 (#1744)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1734)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Delete dependencies-GH-1739.md
fix(deps): update dependency @sanity/color to ^3.0.8 (#1743)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.7 (#1746)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.21.1 (#1742)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.15 (#1741)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update zizmorcore/zizmor-action action to v0.6.1 (#1740)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.14 (#1739)

* fix(deps): update dependency tsdown to ^0.22.14

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/cache action to v6 (#1731)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @playwright/test to ^1.61.1 (#1737)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.21.0 (#1736)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.6 (#1733)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.14 (#1732)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/cache action to v5 (#1730)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.6 (#1729)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.8 (#1728)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.29.0 (#1727)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.5 (#1726)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: add Playwright smoke e2e with ephemeral datasets (#1710)

* feat: add Playwright smoke e2e for test studio

Set up an e2e workspace with Chromium/Firefox smoke coverage,
storageState studio auth matching sanity-io/sanity, fail-fast
token validation, and a CI workflow that builds and previews
the test studio locally.

* fix(e2e): correct Playwright artifact gitignore paths

Paths in e2e/.gitignore are relative to that directory, so leading
/e2e/… never matched results or playwright-report.

* fix(e2e): require project id and dataset from env vars

Source SANITY_E2E_PROJECT_ID and SANITY_E2E_STUDIO_DATASET from
GitHub repository variables in CI (no hardcoding). Rename the
dataset env to SANITY_E2E_STUDIO_DATASET and map both onto the
test-studio SANITY_STUDIO_* vars when starting webServer.

* fix(e2e): stop knip from loading Playwright config

resolveE2eEnv() requires project id and dataset at config load time;
disable the knip Playwright plugin for the e2e workspace so analysis
does not need CI secrets.

* fix(e2e): drop token fallback and debug browser filter

Require only SANITY_E2E_SESSION_TOKEN for auth, and always run
Chromium and Firefox projects (filter via Playwright UI/CLI instead).

* docs(e2e): document plugins-e2e-testing sandbox project

Note that e2e uses Sanity sandbox org project plugins-e2e-testing
(a1psl692) and update .env.example accordingly.

* ci(e2e): post Sanity-style PR status comment

Add a Playwright summary reporter and update the E2E workflow to
post/refresh a pull request comment with pass/fail/flaky/skipped
counts and a link to the Actions run (HTML report as artifact until
Vercel report hosting lands).

* ci(e2e): deploy Playwright HTML report to Vercel

Host the report on a dedicated Vercel project and link pass/fail
counts in the PR status comment, matching sanity-io/sanity. Document
VERCEL_E2E_REPORT_* secret setup in e2e/README.md.

* feat(e2e): add ephemeral per-browser datasets like sanity-io/sanity

Create pr-*/main-* chromium and firefox datasets in CI, expose matching
studio workspaces, and schedule cleanup for closed PRs / stale main runs.

* fix(e2e): bump Sanity client apiVersion to 2026-07-23

* fix(ci): avoid env context in job-level e2e workflow env

GitHub Actions does not allow `env.*` when evaluating job `env:` maps.
Define dataset names at workflow level with inlined format expressions.

* ci(e2e): run chromium and firefox as a parallel matrix

Split setup / per-browser test / report-merge jobs. Each matrix job gets
its own SANITY_E2E_DATASET and runs playwright --project for that browser.

* fix(e2e): avoid pnpm setup command colliding with dataset script

`pnpm --filter e2e setup` invoked pnpm's built-in `setup`, so ephemeral
datasets were never created. Rename to `create-dataset` and call via
`pnpm run`. Fail the CI step if creation fails.

* ci(e2e): scope pull-requests write to jobs that post comments

Satisfy zizmor excessive-permissions: keep workflow at contents:read
and grant pull-requests:write only on setup/report jobs.

* ci(e2e): deploy studio preview to Vercel like sanity-io/sanity

Add deploy-preview job (vercel pull/build/deploy --prebuilt) with
ephemeral datasets baked in; matrix tests run against the preview URL
instead of a local sanity preview server. Adds vercel CLI as a root
devDependency and dev/test-studio/vercel.json build settings.

* ci(e2e): reuse VERCEL_E2E_REPORT_* secrets for the studio preview

The studio preview deploys with the same Vercel project/token as report
hosting, so only one set of Vercel secrets is required.

* fix(e2e): add SPA fallback rewrite to test-studio vercel.json

The deployed studio 404'd on /chromium and /firefox workspace routes,
so the studio never booted and globalSetup timed out waiting for
/users/me. Rewrite all paths to index.html (filesystem first), same as
sanity-io/sanity's studio deployments.

* fix(e2e): drop studio warm-up from globalSetup

The warm-up existed to pay the local dev-server cold-compile cost once
up front; CI now tests a deployed static studio, and the waitForResponse
race was flaky there. Keep the fast env + auth token preflight.

* feat(e2e): dedicated bare e2e-studio with chromium/firefox workspaces

Add dev/e2e-studio — a minimal studio used only by Playwright e2e:
structure tool, one smoke-test schema type, and per-browser workspaces
on the ephemeral datasets. No monorepo plugins yet; each plugin will be
added together with its e2e tests. Reverts the e2e wiring from
dev/test-studio so it stays a pure development studio.

* fix(e2e): address review feedback

- Create ephemeral datasets as private (tests auth via storageState)
- Add contents:read to the periodic cleanup workflow for checkout
- Strip all leading dashes in sanityIdify
- Fix stale e2e package description and report-hosting docs (shared
  Vercel project, not a dedicated one)

* fix(e2e): keep ephemeral datasets public like sanity-io/sanity

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Make test-studio typegen idempotent in CI by forcing schema overwrite (#1725)

* Initial plan

* fix(test-studio): force schema extract during typegen

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1724)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1718)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.8 (#1723)

* fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.9

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @types/google.maps to ^3.65.3 (#1722)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.11 (#1721)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Delete .changeset/dependencies-GH-1697.md
Delete .changeset/dependencies-GH-1692.md
fix(deps): update dependency @sanity/icons to ^5.2.1 (#1720)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.60.0 (#1697)

* fix(deps): update dependency oxfmt to ^0.60.0

* chore(deps): changeset for dependency update

* chore: dedupe lockfile with release age exemptions

* fix: resolve knip unused export errors

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @testing-library/jest-dom to v7 (#1679)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update react monorepo to ^19.2.8 (#1692)

* fix(deps): update react monorepo to ^19.2.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.3 (#1704)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-query to ^5.101.4 (#1706)

* fix(deps): update dependency @tanstack/react-query to ^5.101.4

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(plugin-kit)!: require Node.js 24 and bump held-back CLI deps (#1696)

* feat(plugin-kit)!: require Node.js 24 and bump held-back CLI deps

- engines.node: >=20.19 <22 || >=22.12 -> >=24 (plugin-kit CLI only;
  scaffolded/verified plugins keep the wider pkg-utils range)
- concurrently ^9 -> ^10, execa ^9 -> ^10, inquirer ^12 -> ^14 (all
  previously held back by the permissive engine range)
- migrate the choices prompt from the 'list' type (removed in
  inquirer 13) to 'select'

* fix(plugin-kit): report failed dependency install with the intended hint

execa rejects on non-zero exit, so the exitCode check in
installDependencies was unreachable and init surfaced npm/pnpm install
failures as an unhandled CLI error. Pass reject: false so the caller's
'Failed to install dependencies, try manually running `npm install`'
message is shown instead.

* refactor(plugin-kit): replace several deps with Node 24 builtins and local helpers

Drop chalk, get-it, git-remote-origin-url, github-url-to-object, xdg-basedir,
email-validator, p-props and outdent in favor of util.styleText, fetch, execa+git,
and small local helpers. All remaining CLI deps were already at latest.

* fix(plugin-kit): address review feedback on execa, styleText, and outdent

- catch spawn errors in installDependencies and git remote lookup
- join nodemon's files array before styleText (it requires a string)
- only strip spaces/tabs in outdent so under-indented lines keep content

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): upgrade Sanity monorepo to ^6.6.0 (#1717)

* fix(deps): upgrade Sanity monorepo to ^6.6.0

Bump catalog packages (sanity, @sanity/mutator/schema/util/vision) and
sanity-plugin-media's groq dependency. Map TARGET_NOT_FOUND to Sanity's
structure locale key for missing release/variant documents instead of
reusing the not-ready tooltip.

* fix(deps): force-dedupe Sanity monorepo packages to 6.6.0

Override transitive @sanity/types|mutator|schema|util and groq onto the
catalog version so the lockfile cannot keep a second 6.5.x copy (which
breaks media dts emit). Put groq in the catalog and document the upgrade
checklist in AGENTS.md.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/color to ^3.0.7 (#1716)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(test-studio): enable React profiling on Vercel preview builds (#1713)

* feat(test-studio): enable React profiling on Vercel preview builds

Alias react-dom/client to react-dom/profiling when VERCEL_ENV is preview,
matching the Sanity monorepo test-studio setup, so PR deployments can be
profiled without imposing that overhead on production.

* fix(test-studio): disable auto-updates on preview for profiling

Auto-updates vendor builds hardcode react-dom-client.production.js, which
bypasses the react-dom/profiling alias. Turn auto-updates off when
VERCEL_ENV is preview so React DevTools profiling works on PR deployments.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.1 (#1714)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1709)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.10 (#1703)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1708)
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.7 (#1705)
Remove @sanity/styled-components override, move back to styled-components (#1693)

* Remove @sanity/styled-components override, move back to styled-components

* test: give first-mount UI tests timeout headroom on slow CI runners

* test: force styled-components speedy CSS injection under jsdom

Match sanity#13675 by setting SC_DISABLE_SPEEDY=false in the root
Vitest config so upstream styled-components keeps CSSOM injection in
tests. Revert the per-test timeout bumps that papered over the same
jsdom stylesheet reparse slowdown.

* chore: refresh PR head after merging main

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): upgrade oxlint and oxlint-tsgolint to latest (#1707)
Merge pull request #1702 from sanity-io/cursor/bump-sanity-ui-f3da
fix(deps): update dependency @portabletext/block-tools to ^5.1.12 (#1688)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.3

Bump the catalog range and lockfile to @sanity/ui 3.4.3, and add a
patch changeset for all published plugins that depend on it.

Delete dependencies-GH-1691.md
Version Packages (#1694)
fix(deps): update dependency tsdown to ^0.22.13 (#1691)

* fix(deps): update dependency tsdown to ^0.22.13

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(plugin-kit): bump CLI dependencies to latest compatible majors (#1687)

- chalk ^4 -> ^5, concurrently ^8 -> ^9, execa ^5 -> ^9,
  git-remote-origin-url ^3 -> ^4, inquirer ^8 -> ^12, meow ^9 -> ^14,
  p-props ^4 -> ^6, xdg-basedir ^4 -> ^5, nodemon 3.1.0 -> 3.1.14
- drop @types/inquirer and @types/nodemon (both packages ship their own
  types now)
- add the required meow importMeta option, switch to execa's named
  export, use xdg-basedir's named xdgConfig export and
  git-remote-origin-url's options object
- restore classic validate-on-filtered-value prompt semantics, which the
  modern inquirer rewrite changed (fixes accepting scp-style git remote
  URL defaults)
- concurrently 10, execa 10 and inquirer 13+ are held back as they
  require Node versions beyond plugin-kit's supported engine range

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1689)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1686)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: add per-plugin changesets for latest @sanity/icons and @sanity/ui catalog bumps (#1684)

* chore: add plugin changesets for @sanity/icons and @sanity/ui bumps

* chore: consolidate icons/ui impacted plugin changesets

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1678)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency get-it to ^9.2.0 (#1675)

* fix(deps): update dependency get-it to ^9.2.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.0 (#1677)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.0 (#1680)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/icons to ^5.2.0 (#1676)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix: address review feedback on drag attach timing and imperative handle

- useDrag: attach the mousedown listener in useLayoutEffect so it exists
  before paint (matches the previous synchronous JSX onMouseDown wiring).
- OrderableDocumentList: give useImperativeHandle a dependency list so a
  new handle object is not installed on every render (the structure pane
  stores the handle in state, so per-render handles could loop).

refactor(color-input): extract shared useDrag hook for picker surfaces

Replace the per-component propsRef/AbortController plumbing in
Alpha/Hue/Saturation/EditableInput with a single useDrag hook. It wires a
native mousedown listener via useEffect and keeps callbacks fresh with
useEffectEvent, so drag events always read the latest props. mousemove and
mouseup listeners attach per-gesture and abort on mouseup or unmount.

fix(color-input): address review feedback on drag listeners

- Keep latest props in a ref so window mousemove listeners registered on
  mousedown see current values (matches class `this.props` behavior).
- Normalize EditableInput value comparisons to avoid number/string mismatch.
- Correct orderable-document-list changeset wording (ref-as-prop, not forwardRef).

Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve color-input conflicts from #1673 (simplify components, remove
CustomPicker HOC). Main kept Alpha/Hue/Saturation/EditableInput as class
components pending a follow-up conversion; this PR is that follow-up:
- Accept ColorWrap.tsx deletion (HOC replaced by ColorInput state).
- Re-apply function-component conversions on top of main's AbortController
  listener cleanup, using useRef instead of createRef.

Version Packages (#1674)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(color-input): simplify components, remove HOC (#1673)
Version Packages (#1668)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1671)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate sanity-plugin-workflow styling to vanilla-extract (#1450)

* Migrate sanity-plugin-workflow styling to vanilla-extract

* Keep styled-components catalog:devDependency for peer alignment

After the vanilla-extract migration, the peer can go, but the catalog
devDependency must remain so pnpm resolves the workspace
@sanity/styled-components override (same as google-maps-input).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve the modify/delete conflict on .oxlintrc.json: main moved shared
lint rules into @sanity/plugin-kit/oxlint (#1565). Accept the deletion
and port the Component/PureComponent/createRef bans into
packages/@sanity/plugin-kit/src/oxlint.ts alongside the existing
createElement/forwardRef restrictions.

Also rewrite OrderableDocumentList to use React 19 ref-as-prop instead
of forwardRef so it complies with main's forwardRef ban, and update
skill docs that still pointed at .oxlintrc.json.

ci: request workflows permission for typegen app token (#935)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling (#1588)

* feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling

Drop verify-studio and all verify-package checks that existed to migrate from
or stay compatible with Sanity Studio v2 (incompatible-plugin shim, part:/config:
imports, merged @sanity/* packages, sanity.json parts validation). Package
verification now focuses on current plugin-kit conventions.

* chore(@sanity/plugin-kit): drop unused json5/knip ignores and refresh snapshots

Remove the json5 dependency and readJson5File helper that only served v2
sanity.json parsing, prune stale eslint knip ignoreDependencies, and update
verify-package snapshots after removing v2-upgrade checks.

* fix(@sanity/plugin-kit): address review feedback on path validation and studio detection

Drop the dist-hardcoded path existence check in getPackage entirely: compiled
entry points legitimately don't exist pre-build regardless of outDir, and
verify-package's esmOnly check already bans main/module fields. Also detect
sanity.cli.(js|ts) in findStudioConfig so init/inject refuse cli-config-only
studio directories too.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1669)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency (#1667)

* fix(sanity-plugin-markdown): declare @sanity/client and rxjs as regular deps

Move @sanity/client and rxjs out of inlinedDependencies into dependencies
so they are resolved at runtime instead of bundled into the package.

* chore: update lockfile for sanity-plugin-markdown deps

* fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency

Move @sanity/client from devDependencies to dependencies so tsdown
externalizes it instead of writing it (and transitive rxjs) to
inlinedDependencies. rxjs is not imported by the plugin and is not
needed as a direct dependency once client is externalized.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(typegen): 🤖 ✨ (#1666)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.7 (#1664)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.7

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1665)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1663)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: add @sanity/block-insert-picker (#1601)

* feat(block-insert-picker): add @sanity/block-insert-picker

Slash-command block-insert picker and markdown input rules for Portable
Text fields, extracted from the sanity-io/www-sanity-io docs Studio
(apps/studio/src/plugins/pte, PR #4003 there parameterized the docs
knowledge out in preparation).

Public API: blockInsertPicker({arrayTypeName, items?, inputRules?,
onItemInserted?}) returning the component for an array type's
components.portableText.plugins, plus the codeFenceRule/blockquoteRule
config factories and fence-language helpers. Items derive from the host
array's members with metadata-driven curation (triggers, keywords,
grouping, rank); inserts resolve member initial values, land as one
undo step, and open for editing.

Ported against sanity 6.5.0 / @portabletext/editor 7.10.7 /
@portabletext/plugin-input-rule 6.0.4 (peer editor ^7.10.7 to match the
input-rule plugin's requirement; editor stays a peer, never bundled).
112 tests ported with the source; test-studio example wired under
Home > Block Insert Picker.

* refactor(block-insert-picker): zero-config detection and reviewed v1 API

Applies the pre-publication design review (DESIGN-REVIEW.md in the
pte-plugin-extraction research):

- blockInsertPicker() now works with zero config: the containing array and
  its members are detected via PortableTextMemberSchemaTypesContext
  (quarantined in memberSchemaTypes.ts); arrayTypeName becomes an escape
  hatch. Peer narrowed to sanity ^5.6 (the context export's floor).
- Text blocks are excluded by resolved type chain, fixing aliased
  {name, type: 'block'} members being offered as object inserts (picker and
  input-rule allow-list both).
- New options: labels (translatable chrome), shortcut, openOnInsert
  (global + per-item), presets (well-known-type triggers/keywords),
  filter (receives the bare query in both modes), resolveItems middleware
  with custom item actions (query cleanup included); onItemInserted renamed
  onInsert, events carry mode/query.
- Presentation matches core: icon fallback chain, upperFirst titles, and
  curated descriptions now win over schema descriptions.
- New exports: BlockInsertPicker, MarkdownInputRules, derivePickerItems,
  filterPickerItems, standardBlockPresets, wellKnownInputRules, PickerMode.
- Input rules resolve blockType through member type chains, so
  wellKnownInputRules serves aliased code-input members.
- Review fixes: host-callback errors surface as toasts instead of unhandled
  rejections; anchor-deletion mid-insert shows a warning toast; positional
  row indices tolerate duplicate item ids; replacement-pattern-safe label
  formatting; screen-reader position announcement is translatable.

Deliberate deviations from the review doc: presets carry no description
(avoids a three-way precedence chain).

* test(block-insert-picker): lift cold-start timeout on first picker popover test

* docs: add @sanity/block-insert-picker to Current Plugins table

* chore: trigger pkg.pr.new preview publish

* refactor(block-insert-picker): address Copilot review comments

- insertPickerItem: spread the resolved initialValue before `_key`/`_type`
  so the generated key and target type always win. Sanity's initial-value
  resolver sets `_type` on object values; the values coincide by
  construction here, but ordering keeps the insert correct regardless.
- openBlockOnInsert: correct the stale rationale comment. The package
  enables the React Compiler, so the old "not active" claim was wrong; the
  point is the empty-dep useCallback keeps a stable reference for consumers.
- blockInsertPicker: fix the comment naming the wrong mouse events. Rows
  select via onClick and highlight via onMouseMove (not onMouseDown /
  onMouseEnter), passing an explicit idx to avoid a not-yet-flushed
  highlightedIndex.

* fix(block-insert-picker): case-insensitive fence language table match; trim README install

- normalizeFenceLanguage now compares the typed token to language-table
  `value`/`aliases` case-insensitively, returning the canonical value
  (casing preserved). Previously a table with mixed-case entries never
  matched and the mapped filename was skipped (Cursor Bugbot, low severity).
  Built-in rules and code-input's lowercase table were unaffected; this
  hardens host-supplied custom tables. Adds a mixed-case-table test.
- README: collapse the three install commands to a single `npm install`
  (no --save), matching @sanity/code-input's install section.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(block-insert-picker): restore bracket notation for process.env access

The Copilot Autofix commit rewrote `process.env['NODE_ENV']` to dot
notation, which fails this repo's type-aware lint: `noPropertyAccessFrom
IndexSignature` (TS4111) requires index-signature properties like
`process.env.NODE_ENV` to be accessed with brackets. Restore the bracket
form so `pnpm lint` passes.

* fix(block-insert-picker): address Cursor Bugbot findings

- inputRules: preserve case for unknown fence tokens (`return token`, not the
  lowercased form) so the "pass through as typed" contract holds — e.g.
  `GraphQL` stays `GraphQL` for wellKnownInputRules' no-table path.
- inputRules: spread host `buildValue` before `_key`/`_type` in createInsertRule
  so a stray `_key`/`_type` can't hijack block identity (mirrors the
  insertPickerItem fix).
- blockInsertPicker: close the picker when a select bails on an in-flight
  insert, so a menu reopened during a slow initial-value resolution can't get
  stuck swallowing Enter.
- blockInsertPicker: key sections on the namespaced raw group instead of the
  display label, so a curated group named like `ungroupedSection` can't collide
  with the null-group section's React key.

Two other Bugbot findings need no change: the text-block exclusion
(`isTextBlockMember`) already mirrors Sanity's own `findBlockType` (walk the
type chain to root, match `name === 'block'`), and the initial-value spread in
insertPickerItem was already reordered in an earlier commit.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
feat(code-input): export SUPPORTED_LANGUAGES and LANGUAGE_ALIASES (#1600)

Downstream tooling that resolves external language tokens (markdown
fence infostrings, paste/import pipelines) onto code field values has
had to vendor this list and hand-sync it with the Language selector.
Exporting the canonical table (and the alias map) removes the drift.
Version Packages (#1662)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): note npm provenance re-enabled for sanity-plugin-utils (#1661)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1660)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): bump google maps input for css dts cleanup (#1659)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1658)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/client to ^7.24.0 (#1581)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1657)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1640)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): use turbo transit for build cache invalidation (#1655)

Replace empty dependsOn with a transit node so sanity build still skips
workspace package builds while Turbo invalidates when dependency source changes.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): bump pkg-utils, tsdown-config, and vanilla-extract plugins (#1656)

Update catalog entries to @sanity/pkg-utils ^11.0.13,
@sanity/tsdown-config ^0.19.6, and @sanity/vanilla-extract-vite-plugin
^0.2.5. Exclude lightningcss@1.33.0 (and platform packages) from
minimumReleaseAge so pnpm update/dedupe can resolve the new transitive
deps, and align turbo/generators tsdown with the workspace catalog.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1653)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: remove @typescript/native-preview (use TypeScript 7 native tsc) (#1650)

* chore: remove @typescript/native-preview now that TypeScript 7 ships native tsc

TypeScript 7.0.2 includes the native compiler as `tsc`, so the preview package
and its `tsgo` binary are no longer needed. VS Code settings now point the
TypeScript 7 extension at the workspace `typescript` install.

* Update settings.json

* Update settings.json

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): lock file maintenance (#1651)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): stop waiting on dependency builds (#1652)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Delete dependencies-GH-1642.md
Migrate to date-fns v4 and official @date-fns/tz, matching sanity core (#1569)
chore(deps): dedupe pnpm-lock.yaml (#1649)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.12 (#1647)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1645)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.20 (#1646)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): bump @sanity/vanilla-extract-vite-plugin to ^0.2.4

Drop the Bynder Styles.css.js Vite workaround from the test studio —
0.2.4 keeps the compiler alive under unstable_bundledDev, so plain
*.css.js modules no longer hang processVanillaFile.

fix(deps): update dependency tsdown to ^0.22.12 (#1642)
fix(deps): update dependency styled-components to ^6.4.4 (#1566)
chore(deps): dedupe pnpm-lock.yaml (#1643)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-virtuoso to ^4.18.11 (#1641)

* fix(deps): update dependency react-virtuoso to ^4.18.11

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(agents): catalog shared deps + enable pnpm catalogMode prefer (#1374)
chore(deps): dedupe pnpm-lock.yaml (#1637)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.82.0 (#1639)

* fix(deps): update dependency react-hook-form to ^7.82.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update davelosert/vitest-coverage-report-action action to v2.12.2 (#1638)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1612)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
ci: shard Vitest suite (#1584)

* ci: shard vitest suite

* ci: report sharded test coverage

* chore: ignore Vitest blob reports

* ci: require test shards to succeed in aggregate check

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1634)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(format): 🤖 ✨ (#1633)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1632)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.9 (#1622)

* fix(deps): update dependency tsdown to ^0.22.9

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(assist): keep field group selection while editing AI Assist inputs (#1467)

* fix(assist): keep field group selection while editing AI Assist inputs

The AI Assist instruction inspector renders a nested document form that
reused the host document's pane router. Sanity's document pane mirrors the
router `path` param to programmatic focus, and focus drives the selected
field group. Navigating the instruction form therefore overwrote the host
`path`, re-focused the host document, and reset its field group tab.

Isolate the nested pane's `path` navigation in local state while still
forwarding other params (e.g. selected instruction) to the host router.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* test(test-studio): add AI Assist + field groups fixture (SAPP-3970)

Adds an 'AI Assist: Field groups' document type with Content/SEO field
group tabs so the field-group-reset behavior with AI Assist can be
manually reproduced and verified.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* refactor(assist): use react-fast-compare for param equality

Addresses PR review: the hand-rolled shallowEqualParams returned a false
positive when two param objects had the same key count but different keys
whose values were undefined (e.g. {foo: undefined} vs {bar: undefined}),
because it never verified the key existed on the other object. Reuse
react-fast-compare (already a plugin dependency, used in _lib/useListeningQuery)
instead of a custom check, and add a regression test.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(bynder-input): restore Compact View modal visibility in Studio v6 (#1553)

* fix(bynder-input): restore Compact View modal visibility in Studio v6

Upgrade @bynder/compact-view from 5.1.2 to 5.4.0 and patch its shadow DOM
integration for Sanity Studio v6:

- disableCSSOMInjection on StyleSheetManager so styled-components injects
  CSS into the shadow root instead of leaving empty style tags
- position:fixed + z-index on CompactViewContainer so the portal host
  stacks above Studio UI layers
- widen React peer dependency range to support React 19

Also render BynderModalLayout outside the button flex row.

* fix: unmount bynder layout when not open

* fix(bynder-input): take @bynder/compact-view 5.4.0 as a regular pinned dependency

Implements review feedback on the patch-based approach:
- drop the pnpm patch and the bundling of @bynder/compact-view; pin it
  as a regular dependency instead
- style the Compact View modal host from the plugin with a
  vanilla-extract globalStyle instead of patching Modal.js
- document how to allow the stale react peer range (npm
  --legacy-peer-deps, pnpm peerDependencyRules.allowedVersions) in the
  README, and use the pnpm rule for the workspace itself

* fix(bynder-input): survive Compact View shadow-root re-attachment and fix dev-studio css.js crash

- append a <slot> fallback to Compact View's shadow root while the modal
  is mounted: its attachShadow effect has no cleanup, so when React
  reconnects passive effects on the same host (StrictMode, Activity),
  the second attachShadow() throws and Compact View falls back to the
  host's light DOM, which the slotless shadow tree kept invisible
- work around @bynder/compact-view's Styles.css.js (a plain JS module
  whose name matches vanilla-extract's cssFileFilter and deadlocks the
  compiler under sanity dev's bundled dev mode) in the test studio
- move the container globalStyle to BynderInput.css.ts so it loads with
  the input instead of the lazy modal chunk

* chore(bynder-input): bump changeset to major

Taking @bynder/compact-view as an unbundled regular dependency with a
stale react peer range can fail npm installs without --legacy-peer-deps,
so it's a breaking change.

* chore(bynder-input): widen @bynder/compact-view to ^5.4.0

The exact pin was only needed while the package was patched and
inlined; as a plain regular dependency a caret range is fine.

* chore: merge main and regenerate lockfile

The PR lockfile was generated against a main where
@sanity/vanilla-extract-vite-plugin resolved to 0.2.1; main has since
bumped it to 0.2.2, and the git-merged lockfile on the PR merge ref was
internally inconsistent (ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY in CI).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.19.3 (#1598)

* fix(deps): update dependency @sanity/tsdown-config to ^0.19.3

* chore: dedupe lockfile and add release-age excludes for tsdown transitive deps

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1631)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2 (#1614)

* fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2

* chore(deps): exempt yuku-parser from minimum release age for dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Move turbo/**/*.hbs oxfmt ignore to root config (#1604)

The turbo generator template ignore is monorepo-specific and should
not ship in the shared @sanity/plugin-kit/oxfmt preset consumed by
standalone plugins.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.10 (#1630)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(assist): fix README markdown formatting and document English-only image descriptions (#1628)

* fix(@sanity/assist): respect document language when generating image descriptions

Pass the configured translate.document.languageField as languagePath to the
generate-caption task (both the automatic generation triggered on image
change, and the manual 'Generate image description' field action), so
generated alt text/captions are written in the document's language instead
of always defaulting to English.

Fixes https://github.com/sanity-io/plugins/issues/1606

* docs(@sanity/assist): clarify caption generation language limitation

Testing against the live AI Assist backend confirms that the generate-caption
task currently always writes descriptions in English, regardless of the
languagePath sent from the client (this matches Sanity's own documentation,
which states the automatic image description feature is English-only today
and recommends the Transform Agent Action's image-description operation as a
workaround). Update the changeset and README to accurately describe the
change as forward-compatible plumbing rather than a full fix, and point users
at the documented workaround for non-English alt text today.

* refactor(assist): resolve languagePath inside useGenerateCaption

Compute languagePath from config.translate.document.languageField once
inside useGenerateCaption itself (via useAiAssistanceConfig), instead of
requiring every call site to read the config and thread it through as an
argument. This removes the duplicated config lookup from both
ImageContextProvider and generateCaptionsActions, and matches the cleaner
approach used in sanity-io/plugins#1627.

Also documents in translate/types.ts JSDoc that languageField is reused
for image description generation.

* docs(assist): fix README markdown formatting and add known limitations

The README relied on markdown block elements (headings, lists, code
fences, images) interrupting paragraphs without blank lines between
them. GitHub tolerates this, but stricter renderers (e.g. the plugin
listing on sanity.io) join everything into unreadable walls of text
with literal '### Heading' and '- list item' fragments inline.

- Separate all block elements with blank lines throughout
- Add a 'Known limitations' section documenting that image descriptions
  are always generated in English regardless of
  translate.document.languageField (backend limitation, see #1606),
  with Transform Agent Action and translate-after workarounds
- Fix broken links: empty-href heading, HOW-TO-USE pointing at the old
  standalone repo layout, LICENSE pointing at a non-existent file
- Fix broken bold markup in the 'Enabling the AI Assist API' steps and
  the useExampleFieldActions TOC anchor
- Add missing TOC entries (Known limitations, Caveats, Third party
  sub-processors)

Also reverts the languagePath client plumbing from this branch: the
backend ignores it, so the plugin change had no effect. This PR is now
docs-only.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1619)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11.13.1 (#1624)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.9 (#1625)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.5 (#1626)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.27.0

chore: fix css

fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1 (#1610)

* fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.59.0 (#1599)

* fix(deps): update dependency oxfmt to ^0.59.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.7 (#1594)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Fix TypeGen workflow failure by pinning `@sanity/vanilla-extract-vite-plugin` to 0.1.1 (#1616)

* Initial plan

* fix(typegen): pin vanilla-extract vite plugin to 0.1.1

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): bump tsdown and vanilla-extract

fix(deps): update dependency oxlint to ^1.74.0 (#1603)

* fix(deps): update dependency oxlint to ^1.74.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update zizmorcore/zizmor-action action to v0.6.0 (#1609)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1611)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin (#1590)

* chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin

Swap the vanilla-extract Vite plugin used by the test studio and
@sanity/google-maps-input's vitest config to @sanity/vanilla-extract-vite-plugin,
a Vite 8 rewrite with plugin hook filters and a caching compiler on Vite's
Environment API / ModuleRunner instead of the legacy vite-node. Bump tsdown to
^0.22.8 and @sanity/tsdown-config to ^0.17.0 alongside it.

google-maps-input's vitest.config.ts also needed a small stub plugin: its
dist/bundle.css.js Node/SSR shim matches the upstream cssFileFilter by
filename alone (*.css.js), which made the new plugin try to evaluate it as
real .css.ts source and throw ReferenceError: require is not defined via
ModuleRunner (vite-node tolerated this). The stub renames that module id away
from a .css.js shape before vanillaExtractPlugin() ever sees it.

Documented both the plugin swap and the shim workaround in the
sanity-plugin-best-practices skill and the new-plugin generator templates so
future vanilla-extract plugins get this for free.

* chore(deps): bump @sanity/tsdown-config to ^0.17.1, drop bundle.css.js shim workaround

@sanity/tsdown-config 0.17.1 (via @sanity/vanilla-extract-tsdown-plugin
0.2.1 / @sanity/vanilla-extract-rolldown-plugin 0.1.1) renames the
vanilla-extract Node/SSR compat shim from bundle.css.js to bundle-css.js so
it no longer matches vanillaExtractPlugin()'s cssFileFilter by filename
alone (sanity-io/pkg-utils#3043). That was the root cause of the
require-is-not-defined regression worked around in the previous commit, so
the stub-bundle-css-js-shim plugin is no longer needed.

Rebuilding @sanity/google-maps-input picked up the renamed dist/bundle-css.js
shim and auto-rewrote its package.json ./bundle.css export map (adding a
types condition too). Reverted the vitest.config.ts workaround and the
matching documentation/generator-template changes back to the plain
vanillaExtractPlugin() registration, now noted as a historical gotcha
instead of a live workaround.

* docs(test-studio): note that unstable_bundledDev breaks .css.ts HMR

Document the trade-off discovered while testing the vanilla-extract Vite
plugin swap: with unstable_bundledDev: true, editing a .css.ts file doesn't
hot-reload (a sanity dev restart is required), while plain .ts/.tsx HMR is
unaffected. Comment out the line for a session if fast CSS HMR is needed
(confirmed sub-10ms with it unset).

* docs(styling skill): trim verbose vanilla-extract setup section

The ./bundle.css export map (and its dist/bundle-css.js shim/types) is
generated and kept in sync automatically by @sanity/tsdown-config's
vanillaExtract option, so it doesn't need explaining or a JSON example to
copy. Simplify the vitest.config.ts guidance to why (@sanity/vanilla-extract-
vite-plugin is faster than upstream) without the ModuleRunner/hook-filter/
sanity-build internals. Drop the sanity.cli.ts snippet, since the test studio
already registers the plugin globally in this repo and doesn't need to be
touched per-plugin. Drop the bundle.css.js->bundle-css.js rename history
note, a one-off gotcha now fixed upstream rather than evergreen guidance.

* chore(deps): bump @sanity/tsdown-config to ^0.17.2, @sanity/vanilla-extract-vite-plugin to ^0.1.1

Both are latest-available patch releases (0.17.1 -> 0.17.2 pulls in
@sanity/vanilla-extract-tsdown-plugin 0.2.2 / @sanity/vanilla-extract-
rolldown-plugin 0.1.2; 0.1.0 -> 0.1.1 is the vite plugin's own patch).
tsdown stays at ^0.22.8, already the latest. Benchmarked in the next commit's
message to confirm this doesn't regress anything.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @changesets/cli to ^2.31.1 (#1607)

* fix(deps): update dependency @changesets/cli to ^2.31.1

* chore(deps): exempt @changesets/cli and refresh lockfile with dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.1 (#1593)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1591)
chore(deps): dedupe pnpm-lock.yaml (#1602)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(media): use useMediaIndex so Tags panel works on wide viewports (#1592)

The home-rolled useBreakpointIndex returned -1 above 1920px, which
force-hid the Tags panel. Switch Items to @sanity/ui's useMediaIndex,
which includes a min-width catch-all and matches Controls breakpoints.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency xstate to ^5.32.5 (#1597)

* fix(deps): update dependency xstate to ^5.32.5

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.8 (#1596)

* fix(deps): update dependency tsdown to ^0.22.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsconfig to ^2.2.1 (#1595)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/setup-node action to v7 (#1586)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11 (#1579)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency nanoid to v6 (#1577)

* fix(deps): update dependency nanoid to v6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency groq to ^6.5.0 (#1523)

* fix(deps): update dependency groq to ^6.5.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint (#1565)

* Migrate plugin-kit and monorepo formatting from prettier to oxfmt

- Ship the monorepo's oxfmt settings as a @sanity/plugin-kit/oxfmt preset
- Re-export the preset from the root oxfmt.config.ts (replaces .oxfmtrc.json)
- init/inject scaffold oxfmt (config, devDependency, format script) instead
  of prettier; --no-prettier is now --no-oxfmt
- verify-package: replace the duplicate-prettier-config check with a
  monorepo-aware oxfmt check that expects the config at the workspace root
  (or next to package.json outside monorepos) and flags leftover prettier
  configuration with migration instructions

* Migrate plugin-kit and monorepo linting from eslint to oxlint, drop tsc

- Ship the monorepo's oxlint settings as @sanity/plugin-kit/oxlint-config.json
  (type-aware rules, type checking via oxlint-tsgolint, warnings denied); the
  root .oxlintrc.json extends it and keeps only workspace-specific ignores and
  overrides
- init/inject scaffold oxlint (.oxlintrc.json, oxlint + oxlint-tsgolint
  devDependenc…
cursor Bot pushed a commit that referenced this pull request Jul 28, 2026
… property

Bump alphaCard/previewCard white background to doubled-class specificity so it
beats Card's own background (the old inline style always won); drop the no-op
content property carried over from the styled.div swatch.

fix(color-input): drop unused react-color type re-exports

Knip flagged 12 types re-exported from the react-color barrel that nothing
outside the module imports. Keep only the types consumers actually use.

Merge branch 'cursor/ban-react-class-components-ca2a' into cursor/color-input-vanilla-extract-35df

Keep vanilla-extract styling for Alpha/Hue/Saturation/EditableInput;
ban branch still uses styled-components/inline styles for those files.

fix(color-input): convert vendored react-color components to function components

Convert Alpha/Hue/Saturation/EditableInput and centralize drag wiring in a
shared useDrag hook: a native mousedown listener attached in useLayoutEffect,
useEffectEvent so events always read the latest props, and per-gesture
mousemove/mouseup listeners cleaned up via AbortController. EditableInput
clears its pending blur-sync value once the prop catches up with the
displayed value, so blurring can't revert a typed value.

fix(sanity-plugin-media): convert ReduxProvider to a function component

Initialize the redux store once with a lazy useState initializer.

fix(orderable-document-list): convert OrderableDocumentList to a function component

Expose the structure menu actionHandlers via useImperativeHandle and the
React 19 ref-as-prop pattern, with a dependency list so the handle is not
re-created on every render (the pane stores it in state).

feat(plugin-kit): ban Component, PureComponent and createRef imports from react

Class components are not allowed in this repo — only function components.
createRef is only useful in class components; use the useRef hook instead.
Also point skill docs at the shared @sanity/plugin-kit/oxlint config.

chore: drop color-input oxlint override

Leave shared oxlint rules untouched; the vanilla-extract migration does
not need a package-specific no-restricted-imports lock-in.

Merge branch 'cursor/ban-react-class-components-ca2a' into cursor/color-input-vanilla-extract-35df

Resolve stack conflicts: keep vanilla-extract exports (drop EditableInputStyles),
bump @vanilla-extract/css catalog to ^1.21.2 while retaining @vanilla-extract/dynamic,
and regenerate the lockfile. Preserve EditableInput blur-sync fix from the base branch.

Version Packages (#1735)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
refactor(color-input): migrate styling to vanilla-extract

Replace styled-components and inline style objects with colocated
vanilla-extract stylesheets, using assignInlineVars for dynamic
theme/color values. Builds on the function-component + useDrag
refactor from the ban-react-class-components branch.

Merge remote-tracking branch 'origin/main' into cursor/ban-react-class-components-ca2a

fix(color-input): clear stale blur-sync value when prop catches up

The prop-sync effect early-returned when the normalized prop matched the
displayed value, leaving a blurValue captured from an older prop in place;
blurring would then restore that older value. Clear it (in a transition to
satisfy the React Compiler EffectSetState rule).

fix(deps): bump @sanity/pkg-utils, @sanity/tsdown-config and vanilla-extract plugins (#1769)

* fix(deps): bump @sanity/pkg-utils, @sanity/tsdown-config and vanilla-extract plugins

- @sanity/pkg-utils ^11.0.16 -> ^11.0.17
- @sanity/tsdown-config ^0.21.1 -> ^0.21.2
- @sanity/vanilla-extract-vite-plugin ^0.2.7 -> ^0.2.8

tsdown (^0.22.14) and @vanilla-extract/css (^1.21.2) were already on the
latest release; refreshing the lockfile picks up the transitive
@sanity/vanilla-extract-{integration,rolldown-plugin,tsdown-plugin} updates.

* fix(deps): public-hoist @vanilla-extract/css so node_modules .css.js files resolve it

`@sanity/vanilla-extract-vite-plugin` filters its transform hook on
/\.css\.(js|cjs|mjs|jsx|ts|tsx)/ with no node_modules exclusion, so it also
compiles `@bynder/compact-view/Styles.css.js` — a plain module exporting a CSS
string for a shadow root, not a vanilla-extract module. That package declares no
dependencies, so the compile only finds `@vanilla-extract/css/adapter` when pnpm
happens to hoist it into the hidden .pnpm/node_modules directory. On Vercel it
did not, failing `sanity build` for dev/test-studio with
"Cannot find module '@vanilla-extract/css/adapter'".

Public-hoisting puts @vanilla-extract/css on the root node_modules resolution
path, which is deterministic regardless of the implicit hoist layout.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1766)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.16 (#1768)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(test-studio): add experimental @sanity/themer tool (#1738)

* feat(test-studio): add experimental @sanity/themer tool

Wire themerTool() into the Home workspace so theme presets and colors
can be previewed live against the plugins kitchen sink.

* chore(test-studio): bump @sanity/themer to 0.2.0

* chore(test-studio): use ^0.2.0 for @sanity/themer

Merge main and widen the themer dependency to a caret range.

* chore: dedupe lockfile to resolve @sanity/client type mismatch

pnpm dedupe collapses the 7.24.0 / 7.25.0 client split that broke
type-aware lint after merging main with themer.

* chore: handle lockfile

* chore(knip): ignore unused catalog entry reports in pnpm-workspace.yaml

The catalogued @sanity/types entry is referenced only by the overrides block
in pnpm-workspace.yaml, which knip does not read, so it was reported as an
unused catalog entry and failed `pnpm knip`.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: fix lock

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @vanilla-extract/css to ^1.21.2 (#1764)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.21 (#1763)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.5.0 (#1762)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1761)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
test(document-internationalization): expand Vitest + Playwright e2e coverage (#1712)
chore(deps): dedupe pnpm-lock.yaml (#1754)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @playwright/test to ^1.62.0 (#1760)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.7 (#1759)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency concurrently to ^10.0.4 (#1758)

* fix(deps): update dependency concurrently to ^10.0.4

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.10 (#1757)

* fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.10

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.8 (#1756)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.13 (#1755)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1749)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: fix lockfile

chore(deps): dedupe pnpm-lock.yaml (#1753)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/client to ^7.25.0 (#1747)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.5 (#1745)

* fix(deps): update dependency @sanity/ui to ^3.4.5

* fix(knip): remove unused react-is devDependency from media plugin

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @mux/mux-player to ^3.13.2 (#1751)

* fix(deps): update dependency @mux/mux-player to ^3.13.2

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1748)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.83.0 (#1750)

* fix(deps): update dependency react-hook-form to ^7.83.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @mux/mux-player-react to ^3.13.2 (#1752)

* fix(deps): update dependency @mux/mux-player-react to ^3.13.2

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.2 (#1744)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1734)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Delete dependencies-GH-1739.md
fix(deps): update dependency @sanity/color to ^3.0.8 (#1743)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.7 (#1746)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.21.1 (#1742)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.15 (#1741)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update zizmorcore/zizmor-action action to v0.6.1 (#1740)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.14 (#1739)

* fix(deps): update dependency tsdown to ^0.22.14

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/cache action to v6 (#1731)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @playwright/test to ^1.61.1 (#1737)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.21.0 (#1736)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.6 (#1733)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.14 (#1732)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/cache action to v5 (#1730)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.6 (#1729)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.8 (#1728)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.29.0 (#1727)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.5 (#1726)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: add Playwright smoke e2e with ephemeral datasets (#1710)

* feat: add Playwright smoke e2e for test studio

Set up an e2e workspace with Chromium/Firefox smoke coverage,
storageState studio auth matching sanity-io/sanity, fail-fast
token validation, and a CI workflow that builds and previews
the test studio locally.

* fix(e2e): correct Playwright artifact gitignore paths

Paths in e2e/.gitignore are relative to that directory, so leading
/e2e/… never matched results or playwright-report.

* fix(e2e): require project id and dataset from env vars

Source SANITY_E2E_PROJECT_ID and SANITY_E2E_STUDIO_DATASET from
GitHub repository variables in CI (no hardcoding). Rename the
dataset env to SANITY_E2E_STUDIO_DATASET and map both onto the
test-studio SANITY_STUDIO_* vars when starting webServer.

* fix(e2e): stop knip from loading Playwright config

resolveE2eEnv() requires project id and dataset at config load time;
disable the knip Playwright plugin for the e2e workspace so analysis
does not need CI secrets.

* fix(e2e): drop token fallback and debug browser filter

Require only SANITY_E2E_SESSION_TOKEN for auth, and always run
Chromium and Firefox projects (filter via Playwright UI/CLI instead).

* docs(e2e): document plugins-e2e-testing sandbox project

Note that e2e uses Sanity sandbox org project plugins-e2e-testing
(a1psl692) and update .env.example accordingly.

* ci(e2e): post Sanity-style PR status comment

Add a Playwright summary reporter and update the E2E workflow to
post/refresh a pull request comment with pass/fail/flaky/skipped
counts and a link to the Actions run (HTML report as artifact until
Vercel report hosting lands).

* ci(e2e): deploy Playwright HTML report to Vercel

Host the report on a dedicated Vercel project and link pass/fail
counts in the PR status comment, matching sanity-io/sanity. Document
VERCEL_E2E_REPORT_* secret setup in e2e/README.md.

* feat(e2e): add ephemeral per-browser datasets like sanity-io/sanity

Create pr-*/main-* chromium and firefox datasets in CI, expose matching
studio workspaces, and schedule cleanup for closed PRs / stale main runs.

* fix(e2e): bump Sanity client apiVersion to 2026-07-23

* fix(ci): avoid env context in job-level e2e workflow env

GitHub Actions does not allow `env.*` when evaluating job `env:` maps.
Define dataset names at workflow level with inlined format expressions.

* ci(e2e): run chromium and firefox as a parallel matrix

Split setup / per-browser test / report-merge jobs. Each matrix job gets
its own SANITY_E2E_DATASET and runs playwright --project for that browser.

* fix(e2e): avoid pnpm setup command colliding with dataset script

`pnpm --filter e2e setup` invoked pnpm's built-in `setup`, so ephemeral
datasets were never created. Rename to `create-dataset` and call via
`pnpm run`. Fail the CI step if creation fails.

* ci(e2e): scope pull-requests write to jobs that post comments

Satisfy zizmor excessive-permissions: keep workflow at contents:read
and grant pull-requests:write only on setup/report jobs.

* ci(e2e): deploy studio preview to Vercel like sanity-io/sanity

Add deploy-preview job (vercel pull/build/deploy --prebuilt) with
ephemeral datasets baked in; matrix tests run against the preview URL
instead of a local sanity preview server. Adds vercel CLI as a root
devDependency and dev/test-studio/vercel.json build settings.

* ci(e2e): reuse VERCEL_E2E_REPORT_* secrets for the studio preview

The studio preview deploys with the same Vercel project/token as report
hosting, so only one set of Vercel secrets is required.

* fix(e2e): add SPA fallback rewrite to test-studio vercel.json

The deployed studio 404'd on /chromium and /firefox workspace routes,
so the studio never booted and globalSetup timed out waiting for
/users/me. Rewrite all paths to index.html (filesystem first), same as
sanity-io/sanity's studio deployments.

* fix(e2e): drop studio warm-up from globalSetup

The warm-up existed to pay the local dev-server cold-compile cost once
up front; CI now tests a deployed static studio, and the waitForResponse
race was flaky there. Keep the fast env + auth token preflight.

* feat(e2e): dedicated bare e2e-studio with chromium/firefox workspaces

Add dev/e2e-studio — a minimal studio used only by Playwright e2e:
structure tool, one smoke-test schema type, and per-browser workspaces
on the ephemeral datasets. No monorepo plugins yet; each plugin will be
added together with its e2e tests. Reverts the e2e wiring from
dev/test-studio so it stays a pure development studio.

* fix(e2e): address review feedback

- Create ephemeral datasets as private (tests auth via storageState)
- Add contents:read to the periodic cleanup workflow for checkout
- Strip all leading dashes in sanityIdify
- Fix stale e2e package description and report-hosting docs (shared
  Vercel project, not a dedicated one)

* fix(e2e): keep ephemeral datasets public like sanity-io/sanity

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Make test-studio typegen idempotent in CI by forcing schema overwrite (#1725)

* Initial plan

* fix(test-studio): force schema extract during typegen

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1724)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1718)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.8 (#1723)

* fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.9

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @types/google.maps to ^3.65.3 (#1722)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.11 (#1721)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Delete .changeset/dependencies-GH-1697.md
Delete .changeset/dependencies-GH-1692.md
fix(deps): update dependency @sanity/icons to ^5.2.1 (#1720)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.60.0 (#1697)

* fix(deps): update dependency oxfmt to ^0.60.0

* chore(deps): changeset for dependency update

* chore: dedupe lockfile with release age exemptions

* fix: resolve knip unused export errors

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @testing-library/jest-dom to v7 (#1679)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update react monorepo to ^19.2.8 (#1692)

* fix(deps): update react monorepo to ^19.2.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.3 (#1704)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-query to ^5.101.4 (#1706)

* fix(deps): update dependency @tanstack/react-query to ^5.101.4

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(plugin-kit)!: require Node.js 24 and bump held-back CLI deps (#1696)

* feat(plugin-kit)!: require Node.js 24 and bump held-back CLI deps

- engines.node: >=20.19 <22 || >=22.12 -> >=24 (plugin-kit CLI only;
  scaffolded/verified plugins keep the wider pkg-utils range)
- concurrently ^9 -> ^10, execa ^9 -> ^10, inquirer ^12 -> ^14 (all
  previously held back by the permissive engine range)
- migrate the choices prompt from the 'list' type (removed in
  inquirer 13) to 'select'

* fix(plugin-kit): report failed dependency install with the intended hint

execa rejects on non-zero exit, so the exitCode check in
installDependencies was unreachable and init surfaced npm/pnpm install
failures as an unhandled CLI error. Pass reject: false so the caller's
'Failed to install dependencies, try manually running `npm install`'
message is shown instead.

* refactor(plugin-kit): replace several deps with Node 24 builtins and local helpers

Drop chalk, get-it, git-remote-origin-url, github-url-to-object, xdg-basedir,
email-validator, p-props and outdent in favor of util.styleText, fetch, execa+git,
and small local helpers. All remaining CLI deps were already at latest.

* fix(plugin-kit): address review feedback on execa, styleText, and outdent

- catch spawn errors in installDependencies and git remote lookup
- join nodemon's files array before styleText (it requires a string)
- only strip spaces/tabs in outdent so under-indented lines keep content

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): upgrade Sanity monorepo to ^6.6.0 (#1717)

* fix(deps): upgrade Sanity monorepo to ^6.6.0

Bump catalog packages (sanity, @sanity/mutator/schema/util/vision) and
sanity-plugin-media's groq dependency. Map TARGET_NOT_FOUND to Sanity's
structure locale key for missing release/variant documents instead of
reusing the not-ready tooltip.

* fix(deps): force-dedupe Sanity monorepo packages to 6.6.0

Override transitive @sanity/types|mutator|schema|util and groq onto the
catalog version so the lockfile cannot keep a second 6.5.x copy (which
breaks media dts emit). Put groq in the catalog and document the upgrade
checklist in AGENTS.md.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/color to ^3.0.7 (#1716)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat(test-studio): enable React profiling on Vercel preview builds (#1713)

* feat(test-studio): enable React profiling on Vercel preview builds

Alias react-dom/client to react-dom/profiling when VERCEL_ENV is preview,
matching the Sanity monorepo test-studio setup, so PR deployments can be
profiled without imposing that overhead on production.

* fix(test-studio): disable auto-updates on preview for profiling

Auto-updates vendor builds hardcode react-dom-client.production.js, which
bypasses the react-dom/profiling alias. Turn auto-updates off when
VERCEL_ENV is preview so React DevTools profiling works on PR deployments.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.1 (#1714)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1709)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency @portabletext/editor to ^7.10.10 (#1703)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1708)
fix(deps): update dependency @portabletext/plugin-input-rule to ^6.0.7 (#1705)
Remove @sanity/styled-components override, move back to styled-components (#1693)

* Remove @sanity/styled-components override, move back to styled-components

* test: give first-mount UI tests timeout headroom on slow CI runners

* test: force styled-components speedy CSS injection under jsdom

Match sanity#13675 by setting SC_DISABLE_SPEEDY=false in the root
Vitest config so upstream styled-components keeps CSSOM injection in
tests. Revert the per-test timeout bumps that papered over the same
jsdom stylesheet reparse slowdown.

* chore: refresh PR head after merging main

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): upgrade oxlint and oxlint-tsgolint to latest (#1707)
Merge pull request #1702 from sanity-io/cursor/bump-sanity-ui-f3da
fix(deps): update dependency @portabletext/block-tools to ^5.1.12 (#1688)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.3

Bump the catalog range and lockfile to @sanity/ui 3.4.3, and add a
patch changeset for all published plugins that depend on it.

Delete dependencies-GH-1691.md
Version Packages (#1694)
fix(deps): update dependency tsdown to ^0.22.13 (#1691)

* fix(deps): update dependency tsdown to ^0.22.13

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(plugin-kit): bump CLI dependencies to latest compatible majors (#1687)

- chalk ^4 -> ^5, concurrently ^8 -> ^9, execa ^5 -> ^9,
  git-remote-origin-url ^3 -> ^4, inquirer ^8 -> ^12, meow ^9 -> ^14,
  p-props ^4 -> ^6, xdg-basedir ^4 -> ^5, nodemon 3.1.0 -> 3.1.14
- drop @types/inquirer and @types/nodemon (both packages ship their own
  types now)
- add the required meow importMeta option, switch to execa's named
  export, use xdg-basedir's named xdgConfig export and
  git-remote-origin-url's options object
- restore classic validate-on-filtered-value prompt semantics, which the
  modern inquirer rewrite changed (fixes accepting scp-style git remote
  URL defaults)
- concurrently 10, execa 10 and inquirer 13+ are held back as they
  require Node versions beyond plugin-kit's supported engine range

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1689)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1686)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: add per-plugin changesets for latest @sanity/icons and @sanity/ui catalog bumps (#1684)

* chore: add plugin changesets for @sanity/icons and @sanity/ui bumps

* chore: consolidate icons/ui impacted plugin changesets

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1678)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency get-it to ^9.2.0 (#1675)

* fix(deps): update dependency get-it to ^9.2.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/ui to ^3.4.0 (#1677)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @sanity/preview-url-secret to ^4.1.0 (#1680)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/icons to ^5.2.0 (#1676)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix: address review feedback on drag attach timing and imperative handle

- useDrag: attach the mousedown listener in useLayoutEffect so it exists
  before paint (matches the previous synchronous JSX onMouseDown wiring).
- OrderableDocumentList: give useImperativeHandle a dependency list so a
  new handle object is not installed on every render (the structure pane
  stores the handle in state, so per-render handles could loop).

refactor(color-input): extract shared useDrag hook for picker surfaces

Replace the per-component propsRef/AbortController plumbing in
Alpha/Hue/Saturation/EditableInput with a single useDrag hook. It wires a
native mousedown listener via useEffect and keeps callbacks fresh with
useEffectEvent, so drag events always read the latest props. mousemove and
mouseup listeners attach per-gesture and abort on mouseup or unmount.

fix(color-input): address review feedback on drag listeners

- Keep latest props in a ref so window mousemove listeners registered on
  mousedown see current values (matches class `this.props` behavior).
- Normalize EditableInput value comparisons to avoid number/string mismatch.
- Correct orderable-document-list changeset wording (ref-as-prop, not forwardRef).

Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve color-input conflicts from #1673 (simplify components, remove
CustomPicker HOC). Main kept Alpha/Hue/Saturation/EditableInput as class
components pending a follow-up conversion; this PR is that follow-up:
- Accept ColorWrap.tsx deletion (HOC replaced by ColorInput state).
- Re-apply function-component conversions on top of main's AbortController
  listener cleanup, using useRef instead of createRef.

Version Packages (#1674)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(color-input): simplify components, remove HOC (#1673)
Version Packages (#1668)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1671)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate sanity-plugin-workflow styling to vanilla-extract (#1450)

* Migrate sanity-plugin-workflow styling to vanilla-extract

* Keep styled-components catalog:devDependency for peer alignment

After the vanilla-extract migration, the peer can go, but the catalog
devDependency must remain so pnpm resolves the workspace
@sanity/styled-components override (same as google-maps-input).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Merge origin/main into cursor/ban-react-class-components-ca2a

Resolve the modify/delete conflict on .oxlintrc.json: main moved shared
lint rules into @sanity/plugin-kit/oxlint (#1565). Accept the deletion
and port the Component/PureComponent/createRef bans into
packages/@sanity/plugin-kit/src/oxlint.ts alongside the existing
createElement/forwardRef restrictions.

Also rewrite OrderableDocumentList to use React 19 ref-as-prop instead
of forwardRef so it complies with main's forwardRef ban, and update
skill docs that still pointed at .oxlintrc.json.

ci: request workflows permission for typegen app token (#935)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling (#1588)

* feat(@sanity/plugin-kit)!: remove Studio v2 upgrade and compatibility tooling

Drop verify-studio and all verify-package checks that existed to migrate from
or stay compatible with Sanity Studio v2 (incompatible-plugin shim, part:/config:
imports, merged @sanity/* packages, sanity.json parts validation). Package
verification now focuses on current plugin-kit conventions.

* chore(@sanity/plugin-kit): drop unused json5/knip ignores and refresh snapshots

Remove the json5 dependency and readJson5File helper that only served v2
sanity.json parsing, prune stale eslint knip ignoreDependencies, and update
verify-package snapshots after removing v2-upgrade checks.

* fix(@sanity/plugin-kit): address review feedback on path validation and studio detection

Drop the dist-hardcoded path existence check in getPackage entirely: compiled
entry points legitimately don't exist pre-build regardless of outDir, and
verify-package's esmOnly check already bans main/module fields. Also detect
sanity.cli.(js|ts) in findStudioConfig so init/inject refuse cli-config-only
studio directories too.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1669)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency (#1667)

* fix(sanity-plugin-markdown): declare @sanity/client and rxjs as regular deps

Move @sanity/client and rxjs out of inlinedDependencies into dependencies
so they are resolved at runtime instead of bundled into the package.

* chore: update lockfile for sanity-plugin-markdown deps

* fix(sanity-plugin-markdown): declare @sanity/client as a regular dependency

Move @sanity/client from devDependencies to dependencies so tsdown
externalizes it instead of writing it (and transitive rxjs) to
inlinedDependencies. rxjs is not imported by the plugin and is not
needed as a direct dependency once client is externalized.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(typegen): 🤖 ✨ (#1666)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @tanstack/react-virtual to ^3.14.7 (#1664)

* fix(deps): update dependency @tanstack/react-virtual to ^3.14.7

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1665)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1663)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
feat: add @sanity/block-insert-picker (#1601)

* feat(block-insert-picker): add @sanity/block-insert-picker

Slash-command block-insert picker and markdown input rules for Portable
Text fields, extracted from the sanity-io/www-sanity-io docs Studio
(apps/studio/src/plugins/pte, PR #4003 there parameterized the docs
knowledge out in preparation).

Public API: blockInsertPicker({arrayTypeName, items?, inputRules?,
onItemInserted?}) returning the component for an array type's
components.portableText.plugins, plus the codeFenceRule/blockquoteRule
config factories and fence-language helpers. Items derive from the host
array's members with metadata-driven curation (triggers, keywords,
grouping, rank); inserts resolve member initial values, land as one
undo step, and open for editing.

Ported against sanity 6.5.0 / @portabletext/editor 7.10.7 /
@portabletext/plugin-input-rule 6.0.4 (peer editor ^7.10.7 to match the
input-rule plugin's requirement; editor stays a peer, never bundled).
112 tests ported with the source; test-studio example wired under
Home > Block Insert Picker.

* refactor(block-insert-picker): zero-config detection and reviewed v1 API

Applies the pre-publication design review (DESIGN-REVIEW.md in the
pte-plugin-extraction research):

- blockInsertPicker() now works with zero config: the containing array and
  its members are detected via PortableTextMemberSchemaTypesContext
  (quarantined in memberSchemaTypes.ts); arrayTypeName becomes an escape
  hatch. Peer narrowed to sanity ^5.6 (the context export's floor).
- Text blocks are excluded by resolved type chain, fixing aliased
  {name, type: 'block'} members being offered as object inserts (picker and
  input-rule allow-list both).
- New options: labels (translatable chrome), shortcut, openOnInsert
  (global + per-item), presets (well-known-type triggers/keywords),
  filter (receives the bare query in both modes), resolveItems middleware
  with custom item actions (query cleanup included); onItemInserted renamed
  onInsert, events carry mode/query.
- Presentation matches core: icon fallback chain, upperFirst titles, and
  curated descriptions now win over schema descriptions.
- New exports: BlockInsertPicker, MarkdownInputRules, derivePickerItems,
  filterPickerItems, standardBlockPresets, wellKnownInputRules, PickerMode.
- Input rules resolve blockType through member type chains, so
  wellKnownInputRules serves aliased code-input members.
- Review fixes: host-callback errors surface as toasts instead of unhandled
  rejections; anchor-deletion mid-insert shows a warning toast; positional
  row indices tolerate duplicate item ids; replacement-pattern-safe label
  formatting; screen-reader position announcement is translatable.

Deliberate deviations from the review doc: presets carry no description
(avoids a three-way precedence chain).

* test(block-insert-picker): lift cold-start timeout on first picker popover test

* docs: add @sanity/block-insert-picker to Current Plugins table

* chore: trigger pkg.pr.new preview publish

* refactor(block-insert-picker): address Copilot review comments

- insertPickerItem: spread the resolved initialValue before `_key`/`_type`
  so the generated key and target type always win. Sanity's initial-value
  resolver sets `_type` on object values; the values coincide by
  construction here, but ordering keeps the insert correct regardless.
- openBlockOnInsert: correct the stale rationale comment. The package
  enables the React Compiler, so the old "not active" claim was wrong; the
  point is the empty-dep useCallback keeps a stable reference for consumers.
- blockInsertPicker: fix the comment naming the wrong mouse events. Rows
  select via onClick and highlight via onMouseMove (not onMouseDown /
  onMouseEnter), passing an explicit idx to avoid a not-yet-flushed
  highlightedIndex.

* fix(block-insert-picker): case-insensitive fence language table match; trim README install

- normalizeFenceLanguage now compares the typed token to language-table
  `value`/`aliases` case-insensitively, returning the canonical value
  (casing preserved). Previously a table with mixed-case entries never
  matched and the mapped filename was skipped (Cursor Bugbot, low severity).
  Built-in rules and code-input's lowercase table were unaffected; this
  hardens host-supplied custom tables. Adds a mixed-case-table test.
- README: collapse the three install commands to a single `npm install`
  (no --save), matching @sanity/code-input's install section.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(block-insert-picker): restore bracket notation for process.env access

The Copilot Autofix commit rewrote `process.env['NODE_ENV']` to dot
notation, which fails this repo's type-aware lint: `noPropertyAccessFrom
IndexSignature` (TS4111) requires index-signature properties like
`process.env.NODE_ENV` to be accessed with brackets. Restore the bracket
form so `pnpm lint` passes.

* fix(block-insert-picker): address Cursor Bugbot findings

- inputRules: preserve case for unknown fence tokens (`return token`, not the
  lowercased form) so the "pass through as typed" contract holds — e.g.
  `GraphQL` stays `GraphQL` for wellKnownInputRules' no-table path.
- inputRules: spread host `buildValue` before `_key`/`_type` in createInsertRule
  so a stray `_key`/`_type` can't hijack block identity (mirrors the
  insertPickerItem fix).
- blockInsertPicker: close the picker when a select bails on an in-flight
  insert, so a menu reopened during a slow initial-value resolution can't get
  stuck swallowing Enter.
- blockInsertPicker: key sections on the namespaced raw group instead of the
  display label, so a curated group named like `ungroupedSection` can't collide
  with the null-group section's React key.

Two other Bugbot findings need no change: the text-block exclusion
(`isTextBlockMember`) already mirrors Sanity's own `findBlockType` (walk the
type chain to root, match `name === 'block'`), and the initial-value spread in
insertPickerItem was already reordered in an earlier commit.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
feat(code-input): export SUPPORTED_LANGUAGES and LANGUAGE_ALIASES (#1600)

Downstream tooling that resolves external language tokens (markdown
fence infostrings, paste/import pipelines) onto code field values has
had to vendor this list and hand-sync it with the Language selector.
Exporting the canonical table (and the alias map) removes the drift.
Version Packages (#1662)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): note npm provenance re-enabled for sanity-plugin-utils (#1661)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Version Packages (#1660)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(changeset): bump google maps input for css dts cleanup (#1659)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1658)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/client to ^7.24.0 (#1581)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1657)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1640)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): use turbo transit for build cache invalidation (#1655)

Replace empty dependsOn with a transit node so sanity build still skips
workspace package builds while Turbo invalidates when dependency source changes.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): bump pkg-utils, tsdown-config, and vanilla-extract plugins (#1656)

Update catalog entries to @sanity/pkg-utils ^11.0.13,
@sanity/tsdown-config ^0.19.6, and @sanity/vanilla-extract-vite-plugin
^0.2.5. Exclude lightningcss@1.33.0 (and platform packages) from
minimumReleaseAge so pnpm update/dedupe can resolve the new transitive
deps, and align turbo/generators tsdown with the workspace catalog.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1653)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore: remove @typescript/native-preview (use TypeScript 7 native tsc) (#1650)

* chore: remove @typescript/native-preview now that TypeScript 7 ships native tsc

TypeScript 7.0.2 includes the native compiler as `tsc`, so the preview package
and its `tsgo` binary are no longer needed. VS Code settings now point the
TypeScript 7 extension at the workspace `typescript` install.

* Update settings.json

* Update settings.json

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): lock file maintenance (#1651)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(test-studio): stop waiting on dependency builds (#1652)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Delete dependencies-GH-1642.md
Migrate to date-fns v4 and official @date-fns/tz, matching sanity core (#1569)
chore(deps): dedupe pnpm-lock.yaml (#1649)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.12 (#1647)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1645)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update renovatebot/github-action action to v46.1.20 (#1646)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): bump @sanity/vanilla-extract-vite-plugin to ^0.2.4

Drop the Bynder Styles.css.js Vite workaround from the test studio —
0.2.4 keeps the compiler alive under unstable_bundledDev, so plain
*.css.js modules no longer hang processVanillaFile.

fix(deps): update dependency tsdown to ^0.22.12 (#1642)
fix(deps): update dependency styled-components to ^6.4.4 (#1566)
chore(deps): dedupe pnpm-lock.yaml (#1643)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-virtuoso to ^4.18.11 (#1641)

* fix(deps): update dependency react-virtuoso to ^4.18.11

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(agents): catalog shared deps + enable pnpm catalogMode prefer (#1374)
chore(deps): dedupe pnpm-lock.yaml (#1637)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency react-hook-form to ^7.82.0 (#1639)

* fix(deps): update dependency react-hook-form to ^7.82.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update davelosert/vitest-coverage-report-action action to v2.12.2 (#1638)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1612)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
ci: shard Vitest suite (#1584)

* ci: shard vitest suite

* ci: report sharded test coverage

* chore: ignore Vitest blob reports

* ci: require test shards to succeed in aggregate check

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(typegen): 🤖 ✨ (#1634)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(format): 🤖 ✨ (#1633)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1632)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.9 (#1622)

* fix(deps): update dependency tsdown to ^0.22.9

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(assist): keep field group selection while editing AI Assist inputs (#1467)

* fix(assist): keep field group selection while editing AI Assist inputs

The AI Assist instruction inspector renders a nested document form that
reused the host document's pane router. Sanity's document pane mirrors the
router `path` param to programmatic focus, and focus drives the selected
field group. Navigating the instruction form therefore overwrote the host
`path`, re-focused the host document, and reset its field group tab.

Isolate the nested pane's `path` navigation in local state while still
forwarding other params (e.g. selected instruction) to the host router.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* test(test-studio): add AI Assist + field groups fixture (SAPP-3970)

Adds an 'AI Assist: Field groups' document type with Content/SEO field
group tabs so the field-group-reset behavior with AI Assist can be
manually reproduced and verified.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

* refactor(assist): use react-fast-compare for param equality

Addresses PR review: the hand-rolled shallowEqualParams returned a false
positive when two param objects had the same key count but different keys
whose values were undefined (e.g. {foo: undefined} vs {bar: undefined}),
because it never verified the key existed on the other object. Reuse
react-fast-compare (already a plugin dependency, used in _lib/useListeningQuery)
instead of a custom check, and add a regression test.

Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(bynder-input): restore Compact View modal visibility in Studio v6 (#1553)

* fix(bynder-input): restore Compact View modal visibility in Studio v6

Upgrade @bynder/compact-view from 5.1.2 to 5.4.0 and patch its shadow DOM
integration for Sanity Studio v6:

- disableCSSOMInjection on StyleSheetManager so styled-components injects
  CSS into the shadow root instead of leaving empty style tags
- position:fixed + z-index on CompactViewContainer so the portal host
  stacks above Studio UI layers
- widen React peer dependency range to support React 19

Also render BynderModalLayout outside the button flex row.

* fix: unmount bynder layout when not open

* fix(bynder-input): take @bynder/compact-view 5.4.0 as a regular pinned dependency

Implements review feedback on the patch-based approach:
- drop the pnpm patch and the bundling of @bynder/compact-view; pin it
  as a regular dependency instead
- style the Compact View modal host from the plugin with a
  vanilla-extract globalStyle instead of patching Modal.js
- document how to allow the stale react peer range (npm
  --legacy-peer-deps, pnpm peerDependencyRules.allowedVersions) in the
  README, and use the pnpm rule for the workspace itself

* fix(bynder-input): survive Compact View shadow-root re-attachment and fix dev-studio css.js crash

- append a <slot> fallback to Compact View's shadow root while the modal
  is mounted: its attachShadow effect has no cleanup, so when React
  reconnects passive effects on the same host (StrictMode, Activity),
  the second attachShadow() throws and Compact View falls back to the
  host's light DOM, which the slotless shadow tree kept invisible
- work around @bynder/compact-view's Styles.css.js (a plain JS module
  whose name matches vanilla-extract's cssFileFilter and deadlocks the
  compiler under sanity dev's bundled dev mode) in the test studio
- move the container globalStyle to BynderInput.css.ts so it loads with
  the input instead of the lazy modal chunk

* chore(bynder-input): bump changeset to major

Taking @bynder/compact-view as an unbundled regular dependency with a
stale react peer range can fail npm installs without --legacy-peer-deps,
so it's a breaking change.

* chore(bynder-input): widen @bynder/compact-view to ^5.4.0

The exact pin was only needed while the package was patched and
inlined; as a plain regular dependency a caret range is fine.

* chore: merge main and regenerate lockfile

The PR lockfile was generated against a main where
@sanity/vanilla-extract-vite-plugin resolved to 0.2.1; main has since
bumped it to 0.2.2, and the git-merged lockfile on the PR merge ref was
internally inconsistent (ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY in CI).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
fix(deps): update dependency @sanity/tsdown-config to ^0.19.3 (#1598)

* fix(deps): update dependency @sanity/tsdown-config to ^0.19.3

* chore: dedupe lockfile and add release-age excludes for tsdown transitive deps

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Cody Olsen <81981+stipsan@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): dedupe pnpm-lock.yaml (#1631)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2 (#1614)

* fix(deps): update dependency @sanity/vanilla-extract-vite-plugin to ^0.2.2

* chore(deps): exempt yuku-parser from minimum release age for dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Move turbo/**/*.hbs oxfmt ignore to root config (#1604)

The turbo generator template ignore is monorepo-specific and should
not ship in the shared @sanity/plugin-kit/oxfmt preset consumed by
standalone plugins.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.10 (#1630)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
docs(assist): fix README markdown formatting and document English-only image descriptions (#1628)

* fix(@sanity/assist): respect document language when generating image descriptions

Pass the configured translate.document.languageField as languagePath to the
generate-caption task (both the automatic generation triggered on image
change, and the manual 'Generate image description' field action), so
generated alt text/captions are written in the document's language instead
of always defaulting to English.

Fixes https://github.com/sanity-io/plugins/issues/1606

* docs(@sanity/assist): clarify caption generation language limitation

Testing against the live AI Assist backend confirms that the generate-caption
task currently always writes descriptions in English, regardless of the
languagePath sent from the client (this matches Sanity's own documentation,
which states the automatic image description feature is English-only today
and recommends the Transform Agent Action's image-description operation as a
workaround). Update the changeset and README to accurately describe the
change as forward-compatible plumbing rather than a full fix, and point users
at the documented workaround for non-English alt text today.

* refactor(assist): resolve languagePath inside useGenerateCaption

Compute languagePath from config.translate.document.languageField once
inside useGenerateCaption itself (via useAiAssistanceConfig), instead of
requiring every call site to read the config and thread it through as an
argument. This removes the duplicated config lookup from both
ImageContextProvider and generateCaptionsActions, and matches the cleaner
approach used in sanity-io/plugins#1627.

Also documents in translate/types.ts JSDoc that languageField is reused
for image description generation.

* docs(assist): fix README markdown formatting and add known limitations

The README relied on markdown block elements (headings, lists, code
fences, images) interrupting paragraphs without blank lines between
them. GitHub tolerates this, but stricter renderers (e.g. the plugin
listing on sanity.io) join everything into unreadable walls of text
with literal '### Heading' and '- list item' fragments inline.

- Separate all block elements with blank lines throughout
- Add a 'Known limitations' section documenting that image descriptions
  are always generated in English regardless of
  translate.document.languageField (backend limitation, see #1606),
  with Transform Agent Action and translate-after workarounds
- Fix broken links: empty-href heading, HOW-TO-USE pointing at the old
  standalone repo layout, LICENSE pointing at a non-existent file
- Fix broken bold markup in the 'Enabling the AI Assist API' steps and
  the useExampleFieldActions TOC anchor
- Add missing TOC entries (Known limitations, Caveats, Third party
  sub-processors)

Also reverts the languagePath client plumbing from this branch: the
backend ignores it, so the plugin change had no effect. This PR is now
docs-only.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
chore(deps): dedupe pnpm-lock.yaml (#1619)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11.13.1 (#1624)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.9 (#1625)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update turbo monorepo to v2.10.5 (#1626)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update dependency knip to ^6.27.0

chore: fix css

fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1 (#1610)

* fix(deps): update dependency @codemirror/lang-markdown to ^6.5.1

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency oxfmt to ^0.59.0 (#1599)

* fix(deps): update dependency oxfmt to ^0.59.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/pkg-utils to ^11.0.7 (#1594)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Fix TypeGen workflow failure by pinning `@sanity/vanilla-extract-vite-plugin` to 0.1.1 (#1616)

* Initial plan

* fix(typegen): pin vanilla-extract vite plugin to 0.1.1

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): bump tsdown and vanilla-extract

fix(deps): update dependency oxlint to ^1.74.0 (#1603)

* fix(deps): update dependency oxlint to ^1.74.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update zizmorcore/zizmor-action action to v0.6.0 (#1609)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): lock file maintenance (#1611)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin (#1590)

* chore(deps): replace @vanilla-extract/vite-plugin with @sanity/vanilla-extract-vite-plugin

Swap the vanilla-extract Vite plugin used by the test studio and
@sanity/google-maps-input's vitest config to @sanity/vanilla-extract-vite-plugin,
a Vite 8 rewrite with plugin hook filters and a caching compiler on Vite's
Environment API / ModuleRunner instead of the legacy vite-node. Bump tsdown to
^0.22.8 and @sanity/tsdown-config to ^0.17.0 alongside it.

google-maps-input's vitest.config.ts also needed a small stub plugin: its
dist/bundle.css.js Node/SSR shim matches the upstream cssFileFilter by
filename alone (*.css.js), which made the new plugin try to evaluate it as
real .css.ts source and throw ReferenceError: require is not defined via
ModuleRunner (vite-node tolerated this). The stub renames that module id away
from a .css.js shape before vanillaExtractPlugin() ever sees it.

Documented both the plugin swap and the shim workaround in the
sanity-plugin-best-practices skill and the new-plugin generator templates so
future vanilla-extract plugins get this for free.

* chore(deps): bump @sanity/tsdown-config to ^0.17.1, drop bundle.css.js shim workaround

@sanity/tsdown-config 0.17.1 (via @sanity/vanilla-extract-tsdown-plugin
0.2.1 / @sanity/vanilla-extract-rolldown-plugin 0.1.1) renames the
vanilla-extract Node/SSR compat shim from bundle.css.js to bundle-css.js so
it no longer matches vanillaExtractPlugin()'s cssFileFilter by filename
alone (sanity-io/pkg-utils#3043). That was the root cause of the
require-is-not-defined regression worked around in the previous commit, so
the stub-bundle-css-js-shim plugin is no longer needed.

Rebuilding @sanity/google-maps-input picked up the renamed dist/bundle-css.js
shim and auto-rewrote its package.json ./bundle.css export map (adding a
types condition too). Reverted the vitest.config.ts workaround and the
matching documentation/generator-template changes back to the plain
vanillaExtractPlugin() registration, now noted as a historical gotcha
instead of a live workaround.

* docs(test-studio): note that unstable_bundledDev breaks .css.ts HMR

Document the trade-off discovered while testing the vanilla-extract Vite
plugin swap: with unstable_bundledDev: true, editing a .css.ts file doesn't
hot-reload (a sanity dev restart is required), while plain .ts/.tsx HMR is
unaffected. Comment out the line for a session if fast CSS HMR is needed
(confirmed sub-10ms with it unset).

* docs(styling skill): trim verbose vanilla-extract setup section

The ./bundle.css export map (and its dist/bundle-css.js shim/types) is
generated and kept in sync automatically by @sanity/tsdown-config's
vanillaExtract option, so it doesn't need explaining or a JSON example to
copy. Simplify the vitest.config.ts guidance to why (@sanity/vanilla-extract-
vite-plugin is faster than upstream) without the ModuleRunner/hook-filter/
sanity-build internals. Drop the sanity.cli.ts snippet, since the test studio
already registers the plugin globally in this repo and doesn't need to be
touched per-plugin. Drop the bundle.css.js->bundle-css.js rename history
note, a one-off gotcha now fixed upstream rather than evergreen guidance.

* chore(deps): bump @sanity/tsdown-config to ^0.17.2, @sanity/vanilla-extract-vite-plugin to ^0.1.1

Both are latest-available patch releases (0.17.1 -> 0.17.2 pulls in
@sanity/vanilla-extract-tsdown-plugin 0.2.2 / @sanity/vanilla-extract-
rolldown-plugin 0.1.2; 0.1.0 -> 0.1.1 is the vite plugin's own patch).
tsdown stays at ^0.22.8, already the latest. Benchmarked in the next commit's
message to confirm this doesn't regress anything.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
fix(deps): update dependency @changesets/cli to ^2.31.1 (#1607)

* fix(deps): update dependency @changesets/cli to ^2.31.1

* chore(deps): exempt @changesets/cli and refresh lockfile with dedupe

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
chore(deps): update dependency @vitejs/devtools to ^0.4.1 (#1593)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Version Packages (#1591)
chore(deps): dedupe pnpm-lock.yaml (#1602)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(media): use useMediaIndex so Tags panel works on wide viewports (#1592)

The home-rolled useBreakpointIndex returned -1 above 1920px, which
force-hid the Tags panel. Switch Items to @sanity/ui's useMediaIndex,
which includes a min-width catch-all and matches Controls breakpoints.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(deps): update dependency xstate to ^5.32.5 (#1597)

* fix(deps): update dependency xstate to ^5.32.5

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency tsdown to ^0.22.8 (#1596)

* fix(deps): update dependency tsdown to ^0.22.8

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency @sanity/tsconfig to ^2.2.1 (#1595)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update actions/setup-node action to v7 (#1586)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
chore(deps): update pnpm to v11 (#1579)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency nanoid to v6 (#1577)

* fix(deps): update dependency nanoid to v6

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
fix(deps): update dependency groq to ^6.5.0 (#1523)

* fix(deps): update dependency groq to ^6.5.0

* chore(deps): changeset for dependency update

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint (#1565)

* Migrate plugin-kit and monorepo formatting from prettier to oxfmt

- Ship the monorepo's oxfmt settings as a @sanity/plugin-kit/oxfmt preset
- Re-export the preset from the root oxfmt.config.ts (replaces .oxfmtrc.json)
- init/inject scaffold oxfmt (config, devDependency, format script) instead
  of prettier; --no-prettier is now --no-oxfmt
- verify-package: replace the duplicate-prettier-config check with a
  monorepo-aware oxfmt check that expects the config at the workspace root
  (or next to package.json outside monorepos) and flags leftover prettier
  configuration with migration instructions

* Migrate plugin-kit and monorepo linting from eslint to oxlint, drop tsc

- Ship the monorepo's oxlint settings …
stipsan added a commit that referenced this pull request Sep 29, 2026
…ommit hook on lefthook (#2047)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Brings oxfmt in line with sanity-io/ui's `main`: the same version, the
same config, and the same scripts and CI wiring. The pre-commit hook
moves from husky and lint-staged to lefthook, set up like
sanity-io/sanity's. Supersedes #1919.

## Commits

1. **`chore(deps): update oxfmt to ^0.71.0`**: the catalog goes from
^0.63.0 to ui's ^0.71.0, with ui's oxfmt resolution: oxfmt 0.71.0 and
`tinypool` 2.2.0, the same versions and integrity hashes as ui's
lockfile. There's a patch changeset for `@sanity/plugin-kit`, whose
optional `oxfmt` peer uses the catalog.
2. **`chore(format): align the oxfmt config with sanity-io/ui`**: root
`oxfmt.config.ts` now ignores `storybook-static/**` and `CHANGELOG.md`,
like ui's `.oxfmtrc.json`. `.vscode/settings.json` points
`oxc.fmt.configPath` at `oxfmt.config.ts` instead of the `.oxfmtrc.json`
that #1565 deleted. AGENTS.md records the parity with ui and the
exceptions.
3. **`chore: drop the husky and lint-staged pre-commit hook, like
sanity-io/ui`**: superseded by commit 6, which brings the hook back on
lefthook.
4. **`chore(format): drop the oxfmt preset's .changeset/*.md override,
like sanity-io/ui`**: the `@sanity/plugin-kit/oxfmt` preset loses its
Prettier-era `singleQuote: false` override for `.changeset/*.md`, as
ui's `.oxfmtrc.json` did when ui switched Changesets to oxfmt formatting
(sanity-io/ui#2957). It gets a patch changeset for `@sanity/plugin-kit`,
since the preset is published. This part moved here from #2046 because
it's formatter config. AGENTS.md now explains how changesets get quoted.
5. **`chore(format): run pnpm format`**: requotes this PR's own
changeset from commit 1. Everything else already passed under oxfmt
0.71.
6. **`chore: run oxfmt on staged files with lefthook, like
sanity-io/sanity`**: see below.

## Pre-commit hook: husky and lint-staged → lefthook

This mirrors how sanity-io/sanity replaced husky and lint-staged
(sanity-io/sanity#12755, then sanity-io/sanity#12767 and
sanity-io/sanity#12953):

- **`lefthook` ^2.1.14** as a root devDependency, sanity's range. It
resolves to 2.1.14, as in sanity's lockfile.
- **`allowBuilds: lefthook: true`** in `pnpm-workspace.yaml`, as in
sanity. Lefthook's own install script runs `lefthook install` during
`pnpm install`, and it skips when `CI` is set, which covers GitHub
Actions and Vercel. There's no root `postinstall` script: sanity dropped
its `lefthook install --reset-hooks-path` postinstall in #12767 for this
package build.
- **`lefthook.yml`** with sanity's layout: a parallel `pre-commit` with
two jobs that run oxfmt on staged files with `stage_fixed: true`. The
jobs are `format-scripts` (`*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}`) and
`format-other` (`*.{md,yml,yaml,css,json,jsonc}`).
- `mts`, `cts` and `jsonc` are additions to sanity's globs. They cover
this repo's `.mts`, `turbo.jsonc` and `knip.jsonc` files, which the old
lint-staged glob partly covered.
- sanity's `oxlint --fix` line is left to the oxlint alignment (#2049).
- **Direct binary calls:** the jobs call `node_modules/.bin/oxfmt`
instead of sanity's `pnpm oxfmt`. Running pnpm in a hook triggers pnpm
12's implicit install, which flips the lockfile's peer toggle (see
AGENTS.md). sanity switched to `pnpm oxfmt` in #12953 because lefthook
doesn't put `node_modules/.bin` on `PATH`; the path solves that too.
- **Lockfile:** `pnpm-lock.yaml` gains only lefthook 2.1.14 and its
platform packages (100 lines). A plain install would also have bumped
the Studio's `next` overrides from `6.17.0-next.108` to
`6.17.0-next.112`, so I resolved it through a local registry proxy that
served the locked versions as `next`, as AGENTS.md describes. The
lockfile has no `localhost` or `tarball:` entries.
- **Docs:** AGENTS.md documents the hook, `LEFTHOOK=0`, the one-time
reset for clones that ran husky, and `pnpm
--config.verify-deps-before-run=false <script>` for local runs. README's
note that formatting also runs in a pre-commit hook is back.

## Merges with `main`

`main` is merged in, not rebased, so nothing was force-pushed. After
each merge, `pnpm-lock.yaml` is `main`'s lockfile regenerated with pnpm
12.6.0, invoked directly so `packageManagerDependencies` stays, minus
the peer toggle that AGENTS.md describes.

- **#2039 (lock file maintenance):** only the lockfile conflicted.
- **#2048 (pnpm 12.6.0):** `package.json` conflicted next to the removed
`lint-staged` block. The resolution keeps both sides: `main`'s
`packageManager: pnpm@12.6.0` and `vercel` ^60.1.3, without the block. A
follow-up commit restores `main`'s side of the peer toggle, which the
regeneration had flipped.
- **#2046 (themer move):** only the lockfile conflicted. `pnpm format`
then requoted #2046's three changesets in a separate commit:
`dashboard-widget-vercel-xstate`, `shared-deps-sanity-ui-icons-motion`
and `themer-plugins-monorepo`, 45 frontmatter lines, quote-only. The new
`plugins/@sanity/themer` package and `dev/storybook` already passed.
- **#2053 (Version Packages):** it released and deleted those three
changesets, so the modify/delete conflicts resolve to `main`'s
deletions. It didn't change the lockfile, and `pnpm format` changes
nothing after the merge.

`pnpm-lock.yaml` differs from `main` only by this PR's own changes:

- oxfmt and its bindings go from 0.63.0 to 0.71.0, and `tinypool` from
2.1.0 to 2.2.0.
- husky, lint-staged and their four exclusive dependencies are removed.
- lefthook 2.1.14 and its platform packages are added.

No other version moves, and the Studio stays on `6.17.0-next.108`.

## How it compares with ui

- **Options:** identical, overrides included (`printWidth`, `semi`,
`singleQuote`, `bracketSpacing`, `quoteProps`, `sortImports`,
`sortPackageJson`). They come from the `@sanity/plugin-kit/oxfmt`
preset.
- **Ignore patterns:** `dist/**`, `storybook-static/**`,
`pnpm-lock.yaml` and `CHANGELOG.md` in both. Two differences remain:
  - `turbo/**/*.hbs` is plugins-only (the turbo generator templates).
- ui's `**/sanity.types.ts` is left out. In ui it was for
`apps/docs/sanity.types.ts` and has matched nothing since the docs app
went static. Here, `pnpm typegen` formats
`dev/test-studio/sanity.types.ts` on purpose: the test studio runs
typegen with `formatGeneratedCode: false` and then `pnpm format`.
Ignoring the file would make the next TypeGen PR unformat it.
- **Config file:** plugins keeps `oxfmt.config.ts` re-exporting the
published preset instead of ui's `.oxfmtrc.json`. #1565 made that switch
so the preset is exactly what the monorepo formats with, and
plugin-kit's `verify-package` expects plugins to use it. oxfmt, the Oxc
extension and Changesets' formatter detection all read
`oxfmt.config.ts`. See the open question.
- **Scripts and CI:** the `format` script is identical to ui's (`oxfmt .
--no-error-on-unmatched-pattern`). Neither repo checks formatting on
PRs, and both run the same shared `format-if-needed.yml` on `main`, so
no workflow changes are needed. `oxfmt .` also formats
`.github/workflows/*.yml`, which that workflow can't push, and 0.71
changes none of them. ui has no pre-commit hook; this repo keeps one, on
lefthook.
- **VS Code:** ui points `oxc.fmt.configPath` at its config file, but
plugins pointed it at a file that doesn't exist. I drove `oxfmt --lsp`
directly: with the old setting, the editor silently formats with oxfmt's
defaults (double quotes, semicolons, bracket spacing, unsorted imports),
and with `oxfmt.config.ts` it matches `pnpm format`.

## Changesets

- **Changesets formats what it writes with oxfmt.**
`.changeset/config.json` sets `"format": "oxfmt"` (from #2046). I called
`@changesets/write` (what `changeset add` uses) directly. With the
override it writes `"@sanity/plugin-kit": patch`; without it,
`'@sanity/plugin-kit': patch`, the same as `pnpm format`.
- **`changeset version` is unaffected.** It only hands `CHANGELOG.md`
files to the formatter. This PR ignores those, and oxfmt skips them and
exits 0 thanks to `--no-error-on-unmatched-pattern`.
- **`changeset status` parses the single-quoted frontmatter.** It lists
this PR's two changesets for `@sanity/plugin-kit` 10.0.11.

## What to expect after merging

- **Renovate changesets get a follow-up format PR, as in ui.** The
shared Renovate changesets workflow uses
`mscharley/dependency-changesets-action`, which always writes double
quotes and has no formatting option. After a Renovate PR merges, the
Auto format workflow opens (or updates) a `chore(format): 🤖 ✨` PR that
requotes the changeset. ui has had one after most Renovate merges since
sanity-io/ui#2957: sanity-io/ui#2971, sanity-io/ui#2980,
sanity-io/ui#2986, sanity-io/ui#2998, sanity-io/ui#3002,
sanity-io/ui#3013 and sanity-io/ui#3024.
- **Clones that ran husky need a one-time reset.** They still have
`core.hooksPath=.husky/_`. There, `lefthook install` refuses to install
and prints how to fix it, and `pnpm install` continues. Until the reset,
husky's leftover runner does nothing, so commits simply aren't
formatted. `git config --unset core.hooksPath && pnpm install` fixes it.

## Related PRs

- **#1919** (Renovate, oxfmt ^0.71.0): superseded. This PR has the same
bump, with the lockfile regenerated on current `main`, plus the config
alignment. Close it if Renovate doesn't once this merges.
- **#2046** (themer move) and **#2048** (pnpm 12): merged, and merged
into this PR.
- **#2049** (oxlint) conflicts with this PR in `pnpm-workspace.yaml`,
where its `oxlint` catalog lines sit next to `oxfmt`. Keep both sides.
It could also add sanity's `oxlint --fix` line to the `format-scripts`
job, calling `node_modules/.bin/oxlint` directly.
- **#2050** (vitest 5) conflicts only in `pnpm-lock.yaml`.
- **#2051** (tsdown) doesn't conflict.
- Whichever PR lands later merges `main` and regenerates the lockfile
without committing the peer toggle.

## What Cody needs to do

- Add the `🤖 bot` label: the agent's label tool couldn't edit PRs in
this repository.
- Triage Socket's alert. It flags `lefthook@2.1.14`'s install script
(`node postinstall.js`) as a low-priority warning. That script is what
installs the hook: `allowBuilds: lefthook: true` enables it, as in
sanity-io/sanity, and it skips when `CI` is set. Mark it as acceptable
risk if you agree. Socket's required checks pass either way.
- After merging, tell contributors to run `git config --unset
core.hooksPath && pnpm install` once, so lefthook replaces husky's hook.
- Merge the Renovate follow-up format PRs as they come, as in ui.
- No Vercel, secrets or ruleset changes, and no new secrets or env vars.

## Open question

**`.oxfmtrc.json`:** should plugins switch to ui's JSON config file,
too? That would mean no longer re-exporting the plugin-kit preset, and
it would reverse #1565. I kept `oxfmt.config.ts`, since the formatting
result is already the same as ui's.

## Verification

- **The hook**, tested on throwaway commits, with `pnpm-lock.yaml`
untouched each time:
  - Staged TS and JSON files get formatted and re-staged.
- A tracked `.vscode/settings.json` (its folder is gitignored) is
re-staged without the `[FAILED]` that lint-staged printed.
  - A staged `CHANGELOG.md`, which oxfmt ignores, passes as-is.
- In a partially staged file, only the staged hunk is formatted and the
unstaged hunk is kept.
- The commit that adds the hook ran it on its own files in 0.4 seconds.
- **Clones that ran husky:** with `core.hooksPath=.husky/_`, `lefthook
install` exits 1 with instructions and installs nothing, as described
above.
- **Local checks** at the head commit, with pnpm 12.6.0 on Node 24.21
and `CI=true` (lefthook's install script skips):
- `pnpm install --frozen-lockfile`, which leaves `pnpm-lock.yaml`
untouched
  - `oxfmt --check` (2,018 files)
  - `oxlint` and `knip`
  - the full turbo build (54 tasks)
  - `vitest run` (235 files, 1,509 tests)
  - `pnpm test:browser` (4 files, 7 stories)
- **CI** is fully green at the head commit (`123af257`): build, lint,
knip, all four test shards, coverage, `storybook-test`, both e2e
browsers, Bugbot, and both Vercel checks, whose installs run lefthook's
install script. The branch is up to date with `main`.
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-0b8c4f47-d9af-5929-8254-d25847e7e2dd?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-0b8c4f47-d9af-5929-8254-d25847e7e2dd&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

This branch was successfully deployed

1 active deployment
Preview — 9f0868d4 Deployed Jul 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants