From 12ed08e69077e27d3de11f139a017496a0e0cef0 Mon Sep 17 00:00:00 2001 From: Mathias Picker <48158184+MathiasWP@users.noreply.github.com> Date: Mon, 24 Aug 2026 18:17:08 +0200 Subject: [PATCH] Open where you left off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collections, requests and responses were already durable. Everything about looking at them was not: the selected endpoint, both panes' tabs, the sidebar tab, which loader folders were expanded, and whatever had been typed into the scratch request. Every launch started on an empty scratch request with the sidebar back on Collections — a small thing after a deliberate quit, and a rude one after an auto-update restart nobody asked for. All of it now lives in `session.svelte.ts`, one blob in local storage, restored before the first frame rather than a moment after it, so the window opens as it was rather than visibly rearranging itself. A stored endpoint that no longer exists falls back to the scratch request rather than leaving the pane blank. An update restart also flushes the debounced section writes now. It never did: Rust holds an exit long enough for the frontend to save, but only for an exit it did not ask for itself, and `relaunch` carries a restart code that sails past that handler. The last few hundred milliseconds of edits went with the old process. The same flush runs on `pagehide`, covering a webview torn down without going through either quit path. Window geometry needed nothing — tauri-plugin-window-state already writes it on exit, and the restart path does run through exit. --- .changeset/open-where-you-left-off.md | 13 ++ src/lib/components/Sidebar.svelte | 40 ++-- src/lib/session.svelte.ts | 263 ++++++++++++++++++++++++++ src/lib/update.svelte.ts | 10 + src/routes/+page.svelte | 70 ++++--- tests/e2e/session.spec.ts | 153 +++++++++++++++ 6 files changed, 504 insertions(+), 45 deletions(-) create mode 100644 .changeset/open-where-you-left-off.md create mode 100644 src/lib/session.svelte.ts create mode 100644 tests/e2e/session.spec.ts diff --git a/.changeset/open-where-you-left-off.md b/.changeset/open-where-you-left-off.md new file mode 100644 index 0000000..55bb0ca --- /dev/null +++ b/.changeset/open-where-you-left-off.md @@ -0,0 +1,13 @@ +--- +'fiber': patch +--- + +Open where you left off. + +Collections, requests and responses were already durable. Everything about *looking* at them was not: which endpoint was open, which tab of it, which sidebar tab, which loader folders were expanded, and whatever had been typed into the scratch request. Every launch started on an empty scratch request with the sidebar back on Collections — which is a small thing after a deliberate quit, and a rude one after an auto-update restart nobody asked for. + +All of it is now stored, and restored before the first frame rather than a moment after it, so the window opens as it was rather than visibly rearranging itself. A stored endpoint that no longer exists — deleted elsewhere, or dropped by a loader — falls back to the scratch request instead of leaving the pane blank. + +An update restart also flushes the debounced section writes now. It never did: Rust holds an exit long enough for the frontend to save, but only for an exit it did not ask for itself, and a restart carries a code that sails straight past that. The last few hundred milliseconds of edits went with the old process. The same flush runs on `pagehide`, which covers a webview torn down and brought back without going through either quit path. + +Window size and position needed nothing — `tauri-plugin-window-state` already writes those on exit, and the restart path does run through exit. diff --git a/src/lib/components/Sidebar.svelte b/src/lib/components/Sidebar.svelte index a5c67c2..d6980ff 100644 --- a/src/lib/components/Sidebar.svelte +++ b/src/lib/components/Sidebar.svelte @@ -18,6 +18,7 @@ type DragHint } from '$lib/dnd.svelte'; import { history, type HistoryEntry } from '$lib/history.svelte'; + import { session } from '$lib/session.svelte'; import { theme } from '$lib/theme.svelte'; import DotLoader from '$lib/components/DotLoader.svelte'; import McpTab from '$lib/components/McpTab.svelte'; @@ -32,8 +33,6 @@ let { onOpenSettings, onPickHistory }: Props = $props(); - let tab = $state<'collections' | 'history' | 'mcp'>('collections'); - // Read from the bundle rather than package.json, so what the footer shows is // the version that is actually running — which is the number to quote when // something misbehaves. @@ -211,26 +210,27 @@ return order.map((tag) => ({ tag, rows: groups.get(tag)! })); } + function tagKey(sectionId: string, tag: string): string { + return `${sectionId}\0${tag}`; + } + /** * Folders the user has opened. Closed is the default: a loader can report * hundreds of endpoints across a dozen tags, and opening a collection to a * wall of them is no better than not grouping at all. The folder names are * the map; you open the one you want. + * + * Which ones are open is kept in the session rather than here, so the + * folders you had open are still open the next time the app starts. */ - let openTags = $state>({}); - - function tagKey(sectionId: string, tag: string): string { - return `${sectionId}\0${tag}`; - } - function tagOpen(sectionId: string, tag: string): boolean { if (searching || !tag) return true; - return openTags[tagKey(sectionId, tag)] ?? false; + return session.openTags[tagKey(sectionId, tag)] ?? false; } function toggleTag(sectionId: string, tag: string): void { const key = tagKey(sectionId, tag); - openTags[key] = !openTags[key]; + session.openTags[key] = !session.openTags[key]; } function loadMoreEndpoints(sectionId: string): void { @@ -303,7 +303,7 @@ * kept showing that entry rather than the request's own current response. */ function showCollections() { - tab = 'collections'; + session.sidebarTab = 'collections'; history.stopViewing(); } @@ -312,7 +312,7 @@ * were looking at stops overriding the response pane. */ function showMcp() { - tab = 'mcp'; + session.sidebarTab = 'mcp'; history.stopViewing(); } @@ -807,7 +807,7 @@