Commit Graph
3006 Commits
Author SHA1 Message Date
Saxon Fletcher d0135231fb chore(studio): improve assistant feed performance (#50888)
## Problem

Assistant conversations with multiple query and Edge Function blocks
repeatedly render expensive content while streaming. Scrolling past the
feed boundary can also move the surrounding layout.

## Solution

Memoize unchanged messages, blocks, and code highlighting; batch
streaming UI updates; and skip off-screen query layout while keeping
block state mounted. Preserve streamed status updates and contain
scrolling in the message viewport. The changes are shared by Next and
TanStack.

## Review instructions

1. Compare the base branch and this branch using the same saved
conversation containing 10–20 query, result/chart, and Edge Function
blocks. Keep the browser, viewport, and conversation identical.
2. In Chrome DevTools, record Performance with 4× CPU throttling while
streaming a follow-up, typing in the composer, and scrolling through the
feed. Compare scripting/layout time and long tasks. React DevTools
Profiler should show unchanged completed blocks avoiding renders during
subsequent text updates.
3. Scroll away from query blocks and return. Confirm results, display
settings, selections, and controls retain their state. Run a read-only
query such as `select 1` and check its results still update.
4. Confirm “Thinking…” finishes, Stop retains the latest streamed text,
and approval/skip, copy, edit, and branch actions still work. Repeated
scrolling at the feed boundary must leave the outer layout/composer
stationary; jump-to-latest and following new messages should still work.
5. Repeat in both runtimes: `STUDIO_FRAMEWORK=next pnpm dev:studio` and
`STUDIO_FRAMEWORK=tanstack pnpm dev:studio`. Also check the assistant
sidebar, which shares the feed.

## Validation

- 170 assistant/Explorer tests and one shared CodeBlock test passed;
formatting and Studio lint passed (two existing warnings).
- Browser checks covered both route entry points, viewport
state/geometry, and scroll behavior.
- Review fixes: 23 focused tests, lint, formatting, and full Studio
typechecking passed. Full production builds were not verified.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] No docs content changed; docs authoring skills are not applicable.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Performance**
* Improved responsiveness in the AI assistant by reducing unnecessary
updates while messages stream and conversation history is displayed.
* Optimized query previews, message rendering, and code blocks to keep
the interface smoother during use.
* **Bug Fixes**
* Improved handling of message edits and deletions during generation,
and preserved the latest response when generation is stopped.
* Improved conversation scrolling behavior while keeping conversation
content and scroll areas working as expected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-25 18:09:34 +10:00
Alaister YoungandAlaister Young a47397d5fe fix(common): restore narrow Feature type (#50850)
The platform API now types `ProfileResponse.disabled_features` as
`string[]` (since #48981), which collapsed the `Feature` union to plain
`string`, so `isFeatureEnabled` accepted any string and typos went
uncaught.

**Changed:**
- `Feature` is now a local `RuntimeFeature` union (the profile-driven
flags) plus the keys of `enabled-features.json`, instead of deriving
from the API type
- `useIsFeatureEnabled` casts the merged runtime disabled list to
`Feature[]`, since the profile field is now `string[]`

The runtime feature list duplicates what the backend knows. Once the
enum is restored in the API spec, `Feature` can go back to deriving from
the generated type.

## To test

- `pnpm typecheck` passes
- Passing a bogus string to `useIsFeatureEnabled` / `isFeatureEnabled`
is now a type error
- Nothing behavioral changes, so a quick sanity check that the sidebar /
billing / org settings still render is enough


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **No user-facing changes**
* This update does not change the app’s visible features or behavior. It
includes internal typing adjustments only.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-09-25 17:21:38 +10:00
+8 e273d2b818 chore(studio): move Explorer SQL Editor link to sidebar footer (#50829)
## What

- Moves the temporary "Switch to SQL Editor" button out of the Explorer
sidebar header into a footer section ("Looking for snippets?") with a
short explanation and an **Open SQL Editor** button.
- Replaces the header slot with a menu for the Explorer startup
preference (**Start page** / **SQL query**), instead of linking out to
account preferences.

## How to test

1. Enable the Explorer feature preview and open
`/project/<ref>/explorer`.
2. **Header menu:** click the ⋮ button next to the Explorer title. Pick
**SQL query**, then check that **Explorer startup** on `/account/me`
shows the same value (and vice versa).
3. **Footer:** click **Open SQL Editor**. You should land in the SQL
Editor with the **Back to Explorer** button in its title bar.
4. Open **Notebooks** or **Chats** in the sidebar and check that the
menu and footer are hidden there, like the old button was.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Choose whether the Explorer opens to the Start page or SQL query from
the Explorer preferences menu. Your selection is saved and retained when
you reopen the menu.
  * Access the SQL Editor from the Explorer’s sidebar footer.
* Explorer preferences are available from the Explorer navigation
header.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Tyler <dshukertjr@gmail.com>
Co-authored-by: Nik Richers <nrichers@gmail.com>
Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Jordi Enric <37541088+jordienr@users.noreply.github.com>
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
Co-authored-by: Katerina Skroumpelou <mandarini@users.noreply.github.com>
Co-authored-by: Franek <franek@ferly.co.uk>
Co-authored-by: Franek Richardson <franek@supabase.io>
Co-authored-by: Michał Olszewski <35968924+charconstpointer@users.noreply.github.com>
Co-authored-by: Steven Eubank <47563310+smeubank@users.noreply.github.com>
Co-authored-by: Anthony Lio <lionnet.ant@gmail.com>
Co-authored-by: Joey Lei <6957385+leizerbeam@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
Co-authored-by: Samir Ketema <6003000+samirketema@users.noreply.github.com>
Co-authored-by: K-Dog (Kevin) <k.grueneberg1994@gmail.com>
2026-09-25 14:31:46 +08:00
K-Dog (Kevin) a5ad2ce745 feat: log query/ingest insights (#50570)
We are doing a soft rollout for log pricing including log ingest and log
querying. We currently only want to display usage/soft warnings, which
is why the metrics are filtered out in some components.
2026-09-25 11:45:46 +08:00
Joey Lei d15634ae58 updated guidance for logs pricing, revised manage logs usage pages, introduction of the grace period(#49656)
Supabase Logs is moving to usage-based pricing. We're announcing changes early so impacted projects have time to adjust before billing begins when the grace period through early 2027.
2026-09-24 10:27:23 -07:00
Gildas Garcia 202ca5c7a4 Design System: Fix Select does not show its invalid state correctly (#50859)
## Problem

`Select` does not show its invalid state correctly.
Change anything in [the production form
example](https://supabase.com/design-system/docs/ui-patterns/forms) and
submit;

<img width="982" height="401" alt="image"
src="https://github.com/user-attachments/assets/3b0382e2-b77a-4f79-a9c6-3fa2d7c671a3"
/>

## Solution

Apply the same styles as for the other inputs:

<img width="887" height="354" alt="image"
src="https://github.com/user-attachments/assets/d05e12e1-171e-4e5c-8b49-2ea54ab79ce3"
/>

## Review instructions

Change anything in [the staging form
example](https://design-system-ah0n5wasc-supabase.vercel.app/design-system/docs/ui-patterns/forms)
and submit

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Select triggers now display destructive error styling when marked as
invalid, including updated background and border colors.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-24 14:43:50 +02:00
Joshen Lim 0d3b73794b Joshenlim/fe 4465 experiment with best available region selection (#50851)
## Context

Adds a "Best available region" option in the region selector for the
project creation form
- Should only show up for free plan organizations (will be selected as
the default option instead of the recommended option from GET
`/available-regions`)
- "Recommended" badges will also be hidden in this scenario
- Behaviour should be status quo for non free plan organizations
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/de0a183d-37c0-445d-98ca-c5aaf6353e73"
/>

## To test
Important to ensure that project creation still behaves as per usual
- [ ] Free plan: Creating a project with "best available region" select
creates the project if the recommended region from GET
`/available-regions`
- A quick way to check this is to swap to a paid org and see the
"recommended" general region
- [ ] Free plan: Can also create a project with other regions selected
as per usual
- [ ] Non free plan: Can create project as per usual
- [ ] Verify that everything is status quo if configcat feature flag is
off
2026-09-24 17:38:58 +08:00
Danny White 3ec44e54d2 fix(ui): restore grouped textarea top padding (#50843)
## Problem

- Grouped textareas render text too close to the top border. The
standalone `Textarea` retains its normal top padding.
- `InputGroupTextarea` has overridden the inner textarea with `py-0`
since #44282 on 31 March 2026. The group initially supplied `py-2`;
#44387 removed that outer padding later the same day, leaving no top
spacing. The override was retained during the border and surface updates
in #44690 and #48887.
- The textarea component page had no grouped textarea example, so this
difference was easy to miss.

## Solution

- Give the grouped textarea `pt-2` while retaining `pb-0` for block-end
addons. The outer group continues to own its border and focus styles.
- Add a “With addon” example to the textarea component page to keep this
composition visible.

## Review instructions

- Open `/design-system/docs/components/textarea` and find “With addon”.
Confirm its text has the same top inset as the default textarea and the
block-end addon remains below the input.
- Open `/design-system/docs/ui-patterns/forms` and find “Textarea
Multi-line text input for longer content with addon”. Confirm the text
has the same top inset as the standalone textarea example immediately
above it.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a textarea example with a supporting addon below the text field,
showing a 120-character limit.
* **Bug Fixes**
* Clicking a block-end addon now focuses its textarea as well as an
input.
* **Documentation**
* Added the textarea-with-addon example to the textarea component
documentation.
* **Style**
* Added top padding to textareas within input groups while keeping
bottom padding unchanged, adjusting spacing above entered text.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-24 17:10:28 +10:00
Danny White ddd6711b0d fix(ui): darken light warning fill for readable white text (#50834)
## Problem

Light-mode warning buttons used dark text on an amber that sat above the
on-fill contrast flip, so the label read poorly.

## Solution

Lower light `--warning-lightness` from `0.68` to `0.55` so the shared
foreground formula picks light ink at ~4.5:1.

| Figure |
| --- |
| <img width="1052" height="134" alt="CleanShot 2026-09-24 at 15 39
55@2x"
src="https://github.com/user-attachments/assets/e07f9ffc-dc3f-4e3f-8bce-f993302da61e"
/> |
| _Currently on `master`_ |
| <img width="1040" height="148" alt="CleanShot 2026-09-24 at 15 35
42@2x"
src="https://github.com/user-attachments/assets/199b0303-87cc-43bb-b518-784938911dd2"
/> |
| _Non-compliant (poor contrast) text colour inversion only_ |
| <img width="1054" height="168" alt="CleanShot 2026-09-24 at 15 36
02@2x"
src="https://github.com/user-attachments/assets/c530a594-49de-40a9-a8ad-0208ed5192fc"
/> |
| _What this PR ships: proper contrast, matches other buttons_ |

## Review instructions

1. Open the [design system Button
page](https://design-system-git-dnywh-fix-warning-button-light-text-supabase.vercel.app/design-system/docs/components/button#variants)
on the preview in light mode.
2. Check a `warning` Button: deeper amber fill, light label.
2026-09-24 05:50:39 +00:00
Danny White f815e190be fix(ui): restore light-mode switch thumb contrast (#50828)
## Problem

After the primary colour split in #50697, the checked Switch thumb was
always `bg-black`. That reads well on `primary-bright` in dark mode, but
looks wrong in light mode where the knob should be white-ish.

## Solution

Use `bg-primary-foreground` for the checked thumb. That is the paired
content-on-primary token (near-white in light, dark surface in dark), so
it contrasts on the `primary-bright` track without hardcoding
black/white.

| Before | After |
| --- | --- |
| <img width="452" height="192" alt="CleanShot 2026-09-24 at 12 08
18@2x"
src="https://github.com/user-attachments/assets/43e6068b-f434-4260-87c2-3680e4fc87f0"
/> | <img width="410" height="198" alt="CleanShot 2026-09-24 at 12 07
50@2x"
src="https://github.com/user-attachments/assets/bf15075c-a4c6-4fa1-a14f-1e52b9b0cd4f"
/> |

## Review instructions

1. Open the
[Switch](https://design-system-git-dnywh-fix-switch-thumb-light-supabase.vercel.app/design-system/docs/components/switch)
page on the design-system deploy preview.
2. In light mode, turn a switch on: the knob should be white-ish on the
green track.
3. Switch to dark mode and turn a switch on: the knob should be dark on
the green track.
2026-09-24 14:41:21 +10:00
Danny White d067e81a69 fix(ui): align primary colours across text, buttons, and controls (#50697)
## Problem

Primary colour serves readable text and selected controls, but those
uses need different shades. Light mode needs darker text, while dark
mode needs a deeper button fill. Fixed brand green on interactive chrome
also prevents a custom primary hue from carrying through the interface.
Some slider tracks and selected text are hard to read.

## Solution

- Keep `--primary` for accessible text and small selected indicators.
Use `--primary-solid` for button fills, which need a deeper shade in
dark mode.
- Add `--primary-bright` for focus rings, selected control chrome, chart
accents, and other interactive highlights. It follows `--primary-hue`;
`brand-*` stays fixed for Supabase identity.
- Make slider troughs clearer and text selection translucent with theme
foreground text.
- Document the split in the design-system colour guide.

| Before | After |
| --- | --- |
| <img width="980" height="244" alt="Before: light mode primary
controls"
src="https://github.com/user-attachments/assets/dfae325d-0dfe-4231-8bcd-3f89c4b9d793"
/> | <img width="982" height="204" alt="After: light mode primary
controls"
src="https://github.com/user-attachments/assets/5fdcb531-a6e3-4549-8a13-9d9a5ebe6e20"
/> |
| <img width="610" height="120" alt="Before: slider track"
src="https://github.com/user-attachments/assets/04f768e0-51e8-4d06-9b97-c52f4a34f122"
/> | <img width="622" height="126" alt="After: slider track"
src="https://github.com/user-attachments/assets/95127f4e-13dc-4f0f-b63c-cf5d70a28b42"
/> |
| <img width="652" height="512" alt="Before: dark mode controls"
src="https://github.com/user-attachments/assets/3f88de66-90cc-40ee-8cf1-b5f4eb87b09a"
/> | <img width="658" height="498" alt="After: dark mode controls"
src="https://github.com/user-attachments/assets/906bec30-6ca1-4614-9fb3-6cf5e5feec22"
/> |

## Review instructions

1. Compare light and dark mode in the [colour usage
guide](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#primary-and-brand-colors).
Check primary ink, primary-solid, primary-bright, and fixed brand
swatches.
2. In Studio, open the ‘new table’ sheet in [Table
Editor](https://studio-staging-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/dashboard/project/_/editor).
Tab through the new table sheet's fields and toggles. Check the focus
rings, selected controls, and the sheet's edges in both themes. You do
not need to save a table.
3. Select text in Studio in both themes, including a link or
primary-coloured label. The selection and text should remain legible.
4. Check the
[Field](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/field)
Price Range slider: the unused track should remain visible in both
themes. The selected field card border should follow primary-bright.
5. Check the
[Button](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/button)
and [Radio
Group](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/components/radio-group)
previews. In dark mode, `primary` button fill should be deeper than
primary
[text](https://design-system-git-dnywh-fix-bright-brand-chrome-supabase.vercel.app/design-system/docs/color-usage#text);
selected radios should remain readable.
2026-09-24 09:56:31 +10:00
Saxon FletcherandJoshen Lim 76c4f2b739 feat(studio): add time range to project logs filter bar (#50685)
<img width="1079" height="566" alt="image"
src="https://github.com/user-attachments/assets/b6f360e9-dfcf-4717-86db-1fc9acc4ae6a"
/>


## Problem

Project logs only exposed time-range selection through the sidebar.

## Solution

Add a Time range property to the filter bar using the sidebar’s picker,
preset labels, and date formatting. Keep it synchronized with the
sidebar and timeline, including retention checks.

## Review instructions

1. Open Project Logs and select **Time range** in the filter bar. Choose
a preset and confirm the sidebar and logs update.
2. Select a custom range and confirm its label matches the sidebar’s
date formatting.
3. Change the range in the sidebar or timeline and confirm the filter
bar updates. Remove the time-range pill and confirm the range resets
without removing other filters.
4. Select a range beyond the plan’s log retention and confirm the
upgrade prompt appears.

Validation: 67 focused tests passed, Studio typecheck passed, and
changed Studio files passed lint.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] Docs authoring skills (not applicable: no docs-site topic changes)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added human-readable filter value labels while preserving the
underlying selections.
* Added dedicated log time-range filters with preset and custom ranges.
  * Added inline date-range picker support for flexible layouts.
* Added controls to show or hide filter properties based on
availability.
  * Added consistent date-range formatting and custom range support.

* **Bug Fixes**
  * Invalid or incomplete time-range filters are no longer applied.
  * Time-range filters remain separate from standard column filters.
* Clipboard interactions are limited to the popover date-picker variant.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-24 09:29:59 +10:00
Joshen Lim 5b18f1d084 Allow setting null for connection pool size (#50726)
## Context

Allows users to "reset" the value for connection pool size in pooler
configuration under [database
settings](http://supabase.com/dashboard/project/_/database/settings)
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/4eb98088-1898-423c-8ef6-655fd0573601"
/>

Refer to the [Linear
ticket](https://linear.app/supabase/issue/FE-4425/support-setting-null-for-pool-size-in-pooler-config)
for more details about why this change is needed - its a bit of an
explanation 😅 🙏

## To test
- [ ] Verify that you can save a pool size, and that the GET
`/config/pgbouncer` network request returns `default_pool_size` property
in its response
- [ ] Verify that you can save while leaving the pool size input field
empty, and that the GET `/config/pgbouncer` network request thereafter
doesn't return `default_pool_size` in its response

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Preserved the intended database connection pool setting when no
default pool size is specified, rather than automatically applying a
compute-size-based value.
  * Explicitly entered pool sizes continue to be saved unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-24 01:12:19 +08:00
Saxon FletcherandClaude Opus 5.5 cf5f1545bd feat(studio): add notebook permissions to scoped access tokens (#50764)
## Problem

The Management API now has `/v2/projects/{ref}/notebooks`, gated by the
new `project_notebooks_read` / `project_notebooks_write` FGA
permissions. Studio pins `@supabase/shared-types` 0.1.95, which predates
them, so the scoped access token form can't grant them. Tokens created
with every permission selected still get `403 forbidden` on the notebook
endpoints.

## Solution

- Bump `@supabase/shared-types` to 0.1.96 (Studio and shared-data),
which publishes the notebook permissions.
- Add a **Notebooks** entry to the permission catalog (Project category,
next to SQL Snippets).
- Add minimum roles to `FGA_SCOPE_MINIMUM_ROLE`: read is `readonly`,
write is `developer`, matching the OpenFGA model.

The docs permission tables don't change yet. They're built from the
docs' checked-in v2 spec, which doesn't include the notebook endpoints,
so the row appears on the next spec sync.

## Review instructions

1. In the preview, go to **Account → Access Tokens** and create a scoped
token for a project. Check that **Notebooks** is listed under Project,
and set it to Read-write.
2. List notebooks with the new token:
   ```bash
curl -s -H "Authorization: Bearer $TOKEN"
"https://api.supabase.com/v2/projects/$REF/notebooks"
   ```
It should return `200` with `{ "links": ..., "data": [...] }`, not
`403`.
3. Optional: create a token with Notebooks set to None, repeat step 2,
and check it returns `403`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
  - Added project-level notebook permissions to access tokens.
- Access tokens can now grant read-only or developer-level access for
managing shared project notebooks.
- Project notebook permissions are displayed in the token creation
interface and supporting documentation.


<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:18:28 +08:00
Saxon FletcherandDanny White cd77bebafd chore(ui): refresh shared button styles (#50197)
## What kind of change does this PR introduce?

UI polish / design system: refreshed button styles, related token
tweaks, and a shared floating-button plate.

Resolves DEPR-652.

## What is the current behavior?

Default, primary, and secondary buttons use older fills, borders, and
hover treatments. Primary still leans on brand scale utilities. Default
fills don’t always read as raised chrome across surfaces, and floating
copy / expand / scroll controls can let busy content show through
translucent fills. Call sites hand-roll `rounded-* bg-background`
wrappers for that.

## What is the new behavior?

Refreshes primary, default, and secondary buttons with medium-weight
labels, subtle shadows and inset edges, and smoother transitions.
Light-mode default buttons use a raised fill with an accent hover state,
primary text is brighter, and inline keyboard shortcuts inherit the
button’s colour.

Adds `FloatingPlate`: an opaque `bg-popover` shell for floating default
buttons (and small clusters). Migrates Studio, Docs-related patterns,
www, and `ui-patterns` floaters onto it so busy content no longer shows
through translucent fills. Positioning, z-index, and hover/focus reveal
stay on the plate’s `className`. Use `rounded="full"` for pills.

Also:

- Moves primary onto semantic `--primary` / `--primary-hover` (with a
light-theme override) instead of brand utility fills
- Tokenises button shadows as `--button-shadow-drop` /
`--button-shadow-raised` / `--button-shadow-default` on the Button base
- Aligns hover direction: darken on light mode, lighten on dark mode for
both default and primary
- Default fill stays opaque `bg-card` in light (occlusion) and
translucent `bg-muted` in dark (adapts to the local surface)
- Documents fills and `FloatingPlate` on the design-system Button page
(with a live example)
- Scales shared radius tokens in Studio and www; medium+ Button sizes
use a proportionally softer radius
- Fixes www nav CTA centering (`lg:inline-flex` instead of `lg:block`)
- Query detail Expand/Collapse wires `aria-expanded` / `aria-controls`

| Before | After |
| --- | --- |
| <img width="1074" height="438" alt="CleanShot 2026-09-18 at 15 52
51@2x"
src="https://github.com/user-attachments/assets/ef43da21-b053-4b7e-9ac4-ab8b428228ab"
/> | <img width="1090" height="464" alt="CleanShot 2026-09-18 at 15 50
59@2x"
src="https://github.com/user-attachments/assets/2ddc55fc-4c8d-499c-a280-f3db3d99023c"
/> |
| <img width="1082" height="446" alt="CleanShot 2026-09-18 at 15 52
35@2x"
src="https://github.com/user-attachments/assets/3dd5452d-325a-4e4a-a79d-26c6c6950a31"
/> | <img width="1078" height="446" alt="CleanShot 2026-09-18 at 15 51
13@2x"
src="https://github.com/user-attachments/assets/93666385-3e6e-42e0-9891-9cd6bb935b67"
/> |

## To test

### Design system

- [Button
page](https://design-system-git-chore-button-styles-supabase.vercel.app/design-system/docs/components/button):
default / primary in light and dark; hover should darken on light,
lighten on dark
- Same page: [Floating over
content](https://design-system-git-chore-button-styles-supabase.vercel.app/design-system/docs/components/button#floating-over-content)
/ [Floating
plate](https://design-system-git-chore-button-styles-supabase.vercel.app/design-system/docs/components/button#floating-plate)
example; Copy over SQL should stay opaque
- Spot-check hover on a code preview Copy control

### Docs

[Docs deploy
preview](https://docs-git-chore-button-styles-supabase.vercel.app/docs):

- [Docs
homepage](https://docs-git-chore-button-styles-supabase.vercel.app/docs):
top-right **Sign up** / **Dashboard** primary; menu icon beside it
(default icon button)
- Shrink below `lg` and open the hamburger drawer: bottom **Sign in**
(default) + **Start your project** (primary) medium block buttons
- Tab once for **Skip to content** (FloatingPlate)
- [MCP
guide](https://docs-git-chore-button-styles-supabase.vercel.app/docs/guides/ai-tools/mcp):
project picker
- [Apple
login](https://docs-git-chore-button-styles-supabase.vercel.app/docs/guides/auth/social-login/auth-apple):
**Generate Secret Key** button in the Apple Secret Generator
- Optional opacity check: any guide code block Copy control (e.g. at the
bottom of [Import data into
Supabase](https://docs-git-chore-button-styles-supabase.vercel.app/docs/guides/database/import-data))

### Studio

[Studio deploy
preview](https://studio-staging-git-chore-button-styles-supabase.vercel.app/):

- **Observability → Query Performance**: open a query detail →
Expand/Collapse pill + SQL Copy chip (dark: no bleed-through)
- **Observability → Query Insights**: select a query → Clear query pill
- **Table Editor → any table → Definition** → floating **Open in SQL
Editor**
- **Connect → Framework → Add files**: Copy on the code tabs
(FloatingPlate; light hover follow-up is DEPR-694)
- Tab once for **Skip to content**

### WWW

- [www deploy
preview](https://zone-www-dot-com-git-chore-button-styles-supabase.vercel.app/):
nav Sign in / Start your project vertical centering; hero medium CTAs
radius

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-09-23 03:43:00 +00:00
Danny WhiteandJoshen Lim 05a45dd1ed feat(studio): rename Replication to Pipelines (#50637)
## What kind of change does this PR introduce?

Feature and docs update.

## What is the current behavior?

The Dashboard lists Pipelines destinations under Database > Replication.
Read replicas have moved to Infrastructure, but the temporary notices
remain on the destinations page and new destination sheet.

Closes PIPE-1021.

## What is the new behavior?

The canonical Dashboard routes are Database > Pipelines, while legacy
Replication list and detail URLs permanently redirect to the equivalent
Pipelines routes. Navigation, command palette, shortcuts, pipeline
links, docs, and current marketing copy use Pipelines. Read-replica
notices and their obsolete dismissal state are removed.

| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Replication Database Agua Basket
Supabase"
src="https://github.com/user-attachments/assets/53f9f565-1ed1-43e9-a7d9-b66b2a47e948"
/> | <img width="1024" height="759" alt="2540"
src="https://github.com/user-attachments/assets/14ab2d61-d01c-483f-9d4f-0ac286dae159"
/> |

The Management API, pipeline behaviour, replication logs, and Postgres
replication terminology remain unchanged.

## To test

- Open `/project/<ref>/database/pipelines` and confirm the Database
navigation, page header, and pipeline breadcrumb say Pipelines.
- Open
`/project/<ref>/database/replication?source=bookmark#destinations` and a
legacy pipeline detail URL. Confirm each redirects to the matching
Pipelines URL while preserving parameters and fragments.
- From the Pipelines page, open Add destination. Confirm no read-replica
migration notice appears.
- Open the Pipelines guide and confirm its Dashboard steps lead to
Database > Pipelines.

## Before merge

- [ ] Get changelog entry reviewed
https://github.com/supabase/changelog/pull/262 and prepare to merge
simultaneously

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added dedicated **Database > Pipelines** pages for pipeline lists and
details.
- Added permanent redirects from legacy Replication URLs to their
corresponding Pipelines pages.
- Read replica management links now open **Settings > Infrastructure**.

- **Documentation**
- Updated Pipelines setup, monitoring, troubleshooting, and usage
guidance to reference the current dashboard locations.
  - Updated Realtime guidance to use **Database > Publications**.

- **Updates**
- Renamed dashboard navigation, breadcrumbs, commands, and keyboard
shortcuts from **Replication** to **Pipelines**.
  - Removed the “Read replicas have moved” notification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-23 08:52:07 +10:00
Julian Domke f65ee588c1 feat(stripe-atlas): wire up redemption flow (#50575) 2026-09-22 17:57:40 +02:00
Danny White ec53175b8a refactor(ui): rename text-brand to text-primary (#50564)
## What kind of change does this PR introduce?

Refactor. Follow-up to #49871.

## What is the current behavior?

Branded (green) text still uses the `text-brand` classname while the
colour comes from `--primary`.

## What is the new behavior?

**Rename-only:** `text-brand` → `text-primary` across callsites and
docs. Leftover `bg-brand` / `border-brand` alias to `brand-default`. No
intentional colour changes in this PR.

This better matches how we treat our green in other components and
props, like `variant="primary"` for green buttons.

## To test

On light mode: smoke-test that branded text still looks like #49871
(readable green, not the bright fill).

-
[Homepage](https://zone-www-dot-com-git-dnywh-depr-316-text-brand-de2380-supabase.vercel.app/):
“Scale to millions” uses `text-primary`
- [Docs
homepage](https://docs-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/docs):
branded links like “More on self-hosting” are still readable
- [Typography
docs](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/typography):
documents `text-primary`
- [Colour
usage](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/color-usage):
`text-primary` is visibly darker than `bg-brand-default` _on light mode_
- [Studio auth
providers](https://studio-staging-git-dnywh-depr-316-text-brand-to-60fa6c-supabase.vercel.app/dashboard/project/_/auth/providers):
enabled provider badge text readable; status dot stays bright green

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Updated branded text, link hover states, icons, badges, indicators,
and highlighted content across the Design System, Docs, Studio, Learn,
UI Library, and marketing experiences to use the primary theme color.
* Updated syntax highlighting and table-of-contents styling for
consistent primary-color presentation.
  * Refined brand color fallback behavior for bright fills and borders.

* **Documentation**
* Updated color-usage and typography guidance to recommend the primary
text utility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 16:19:03 +10:00
Danny WhiteandJoshen Lim a31ca2bad0 fix(ui): make brand text readable across themes (#49871)
## What kind of change does this PR introduce?

Bug fix and design-system update. Resolves DEPR-316.

Follow-up rename (`text-brand` → `text-primary`) is in a dedicated PR
(https://github.com/supabase/supabase/pull/50564) stacked on this one.

## What is the current behavior?

`text-brand` resolves to the canonical bright brand green in places that
need readable text, which fails WCAG AA on light surfaces. A separate
`text-brand-display` token adds another green for large type.

## What is the new behavior?

- `text-brand` maps to accessible `--primary` (light mode darkened to
meet ~4.5:1 AA)
- `--hue` / `--primary-hue` aligned to 157.5
- `text-brand-display` removed; former display callsites use
`text-brand`
- Bright fills/borders stay on `brand-default`
- Design-system colour and typography docs updated

| Before | After |
| --- | --- |
| <img width="514" height="112" alt="CleanShot 2026-09-02 at 11 13
09@2x"
src="https://github.com/user-attachments/assets/4e0138a9-a32d-4e4c-a426-90736706e1e7"
/> | <img width="512" height="138" alt="CleanShot 2026-09-21 at 11 42
05@2x"
src="https://github.com/user-attachments/assets/164cc5b1-a0c5-4e93-95f1-80016641a114"
/> |
| <img width="864" height="266" alt="CleanShot 2026-09-02 at 11 13
53@2x"
src="https://github.com/user-attachments/assets/3c1ca53f-bf9e-431e-bc15-816b4a275b8e"
/> | <img width="882" height="248" alt="CleanShot 2026-09-21 at 11 41
37@2x"
src="https://github.com/user-attachments/assets/24828e7b-ed6b-44cb-b9dc-becc3398bdfc"
/> |
| <img width="782" height="692" alt="CleanShot 2026-09-02 at 11 16
30@2x"
src="https://github.com/user-attachments/assets/fc871977-77bc-47fb-9e0e-9284e0ecd5cc"
/> | <img width="730" height="690" alt="CleanShot 2026-09-21 at 11 42
52@2x"
src="https://github.com/user-attachments/assets/bf479515-d5f9-471e-b82d-f097c0f4b56c"
/> |
| <img width="480" height="306" alt="CleanShot 2026-09-02 at 11 18
53@2x"
src="https://github.com/user-attachments/assets/03f341f4-f02e-44f8-a2b2-8c31670d0427"
/> | <img width="470" height="300" alt="CleanShot 2026-09-21 at 11 43
19@2x"
src="https://github.com/user-attachments/assets/9df18217-d5e6-48b8-ba0b-579d2664b94b"
/> |
| <img width="960" height="300" alt="CleanShot 2026-09-02 at 11 32
04@2x"
src="https://github.com/user-attachments/assets/6b1d9373-7a71-4247-81ff-26441604b09d"
/> | <img width="980" height="306" alt="CleanShot 2026-09-21 at 11 44
13@2x"
src="https://github.com/user-attachments/assets/41ad4784-02ec-4b29-b860-32af9fa79aa8"
/> |
| <img width="924" height="214" alt="CleanShot 2026-09-02 at 11 34
44@2x"
src="https://github.com/user-attachments/assets/1de661fe-c7b6-499b-a94f-e4737436ec79"
/> | <img width="752" height="162" alt="CleanShot 2026-09-21 at 11 44
56@2x"
src="https://github.com/user-attachments/assets/1811890f-0660-4445-84e9-447720954fa1"
/> |

## To test

Test each callsite **in light mode** (dark mode is largely unchanged).

### WWW

-
[Homepage](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/):
“Scale to millions” uses readable brand text (display token is gone)
-
[Careers](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/careers):
small “Careers” eyebrow readable; green dividers stay bright
`brand-default`
-
[Contact](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/contact-us):
email / policy links use readable brand text
-
[Regions](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/regions):
“Ask about early access to BYOC” readable

### Docs

- [Docs
homepage](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs):
“DOCS” wordmark and resource links readable
- [Database
overview](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/guides/database/overview):
nav / footer brand links readable
- [JavaScript
reference](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/reference/javascript/introduction):
active sidebar treatment readable

### Design system

-
[Typography](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/typography):
documents `text-brand` only (no display)
- [Colour
usage](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/color-usage):
`text-brand` vs `bg-brand-default`
- [Design-system
homepage](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/):
brand text examples across themes

### Studio

- [Auth
providers](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/auth/providers):
enabled provider badge text readable; status dot stays bright
- [Database
policies](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/database/policies?new=true):
template hover text more legible
- [Database
connections](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/observability/connections):
“Live” status readable; animated dot stays bright green

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-22 10:07:28 +10:00
5bdfb8743c fix(telemetry): give warehouse_disabled the same schema and table counts as warehouse_enabled (#50643)
<!-- ccr-slack-attribution -->
_Requested by **Pam Chia** · [Slack
thread](https://supabase.slack.com/archives/C076KTY11DF/p1789979663384919?thread_ts=1789953229.116889&cid=C076KTY11DF)_

## 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?

Bug fix (telemetry).

## What is the current behavior?

**Before:** Disabling Warehouse fires `warehouse_disabled` with no
properties at all, while enabling it fires `warehouse_enabled` with
`schemaTargetCount` and `tableTargetCount`. Disables can be counted, but
nothing says how much was being replicated when the user turned it off,
so churn cannot be segmented by the size or shape of the setup being
torn down.

## What is the new behavior?

**After:** `warehouse_disabled` carries `schemaTargetCount` and
`tableTargetCount` with exactly the same meaning they have on
`warehouse_enabled`: schemas replicated in full, and tables replicated
individually on top of those. A disable of a project replicating one
whole schema plus two loose tables now reports one schema target and two
table targets, so enable and disable volume line up on the same two
properties.

## Additional context

**How:** The counts are read once, when the user confirms the dialog,
and held in a ref until the mutation succeeds. The setup mutation's own
`onSuccess` invalidates the setup-status and replication-sources queries
and awaits those refetches before the caller's callback runs, so
anything read inside `onSuccess` already reflects the post-disable state
and would report nothing replicated. The event is tracked from that
hook-level `onSuccess` rather than a `mutateAsync` callback: the status
refetch swaps the Disable card out of the panel, and mutate-level
callbacks are skipped once the component has unmounted.

The shape is reproduced from the `supabase_warehouse` publication
through the same helpers the table picker uses — the publication's
tables become a selection, and that selection is mapped back to targets
against the project's selectable schemas. Counting distinct schemas and
tables off the replicated-table list instead would put a different
meaning behind the same property names: a fully covered schema would be
counted as its individual tables rather than as one schema target, and
the two events would no longer be comparable.

Both properties are optional. The replicated-table list is assembled
from four queries, and when they have not resolved the properties are
omitted rather than sent as `0`, so "unknown" is never recorded as
"nothing was replicated".

Tests: unit tests for the extracted `buildSchemasWithTables` helper, and
a component test that drives the disable dialog against a publication
covering one schema in full plus one table from another.

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

https://claude.ai/code/session_0197pGnhiAkhiiYiRxY3qVFY

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Pamela Chia <pamelachiamayyee@gmail.com>
2026-09-21 20:58:13 +08:00
claude[bot]andClaude aaa1b8c0df fix(ui): fail copyToClipboard when the Clipboard API is unavailable (#50641)
<!-- ccr-slack-attribution -->
_Requested by **Pam Chia** · [Slack
thread](https://supabase.slack.com/archives/C076KTY11DF/p1789979683276099?thread_ts=1789953317.522459&cid=C076KTY11DF)_

## 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?

Bug fix.

## What is the current behavior?

**Before:** `copyToClipboard` writes text with
`navigator.clipboard?.writeText(text)`. When `navigator.clipboard` is
undefined — an insecure context, such as self-hosted Studio served over
plain http or Studio reached over a LAN IP, where `ClipboardItem` is
also undefined so the Safari branch above is skipped — the optional
chaining makes the whole expression resolve to `undefined`. Nothing
throws, so the `catch` never runs and the success callback on the next
line runs anyway. The caller is told the copy succeeded: the UI shows
its "Copied!" confirmation state and the copy-tracking telemetry event
fires as a successful copy, even though nothing reached the clipboard.
That contradicts the documented contract of those events, which are
defined as firing only when the clipboard write succeeded.

## What is the new behavior?

**After:** the missing-clipboard case fails instead of silently
succeeding. The callback does not run, no copy event fires, and the
error toast that the function already shows on failure (`Unable to copy
to clipboard`) is what the user sees. Every working path behaves exactly
as before, including the Safari `ClipboardItem` branch, which is
untouched.

## Additional context

How: throw when `navigator.clipboard` is missing, inside the `try` block
that already exists, so the case lands in the existing `catch` and its
error toast rather than falling through to the success path. The
now-redundant optional chaining on the write is dropped. One case was
added to the existing shared clipboard util tests asserting that the
callback does not fire and the error toast shows when the Clipboard API
is unavailable; it fails on `master` and passes with this change.

Linear:
[GROWTH-1261](https://linear.app/supabase/issue/GROWTH-1261/clipboard-copy-helper-reports-success-when-the-clipboard-api-is)

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

https://claude.ai/code/session_01QdJB22CngN3tpc7Kfdpram

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-21 19:54:37 +08:00
Danny White 512201dcd0 chore(ui): remove the Classic Dark theme (#50387)
## What kind of change does this PR introduce?

Chore.

## What is the current behaviour?

Classic Dark remains available across the shared theme library and
several apps. Studio now supports System, Dark, and Light as its theme
modes, but still carries compatibility paths for Classic Dark.

## What is the new behaviour?

- Removes Classic Dark from shared theme options, application commands,
stylesheets, previews, examples, and replay handling.
- Deletes the Classic Dark and faux Classic Dark stylesheets.
- Removes the now-unused Classic Dark branches from Studio theme colour
controls.
- Migrates `classic-dark` to `dark` so first rendered frame renders Dark
(not Light)

| After |
| --- |
| <img width="1458" height="1778" alt="CleanShot 2026-09-18 at 11 07
40@2x"
src="https://github.com/user-attachments/assets/679bf87f-a3c1-4599-ad2f-292d98d0b856"
/> |

## To test

1. In Studio, open Account Preferences → Appearance. Confirm the
available themes are System, Dark, and Light, and that theme colour
controls still work in each resolved mode.
2. Set the `theme` local storage value to `classic-dark`, then reload
Studio. Confirm it renders as Dark immediately and the stored value
becomes `dark`.
3. Open the theme switcher in Design System, Learn, and UI Library.
Confirm Classic Dark is no longer available and Light, Dark, and System
still apply correctly.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Changes**
* Removed the Classic Dark theme option from theme menus and settings
across the application.
* Classic Dark selections are automatically migrated to the standard
Dark theme.
* Updated theme documentation and demonstrations to list only System,
Light, and Dark.
* Removed Classic Dark styling and preview support; existing Dark,
Light, and System themes remain available.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-21 10:53:14 +10:00
Nik RichersandNik Richers e3c677fc5a feat(docs): track prompt panel copies in PostHog (#50482)
## 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?

Add telemetry for `PromptPanel` to help us understand how people
interact with our AI prompts better.

 Relates to DOCS-1393

Dashboard(restricted access): [Docs: AI prompt
affordances](https://eu.posthog.com/project/34344/dashboard/957235)

## What is the current behavior?

The docs homepage cover renders a setup panel with "AI Prompt" and "CLI"
tabs, and guides render `AiPrompt` blocks. Both are built on the shared
`PromptPanel`, whose copy button called `copyToClipboard` and nothing
else. Copying was therefore unmeasured, while the neighbouring
affordances (`ask_ai_clicked`, `agent_setup_clicked`,
`copy_as_markdown_clicked`) are already instrumented.

## What is the new behavior?

`PromptPanel` takes an optional `telemetry` prop. When it is set, the
panel sends a new docs-owned event after a **successful** clipboard
write, so instrumentation lives in the shared component instead of a
forked homepage copy button.

New event in `packages/common/telemetry-constants.ts`:

| | |
| --- | --- |
| `action` | `docs_ai_prompt_copied` |
| `source` | `homepage` \| `guide` \| `agent_setup` |
| `tab` | `prompt` \| `cli` (omitted for panes outside that set) |
| `promptId` | prompt id, when the panel comes from an `AiPrompt` block
|

Wired consumers: `HomePageCover` (`homepage`), `AiPrompt` (`guide` by
default, plus `promptId`), and `AgentSetup` (`agent_setup`). No prompt
body text and no PII is sent.

Studio's existing `ai_prompt_copied` event is deliberately left alone:
it has a different owner and surface, and merging the two would blend
unrelated funnels.

### Proof it works

```
$ pnpm run test:local:unwatch features/ui/PromptPanel.telemetry.test.ts

 RUN  v5.0.0 /apps/docs

 Test Files  1 passed (1)
      Tests  4 passed (4)
   Duration  775ms
```

## Additional context

Test plan, run against a local docs server with a stub telemetry
endpoint so the request bodies could be read directly:

| Case | Observed payload |
| --- | --- |
| Homepage, AI Prompt tab | `{"source":"homepage","tab":"prompt"}` |
| Homepage, CLI tab | `{"source":"homepage","tab":"cli"}` |
| Next.js quickstart `AiPrompt` |
`{"source":"guide","tab":"prompt","promptId":"nextjs"}` |
| `automate-with-agents/health` `AgentSetup` |
`{"source":"agent_setup","tab":"prompt","promptId":"monitoring-agent-health"}`
|
| Clipboard write rejected | no request sent, error toast shown, button
does not flip to "copied" |

The failure case was re-checked with a control click on the same page
after restoring a working clipboard, which did send the event, so the
negative result is not just a missed handler.

Also run: `turbo typecheck --filter=docs --filter=common` (passes),
Prettier check on the touched files (passes), and ESLint on the touched
docs files (no new findings; the one warning on `HomePageCover` is the
pre-existing default export).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Successful prompt copies are now tracked across the homepage,
documentation guides, and agent setup experiences.
* Copy activity records the prompt’s source, selected format, and
associated prompt when available, providing more complete usage
insights.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Nik Richers <nik@validmind.ai>
2026-09-20 17:08:03 +00:00
Anthony Lio 222127b5c2 fix(ui-patterns): multi select cropped caret + extra padding (#50323)
## What kind of change does this PR introduce?

bug fix on multi select ui patterns component following up with #49986

## What is the current behavior?

- extra left padding on medium size
- cropped caret on tiny size

## What is the new behavior?

- updates multi select style padding + caret
- refactors test

`caret`
| state | preview |
| -------|------|
| before | <img width="594" height="362" alt="image"
src="https://github.com/user-attachments/assets/14af14f6-348a-44be-b0ae-42fdb9a4f4ce"
/> |
| after | <img width="594" height="362" alt="image"
src="https://github.com/user-attachments/assets/44dab5ae-944d-43fe-8c4e-0af44a0e1fc7"
/> |

`padding`
| state | preview |
| -------|------|
| before | <img width="594" height="362" alt="image"
src="https://github.com/user-attachments/assets/b1ad7f66-9952-463d-aebb-01fee8748aef"
/>|
| after | <img width="594" height="362" alt="image"
src="https://github.com/user-attachments/assets/a5891c45-67b3-4926-97c5-a2ad7027bd5c"
/> |


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Multi-select triggers now display the selected value while retaining
the placeholder when empty.
* Improved sizing, spacing, and minimum widths across multi-select
controls for more consistent layouts.
* Delete controls now provide clearer click targets and hover feedback.
* Decorative chevron icons are hidden from assistive technologies for
improved accessibility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-18 10:38:27 +03:00
Anthony Lio 85f19367ec fix(ui): button popup layout shift on click (#50468)
## What kind of change does this PR introduce?

Bug fix on ui button component

## What is the current behavior?

button scale transition is applied when a popup get displayed causing a
slight layout shift (popup position change on active state)

## What is the new behavior?

- prevents scale transition on button displaying popup

| state | preview |
| -------|------|
| before | <video
src="https://github.com/user-attachments/assets/d736f27c-0d0e-4dfa-877f-6b22a09db15e"
/> |
| before | <video
src="https://github.com/user-attachments/assets/762de503-ac54-48cb-b689-8a0f8e83cc4c"
/> |

## Test
1. visit `/project/default/explorer/query/${id}` or `
/docs/guides/ai-tools/mcp`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Dropdown and other menu-trigger buttons no longer shrink when clicked.
  * The press-scale animation remains available for standard buttons.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-18 08:51:09 +03:00
Pamela Chia 64ab76262e feat(studio): exhaustion banner links to metrics (#50276) 2026-09-17 22:23:10 +02:00
Pamela Chia 66d4b4c19b chore(studio): remove expired tos update banner (#50533) 2026-09-18 00:52:40 +08:00
Lukas Bernert 055cc7b956 docs: state disk limits as per-size minimums, align burst copy (#50016)
## What kind of change does this PR introduce?

Docs update: states disk limits as per-size minimums and aligns burst
copy across pages. Follow-up to #49996 (compute descriptions).

Fixes PROD-658

## What is the current behavior?

- The disk limits table and surrounding prose describe a narrower set of
configurations than a compute size can run on
- Burst thresholds are inconsistent across pages (three different
variants), and one section contradicts itself
- Burst is described as CPU behavior, when the burst users observe is
disk IO

## What is the new behavior?

- `shared-data/compute-disk-limits.ts`: Medium baseline throughput
adjusted to 39 MB/s: the lowest value across configurations
- `compute-and-disk`: disk limits presented as minimums ("at least");
burst described as disk IO drawing on a disk IO budget; consistent
thresholds: burst available up to 2XL, baseline equals maximum from 8XL
- Troubleshooting guides (`exhaust-disk-io`,
`failed-to-retrieve-tables`, `interpreting-supabase-grafana-io-charts`)
aligned to the same threshold; `failed-to-retrieve-tables` keeps the
~30-minutes-per-day burst window with the corrected size range
- Section anchors unchanged

## Self-review

- Values verified against the AWS EBS-optimized performance data
(`describe-instance-types`) for every configuration per size; content
cross-checked with the internal runbooks (linked in PROD-658)
- `supa-mdx-lint`: no findings in changed files
- `pnpm build:guides-markdown` clean; generated `.md` exports show the
new values and prose
- All changed pages verified rendering in the local dev app
- `pnpm typecheck` passes (shared-data + docs)
- Note: `compute-disk-limits.ts` also feeds Studio (disk validation, IO
budget tooltips). The only value change (Medium 43 → 39 MB/s) surfaces
there as one chart tooltip label; conservative direction.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Clarified the differences between shared and dedicated CPU resources.
- Updated disk I/O guidance to explain baseline and burst limits as
minimums.
- Documented disk I/O bursting for compute sizes up to 2XL, including
expected duration and limitations.
- Clarified that 8XL and larger compute sizes have consistent
performance without burst capacity.
- Updated the documented baseline throughput for medium compute
resources.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 12:26:45 +02:00
Saxon Fletcher 0043e6f53b feat(studio): add Explorer onboarding and startup preference (#50493)
<img width="1454" height="920" alt="image"
src="https://github.com/user-attachments/assets/a289b618-2bd2-4957-ac49-71d4e372d2cc"
/>


## 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.

## What is the current behavior?

Explorer always opens on its start page, without onboarding or a startup
preference.

## What is the new behavior?

Adds one-time onboarding with wireframe option cards and a collapsed
Learn more section. Users can start on the Explorer start page or in a
new SQL query tab, and change that choice in Account preferences →
Dashboard. Preferences persist per account in the browser.

## Additional context

How to test:
1. With Explorer enabled and fresh browser storage, open Explorer and
select either startup option. Confirm Open Explorer follows the
selection and onboarding stays dismissed after reload.
2. Change Explorer startup in Account preferences → Dashboard, then
reopen Explorer. SQL query should create one normal query tab; Start
page should restore the pinned home tab.
3. Use the keyboard to select an option and toggle Learn more. Expand it
in a short viewport and check that the page scrolls normally.

Validation: 235 tests pass, including 20 new cases; Studio typecheck and
formatting pass.

The local production build was stopped during compilation and was not
verified locally.



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added an Explorer onboarding experience with startup-view selection,
guidance, and a Learn more section.
- Added Explorer settings to choose between the Start page and SQL query
views.
  - Explorer preferences now persist across sessions and accounts.
  - Explorer can open directly to a new SQL query when selected.
- The Explorer Home tab is shown based on the selected startup
preference.
- **Accessibility**
  - Reduced-motion settings now disable the Explorer loading animation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 15:53:18 +10:00
Danny White 3e2d54eccb feat(studio): add Warehouse table management and disable (#50195)
## What kind of change does this PR introduce?

Feature and UI polish.

## What is the current behavior?

Warehouse setup uses a schema accordion for table selection. Once
Warehouse is enabled, users cannot remove replicated tables or disable
Warehouse from Studio.

## What is the new behavior?

- Replaces the schema accordion with one grouped, searchable table
selector.
- Still allows for **Select all** and **Clear** actions for each schema.
- Starts first-time setup with no tables selected and preselects current
replicated tables when editing.
	- Adds support for removing previously replicated tables.
- Adds a confirmed **Disable Warehouse** action.
- Tracks successful Warehouse enable and disable actions.

Disabling Warehouse removes its replication pipeline, publication,
catalogue access, and foreign tables. Copied data remains in DuckLake
storage until the user deletes it. Re-enabling a table rebuilds its data
rather than reusing the retained copy.

| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Integrations Test US East 1 testdw
Supabase"
src="https://github.com/user-attachments/assets/bded025b-1d45-41dc-8a35-9159baf8f9b7"
/> | <img width="1024" height="759" alt="Integrations test Teamer
Supabase"
src="https://github.com/user-attachments/assets/69026d94-98a0-4878-ab58-2e9697296d93"
/> |
| <img width="1280" height="1323" alt="Integrations Test testdw
Supabase"
src="https://github.com/user-attachments/assets/3f71e754-1a87-4d58-a7b9-dd39d3e0ac5a"
/> | <img width="1280" height="1323" alt="Integrations Regular AWS
Teamer Supabase"
src="https://github.com/user-attachments/assets/758ed48e-9ed6-45d3-ae94-e171147a21d5"
/> |
| _Feature did not exist_ | <img width="1024" height="759"
alt="Integrations Regular AWS Teamer Supabase"
src="https://github.com/user-attachments/assets/c977ac57-8b0c-4482-882b-69ad7602b5df"
/> |

## Additional context

Platform support for updating and disabling Warehouse was added in
[supabase/platform#38190](https://github.com/supabase/platform/pull/38190).

### To test

1. Open `/project/{ref}/integrations/warehouse/overview` before setup.
2. Confirm **Tables to replicate** starts at zero and **Enable
Warehouse** is disabled until a table is selected.
3. Confirm each schema's **Select all** and **Clear** actions update
every table in that schema.
4. Enable Warehouse with a partial selection and wait for setup to
complete.
5. Edit the selection, add and remove replicated tables, then confirm
the saved selection is reflected in the publication.
6. Disable Warehouse, confirm the retention warning, and verify the
integration returns to its initial state.
7. Re-enable Warehouse and confirm selected tables are rebuilt.
8. Trigger a replication pipeline limit error and confirm the inline
guidance links to Database Replication.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Added the ability to disable Warehouse from the setup panel.
  - Warehouse setup now starts with no table selections.
- Editing a setup preselects replicated tables and supports updating
selections, including removing tables.
- Added searchable schema and table selection with screen-reader count
announcements.
  - Added telemetry tracking for initial Warehouse enablement.

- **Bug Fixes**
- Warehouse disable failures now show an error while keeping the
confirmation dialog open for retry.
  - Configuration updates now refresh related data automatically.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 14:17:00 +10:00
Wen Bo Xie 20d09b4a72 docs(auth): clarify OAuth 2.1 server pricing is included in Auth MAUs (#49753)
OAuth 2.1 server had a single pricing statement anywhere, and it said
the feature is free during beta. This states the actual model everywhere
the feature is documented or sold: there is no separate charge, and
users who sign in through the OAuth server count toward Auth MAUs.

- docs getting started: replace the "free during beta" sentence with the
MAU-based pricing statement
- docs overview: add a Pricing section linking to the MAU usage guide
and the pricing page
- docs MCP authentication: note that agents authenticate as existing
users, and MAUs count per distinct user, so multiple agents for one user
count once
- www pricing comparison table: add an "OAuth 2.1 Server" row (included
on all plans) with a tooltip, and extend the MAU tooltip to cover OAuth
server sign-ins

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Clarified that OAuth 2.1 Server is available on all plans without a
separate charge.
* Explained that OAuth sign-ins count toward Monthly Active Users
(MAUs), with multiple agents for one user counted once.
  * Added links to MAU and pricing guidance.

* **Pricing**
* Added OAuth 2.1 Server as a plan feature and updated billing
descriptions for greater clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-17 10:49:36 +08:00
cc540ff302 feat(studio): add safe theme colour controls (#49804)
## What kind of change does this PR introduce?

Feature.

## What is the current behaviour?

Studio Appearance preferences only select a theme mode. The underlying
theme colours cannot be adjusted, and the existing proof of concept
allowed unsafe combinations and introduced a bespoke Slider variant.

## What is the new behaviour?

- Preserves the existing System, Dark, Light, and Classic Dark theme
options. Classic Dark remains a fixed preset.
- Adds four theme colour controls using the existing Supabase Slider
unchanged. Each control presents a consistent 0 to 100 scale mapped to
bounded light and dark ranges.
- Previews colour changes while dragging and persists them once the
interaction finishes, including rapid pointer gestures.
- Stores light and dark overrides separately, validates stored values,
clamps legacy values, and removes overrides that return to their shipped
defaults.
- Adds concise descriptions for Chroma, Contrast, Surface, and Elevation
step, with a scoped Reset action shown only when the active theme
differs from its defaults.
- Keeps Slider in a stable shared chunk so production builds do not
create a circular dependency between generated UI chunks.

| Before | After |
| --- | --- |
| <img width="1448" height="1284" alt="CleanShot 2026-09-15 at 14 33
53@2x"
src="https://github.com/user-attachments/assets/d55151c7-b2a9-40c6-9468-e77ae685ac38"
/> | <img width="1454" height="1958" alt="CleanShot 2026-09-15 at 17 48
47@2x"
src="https://github.com/user-attachments/assets/9d302e67-76cc-4341-948c-81713dea2e93"
/> |

## To test

1. Open `/account/me` and scroll to Appearance.
2. Switch between System, Dark, Light, and Classic Dark. Confirm the
same four modes remain available in the account theme menu.
3. Confirm Classic Dark retains its existing appearance and does not
show theme colour controls.
4. In System, Dark, or Light, move each Theme colors slider to both
ends. Confirm the dashboard previews the change, remains readable, and
the theme cards do not shift or remount.
5. Reload the page and confirm colour changes persist separately for
Light and Dark.
6. Return all sliders to their defaults, or select Reset, and confirm
the Reset action disappears.
7. In System mode, change the operating system theme and confirm each
resolved mode restores its own colour settings.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-09-17 10:33:59 +10:00
Miranda Limonczenko 91b7df64c2 fix(ui): report clipboard write failures instead of rejecting (#50292)
Closes DOCS-1390

## Problem

Sentry [DOCS-AA](https://supabase.sentry.io/issues/7727380816/) reports
`NotAllowedError: Failed to execute 'write' on 'Clipboard': Write
permission denied.` as an unhandled promise rejection.

The error names `write`, not `writeText`, which places it in the
`ClipboardItem` branch of `copyToClipboard`. That branch has two
problems:

- The write runs inside a `setTimeout`, so the surrounding `try/catch`
has already returned by the time it executes. A denied write routes to
the promise's `reject`.
- No caller attaches a `catch`. All call sites either fire-and-forget or
`await` inside an async handler with no `try/catch`, so the rejection
surfaces as an unhandled rejection.

The user-visible effect is worse than the Sentry noise. On that branch
the copy fails with no feedback at all, because the `toast.error` in the
outer `catch` is unreachable from inside the `setTimeout`. The
`writeText` branch does show the toast, so the two paths disagree.

The issue is filed against auth docs, where it surfaced, but the fix
belongs in `packages/ui`. The same branch runs in Studio and www.

## Solution

- Handle the failure inside the `setTimeout`, where it happens: report
it and resolve.
- `copyToClipboard` no longer rejects on either path, matching what the
`writeText` branch already did. No caller relied on rejection.
- Add regression tests for a denied write on both branches.

## Manual testing

1. Run the unit tests. Four `copyToClipboard` cases pass, including the
two new denial cases.

   ```
pnpm --filter studio exec vitest run lib/helpers.test.ts -t
copyToClipboard
   ```

2. Confirm the new test is a real guard. Revert `clipboard.ts` and
rerun. The write case fails with `promise rejected ... instead of
resolving`.
3. Confirm the ratchet is unchanged.

   ```
   pnpm --filter studio run lint:ratchet
   ```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Clipboard write failures now display an error notification instead of
causing an unhandled rejection.
* Copy operations resolve consistently when clipboard access is denied
or unavailable, including Safari clipboard support.
* Failed copy attempts no longer trigger completion callbacks,
preventing misleading success behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 19:01:21 +00:00
claude[bot]andClaude 0e7cfac721 fix(shared-data): restore sign-in testimonial with correct avatar (#50466)
<!-- ccr-slack-attribution -->
_Requested by **Alaister Young** · [Slack
thread](https://supabase.slack.com/archives/C0161K73J1J/p1789557911237269?thread_ts=1789557911.237269&cid=C0161K73J1J)_

## Before

The Studio sign-in page
(`apps/studio/components/layouts/SignInLayout/SignInLayout.tsx`) shows a
rotating testimonial next to the auth form, picking one tweet object
from `packages/shared-data/tweets.ts` and rendering its `text`,
`handle`, and `img_url` together. Two entries in the data file pointed
at the *same* avatar image file (`JwLEqyeo_400x400.jpg`): one attributed
to `orlandopedro_` and one to `pontusab`, despite being different people
with different quotes. That file was confirmed (byte-for-byte) to
actually be `pontusab`'s real photo, so `orlandopedro_` had no correct
avatar checked in.

## First attempt

The initial fix (this PR's first commit) removed the `orlandopedro_`
entry entirely, since no verified avatar was available for that handle
at the time, following this repo's precedent (PR #38500) for resolving
this class of bug by deleting the erroneous entry.

## Correction

Jordi confirmed the correct profile picture for `orlandopedro_` in the
Slack thread, so instead of leaving the entry deleted, this PR now
**restores** it with the correct avatar:
- Added `apps/www/public/images/twitter-profiles/ZjIOtCGg_400x400.jpg`
(downloaded from the user-provided URL), following the existing filename
convention used by other entries in that directory (the image's own
Twitter CDN slug + `_400x400.jpg`).
- Restored the `orlandopedro_` object in
`packages/shared-data/tweets.ts` (same quote text, handle, and URL as
originally) with `img_url` now pointing at the new, correct image file.

The `pontusab` entry is untouched throughout.

## Test plan

- Verified the restored object diffs as an exact re-add of the
originally removed entry, with only `img_url` changed to the new file.
- Verified the downloaded image is a valid 400x400 JPEG.
- Verified brace/object structure of `tweets.ts` is balanced after the
edit.
- Could not run `pnpm install` in this environment (blocked on
`npm.jsr.io`), so lint/prettier/build were not executed; verified the
diff manually instead.

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

https://claude.ai/code/session_01A1PbXuRBeaC7G3Mgb7X3eY

---
_Generated by [Claude
Code](https://claude.ai/code/session_01A1PbXuRBeaC7G3Mgb7X3eY)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-16 22:18:20 +10:00
Jordi Enric 24e81e543e ci(api-types): summarize production type differences (#50263)
## Problem

The production API types check reports mismatched filenames without
showing which declarations differ, making drift difficult to diagnose.

## Fix
<img width="1771" height="1012" alt="Shotbase
Capture-AB6E5CC9-905B-46E3-AD7B-A2DE868D9E95"
src="https://github.com/user-attachments/assets/81666e89-b79f-4456-a187-43af16ce2bec"
/>

Print a unified diff for each mismatched file with line numbers and
committed/production labels. Append escaped, bounded previews to the
GitHub Actions summary, with full diffs in the step logs, while
preserving the failing check.

## How to test

- Run `node --test
packages/api-types/scripts/verify-production-types.test.mjs` (all six
tests pass).
- Tests cover diff direction and line numbers, multiple files, summary
appending and escaping, preview truncation, local logging without an
Actions summary, and diff command failures.
- Both changed files were formatted with the repository Prettier
configuration.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added detailed difference reporting when generated production types do
not match committed types.
* CI logs now include readable unified diffs, with large outputs safely
truncated and escaped.
* GitHub Actions summaries can include mismatched type files and their
differences while preserving existing summary content.

* **Bug Fixes**
* Improved diagnostics for missing type files and type verification
failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 12:46:10 +02:00
99be7f92ce feat(studio): add Privacy Policy update notice (#50397)
## Summary

Adds a compact Privacy Policy update notice for signed-in Studio users
on organization landing pages.

- Shows on `/org`, `/organizations`, and `/org/:slug`
- Opens the approved policy explanation in a dialog
- Links to the Privacy Policy and `privacy@supabase.com`
- Persists acknowledgement in a dated local storage key
- Stays off project and organization settings routes so it cannot cover
product controls

## Why

The Privacy Policy changes the data controller from Supabase, Inc. to
Supabase Pte. Ltd. User rights and protections are unchanged.

This restores the established authenticated Studio notification pattern:

- [#35923](https://github.com/supabase/supabase/pull/35923): May 2025
Privacy Policy notice
- [#43681](https://github.com/supabase/supabase/pull/43681) and
[#43889](https://github.com/supabase/supabase/pull/43889): March 2026
Privacy Policy notice and design pass
- [#45632](https://github.com/supabase/supabase/pull/45632): May 2026
Terms of Service notice
- [#48524](https://github.com/supabase/supabase/pull/48524): current
reusable Studio banner stack

## Release order

The policy content and Studio notice deploy independently. Keep this PR
in draft until [#50392](https://github.com/supabase/supabase/pull/50392)
is approved, merged, and live. The notice appears immediately when this
Studio change deploys.

## To test

1. Open Studio on `/organizations` or an organization project-list page.
2. Confirm the compact Privacy Policy notice appears.
3. Open **Learn more** and confirm the dialog copy and both links.
4. Select **Understood** or close the notice.
5. Reload and confirm the notice remains dismissed.
6. Remove `privacy-policy-update-2026-09-16-dismissed` from local
storage and confirm the notice returns.
7. Open a project route and confirm the notice is absent.

## Verification

- Prettier passes on changed files.
- ESLint passes on changed Studio files.
- Focused Vitest suites pass: 25 tests.
- Studio Unit Tests & Build Check passes.
- TypeScript & Lint, UI Tests, Studio Docker Build, dead-code, ratchet,
and validation workflows pass.
- All four self-hosted Studio E2E shards pass for both router
implementations.
- All deploy previews pass.
- The Studio preview rendered the compact notice on the organization
landing page without console errors. The dialog and dismissal flow still
need an authenticated browser pass after the session redirected to
sign-in.

A direct local Studio TypeScript check reaches one existing unrelated
error in
`packages/ui-patterns/src/McpUrlBuilder/components/InstructionBlocks.tsx`;
no changed file reports an error and the required TypeScript CI workflow
passes.

## Measurement

Success means signed-in users can find the updated policy from the
organization landing experience without interrupting project work. The
dated dismissal key confirms acknowledgement locally. CI protects the
non-blocking route scope, and Privacy can monitor questions sent to
`privacy@supabase.com` after release.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Pamela Chia <pamelachiamayyee@gmail.com>
2026-09-16 17:51:25 +08:00
Joshen Lim 32b1a0f492 FilterBar to disable filter options if already selected (#50331)
## Context

Updates FilterBar UI component to prevent selecting a filter option
that's already selected + adds a check:
<img width="532" height="368" alt="image"
src="https://github.com/user-attachments/assets/609f3da2-d627-48d5-9c3e-d6adbc24110b"
/>
<img width="577" height="373" alt="image"
src="https://github.com/user-attachments/assets/5b56eefc-c87b-4254-b1e2-1a5e521b08fe"
/>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Filter menus now identify values already used in another condition for
the same property.
  * Duplicate values are shown as disabled and cannot be selected.
  * Optional selection indicators can be displayed in filter menus.
* Disabled options include clear visual styling and accessibility
information.

* **Bug Fixes**
* Prevented disabled filter values from being selected through mouse
interactions or keyboard navigation.
  * The active condition’s own value remains available for selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 16:33:36 +08:00
Gildas Garcia 7ab2a0d84f Fix TextConfirmModal does not reset its state (#50406)
## Problem

`TextConfirmModal` does not reset its state after closing, whether users
confirmed or not. If they would restart the action, the confirmation
text they may have entered is kept, preventing the secure confirmation.

Also fixed an accessibility issue as we didn't enable the submit button
until the form was valid

## Solution

Reset the form state whenever the dialog opens.

## How to test

- On
https://studio-staging-git-gildasgarcia-design-505-rese-196b28-supabase.vercel.app/dashboard/account/security
- Either:
  - Add an MFA if you haven't already
  - Generate recovery codes if you already have an MFA
- Click the _Regenerate recovery codes_

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Improvements**
- Text confirmation dialogs now reset their input whenever opened or
closed, ensuring a fresh form for each use.
- Confirmation actions remain available unless the dialog is processing
a submission.
- Copy-to-clipboard actions now provide an accessible announcement when
text has been copied.

- **Tests**
- Added coverage for successful confirmation, cancellation, invalid
submissions, input reset behavior, and recovery-code regeneration
retries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 10:24:17 +02:00
7fce0a12d9 feat(design-system): first pass at db report chart colours (#46787)
## 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?

This is a first draft at introducing semantic colours to our
Observability charts. This moves away from just random colours being
assigned to prop after prop. They're only scoped to the Database reports
right now, but if it flows nice, we can open it up to the other reports
too.

This also aims to tone down some of the harsher colours in our charts,
such as the orange which sometimes can look like a warning metric/prop.

| Before | After |
|--------|--------|
| <img width="839" height="336" alt="Screenshot 2026-06-10 at 09 14 56"
src="https://github.com/user-attachments/assets/222747c5-973b-4165-aa53-df7b93412ad3"
/> | <img width="950" height="341" alt="Screenshot 2026-09-14 at 18 14
47"
src="https://github.com/user-attachments/assets/836f3ddd-4a97-4064-b8cf-3a3b435417ac"
/> |

cc @supabase/design for additional thoughts.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added semantic chart color roles with light and dark theme variants
for consistent visualizations.
* Standardized colors and fills across database, networking, storage,
and connection charts.
  * Maximum-value lines now use configured chart colors when available.
  * Added chart palette reference and stress-test examples.
* Added stacked bar charts, customizable margins, and gradient-filled
line charts.
* Improved multi-series bar chart focus and date-range footer alignment.

* **Documentation**
* Documented the chart palette, theme variants, accessibility guidance,
and usage recommendations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
2026-09-16 09:18:40 +01:00
Saxon FletcherandClaude Opus 5 32341830b3 docs: organize observability by task and move SQL logs to Explorer (#50074)
## 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?

Documentation update.

## What is the current behavior?

The observability overview and access page overlap; configuration
interrupts querying; related guides send log queries to the old editor.

## What is the new behavior?

The observability overview and navigation follow the same four sections:
Read project data, Detect and diagnose, Hire an agent, and Configure and
export. The overview absorbs the redundant access page, with permanent
redirects for both HTML and Markdown URLs.

“Query logs with SQL” owns ClickHouse querying through MCP, the
Management API, and Explorer with query source Logs. Logging
configuration moves to its own guide; sources, captured headers, and
limits live in the field reference. Inspection links to canonical
diagnostic SQL. Related Storage and database guides use the replacement
Explorer workflow and retain existing anchors where headings move.

## Additional context

Validation: Markdown generation, docs typecheck, targeted ESLint,
formatting, and content-listing tests. Browser overview/navigation
checked; old HTML and Markdown URLs return 308, and the new
configuration page returns 200 in both formats. Three ClickHouse
examples and the Postgres configuration query ran in a disposable
container sandbox. Changed pages have no MDX lint violations;
repository-wide existing failures remain.

Self-review: the Management API request was verified against its
published schema but not sent to a hosted project. Realtime ingestion
and hosted logging configuration still need a hosted smoke check. No
compatibility path for the deprecated logs engine is documented.

Stage 2 of 3; depends on stage 1.


Stack: #50073 → #50074 → #50075.

Production docs build also passes at the stack tip after standard
reference generation.



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Reorganized observability guidance around reading data, detecting
issues, diagnosing problems, agent setup, and exporting data.
  - Added a guide for configuring Postgres and Realtime logging.
- Updated log investigation instructions to use Explorer, SQL queries,
and clearer filters.
  - Added log source, field, and captured-header references.
  - Improved advisor guidance and database performance troubleshooting.
  - Added redirects for moved observability content.

- **Accessibility**
- Improved screen-reader labels for copy and feature-selection controls.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 17:03:43 +10:00
Joshen Lim e296d0ae4f Joshenlim/fe 4373 add ilike comparator for pathname in unified logs (#50386)
## Context

In unified logs, filtering on pathname can benefit using the `ilike`
comparator so this PR adds support for that
<img width="423" height="247" alt="image"
src="https://github.com/user-attachments/assets/31e5f09b-7506-4588-90e3-ee705f805d43"
/>


FilterBar is also updated to omit facet values if the selected
comparator is `ilike`, otherwise it doesn't really make sense to show a
dropdown of values for users to select as the value for `ilike`
filtering.
<img width="240" height="62" alt="image"
src="https://github.com/user-attachments/assets/b5fc97e7-d826-4184-8b70-bfb4875d1822"
/>

The facet values should still show if the selected comparator is an
equals comparator
<img width="391" height="154" alt="image"
src="https://github.com/user-attachments/assets/7ccded04-3db1-4406-af40-4377ffe3bd8d"
/>

Related to https://github.com/supabase/supabase/pull/50394 - am also
updating ilike comparator logic for unified logs to implicitly wrap the
provided string with `%`, but only if the string doesn't already contain
a `%` or `_` for UX convenience. (Unified logs already had this
behaviour, except the latter part RE omitting default `%` if string
already has) - this affects pathname and event_message searching



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **New Features**
* Added case-insensitive pathname filtering for logs with ILIKE and NOT
ILIKE.
* Added pathname support for comparison and pattern-matching operators.
  * Preserved user-provided `%` and `_` wildcard patterns in searches.

* **Bug Fixes**
* Corrected BigQuery pathname filtering for consistent case-insensitive
matching.
* Prevented misleading exact-value suggestions for pattern-based
searches.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 14:34:41 +08:00
Joshen Lim b1a9e072ec Update table editor ilike related comparators to implicitly wrap filter string with % if not provided (#50394)
### Context

For table editor - the `ilike` related comparators expect users to input
a `%` in the filter string, which for non-developers might not be
intuitive.

Hence opting to implicitly wrap the filter string with `%` in the query
when filtering if non provided
<img width="1182" height="755" alt="image"
src="https://github.com/user-attachments/assets/819c39f5-fcbf-4213-95b3-3ad1ee901f47"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved pattern-match filters so bare text values perform contains
matching by automatically wrapping them with wildcards.
* Preserved explicit wildcard patterns using `%` or `_` without adding
additional wildcards.
* Improved handling of empty values for non-text filters while retaining
existing numeric filter validation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 14:32:19 +08:00
Danny White 7e9c483625 feat(ui): support bounded MultiSelector wrapping (#50439)
## What kind of change does this PR introduce?

UI component enhancement and Design System documentation update.

## What is the current behavior?

`MultiSelectorTrigger` can either limit the number of visible badges or
wrap every selected badge. Consumers cannot combine a numeric limit with
wrapping.

## What is the new behavior?

Adds `wrapBadges` so consumers can combine it with a numeric
`badgeLimit`. For example, `badgeLimit={3} wrapBadges` renders up to
three wrapped badges followed by the remaining `+n` count.

Existing `badgeLimit=\"wrap\"` behaviour remains supported.

## To test

1. Open the Design System Multi Select page.
2. Find the **Wrapped badge limit** example.
3. Confirm three selected fruit badges wrap within the trigger and the
remaining selections appear as `+2`.
4. Remove or add selections and confirm the visible badges and remaining
count update together.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added a `wrapBadges` option to multi-select triggers, allowing
selected badges to wrap across multiple lines while retaining a numeric
badge limit.
- Numeric limits now show the specified badges and an overflow count for
additional selections.
  - Updated the example with controls for adjusting the badge limit.

- **Documentation**
  - Clarified multi-select badge limit and wrapping behavior.

- **Tests**
- Added coverage for badge limits, wrapping, visible selections, and
overflow counts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-16 12:27:16 +10:00
Anthony LioandDanny White 38f448b01c fix(ui): tab component (#50183)
## What kind of change does this PR introduce?
bug fix on tab component + a small refactor

## What is the current behavior?

the active tab in an underline list gets border-b-2 while its siblings
get nothing, so it's 2px taller and its label sits higher than the rest
causing a smol layout shift within docs

## What is the new behavior?

- adds one absolute positioned bar that slides between tabs so nothing
moves
- favors track under an underline as an inset shadow vs a border

| state | preview |
| -------|------|
| before | <video
src="https://github.com/user-attachments/assets/b73820a6-2994-46d1-aa98-452681f0fef2"
/> |
| after | <video
src="https://github.com/user-attachments/assets/054cd67d-a50c-4aef-9340-a1e1d515047b"
/> |


## Test
1. visit [api
reference](https://docs-git-antlio-ui-components-tabs-supabase.vercel.app/docs/reference/javascript/installing?platform=npm&queryGroups=platform)
2. visit a [guide
](https://docs-git-antlio-ui-components-tabs-supabase.vercel.app/docs/guides/database/prisma)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added an animated tab indicator that follows the active tab and adapts
to layout changes.
  - Added support for customizing tab indicator styling.
- Respects reduced-motion preferences by disabling indicator transitions
when appropriate.

- **Style**
  - Streamlined tab borders, spacing, and underlined-tab styling.
- Improved tab panel spacing and standardized tab behavior in
documentation examples.
- Centralized easing behavior for smoother overlays, dropdowns, slides,
and panels.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-09-16 00:00:27 +03:00
Ali Waseem 5b099ee03f chore: share Sentry browser-noise filters between studio and docs FE-4392 (#50407)
Studio and docs each kept their own Sentry `ignoreErrors` list, so
browser-extension and DOM-mutation noise that Studio already filtered
still reached Sentry from docs. Moved the app-agnostic filters (network,
extension DOM mutation, non-Error throws, cross-origin script errors)
into `packages/common/sentry.ts` and spread them into both client
configs, leaving app-specific entries local. Docs will stop reporting
extension-driven `insertBefore`/`removeChild` crashes, matching Studio's
existing behavior — `ignoreErrors` drops events before `beforeSend`
runs, so the error-boundary exemption no longer applies to them.

Fixes FE-4392

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Reduced non-actionable browser noise in error monitoring by filtering
known network, browser extension, DOM-manipulation, cross-origin, and
non-error failures.
- Applied consistent filtering across the documentation site and studio
error tracking.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 09:38:36 -06:00
Jordi Enric fa7c223209 fix(studio): use Compute management endpoints FUNC-896 (#50393)
## Problem

Studio still called the legacy `/workers` Management API routes and used
the old `project_worker` response contract, so Compute instances could
not be listed or retrieved after the API rename. The production API type
check also detected drift in the v1 and platform declarations.

## Fix

- Regenerate the v1, v2, and platform API declarations from the deployed
schemas.
- Update Studio list and detail queries to `/compute`.
- Align typed fixtures with the Compute response schemas and
`project_compute_instance` resource type.
- Update platform response type references to the generated `_Output`
schema names.

## How to test

- Run `pnpm api:verify-types`.
- Run `pnpm --filter api-types test`.
- Run `pnpm --filter studio test data/compute/compute.utils.test.ts
"tests/pages/project/[ref]/compute/index.test.tsx"`.
- Run `pnpm --filter studio typecheck`.
- Run `pnpm --filter common typecheck`.
- Run `pnpm --filter studio lint:ratchet`.

Expected result: production API declarations are synchronized, and
Studio requests the `/compute` list and detail endpoints and renders
`project_compute_instance` responses successfully.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Improvements**
* Updated API response handling across profiles, backups, notifications,
integrations, warehouses, access tokens, payments, and other Studio
workflows for more accurate serialized data.
* Compute instance pages and queries now use the compute-specific API
endpoints and response data.
* Improved feature-flag type handling when disabled feature data is
unavailable.

* **Tests**
* Updated automated coverage and fixtures to reflect current compute and
API response formats.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 12:50:56 +02:00
Jordi Enric 8984305b1e feat: sample non-crash sentry errors at one percent (#50339)
## Problem

Browser Sentry reporting sends ordinary application errors at full
volume even though full-page crashes are the highest-priority signal.

## Fix

Sample eligible browser errors without `globalErrorBoundary` at 1%
across Studio, www, and docs. Keep 100% of eligible errors tagged with
`globalErrorBoundary`, preserve consent and existing noise filters, and
record the applied rate in `codeSampleRate`.

## How to test

- Run `node node_modules/vitest/vitest.mjs run
../../packages/common/sentry.test.ts lib/sentry-capture.test.tsx` from
`apps/www`.
- Run `node node_modules/vitest/vitest.mjs run
lib/sentry-client-options.test.ts` from `apps/studio`.
- Expected result: tagged page crashes bypass sampling, ordinary errors
use the 1% cutoff, and Studio applies sampling once while preserving its
existing filters.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Improved error reporting reliability by ensuring page-crash errors are
captured without sampling.
- Non-crash application errors are now sampled at a low rate, with
sampling metadata retained for monitoring.
- Updated filtering behavior so relevant Studio errors continue to be
reported consistently, including errors previously affected by
client-side filtering.
- Preserved filtering for third-party-only errors that do not represent
application failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 11:43:24 +02:00
Gildas Garcia 63bedef77f MFA Recovery codes: allow users to download their recovery codes (#50267)
## What kind of change does this PR introduce?

After users have set up a new MFA (first or not), we must:

- check whether recovery codes have already been generated
- if there are none, generate recovery codes and display them, "forcing"
users to copy them
- if already generated, show them how many are still available

> [!NOTE]
> The _Delete my recovery codes_ button in last screenshot only appear
on local and staging environments

## How to test

- On an account that doesn't have recovery codes generated yet and has
an MFA added
- You should see an admonition suggesting to generate the codes

## Screenshots

<img width="729" height="306" alt="image"
src="https://github.com/user-attachments/assets/79ba3870-4ef8-4571-9fd6-36eed20c9c24"
/>

<img width="550" height="356" alt="image"
src="https://github.com/user-attachments/assets/1632611a-996a-470d-b6cd-a4693b0f4602"
/>

<img width="719" height="205" alt="image"
src="https://github.com/user-attachments/assets/73cef611-05cf-4fac-bbd2-243f9b28e48d"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added support for generating, copying, and confirming MFA recovery
codes.
- Added recovery-code status visibility, including remaining and
exhausted codes.
  - Added the ability to delete recovery codes with confirmation.
- Added clear loading, success, and error states for recovery-code
actions.
  - Recovery-code status refreshes after codes are generated or deleted.

- **Bug Fixes**
- Recovery-code notices now remain visible when all codes have been
used.
  - Recovery-code dialogs can now be closed after generation errors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 11:16:47 +02:00
bd95407a9c fix(ui): validate stored resizable-panel layout before handing it to react-resizable-panels (#50349)
## Summary
- `serverCompatibleLocalStorage.getItem()` already guards against
localStorage access itself throwing (SSR, private browsing, sandboxed
iframes), but returns whatever string is stored without checking it's
valid JSON.
- `react-resizable-panels`' own `useDefaultLayout()` calls
`JSON.parse()` on that value with no try/catch, so a stored value that
isn't valid JSON (overwritten by another script sharing the origin, a
browser extension, or a leftover value from a previous format) throws
and crashes the whole panel group instead of falling back to the default
layout.
- Fix: validate the value is parseable JSON in `getItem()` itself
(matching the file's existing best-effort persistence philosophy) and
return `null` — same as a missing value — when it isn't.

## Evidence (Sentry, past week)
- [SUPABASE-APP-KA8](https://supabase.sentry.io/issues/7726278966/) —
`SyntaxError: Unexpected token 'K', "KV-OK" is not valid JSON`,
full-page crash via `globalErrorBoundary` on `/project/[ref]/sql/[id]`.

## Test plan
- [ ] Manually confirmed the existing `transformLayoutKey`/try-catch
behavior for a missing or inaccessible key is unchanged
- [ ] Considered adding a unit test for
`serverCompatibleLocalStorage.getItem()`, but it isn't currently
exported; happy to export it and add a test if reviewers want one

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

https://claude.ai/code/session_01RUrmUfMBpPqkgerh9onNTM

---
_Generated by [Claude
Code](https://claude.ai/code/session_01RUrmUfMBpPqkgerh9onNTM)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-09-14 13:39:54 -06:00
Sean Oliver d439ba57f4 feat(studio): mask HTML attributes in session replay (#48818)
## 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?

Hardening ahead of any decision to enable session replay, plus a
dependency bump. Follow-up to #48515.

### What's inside

- ~50 lines of logic: the callback, the `url()` pattern, and the theme
and SVG-reference gates
([session-replay.ts](https://github.com/supabase/supabase/pull/48818/changes#diff-b7e4f10387ee7a116dd1673f70a55c0ba066687ff2d228bc2320eba75a349fac))
- ~170 lines of allowlist, one attribute name per line, skimmable ([same
file](https://github.com/supabase/supabase/pull/48818/changes#diff-b7e4f10387ee7a116dd1673f70a55c0ba066687ff2d228bc2320eba75a349fac))
- ~150 lines of comments saying why each group is allowlisted, since a
wrong entry is a privacy or a fidelity bug ([same
file](https://github.com/supabase/supabase/pull/48818/changes#diff-b7e4f10387ee7a116dd1673f70a55c0ba066687ff2d228bc2320eba75a349fac))
- ~430 lines of tests, one case per policy decision
([session-replay.test.ts](https://github.com/supabase/supabase/pull/48818/changes#diff-f9feb872ad0136cf87c7e9fb2af72eb3f4019464c06f0b7dd050ffb85373ccb8))
- 1 line of dependency bump, plus its lockfile
([package.json](https://github.com/supabase/supabase/pull/48818/changes#diff-50d7c39a9430d37971aa76858165ab4f7921c4cc4340b28e9b673ce6982e63cf))

## What is the current behavior?

Session replay is disabled in every environment, and no recordings
exist. This is about what a recording *would* contain if it were ever
switched on.

Attributes are the one channel replay masking cannot reach. `maskTextFn`
only sees DOM text nodes, so a component interpolating customer data
into a `placeholder`, `title` or `aria-label` would be captured
verbatim. Before `posthog-js` 1.413.0 there was no hook for it at all,
and the only mitigation was blocking the element, which drops it from
the capture entirely.

Two places in Studio where that would apply:

- `CreateOrUpdateCustomProviderSheet.tsx:506-507` interpolates the
project's API host into both `value` and `placeholder`. The `value` is
masked. The `placeholder` is not.
- `FileExplorerHeader.tsx:185` renders `Search in
${currentFolderName}...`, a customer storage folder name.

The list is not complete. Any component echoing context into a tooltip
reproduces it, and the author has no reason to be thinking about replay.

Linear [GROWTH-1094](https://linear.app/supabase/issue/GROWTH-1094).
Blocks [GROWTH-1073](https://linear.app/supabase/issue/GROWTH-1073).

## What is the new behavior?

`maskAttributeFn` with a default-deny policy: an allowlist of the
attributes replay needs to render, everything else masked.

### Policy edge cases

- **rrweb's `rr_*` layout attributes have to be allowlisted
explicitly.** posthog-js only applies its own exemption for those when
`maskAllElementAttributes` does the masking. A callback does not get the
exemption.
- **HTML `id` is masked. SVG `id` passes.** `AreaChart.tsx:119` emits
`<linearGradient id="colorUv">` and references it as
`fill="url(#colorUv)"`, so masking it breaks the gradient. But Studio
also binds customer-named values to `id` (`bucket.id` is a storage
bucket name). Split on `element.namespaceURI`.
- **SVG reference attributes pass only fragment-only targets.** recharts
clips every series with `clip-path="url(#clipPath-<id>)"`, so
`clip-path`, `mask`, `filter`, `marker-*`, `fill` and `stroke` have to
survive. They accept external URLs too, so the policy checks the target
rather than allowlisting the attribute name.
- **The `url()` pattern consumes escaped delimiters and ignores case.**
A target containing a quote serializes as `\"` and one containing a
bracket as `\)`, so a naive `[^")]*` stops at the backslash and leaves
the tail of the URL recorded. `URL(...)` is the same function as
`url(...)`. A token the pattern cannot parse falls through to a masking
fallback rather than passing.
- **`url()` targets inside `style` are masked, keeping the
declarations.** The feedback widget puts `toPng(document.body)`, a
base64 PNG of the whole dashboard, into a `background-image`, and the
storage preview panes put signed object URLs there. No other masking
path covers those, because they are not text nodes, a canvas, a network
request or an `img src`.

The config also pins `maskAllElementAttributes: false`. Left unset it
resolves from the PostHog UI, and `true` discards `maskAttributeFn`
entirely.

The `posthog-js` floor rises to `^1.416.1`, the first version carrying
both attribute masking and the "coarse option wins" precedence.

This does not enable recording anywhere.

## Additional context

### Verification

Ran on the studio-staging preview against a live session: 817 seconds,
190 clicks, 82 keypresses. Staging has no server-side masking config, so
everything masked came from this code.

| Check | Result |
|---|---|
| Storage folder search placeholder | Asterisked. Pre-fix it read
`Search in <folder>...` |
| Custom auth provider sheet | Fully masked, including the callback URL
field |
| Canary folder name in event properties | 0 hits, with 51 events in the
session as the control |
| Console capture | `console_log_count: 0` despite the project having
`capture_console_log_opt_in: true` |
| Telemetry regression | None: `$pageview` x34, `$pageleave` x5,
`$groupidentify` x4, `$identify` x1 |

Recording was scoped to that one preview by an origin restriction plus a
URL trigger. Both were reverted afterwards along with the project
toggle.

The policy has 175 unit tests. Separately, the config was bundled with
esbuild and applied to a DOM reproducing Studio's serialized output (the
AreaChart gradient, a recharts `clip-path`, a lucide icon, an inline
`background-image`), and the chart, gradient fill and icon come out
pixel-identical.

### Known fidelity costs

- `img src` is masked, so images don't render in replay. Storage object
URLs are signed customer content.
- `ProviderIcon` renders its mark as `maskImage: url(<src>)` and
`normalizeIconPath` accepts absolute URLs, so provider icons don't
render either.

### Out of scope

rrweb records `<style>` element text without calling either masking
function, because its text-node serializer skips masking when the parent
is `STYLE`. This PR does not reach that channel. Fixed separately in
#50270 / [GROWTH-1229](https://linear.app/supabase/issue/GROWTH-1229).

`captureJsonLd` also defaults on as of PostHog's 2026-08-30 defaults,
which is a capture channel masking doesn't reach. Studio renders no
`ld+json`, so it's inert there, and pinning it off was left out to keep
this PR to its scope.

### The allowlist is the weak part

The policy is default-deny over attribute *names*, so its surface is
every attribute any shipped library emits, and that set grows with each
dependency. A miss is also invisible to these tests, which assert what
the function returns rather than whether some selector elsewhere still
matches. Both failure directions are reachable that way: an attribute
carrying customer data, and an attribute a stylesheet needs.

[GROWTH-1232](https://linear.app/supabase/issue/GROWTH-1232) tracks the
mechanism change: scope by namespace instead of by name, since 50 of the
159 entries exist only to serve SVG rendering, plus a conformance test
that derives the expected set from the codebase so a new dependency
fails CI rather than degrading a replay. Deliberately not done here,
since rewriting the mechanism of a privacy control buys maintainability
rather than correctness.
2026-09-14 09:48:58 -07:00