Commit Graph
7 Commits
Author SHA1 Message Date
Saxon Fletcher 39c3960215 fix(studio): harden Explorer query persistence (#49039)
<img width="1690" height="1034" alt="image"
src="https://github.com/user-attachments/assets/2dc5b10b-8e64-413b-861e-8b0b62e2d880"
/>

## Summary

- debounce draft persistence and flush pending edits when the page exits
- validate untrusted local storage with Zod, recover from malformed
entries, and retain the 50 most recently updated drafts
- harden route restoration, recent-item routing, close confirmation, and
local cleanup
- disable execution while project or replica data is resolving and fail
closed for missing replicas
- use an HTTP-safe UUID generator for self-hosted Studio
- adopt the upstream Explorer toolbar title API
- expand component and state coverage for persistence and execution
behavior

## To test

1. Open Explorer, select **Run SQL**, then enter SQL and rename the
query.
2. Reload the page and confirm the draft is restored; close its tab and
confirm it is discarded after the prompt.

## Why

This layer makes local-only Explorer drafts resilient to rapid edits,
reloads, stale browser data, and tab lifecycle edge cases.

## Impact

Queries remain local-only, and closing their tabs discards them after
confirmation. Save-as-notebook functionality remains intentionally out
of scope.

## Validation

- fresh non-incremental Studio TypeScript check
- 68 Vitest tests pass across Explorer, query sources, tabs layout, and
query/tab state
- Studio ESLint ratchet and Prettier check both clean


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

## Summary by CodeRabbit

- **New Features**
- Explorer query drafts now save automatically and restore reliably
across navigation, tab closures, page exits, and visibility changes.
- Recent query items now open directly to their associated Explorer
query.
- Logs time-range selections are handled consistently, including custom
ranges and preset matching.

- **Bug Fixes**
  - Prevented stale query loading states when switching between queries.
  - Improved handling of invalid or outdated saved drafts.
  - Limited saved drafts to the 50 most recently updated queries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-14 13:16:54 +07:00
Saxon FletcherandClaude Opus 5 85db94762c feat: add routed assistant chat tabs to Explorer (#49031)
<img width="1695" height="1042" alt="image"
src="https://github.com/user-attachments/assets/b02e64ff-32ac-4aee-bda5-00b56f3a29e5"
/>


## Summary

- add routed Explorer chat pages backed by explicit assistant chat IDs
- wait for persisted assistant state before creating chats so cold-load
creation is not overwritten
- register chat tabs and keep tab labels, navigation, close behavior,
and missing-chat cleanup in sync
- create and branch Explorer chats without changing the assistant
sidebar selection
- support both Next.js and TanStack Router paths

This is PR 2 of 3 and is stacked on #48973. Review and merge #48973
first. The Explorer discovery, toolbar, and cross-surface entry points
follow in #49032. This PR focuses purely on setting up chat tab types,
routes and assistant conversation.

## To Test
- Create a new assistant chat via the assistant sidebar, send a message
etc
- Copy the chat id
- Visit /explorer/chat/[id]
- Verify chat shows up, you can send more messages, chat is synced
across tab and sidebar
- Close the tab

## Test plan

- `mise exec node@22 -- pnpm --dir apps/studio exec tsc --noEmit`
- focused Vitest suite: 4 files / 30 tests covering assistant hydration,
chat creation, routed chat rendering, and tab lifecycle
- ESLint on changed TypeScript files
- Prettier check on changed source files


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

* **New Features**
* Added Explorer chat pages with support for opening, selecting, and
branching chats.
* Added chat tabs, stable navigation, chat icons, and fallback behavior
when tabs are closed.
* Added chat creation that waits for assistant state to finish loading.
* **Bug Fixes**
  * Removed tabs for deleted or unavailable chats.
  * Improved editor tab navigation and history clearing behavior.
* **Tests**
* Added coverage for chat routing, tab management, chat creation, and
assistant-state loading.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:19:42 +10:00
Saxon Fletcher 75d16f360f feat(studio): add Explorer query tabs (#49038)
<img width="1693" height="1037" alt="image"
src="https://github.com/user-attachments/assets/51fdf618-f06e-45ca-bf30-e1307dfbb372"
/>


## Stack

Depends on #49041. Followed by #49028.

## Summary

- add a dedicated ad-hoc query tab type and route under Explorer
- connect query tabs to the shared `QueryEditor` through a `QueryTab`
lifecycle adapter
- add local query draft/result state and restore query tabs from their
routes
- confirm before closing populated local-only drafts and clean up their
state on close

## To test

1. Open Explorer, select **Run SQL**, enter `select 1`, and run the
query.
2. Rename the query, reload the page, then close the tab and confirm the
discard prompt appears.

## Why

Explorer needs a lightweight place to run SQL without creating a
snippet. This layer adds the query-tab lifecycle on top of the shared
editor foundation.

## Impact

Queries in this layer run against the selected project's primary
database. Drafts are local to the browser and are discarded when their
tabs are closed.

## Validation

- fresh non-incremental Studio TypeScript check
- 22 focused tests across query draft state, tab state, and notebook tab
registration

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

* **New Features**
* Added support for creating, opening, editing, and running SQL queries
in Explorer.
  * Added project-scoped persistence for query drafts and results.
  * Added dedicated query routes, query icons, and query tabs.
  * Added unsaved-changes warnings when closing query tabs.
  * Added a pinned Explorer Home tab and “New query” option.
  * Improved notebook tab registration and editor tab organization.

* **Bug Fixes**
  * Improved tab navigation, closing behavior, and layout.

* **Tests**
* Added coverage for query persistence, cleanup, restoration, and tab
navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-13 23:03:08 +07:00
Charis b3c5c9fc04 feat(studio): logs snippets in SQL editor nav, search, and tabs (#48457)
## What

PR 7 of the SQL-editor query-source (Database vs Logs) stack. Surfaces
`log_sql` snippets as a distinct query source across the SQL editor
sidebar. Stacked on **`charislam/toolbar-ui-creation-flow`** (PR 6 —
toolbar UI + creation flow); review/merge that first.

Nothing is user-visible until the flags roll out — every entry point
requires **both** `sqlEditorLogsSource` **and** `otelLegacyLogs`.

## Changes

- **Nav** — a flag-gated **Logs** section (`LogsSnippetsSection`) backed
by its own single-type `log_sql` query. The active snippet is injected
only into the section it belongs to, via a shared
`withActiveSnippet(snippets, active, belongsPredicate)` helper (also
DRYs the private/favorites/shared injections).
- **Search** (`SearchList`) — a **Logs** result group with a shared,
extracted `SqlSnippetTree`; the "N results found" count now sums
database + logs, with loading/empty states covering both queries.
- **Tabs** — an immutable `sqlSource` field on tab/recent-item metadata
(set at tab creation, lazily backfilled once the snippet loads via
`useEffectEvent`), and a distinct `ScrollText` icon via a shared
`LogsSnippetIcon`. Tab cleanup treats `log_sql` tabs as live and only
prunes them when logs data is authoritative (`canPruneLogsTabs`), so a
disabled/erroring logs query never wrongly deletes logs tabs or blocks
database-tab cleanup.
- **Data layer** — `useSqlSnippetsQuery` gains an optional `type` param
so logs reuse the same `SnippetWithContent` shape as the other sections
(no casts).

## Tests

- `state/tabs.test.ts` — `sqlSource` backfill + creation-time
carry-through.
- `components/layouts/Tabs/Tabs.utils.test.tsx` — cleanup prunes stale
database/logs snippets, keeps live ones, and preserves logs tabs when
logs data isn't authoritative.

## Verification

- `pnpm --filter studio typecheck` ✓
- `pnpm --filter studio run lint:ratchet` ✓
- `pnpm test:studio` (affected suites) ✓

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

* **New Features**
* Added a collapsible Logs section to the SQL editor sidebar for
browsing, sorting, selecting, renaming, and deleting log queries.
* Expanded SQL search with separate, paginated results for database and
log queries.
* Added dedicated log-query icons across navigation, tabs, previews, and
recent items.
* **Bug Fixes**
* Improved tab and recent-item cleanup while preserving active log
queries and accurate source metadata.
* **Tests**
* Added coverage for log tab cleanup and SQL source metadata
synchronization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 09:02:39 -04:00
Charis 1987f19d0a feat(sql-editor): add manual save feature preview (#47745)
## What

Adds an opt-in **SQL Editor manual save** feature preview that switches
the SQL Editor from autosaving every edit to saving only on demand, and
hardens the tab-close flow so unsaved edits are handled correctly.

## Changes

**Feature preview**
- New `sqlEditorManualSave` flag + `UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE`
local-storage toggle, wired into the Feature Preview modal with an
explanatory panel.
- `useIsSqlEditorManualSaveEnabled` gates behavior on both the flag and
the user's preview opt-in.

**Editor toolbar**
- Save button (with `Cmd+S`) next to Run, plus an autosave status
indicator showing dirty/saving/saved state and a shortcut to disable
autosave (emits a `sql_editor_autosave_disable_clicked` telemetry
event).

**Discard on close**
- Closing a snippet tab with unsaved edits prompts for confirmation and,
on confirm, actually discards the local edits and evicts the cached
server copy so the snippet reopens clean.

**Decouple tab layout from SQL specifics**
- Tabs store gains a generic per-type close-handler registry
(`registerTabCloseHandler` / `getCloseConfirmation` / `closeTabs`). The
SQL editor registers its discard + confirmation behavior from the save
coordinator.
- Low-level `removeTab`/`removeTabs` (rename/move re-keying, stale
cleanup) intentionally do **not** trigger discard.
- Adds `statusOnDiscard` lifecycle transition and `clearSnippetContent`
store action.

## Testing
- `pnpm --filter=studio typecheck` — clean.
- Added unit tests for the close-handler registry (fires on single/multi
close, skips re-keying/cleanup removals, respects tab type, selects
confirmation copy, unregisters cleanly).

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

* **New Features**
* Added a SQL editor manual-save preview with a “Save” button and
`Cmd+S`, plus a modal option to disable manual-save/preview.
* Added “unsaved changes” tab status indication when manual-save is
enabled.
* Introduced tab-type-specific close confirmations (shown only when
needed).
* **Bug Fixes**
* In manual-save mode, closing a SQL tab with unsaved edits now clears
local snippet content and refreshes it on reopen.
* **Tests**
  * Added coverage for tab close handlers and confirmation behavior.
* **Chores**
* Added a persisted setting allowlist entry and tracked autosave-disable
clicks via telemetry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-09 08:37:38 -04:00
9af6e65df4 fix(studio): DOM-nesting hydration errors, ghost deleted-snippet nav, and migrations query 400s (#47667)
App-level fixes that reproduce on BOTH the Next and TanStack builds —
split out of #47657 (which stays TanStack-only) for reviewability. All
were found by a full-site click-through of the dashboard.

## Invalid HTML nesting (React 19 "will cause a hydration error" console
errors)

- **FormLayout description rendered in a `<p>`**
(`packages/ui-patterns`): consumers pass arbitrary JSX (the RowEditor's
`created_at` timezone note passes a `<div>` with `<p>`s) →
`<p>`-in-`<p>` / `<div>`-in-`<p>`. Container is now a `<div>` with
identical classes (Tailwind preflight makes them render the same).
- **Switch toggles nested inside Tooltip trigger buttons**
(button-in-button) in ColumnEditor ("Allow Nullable" + "Is Unique"),
ExtensionRow, and PublicationsTableItem → repo-standard `TooltipTrigger
asChild` + `<div>` wrapper.
- **Saved log queries rendered a `<div>` directly inside `<tbody>`**
(`/logs/explorer/saved`) → rows are now proper `<tr><td colSpan>`
wrappers; the component itself is untouched (it's valid in its sidebar
usage).
- **Nested anchors in observability metric cards**: a card-level
`<Link>` wrapped MetricCard's "More information" `<Link>` (identical
URLs) → the chevron affordance renders as a `<span>` when no `href` is
passed; clicks bubble to the card link, tooltips preserved.
Design-system standalone usage unaffected.
- **`objectFit="cover"` passed to modern `next/image`** on the featured
integration card (unknown-prop warning) — the className already had
`object-cover`; prop dropped.

## Ghost dead-snippet after deletion

Deleting the active SQL snippet left its id in `useDashboardHistory`
(`history.sql`), so the "SQL Editor" nav item navigated to
`/sql/<deleted-id>` — content fetch 404s, no editor pane renders, and a
phantom tab reappears. Fixed both ends: delete flows now purge dashboard
history (and the tabs store clears a stale `previewTabId`), and
`/sql/[id]` treats a snippet 404 as "clean up + `router.replace` to
`/sql/new` + toast" instead of rendering the dead state. Unit tests for
the store/history cleanup.

## `pg-meta` migrations query 400s on every project load

`ActivityStats` on project home runs the migrations list query, whose
SQL was a bare `select * from supabase_migrations.schema_migrations` —
that table only exists once a migration has run, so every other project
logged a failed `?key=migrations` request on every load (visible in
production consoles too). The SQL is now guarded with `to_regclass` +
`query_to_xml` (same pattern as the advisor lints' `storage.buckets`
guard), returning zero rows instead of erroring; legacy version-only
tables still work. Tested against real dockerized Postgres (absent
table, populated ordering, special chars, legacy schema) + MSW hook
tests.

Found and verified via /test-supabase-local (browser click-through +
console audit on both builds).

## To test

Console must stay free of React DOM-nesting errors ("cannot be a
descendant of" / "cannot contain a nested") on each surface:

1. Table editor → Insert row panel (`created_at` field renders its
timezone note) and Edit column panel ("Allow Nullable"/"Is Unique"
tooltips still hover).
2. `/database/extensions` and `/database/publications` → toggle switches
render, tooltips hover.
3. `/logs/explorer/saved` (with ≥1 saved query) → rows render full-width
inside the table, hover shows Actions.
4. `/observability` → no nested-anchor error on load; card body click
and the chevron both navigate; label help-icons still show tooltips.
5. `/integrations` → no `objectFit` unknown-prop warning; featured card
images still cover.
6. **Ghost snippet**: open a SQL snippet → delete it via the sidebar →
click the "SQL Editor" nav item → lands on `/sql/new` (no phantom tab,
no 404 content fetch). Direct-load `/sql/<random-uuid>` → toast +
redirect to `/sql/new`.
7. **Migrations 400**: load project home with a project that has never
run a migration → the `pg-meta/<ref>/query?key=migrations` request
returns **200** with `[]` (previously a 400 on every load). Database →
Migrations still lists real migrations when they exist.


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

## Summary by CodeRabbit

* **Bug Fixes**
* Deleted SQL snippets are fully removed from dashboard history and
stale editor/tab state; users are redirected with a toast.
  * Closing preview tabs no longer leaves stale references.
* Improved toggle/tooltip/dialog interactions to avoid broken UI,
including metric headers showing tooltips even without direct links.
* Migrations display safely when migration tables/relations are missing.

* **UI Improvements**
* Refreshed layout for saved queries, form descriptions, and integration
imagery.

* **Tests**
* Added coverage for snippet history cleanup, tab removal, migrations
SQL behavior, and query edge cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


---

### Review feedback: `query_to_xml` breaks on Multigres (Ivan)

The defensive migrations query (added here to stop the `?key=migrations`
400 when the table doesn't exist yet) originally guarded with
`query_to_xml`, which is forbidden through Multigres's pooler (MUL-736 /
PSQL-1318). Rewritten without `query_to_xml`/`xmltable` using the
splinter#170 pattern: a PL/pgSQL `do` block guarded by `to_regclass`
(PL/pgSQL defers planning, so a missing table never errors) stashes the
rows into a transaction-local GUC via `set_config`, and a trailing
`select` reads them back with `jsonb_array_elements`. Verified that
postgres-meta sends the whole SQL as one simple-query string → single
implicit transaction → the local GUC survives to the `select` and
doesn't leak into the pooled connection. 6/6 dockerized-Postgres tests
(absent table → `[]`, populated/ordered/special-chars, legacy
version-only table, full pg-meta-shaped multi-statement string, GUC
non-leakage).

Note (out of scope, pre-existing):
`packages/pg-meta/src/sql/studio/advisor/lints.ts` still uses
`query_to_xml` — a separate pre-existing Multigres risk that should get
its own splinter-pattern sync.

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Saxon Fletcher <saxonafletcher@gmail.com>
2026-07-08 12:32:11 +08:00
Danny White 87cd172567 fix: keep recent items labels in sync after renames (#44155)
## What kind of change does this PR introduce?

Bug fix.

## What is the current behavior?

The `Recent items` list shown in the table editor empty-state can keep
showing an old label after a table or SQL snippet is renamed.

That UI is mounted from:
- `/project/[ref]/editor`
- `/project/[ref]/editor/new`

## What is the new behavior?

- Re-adding an existing recent item now refreshes its label and metadata
instead of only bumping the timestamp.
- Updating an open tab label now also updates the matching recent item,
so rename flows stay aligned with the recent-items list.
- The fix is applied in the shared tabs store, so table and SQL editor
tab state stay consistent even though the visible `Recent items` UI
currently appears in the table editor empty-state.
- Adds a small store-level regression test for both cases.

| Before | After |
| --- | --- |
| <img width="1024" height="563" alt="test Table Editor Mallet Toolshed
Supabase-DA49998A-FE09-4197-8EE7-5D5366FABDC7"
src="https://github.com/user-attachments/assets/d0475437-4486-4581-82f8-a84b97b08250"
/> | <img width="1024" height="563" alt="test Table Editor Mallet
Toolshed Supabase-8F2B6CC9-0721-4348-A094-EE94D6919B08"
src="https://github.com/user-attachments/assets/f055be35-4373-4b1b-a2e1-ffe59f12c177"
/> |
2026-03-25 16:03:17 +08:00