Commit Graph

4 Commits

Author SHA1 Message Date
Joshen Lim
a1686025b6 Joshenlim/fe 4291 keep unsaved notebooks accessible after page refresh (#49673)
## Context

Changes here adds a "Draft" state for notebooks with a new
`notebook-drafts` store - similar to how we handle query tabs in the
explorer.
This implies that if a user refreshes the tab while there's unsaved
changes to notebooks, the changes can be persisted locally and the user
will be able to continue from where they left off.

This also implies that If you create a new notebook (OR open an existing
notebook and make some changes) and refresh the browser, we no longer
show the native browser confirmation dialog about discarding changes.

We also reuse the existing confirmation dialog when saving a notebook if
its draft has diverged from the server side content - just updated the
language to be more generic rather than saying that the Assistant made
changes
<img width="429" height="238" alt="image"
src="https://github.com/user-attachments/assets/5c392aed-1633-4428-8060-28f495a01f04"
/>

Also fixes an unrelated issue - renaming a notebook should mark the
notebook as having unsaved changes (with the orange indicator)

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

## Summary by CodeRabbit

* **New Features**
* Unsaved notebook edits are saved locally and restored when reopening
Studio.
* Drafts are scoped by project and protected from server changes through
conflict detection.
* Notebook tabs indicate unsaved changes, including drafts from unsaved
notebooks.
* **Bug Fixes**
* Closing a tab with local edits prompts for confirmation and removes
its saved draft.
  * Notebook save state reflects the server-confirmed update time.
  * Conflict messages clearly describe changes made on the server.
* **Style**
  * Improved keyboard focus behavior for tab controls.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 17:17:25 +08:00
Charis
eabb87564b fix(studio): resolve dirty notebook save conflicts (#49540)
## Summary
- require an explicit choice before saving a notebook that diverged
while dirty
- let users save over assistant changes or discard their local edits,
with deleted notebooks recreating safely
- keep dismissals side-effect free and close deleted notebook tabs when
edits are discarded

## Testing
- pnpm --filter studio exec vitest run
components/interfaces/Explorer/__tests__/ExplorerNotebookTab.assistant-cache-invalidation.test.tsx
data/content/notebooks/notebook-cache.test.ts --reporter=dot
- pnpm --filter studio typecheck

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

- **New Features**
- Added conflict handling when server-side notebook changes overlap with
local edits.
- Users can overwrite, recreate, discard, or dismiss changes through a
confirmation dialog.
- Deleted notebooks can be recreated when saved, while discarded deleted
notebooks are automatically removed from open tabs.
  - Conflict dialogs remain open while an action is in progress.

- **Bug Fixes**
- Improved notebook cache cleanup to remove stale and unsaved notebook
data reliably.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-26 17:12:10 +08:00
Charis
de3a8799d6 fix(studio): invalidate notebook caches after assistant create/update (#49415)
## Summary
- The assistant's `create_notebook`/`update_notebook` tools run entirely
server-side, so an open notebook tab's React Query cache and Valtio
store never learn a write happened — the tab keeps showing stale content
until a manual reload.
- Adds `collectNotebookCacheEffects`/`applyNotebookCacheEffects`
(`apps/studio/lib/ai/notebook-cache-invalidation.ts`), which scan
finished assistant messages for completed
`create_notebook`/`update_notebook` tool calls and evict the affected
notebook via `evictNotebookFromCaches`
(`apps/studio/data/content/notebooks/notebook-cache.ts`), plus
invalidate the nav list.
- Wired into `createChatInstance`'s `onFinish` in
`state/ai-assistant-state.tsx`, with per-chat dedupe so replayed history
isn't reprocessed.
- Removes the cache entry outright rather than invalidating it, since a
remounting `useNotebookQuery` would otherwise read the stale cached
value synchronously before its refetch lands.
- Explicitly skips eviction when the open tab has unsaved local edits,
so an assistant write can't silently discard them.

Related: [FE-4235](https://linear.app/supabase/issue/FE-4235)

**Out of scope:** this only protects the client-side cache/store from
being clobbered after the fact. Preventing the assistant's
`update_notebook` tool call itself from overwriting a user's unsaved
edits (a data-layer conflict, not a cache-freshness one) is tracked
separately in [FE-4255](https://linear.app/supabase/issue/FE-4255).

## Test plan
- [x] `pnpm test:studio -- notebook-cache notebook-cache-invalidation
ai-assistant-state.notebook-cache-invalidation
ExplorerNotebookTab.assistant-cache-invalidation
ExplorerNotebookTabCoordinator` — all passing
- [x] Reproduction-first component test
(`ExplorerNotebookTab.assistant-cache-invalidation.test.tsx`) — verified
it fails without the fix (stale content persists) and passes with it
- [x] Regression test for the dirty-notebook guard (an edited, unsaved
notebook is left untouched by an assistant write)
- [x] `pnpm typecheck --filter=studio` / `pnpm lint --filter=studio`
clean


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

* **Bug Fixes**
* Notebook changes made through the AI assistant now appear correctly in
open notebook tabs and after reopening them.
* Saved notebook caches are refreshed after completed create or update
actions, preventing stale content from being displayed.
  * Unsaved notebook changes are preserved during cache cleanup.
  * Closing a notebook tab now consistently removes its cached content.

* **Tests**
* Added coverage for assistant-driven updates, remounts, duplicate
actions, project context changes, and cache behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 14:15:27 +08:00
Charis
48cc37f0d2 refactor(studio): extract notebook cache eviction helper (#49414)
## Summary

Part 1 of the FE-4247 stack
([FE-4247](https://linear.app/supabase/issue/FE-4247/assistant-invalidate-cache-after-notebook-editdeletion)).
Pure refactor, no behavior change — extracts the notebook cache eviction
logic that `ExplorerNotebookTabCoordinator` had open-coded into a shared
helper, so the upcoming assistant create/update/delete cache
invalidation (PR 2/3 in the stack) can reuse it instead of duplicating
the two-cache-layer eviction dance.

- New `evictNotebookFromCaches({ queryClient, projectRef, id, mode })`
in `apps/studio/data/content/notebooks/notebook-cache.ts`. `mode:
'refresh' | 'remove'` selects `invalidateQueries` vs `removeQueries` on
`contentKeys.resource`. Drops the notebook from `notebooksState` only
when its status is `'saved'`, matching the original open-coded guard
exactly. Returns whether it evicted, so callers can branch.
- `ExplorerNotebookTabCoordinator` now calls the helper with `mode:
'remove'` instead of inlining the logic.

## Test plan

- [x] `pnpm test:studio -- notebook-cache
ExplorerNotebookTabCoordinator` — new helper tests
(refresh/remove/dirty-guard/unknown-id) and existing coordinator tests
all pass
- [x] `pnpm typecheck --filter=studio`
- [x] `pnpm lint --filter=studio` — 0 errors, no new warnings

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

- **Bug Fixes**
- Improved detection of unsaved notebook changes for tab indicators and
close confirmations.
- Empty, never-saved notebooks are no longer included in discard
prompts.
- Improved cache cleanup when closing saved notebooks while preserving
unsaved work.
  - Added safeguards for missing notebook records.
- **Tests**
- Added coverage for notebook cache refresh, removal, preservation, and
no-op scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-24 15:59:41 +08:00