## Problem
- API may return a non-array shape that can crash `getKeys` because of
an hard coded cast
- getting API keys is cumbersome as consumers have to call two functions
## Solution
- consolidate `useAPIKeysQuery` + `getKeys` into a single `useAPIKeys`
hook
- guard `getKeys` so that it doesn't crash if passed a non array value
- update usages
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Unified how project API keys are retrieved across the studio,
resulting in more consistent loading/error handling and slight
responsiveness improvements when showing keys and related command
snippets. UI and permissions behavior remain unchanged for end users.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
smol fix :D
- closes https://github.com/supabase/supabase/issues/46730
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Corrected table count in footer to show the proper number and
singular/plural wording when pagination is active.
* Ensured table lists refresh correctly after deleting a table so
paginated/infinite lists update properly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Refactored the Database Create Index side panel to align with the
recommended Studio form patterns.
## What is the current behavior?
The side panel was using a legacy layout that did not follow the common
Studio form patterns.
It's also not a real form.
## What is the new behavior?
The Create Index side panel in the Database section is now consistent
with the Studio form patterns.
It's now a real form using `react-hook-form`
## Additional context
Before:
<img width="668" height="855" alt="image"
src="https://github.com/user-attachments/assets/d15ee63c-8d03-4780-8945-1792f0493133"
/>
After:
<img width="606" height="856" alt="image"
src="https://github.com/user-attachments/assets/78e0269c-e79c-42d1-a2c2-9465c0b0c46a"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **UI Improvements**
* Reworked index-creation panel into a validated, form-driven workflow
with clearer field interactions, multi-column selector, live read-only
SQL preview, and updated submit/cancel behavior.
* Footer actions: Cancel resets the form and closes the panel; Create
triggers form submission and reflects loading state.
* **Tests**
* Updated end-to-end flow to click "Select a table" (replacing "Choose a
table") during index creation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
- Noticing our code we have many patterns of calling localstorage and
handling those errors
- We should add those in a single well tested file
- Handle those errors in the singleton which makes it easier for us to
debug customer issues. Logger is outputing local storage warnings for
feature we expose
- Side effect of this is random crashes on studio when local storage
isn't available or handled correctly
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Improved browser storage handling across the app for more reliable
persistence and graceful behavior in restricted or non-browser
environments (settings, previews, charts, tabs, sign-in/session flows,
integrations, and UI state).
* **New Features**
* Introduced a safe storage layer to standardize and harden
local/session persistence.
* **Tests**
* Added comprehensive tests covering the new safe storage behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Feature / performance improvement.
## What is the current behavior?
The Database > Tables list renders every table for the selected schema
in a single `useTablesQuery({ includeColumns: true })` fetch.
## What is the new behavior?
- Database > Tables list uses `useInfiniteTablesQuery` with a 50-row
page size, streaming pages as the user scrolls.
- An `IntersectionObserver` sentinel attached to the footer status row
triggers `fetchNextPage()` while `hasNextPage` is true; the same row
doubles as the table count / loading indicator.
- Search is debounced (300 ms) and passed as `nameFilter` to the hook,
which forwards it to pg-meta's `getTablesPaginatedSql`. The backend
returns only matching rows, so search works across the whole schema
rather than only the loaded pages.
## Additional context
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Debounced search filtering for tables
* Infinite-scroll pagination with sentinel-driven loading
* Footer shows “Loading more tables…” and dynamic table counts
* **Bug Fixes**
* Ensure table list refresh after create/duplicate by invalidating
infinite-list cache
* **Tests**
* Updated end-to-end waits and helpers to match the new paginated
table-loading API
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
Because we have controller inputs and zod validation on numbers, many of
them cannot be cleared correctly as deleting their value resets it to
`0`.
## Solution
Update the `Input` component to allow those editions by always storing
and displaying the user entered value
## How to test
- Open the webhook page and add/edit one
- Clear its timeout value and observe that it is not reset to `0`
- Same for:
- Database network restrictions
- API settings max rows
- Disk size modal
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Standardized numeric form input handling across examples, settings,
and modals — inputs now rely on form bindings and schema coercion for
consistent parsing and simplified behavior.
* **Chores**
* Added form resolver utilities and a user-event testing library to
development dependencies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES/NO
## What kind of change does this PR introduce?
Bug fix, feature, docs update, ...
## What is the current behavior?
Please link any relevant issues here.
## What is the new behavior?
Feel free to include screenshots if it includes visual changes.
## Additional context
Add any other context or screenshots.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Accessibility**
* Improved screen-reader label for table row action menus so table
controls are clearer for assistive‑technology users.
* **Tests**
* Enhanced end-to-end test reliability: tightened selectors, added
dialog/toast visibility and API-wait synchronization, scoped lookup
fixes, removed redundant cleanup helper, and updated test setup to mark
a terms-of-service dismissal to reduce flakiness.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Performance improvement / feature
## What is the current behavior?
The Schema Designer fetches all tables in a single request via
`useTablesQuery`. For schemas with 400+ tables this blocks first paint
on a large payload.
## What is the new behavior?
`SchemaGraph` uses `useInfiniteTablesQuery` (pageSize: 100) so the first
100 tables paint immediately. A "Load more tables" button appears above
the legend whenever more pages remain, letting users load the rest on
demand.
## Additional context
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a "Find table…" selector and keyboard shortcut to quickly locate
and focus tables; supports incremental loading and debounced name search
(with literal wildcard handling).
* Schema Graph shows a bottom "Load more tables" control with loading
state and preserves view after loading more.
* **Refactor**
* Table listing switched to infinite/paginated retrieval and improved
"no tables" logic; server-side name filtering supported.
* **Tests**
* E2E tests add a schema-visualizer wait helper and update flows to
support the paginated visualizer.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46402?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Studio is on `react@^19.2.6`, and `useEffectEvent` shipped stable in
React 19.2 with the same signature as the userland polyfill. This drops
the local hook in `apps/studio` and `apps/www` in favor of the built-in.
**Removed:**
- `apps/studio/hooks/useStaticEffectEvent.ts`
- `apps/www/hooks/useStaticEffectEvent.ts`
- `.claude/skills/use-static-effect-event/` — skill is obsolete
**Changed:**
- 26 call sites: dropped the `useStaticEffectEvent` import, added
`useEffectEvent` to the existing `react` import, renamed call sites
- `.claude/CLAUDE.md`: `apps/studio` row updated React 18 → React 19
- `.claude/skills/vercel-composition-patterns/SKILL.md`: removed stale
"Studio uses React 18, skip these patterns" warning
## To test
- `pnpm typecheck --filter=studio` — passes locally
- `pnpm typecheck --filter=www` — passes locally
- `grep -rn "useStaticEffectEvent"` returns nothing outside
`node_modules`
- Smoke-test areas that use the hook: schema visualizer edges
(intersection check), spreadsheet import, sign-in/CLI login flows, side
panels with unsaved-changes prompts
**Out of scope:** pre-existing Tailwind lint warning on
`DefaultEdge.tsx:141` (`outline` + `outline-1` conflict) — unrelated to
this migration
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Internal event handling migrated to React’s built-in event hooks
across the Studio app; no user-facing changes.
* **Documentation**
* Clarified React 19 compatibility and noted Studio now targets React
19.
* Removed obsolete documentation for a deprecated internal hook.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46415?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
## Problem
Some pages still uses the deprecated `Modal` for:
- Legacy JWT new secret
- PITR recovery confirmation
## Test
Hard to test the JWT. I had to force its display by settings its `open`
prop to `true` in
`apps/studio/components/interfaces/JwtSecrets/jwt-settings.tsx`
## Solution
- use `Dialog` instead
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Improved database restore (PITR) confirmation: replaced modal flow
with an alert dialog, reorganized review sections (timing, warnings),
and made the restore action await completion for clearer feedback.
* Redesigned JWT secrets dialog: replaced modal with dialog primitives,
centralized form and state reset on open/close, moved confirmation into
dialog footer, and disabled confirm during submission.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46366?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Bug fix.
## What is the current behavior?
On pages where the table has horizontal overflow (e.g. Database Tables
with its 7 columns), `ShadowScrollArea` unconditionally applies
`!bg-surface-200` to `td:last-child` on row hover. Since `TableCell` has
`transition-colors`, this gives the last cell an explicit animated
background change — making it visually distinct from every other cell,
which simply inherit the row's hover bg transparently.
This manifests as the last cell appearing to have its own independent
hover state, separate from the row hover.
## What is the new behavior?
The `!bg-surface-200` override on `td:last-child` is moved inside the
`stickyLastColumn` block, since that's the only case where an explicit
background is actually needed (sticky cells require a solid bg to avoid
showing scrolled content behind them). Non-sticky tables no longer get
the spurious cell-level hover.
Also reverts the MoreVertical dropdown trigger in `TableList` back to
`type="default"` (undoes the workaround from #46247).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed hover styling behavior in horizontally scrollable tables to
apply only when appropriate.
* **Style**
* Updated the visual appearance of the row action dropdown button.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46251?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove useless expire snapshot configuration
> This is an alpha feature not already deployed to any of our customers
so breaking changes are not an issue.
Signed-off-by: Benjamin <5719034+bnjjj@users.noreply.github.com>
## What kind of change does this PR introduce?
Feature, bug fix, and docs update. Addresses the AlertDialog async
action behaviour discussed in Slack and follow-up PR feedback.
## What is the current behavior?
`AlertDialogAction` delegates directly to Radix, so confirm actions
close the dialog immediately on click. Async mutation flows have to use
`asChild` with `event.preventDefault()` and a custom loading button to
keep the dialog open while work is in flight.
## What is the new behavior?
- `AlertDialogAction` now accepts async handlers and a controlled
`loading` prop. Promise-returning actions keep the dialog open, show the
existing Button loading state, disable cancel/dismissal while pending,
close on success, and stay open on rejection.
- Existing workaround usages in Studio have been migrated to the direct
action API (see 'To test' callsite list below)
- design-system docs now include async action examples and
`AlertDialogBody` guidance for inline feedback
https://github.com/user-attachments/assets/1af66410-e9f9-4231-9c6d-fe650bd717a4
## Additional context
- [ ] Once #45572 is rebased onto this change, `ResetTemplateDialog`
should use `AlertDialogAction loading={isResettingTemplate}` with a
promise-returning reset handler instead of a plain loading `Button` in
`AlertDialogFooter`.
## To test
- [x] On Studio API Keys settings, use a project with no publishable or
secret API keys, click the “Create API keys” banner action, and confirm
the Alert Dialog stays open with loading until the default publishable
and secret keys are created.
- [x] Delete a JIT database access rule and confirm the Alert Dialog
stays open with loading until deletion succeeds, and stays open with
inline feedback if it fails.
- [x] With temporary access disabled and existing rules configured,
enable temporary access and confirm the “This will activate existing
rules” Alert Dialog stays open with loading until the configuration
update succeeds, and stays open with inline feedback if it fails.
- [x] Disable external replication and confirm the Alert Dialog stays
open with loading until the mutation succeeds.
- [x] Enable Index Advisor and confirm the Alert Dialog stays open with
loading until the mutation succeeds, and stays open with inline feedback
if it fails.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Alert dialogs support async actions with built-in loading, dismissal
blocking while pending, and preserved dialog on error.
* Two interactive examples demonstrating async success and error flows.
* **Improvements**
* Dialogs now surface inline error messages and consistent
loading/confirm behavior across flows (create keys, replication, JIT DB
access, index advisor).
* Minor UI refinements for action controls.
* **Documentation**
* Docs updated with async-action guidance and inline-error
recommendations.
* **Tests**
* New test suite validating async dialog behaviors.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45960)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
Towards [FE-3428](https://linear.app/supabase/issue/FE-3428).
## Summary
Cheap render-cost fixes so the Schema Visualizer stays responsive at
400+ tables. No data-layer or API changes — pure render-side work,
isolated to the Schema Visualizer.
- Enable `onlyRenderVisibleElements` on `<ReactFlow>` so xyflow skips
mounting nodes and edges outside the viewport.
- Memoize `TableNode` (custom comparator) and `DefaultEdge` so they
don't re-render on unrelated state changes (pan/zoom, theme toggle,
parent re-renders).
- Drop `resolvedTheme` from the layout effect deps — theme is purely
visual; toggling it no longer re-runs Dagre and replaces every
node/edge.
- `handleSelectionChange` now (a) skips `setEdges` entirely when no
edge's `animated` flipped, and (b) preserves the object reference for
untouched edges so memoized edges don't re-render.
- Precompute a `Map<tableName, …>` lookup in `getGraphDataFromTables` so
FK handle resolution is O(1) per relationship instead of scanning every
table+column.
A follow-up PR will add a `useInfiniteTablesQuery` and switch both the
Schema Visualizer and Database > Tables list onto it for progressive
loading and infinite scroll.
## Caveats
- `TableNode` reads `selectedEdge` from context, so selecting an edge
still re-renders all visible `TableNode`s via context propagation. The
memo guards against props-driven re-renders (pan/zoom/theme).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Optimized database schema graph rendering with improved component
caching and efficient relationship lookups.
* Enhanced visual element rendering to reduce unnecessary re-renders for
large schemas.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46235?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
The `_Shadcn_` suffix isn't needed anymore on `Command` components
## Solution
- Remove the `_Shadcn_` suffix
- Simplify UI package exports
- Apply prettier
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Simplified command component imports and exports across the UI library
by removing internal naming aliases and adopting direct component
references. Updated the public UI package barrel export to use wildcard
re-exports for cleaner API surface.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46153?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Feature. Resolves AUTH-879.
## What is the current behavior?
Studio does not provide a way to reset an Auth email template back to
the default subject and body once it has been customised.
## What is the new behavior?
Studio shows a `Reset template` action when Platform reports that the
selected Auth email template subject or body has been customised. The
action opens a confirmation dialog, calls the dedicated Platform reset
endpoint, and refreshes the editor with the default subject and body
returned by the API.
The Auth config save/reset mutations now run their user-facing success
handling before refreshing Auth lint data, so the success toast and
local editor cleanup are not delayed by lint refetches.
## Additional context
Depends on supabase/platform#32417.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Reset email templates to defaults via a confirmation dialog; button
appears when custom content is detected and respects update permissions.
* Typed email-template definitions and improved template variable
display and descriptions.
* **Tests**
* Added tests covering template reset visibility, confirmation flow,
state updates, permission handling, and toast notifications.
* **Documentation**
* Example email template placeholders updated for internationalization
and provider snippets.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45572)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## Summary
Third PR in the SafeSql migration stack. Flips the input/output types on
`pgMeta.functions/policies/triggers`'s `.create/.update/.remove` to use
`SafeSqlFragment`, and updates every Studio consumer atomically.
### pg-meta
- `pgMeta.functions/policies/triggers` `.create/.update/.remove` now
return `{ sql: SafeSqlFragment }` and accept branded input parameters
(`PGFunctionCreate`, `PGSavedFunction`, `PolicyCreate/UpdateParams`,
`PGTriggerCreate` with branded condition).
- `QueryModifier.toSql()` returns `SafeSqlFragment`.
### Studio consumers updated to the new branded API
- `data/database-functions/*` (query, create/update/delete mutations)
- `data/database-policies/*` (create, update mutations)
- `data/database-triggers/database-trigger-update-transaction-mutation`
- `components/Database/Triggers/TriggerSheet`
- `components/Database/Functions/CreateFunction`
- `components/Auth/Policies/PolicyEditorPanel`
These consumers land atomically with the pg-meta API change because the
input-type strictness flip (string → `SafeSqlFragment` for SQL fields)
forces every call site to update together.
## Stack
- 1/7: #45897 (merged)
- 2/7: #45903 (merged)
- 3/7: this PR
- 4/7–7/7: upcoming
## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm --filter @supabase/pg-meta test` passes
- [x] Dev-server smoke test: function editor, policy editor, trigger
sheet
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Strengthened SQL safety across policy, function, and trigger workflows
by converting raw SQL strings to typed SQL fragments and safer
composition
* Updated editor behavior to handle policy conditions/checks as typed
SQL fragments with improved initialization and template handling
* Aligned query and modifier interfaces to return typed SQL fragments
for safer composition
* **Tests**
* Updated tests to use typed SQL fragments and synchronous builders
where applicable
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45990)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
The `_Shadcn_` suffix isn't needed anymore on `Select` components
## Solution
Remove it. No other changes
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Updated internal component architecture to standardize and simplify
the codebase. These changes improve code maintainability and consistency
across the application without affecting existing functionality or user
experience.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45988)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
The `_Shadcn_` suffix isn't needed anymore on label component
## Solution
Remove it. No other changes
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Standardized Label usage across the codebase by removing the legacy
alias and using the direct Label export from the UI package
consistently.
* **Documentation**
* Updated component examples and docs to use the standardized Label
component in usage snippets and demos.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45986)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
We have multiple Popover components
## Solution
- [x] migrate Popover usages to Shadcn components
- Migrated JSON and text editor in the `TableEditor` (inline row
edition)
- Migrated the template popover in the logs explorer templates page
- [x] remove `_Shadcn_` suffix from Popover components (renaming +
prettier)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Unified popover implementation across the app and design system;
dropdowns, calendars, menus and tooltips now use a consistent popover
API with no visual or interaction changes.
* **Chores**
* Minor prop typing update for the logs date-picker to align with the
consolidated popover content type.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45980)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Refactor + bug fixes (part of the SafeSql migration stack — PR 2 of 7,
stacks on top of #45897).
## What is the current behavior?
- `pgMeta.columns.create` and the table-editor SQL builder take column
type as a string with array suffix and schema baked in (e.g.
`'private.test_enum'`, `'int4[]'`).
- The studio table-editor SQL emits the legacy schema-embedded `format`
string for enums in non-public schemas, while the pg-meta columns SQL
already returns the new shape (bare `format` + separate
`format_schema`). The two queries disagree on how to represent the same
column, surfacing as a false-positive type mismatch in the FK selector
when both ends are an enum from a non-public schema.
- The FK selector compares column types by `format` alone — same-named
enums in different schemas appear equal, and arrays vs. scalars of the
same base type pass the family check.
- `displayColumnType` renders arrays as the raw `_typname` pg-meta emits
(e.g. `_int4` instead of `int4[]`).
## What is the new behavior?
**pg-meta**
- Introduce `ColumnTypeRef` (`{ schema?, name, isArray? }`) for column
type input, replacing the legacy string-with-array-suffix format.
`pgMeta.columns.create` and the table-editor SQL builder consume the new
shape.
- Add `format_schema` to the column zod schema; pg-meta SQL emits the
type's schema for the table editor's ColumnType dropdown.
- `pgMeta.columns.create` returns a `SafeSqlFragment`.
- Studio table-editor SQL now emits bare `format` + `format_schema`,
matching pg-meta's columns SQL.
**Studio**
- `SafePostgresColumn`/`SafePostgresTable` extend the new `PG*` types
(master dropped postgres-meta).
- Pipe `ColumnTypeRef` through `SidePanelEditor` → `ColumnEditor` →
`TableEditor`, along with the column-create mutation, table
retrieve/list queries, and the `TableList`/`ColumnList` surfaces.
- `displayColumnType` helper renders arrays as `type[]` (or
`schema.type[]`) and handles non-implicit schemas.
- FK selector now carries `sourceIsArray`/`targetIsArray` and compares
the full `(format, format_schema, isArray)` triple. Family checks for
numeric/text/uuid skip when either side is an array (FKs across array
boundaries are never compatible).
- Type-mismatch and type-notice alerts pass `isArray` to the display
helper.
- Bundle `Policies.utils` + `Policies.types` + `sql-policy-mutation`,
`PolicyEditorModal`, and `SchemaGraph` here because `SidePanelEditor`
consumes `acceptGeneratedPolicy`/`AcceptedGeneratedPolicy` — splitting
requires temporary overloads with no architectural payoff.
## Additional context
Part of the SafeSql migration stack. Stacks on top of #45897.
### Manual test checklist
Surfaces touched by this PR — please exercise each:
**Table editor**
- [x] Create a new table with a mix of column types (scalar, array,
enum, foreign key)
- [x] Add a column to an existing table; verify the type dropdown lists
scalars + arrays separately and shows schema-qualified names for
non-public enums
- [x] Edit an existing column's type (scalar ↔ array, switch between
enums in different schemas) and save
- [x] Verify enum types from a non-public schema (e.g.
`private.my_enum`) display as `private.my_enum` in the column list
**Foreign key selector**
- [x] Open the FK selector for a column and pick a target column with a
matching type — no mismatch warning
- [x] Pick a target column whose type differs only by schema (two
same-named enums in different schemas) — should show a type-mismatch
alert
- [x] Pick a target column where one side is an array and the other is a
scalar of the same base type — should show a type-mismatch alert (no
auto-cast across array boundary)
- [x] When FK target sets the column type, verify `format_schema` and
`isArray` are preserved on the source column
- [x] Type-mismatch and type-notice alert messages render array types as
`type[]` (not `_type`)
**Column list / table list**
- [x] Schema-qualified type names display correctly for columns whose
type lives in a non-public schema
- [x] Array columns display as `type[]` (or `schema.type[]`)
**Policies (bundled due to import dependency)**
- [x] Open the Policies page; create/edit/delete a row-level policy via
the modal
- [x] Generate a policy via the AI assistant and accept it through
`SidePanelEditor` — verify the accepted policy lands in the editor
correctly
**Schema visualizer**
- [x] Open the Schemas → Schema Visualizer page; verify it renders
without type errors and shows tables/relationships
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Improvements**
* Support for column types in non-public schemas and richer column type
presentation (includes schema and array info).
* Stronger SQL safety around policies and constraints; draft policy SQL
is now promoted explicitly on save.
* Improved foreign-key type validation and compatibility checks using
enhanced type metadata.
* **Tests**
* Updated snapshots and tests to reflect new column metadata and SQL
fragment handling.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45903)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Closes#45850.
## Summary
`TriggerList`, `EventTriggerList`, and `FunctionList` built cross-links
between the database triggers and functions pages by interpolating
user-controlled identifiers directly into the URL query string. A
function or schema name containing `&`, `=`, `+`, or `#` corrupted the
destination filter and routed users to the wrong row.
Adds `getDatabaseFunctionsHref` to `TriggerList.utils` (used by both
`TriggerList` and `EventTriggerList`) and a new
`getDatabaseTriggersHref` in `FunctionList.utils`, both with
`encodeURIComponent` wraps. Replaces the three inline interpolations.
`FunctionList` only has a single search param (no schema) because the
link filters by function name only, so its helper takes one less
argument.
Same pattern as #45385.
## Test plan
Added `TriggerList.utils.test.ts` covering `getDatabaseFunctionsHref`
(plain, special chars in name, special chars in schema, both, undefined
inputs) and `FunctionList.utils.test.ts` covering
`getDatabaseTriggersHref` (plain, special chars in name, plus signs and
spaces, undefined inputs). Nine tests total.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Improved database navigation links for functions and triggers by
standardizing link generation through centralized utility functions.
This ensures consistent URL encoding and parameter handling across the
application.
* **Tests**
* Added comprehensive test coverage for database navigation link
utilities, including edge cases with special characters and empty
parameters.
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45851)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Refactor / dependency cleanup.
## What is the current behavior?
`apps/studio` lists both `@supabase/pg-meta` (workspace package) as a
runtime dep and `@supabase/postgres-meta` (external npm package,
`^0.64.4`) as a devDependency. The external package is used only for
type imports across 44 files — there is no runtime usage and no codegen
pipeline that needs it.
## What is the new behavior?
Every `Postgres*` type import (`PostgresTable`, `PostgresColumn`,
`PostgresPolicy`, `PostgresTrigger`, `PostgresView`,
`PostgresMaterializedView`, `PostgresForeignTable`, `PostgresSchema`,
`PostgresPublication`, `PostgresRelationship`, `PostgresPrimaryKey`) is
replaced with its `PG*` counterpart from `@supabase/pg-meta`, and the
external dep is removed from \`apps/studio/package.json\`. Top-level
type re-exports were added to \`packages/pg-meta/src/index.ts\` so
consumers can import directly from the package root.
Two latent issues surfaced by the stricter pg-meta types are also fixed:
- \`data/foreign-tables/foreign-tables-query.ts\` was casting
foreign-table results as \`PostgresView[]\`; corrected to
\`PGForeignTable[]\`.
- \`pg-meta\`'s \`PGTrigger\` Zod schema declared
\`orientation\`/\`activation\` as \`z.string()\`, inconsistent with
pg-meta's own \`getDatabaseTriggerUpdateSQL\` helper that requires the
narrow literal unions; tightened to \`z.enum\`.
## Additional context
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Updated internal TypeScript type definitions across the codebase to
use the latest type system from `@supabase/pg-meta`.
* Removed `@supabase/postgres-meta` dependency.
* Enhanced type validation for database triggers and schemas to enforce
stricter constraints.
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45844)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
We have multiple `Collapsible` components.
## Solution
Reduce their number by using only the one from shadcn.
I haven't noticed any visual nor functional changes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Migrated expandable/collapsible UI to a unified shadcn-based
implementation for more consistent expand/collapse behavior across the
app.
* **Style**
* Updated listbox check icon sizing and removed obsolete collapsible
open/close animations.
* **Chores**
* Removed deprecated collapsible variants and consolidated UI component
surface for simpler maintenance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
On the Database Extensions page, the Version column takes up
disproportionate horizontal space compared to the Description column,
making the table harder to read.
## Fix
Added `w-28` to the Version `TableHead` and its corresponding
`TableCell` in `ExtensionRow`. This constrains the column to a width
appropriate for short version strings and gives the Description column
more room.
## How to test
1. Open a project in Studio and navigate to Database > Extensions.
2. Confirm the Version column is now narrow and the Description column
has proportionally more space.
3. Verify all version strings are still fully visible (e.g. `1.4.8`,
`2.5.2`).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Fixed the "Version" column and its cells to a consistent width for
improved table alignment.
* Preserved monospace styling and spacing in version cells for
readability.
* Removed the max-width limit on comment text cells so comments can use
more space while retaining hover tooltips for full text.
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45786)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Problem
The dashboard renders all timestamps in the browser's local timezone.
When debugging app issues, users often want to see logs and timestamps
in a different timezone (e.g. their app's deployment region) without
changing their OS clock.
## Fix
- New Timezone submenu in the user-avatar dropdown, sitting next to the
existing Theme picker. Search-as-you-type combobox over the full IANA
catalog plus an Auto detect option.
- Selection persists in localStorage (`supabase-ui-timezone`) and
survives `clearLocalStorage()`. No backend schema change.
- New `lib/datetime.tsx` exposes pure timezone-aware formatters
(`formatDateTime`, `formatDate`, `formatTime`, `formatFromNow`,
`toTimezone`) plus a `TimezoneProvider` and matching React hooks
(`useTimezone`, `useFormatDateTime`, ...). The pure functions take `tz`
explicitly so they're easy to unit test (17 vitest cases covering DST
transitions, multi-tz formatting, unix-micro/Date inputs, invalid-tz
fallback).
- The selected timezone propagates to every existing `<TimestampInfo>`
in Studio via a new `TimestampInfoProvider` context exported from
`ui-patterns`. No per-callsite changes needed for those ~20+ surfaces.
- The `UnifiedLogs` date column migrates off `date-fns` to the new
`useFormatDateTime` hook (the rest of the date-fns callers stay as-is,
since they're either internal range math or non-display).
- `ALL_TIMEZONES` (~600 entries) moves out of `PITR.constants.ts` into a
shared `lib/constants/timezones.ts`. PITR keeps a re-export shim so its
callers don't move. New `TIMEZONES_BY_IANA` dedupes the catalog by
primary IANA name (the original list contains both PDT and PST rows for
`America/Los_Angeles`, etc.) and `findTimezoneByIana` provides reverse
lookup.
- Telemetry: `timezone_picker_clicked` PostHog event with
`previousTimezone`, `nextTimezone`, `isAutoDetected` properties.
Notes for reviewers:
- Bare `dayjs(x).format(...)` calls (~157 files) intentionally still
render in browser-local time. Surfaces opt in by switching to the new
wrappers, so this PR is the abstraction plus logs adoption; broader
migration is a follow-up.
- Two `// prettier-ignore` lines (`apps/studio/pages/_app.tsx`,
`apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx`)
work around a pre-existing local-tooling issue where
`prettier-plugin-sql-cst` strips angle-bracket type arguments under
certain conditions. Project's pinned prettier (3.8.1) does not strip;
the issue surfaces with a globally-installed prettier. Worth tracking
separately.
- Hydration: `guessLocalTimezone()` and `useLocalStorageQuery` are
client-only. Studio is mostly CSR via the Pages Router, but any SSR'd
`<TimestampInfo>` may briefly render in the server's tz before client
hydration. Existing behavior already had this mismatch with `.local()`;
this PR does not regress it.
- Backend timestamps round-tripped through query params and mutations
stay UTC. The picker is display-only.
## How to test
- Run `pnpm dev:studio`, sign in.
- Open the user avatar dropdown (top right). Hover Timezone.
- Search for "tokyo", pick `(UTC+09:00) Osaka, Sapporo, Tokyo`.
- Open any project, navigate to Logs (e.g. `Project > Logs > Edge
Functions`). Hover a log row's timestamp; the popover should show UTC,
the chosen tz (`Asia/Tokyo`), and the relative time. Visible cell text
should be in JST.
- Visit any page that uses `<TimestampInfo>` (Database > Backups,
Project Pause state, Edge Function details). Same tooltip should reflect
Asia/Tokyo.
- Refresh the page; timezone is still Asia/Tokyo.
- Reopen the picker, choose Auto detect; timestamps revert to browser
local.
- Run `pnpm --filter studio test lib/datetime.test.ts`. 17 tests should
pass.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Timezone selector added to the user menu with auto-detect and manual
override
* App-wide timezone provider and hooks plus a shared timezone catalog
for consistent timezone-aware display
* Timestamp components accept an optional timezone prop and respect user
preference (persisted)
* **Bug Fixes / Improvements**
* Logs and timestamp displays now use the new timezone formatting hooks
* **Tests**
* Added comprehensive datetime and timezone catalog tests
* **Telemetry**
* Telemetry event added for timezone picker interactions
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Context
Just refactors Database publications pages to use the `PageLayout`
component, otherwise was missing a header currently
Also fix search results empty state for publications pages
### Before
<img width="1144" height="550" alt="image"
src="https://github.com/user-attachments/assets/e178d31a-313e-48f3-a87c-bf26b13fef9e"
/>
### After
<img width="1145" height="396" alt="image"
src="https://github.com/user-attachments/assets/82a29529-46d8-40eb-ad25-9b424995e89e"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a dedicated shimmering skeleton for publications table loading
states
* **Refactor**
* Restructured Publications interface for unified table rendering
* Unified loading, error, empty and "missing selection" states into the
table
* Moved empty-results to render inside the table
* Removed the back-navigation button
* Page layout and section structure refactored for clearer spacing and
navigation
* **Style**
* Improved loading visuals with skeleton rows
* Updated empty-results styling for a cleaner table appearance
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Wires the existing `list-page.*` shortcuts up to the Database →
Replication and Database → Migrations pages, so they get the same hotkey
behavior as Roles, Tables, Publications, etc. No new shortcut IDs were
added.
**Migrations page**
- Shift+F → focus the migration search input (label: "Search
migrations")
- F C → clear the search filter
**Replication / Destinations page**
- Shift+F → focus the destinations filter input (label: "Search
destinations")
- F C → clear the filter
- Shift+N → open the Add Destination panel. Wrapped with `<Shortcut>` so
the keybind tooltip shows on hover, and gated on
`!!newDestinationDefaultType` so it stays disabled when no destination
type is available.
Closes
[FE-3141](https://linear.app/supabase/issue/FE-3141/add-shortcuts-for-database-replication-and-migration-page).
## Test plan
- [x] On the Migrations page, press Shift+F → search input focuses &
selects existing text.
- [x] On the Migrations page, type a query then press F C → search
clears.
- [x] On the Replication page, press Shift+F → filter input focuses &
selects.
- [x] On the Replication page, press Shift+N → Add Destination panel
opens (when a destination type is available).
- [x] Hover the "Add destination" button → keybind tooltip shows
Shift+N.
- [x] On the Replication page, type a filter then press F C → filter
clears.
- [x] All four shortcuts appear in Cmd+K under "Shortcuts" while on the
respective page.
- [ ] Disabling list-page shortcuts in Preferences disables them on
these pages too.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added keyboard shortcuts for search field focus and filter reset in
Database Migrations and Destinations pages
* Added keyboard shortcut for "Add destination" action in Destinations
page
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Mark provenance of SQL via the branded types SafeSqlFragment and
UntrustedSqlFragment. Only SafeSqlFragment should be executed;
UntrustedSqlFragments require some kind of implicit user approval (show
on screen + user has to click something) before they are promoted to
SafeSqlFragment.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Editor and RLS tester show loading states for inferred/generated SQL
and include a dedicated user SQL editor for safer edits.
* **Refactor**
* Platform-wide SQL handling tightened: snippets and AI-generated SQL
are treated as untrusted/display-only until promoted, improving safety
and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds the `O→S` / `Shift+F` / `Shift+N` / `F→C` shortcut set across every
Database listing page, on top of the schema-visualizer pattern from
#45386. Fixes [FE-3131](https://linear.app/supabase/issue/FE-3131).
A shared `list-page.*` registry replaces what would have been a per-page
registry file for each route, and `useShortcut`/`Shortcut` gain a
`label` override so a single ID renders contextually in Cmd+K and hover
tooltips.
## Pages and shortcuts
| Page | `O→S` | `Shift+F` | `Shift+N` | `F→C` |
| --- | --- | --- | --- | --- |
| Tables | Open schema selector | Search tables | Create new table |
Clears search + entity-type filter |
| Functions | Open schema selector | Search functions | Create new
function | Clears search + Return Type + Security |
| Triggers — data | Open schema selector | Search triggers | Create new
trigger | Clears search + Table filter |
| Triggers — event | — | Search event triggers | Create new event
trigger | Clears search + Owner filter |
| Enumerated Types | Open schema selector | Search enumerated types |
Create new enumerated type | Clears search |
| Indexes | Open schema selector | Search indexes | Create new index |
Clears search |
| Roles | — | Search roles | Add new role | Clears search + filter type
→ 'all' |
| Publications | — | Search publications | — | Clears search |
| Extensions | — | Search extensions | — | Clears search |
`Shift+N` only fires when the page-specific gate allows it (permission +
unlocked schema + any other prerequisite like `hasTables` for triggers).
## Test plan
- [ ] On each of the 9 pages, all four shortcuts behave as listed in the
table above
- [ ] Hover the wrapped controls — tooltip shows the page-specific label
and the right keybind badge
- [ ] Open the schema selector via `O→S` — the hover tooltip is
suppressed while the popover is open
- [ ] `Shift+N` is a no-op on locked schemas / when permission is
missing / when a prereq fails (e.g. no tables on Triggers data)
- [ ] `Shift+F` focuses the search input; while focused, Escape clears
(with text) → Escape blurs (when empty)
- [ ] `Cmd+K` shows the page-specific shortcut name while on each page;
the entry goes away on navigation
- [ ] `Mod+/` reference sheet shows one "List pages" group with 4
generic entries
- [ ] No regression to existing schema-visualizer / table-editor /
SQL-editor shortcut behavior
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a dedicated list-page shortcut group in the shortcuts reference.
* **Improvements**
* Keyboard shortcuts across database list pages: focus & select search,
reset filters, and create-new-item shortcuts.
* Escape now clears/searches or blurs inputs to avoid accidental popover
closes.
* Create/new buttons respond to shortcuts when allowed; disabled actions
keep permission tooltips for clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
The Database tables filter has a smaller tap-target than its visual
container.
This was probably introduced when we added the Shadcn `input-group`.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Updated internal styling implementation for improved code
maintainability.
---
**Note:** This is a minor internal refactor with no visible changes to
user-facing functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Feature and design-system cleanup. Resolves DEPR-551.
## What is the current behavior?
Admonition supports several overlapping content shapes, but it
previously did not support a first-class success state or
description-only usage cleanly. Title-only usage was also possible,
which made some callouts read like floating headings without body copy.
Docs MDX Admonitions could also pick up prose spacing around rich
children, while the design-system Tailwind config emitted an
ESM/CommonJS warning in the design-system app.
## What is the new behavior?
Adds a `success` Admonition type, description-only support, and a
stricter content contract: `title` or legacy `label` now requires either
`description` or `children`. Existing title-only Studio callsites have
been converted to description-only callouts.
The design-system docs now include examples for description-only and
success Admonitions, plus guidance for `title`, `description`,
`children`, and legacy `label` usage.
This also tightens Admonition body spacing so rich MDX children keep
docs link/code styling without inheriting excessive prose margins, and
renames the design-system Tailwind config to `tailwind.config.cjs` so it
matches its CommonJS syntax.
Warning and destructive alerts now explicitly set `text-foreground`,
preventing nested Admonition titles from inheriting muted
form-description colour after the Tailwind v4 cascade changes.
| Before | After |
| --- | --- |
| <img width="1818" height="388" alt="Image"
src="https://github.com/user-attachments/assets/283a1853-348a-4d74-a408-013957350e5e"
/> | <img width="1380" height="462" alt="Image"
src="https://github.com/user-attachments/assets/e5761e8e-3697-423b-805b-45110205099a"
/> |
| <img width="1398" height="550" alt="CleanShot 2026-04-28 at 15 12
41@2x"
src="https://github.com/user-attachments/assets/982694d9-5461-4362-8bae-a6e2b4c60e8b"
/> | <img width="1402" height="450" alt="CleanShot 2026-04-28 at 15 13
09@2x"
src="https://github.com/user-attachments/assets/0b1257c4-6b58-4c39-a182-4861a9e378ee"
/> |
| <img width="1640" height="716" alt="CleanShot 2026-04-28 at 15 17
25@2x"
src="https://github.com/user-attachments/assets/a5be4d5f-2bf7-4dc2-b396-56129fe64ec9"
/> | <img width="1630" height="716" alt="CleanShot 2026-04-28 at 15 16
00@2x"
src="https://github.com/user-attachments/assets/0d589252-aaf8-4efc-9d81-15ec4f99ec61"
/> |
| Design System Docs |
| --- |
| <img width="1646" height="1864" alt="CleanShot 2026-04-28 at 14 59
15@2x"
src="https://github.com/user-attachments/assets/12d13595-8972-4fb2-a04a-fb916388ebb6"
/> |
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a "success" admonition variant and new example previews
demonstrating success and description-only usages.
* **Documentation**
* Clarified admonition guidance: when to use title vs description vs
children; added example sections for short callouts and success
messages.
* **Refactor**
* Standardized UI by moving short/advisory text into description across
the app and harmonized trailing punctuation.
* **Style**
* Ensured warning/destructive admonitions use consistent foreground text
styling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds the first batch of keyboard shortcuts for the Database → Schema
Visualizer page, following the registry pattern established for the SQL
editor and table editor.
Fixes [FE-3115](https://linear.app/supabase/issue/FE-3115).
## Shortcuts
| Shortcut | Action |
| --- | --- |
| `Mod+Shift+C` | Copy schema as SQL |
| `Mod+Shift+M` | Copy schema as Markdown |
| `D` then `P` | Download schema as PNG |
| `D` then `S` | Download schema as SVG |
| `O` then `A` | Open the auto-layout confirmation dialog |
| `O` then `S` | Open the schema selector |
All six entries appear in the Cmd+K command menu under "Shortcuts" and
in the global shortcuts sheet (`Mod+/`) under a new "Schema Visualizer"
group while the page is mounted. None are surfaced in Account →
Preferences yet (`showInSettings: false`), matching how the SQL/table
editor batches shipped.
The schema selector and auto-layout button are wrapped in the unified
`Shortcut` component so the keybind is shown on hover (Linear-style).
The dropdown items for copy/download don't get hover hints since
tooltips on dropdown items don't make sense — they're discoverable via
Cmd+K instead.
## Toasts
Each user-visible action now confirms via a sonner toast:
- `Successfully copied as SQL` — fires on Copy as SQL (button or
`Mod+Shift+C`).
- `Successfully copied as Markdown` — fires on Copy as Markdown
(dropdown or `Mod+Shift+M`).
- `Successfully downloaded as PNG` / `Successfully downloaded as SVG` —
already present in `useExportSchemaToImage`; fires on click or `D → P` /
`D → S`.
- `Failed to download current view: …` — error toast on download failure
(also pre-existing).
## Notes
- `Mod+Shift+C` and `Mod+Shift+M` collide with the SQL editor's
`results.copy-csv` / `results.copy-markdown` shortcuts. They coexist
cleanly because `useShortcut` only fires while the hook is mounted, and
the two pages live on different routes. Both labels appear in the global
shortcuts sheet honestly scoped per surface.
- `SchemaSelector` was promoted to a `forwardRef` component that spreads
extra props onto its outer `<div>`. This was needed for `<TooltipTrigger
asChild>` to attach event handlers and the ref properly — previously
they were silently dropped and the hover tooltip didn't render.
- `SchemaSelector` and the auto-layout `AlertDialog` accept controlled
`open` props now so the shortcuts can drive them and the tooltip can be
suppressed while the popover/dialog is open (`Shortcut` gained a
`tooltipOpen` passthrough for this).
- Auto-layout still pops the existing confirmation dialog rather than
running directly — destructive enough to keep the guardrail.
## Test plan
- [x] On the Schema Visualizer page, each of the six shortcuts fires the
corresponding action.
- [x] Hover the schema selector and the Auto layout button — tooltip
shows the action label and keybind badge.
- [x] Open the schema selector popover (click or `O → S`) — hover
tooltip is suppressed while open.
- [x] Open the auto-layout confirm dialog (click or `O → A`) — hover
tooltip is suppressed while open.
- [x] Cmd+K shows all six entries under "Shortcuts" while on the page;
navigating away unregisters them.
- [x] `Mod+/` shortcuts sheet has a "Schema Visualizer" group listing
all six.
- [x] Copy as SQL / Markdown each fire a confirmation toast; PNG / SVG
downloads each fire a confirmation toast.
- [x] On the SQL editor results page, `Mod+Shift+M` / `Mod+Shift+C`
still copy results (no regression from the duplicate keybinds).
- [x] The download dropdown items still work via click; PNG/SVG
downloads succeed.
- [x] All other consumers of `SchemaSelector` (~15 callsites) render
unchanged after the `forwardRef` promotion.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Keyboard shortcuts for schema visualizer: copy as SQL/Markdown,
download PNG/SVG, auto-layout, and focus selector
* Success toasts when copying content to clipboard
* **Improvements**
* Schema selector and auto-layout dialog can be opened/closed via
keyboard and programmatically
* Shortcut tooltips can be suppressed when related overlays/dialogs are
open
* Schema Visualizer added to the shortcuts reference sheet
* **Tests**
* E2E tests dismiss transient toasts to avoid UI interference
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved rendering and visual consistency of exported database schema
images by refining font handling during SVG and PNG export processes.
Schema diagrams now display with better visual fidelity when exported.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR migrates the whole monorepo to use Tailwind v4:
- Removed `@tailwindcss/container-queries` plugin since it's included by
default in v4,
- Bump all instances of Tailwind to v4. Made minimal changes to the
shared config to remove non-supported features (`alpha` mentions),
- Migrate all apps to be compatible with v4 configs,
- Fix the `typography.css` import in 3 apps,
- Add missing rules which were included by default in v3,
- Run `pnpm dlx @tailwindcss/upgrade` on all apps, which renames a lot
of classes
- Rename all misnamed classes according to
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities in all
apps.
---------
Co-authored-by: Jordi Enric <jordi.err@gmail.com>