fix: follow a saved page item in the tab's own workspace and restore in-frame rows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-09-17 17:49:39 +02:00
co-authored by Claude Opus 5
parent 09ab9b8e6a
commit 6fb48c64df
6 changed files with 26 additions and 15 deletions
@@ -473,6 +473,12 @@
current.path = npath
}
/** The path the resource has in `ws`: after a save, the one it was saved under. Each
* workspace-specific version keeps its own, so the selected one says nothing about `ws`. */
export function pathIn(ws: string): string | undefined {
return initialStates[ws]?.path
}
/** Whether the write landed. It toasts its own failure, so most callers ignore this;
* one that follows the save with bookkeeping of its own has to know not to. */
export async function save(): Promise<boolean> {
@@ -41,8 +41,9 @@
inline?: boolean
onRestored?: () => void
/** Fires after Save has written, for a caller showing state derived from the
* resource — `onRestored` only covers restoring an old version. */
onSaved?: () => void
* resource — `onRestored` only covers restoring an old version. `path` is where the
* resource now lives in this drawer's workspace, undefined when the save failed. */
onSaved?: (path: string | undefined) => void
} = $props()
let drawer: Drawer | undefined = $state()
@@ -53,7 +54,8 @@
let resourceEditor:
| {
save: () => void
save: () => Promise<boolean>
pathIn: (ws: string) => string | undefined
localDraftDeployed: () => unknown
localDraftCurrent: () => unknown
discardLocalDraft: () => void
@@ -223,10 +225,10 @@
// Closed before the write is awaited, the way it always was: `save()` toasts its
// own failures and never rejects, so waiting would only add visible lag to every
// caller of this drawer. `onSaved` still fires after the write lands.
const saved = resourceEditor?.save()
const editor = resourceEditor
const saved = editor?.save()
drawer?.closeDrawer()
await saved
onSaved?.()
onSaved?.((await saved) ? editor?.pathIn(effectiveWorkspace) : undefined)
}}
disabled={!canSave}
>
@@ -50,7 +50,8 @@
/** Render in place, filling the parent, with no drawer or close button — for a host
* that gives the editor a whole pane. */
inline?: boolean
/** Fires once a save lands, with the path the variable now lives at. */
/** Fires once a save lands, with the path the variable now lives at in `workspace` —
* not in the workspace-specific version selected, which can be another's. */
onSaved?: (path: string) => void
} = $props()
// Sole ambient read in this file: the acting workspace is an input, and only its
@@ -271,7 +272,7 @@
async function save(): Promise<void> {
const dirty = dirtyWorkspaces
const savedPath = current?.path ?? editPath ?? ''
const savedPath = (curWs ? states[curWs]?.draft?.path : undefined) ?? editPath ?? ''
try {
for (const ws of dirty) {
const s = states[ws].draft!
@@ -144,7 +144,9 @@
bind:this={resourceEditor}
inline
workspace={workspaceId}
on:refresh={(e) => onSaved(typeof e.detail === 'string' ? e.detail : undefined)}
onSaved={(path) => {
if (path !== undefined) onSaved(path)
}}
onRestored={() => savedNonce++}
/>
{:else if triggerKey}
@@ -210,11 +210,11 @@ export function hydratePreviewTabs(session: {
seen.add(t.id)
// Rebuilt field-by-field so stray properties on old saved records (e.g. the
// retired `pinned` flag) don't survive hydration and get persisted back.
// A list page saved with a row's drawer open comes back as that row's own tab.
const url = pageItemLocation(t.url)
// A list page saved with a row's drawer open comes back as that row's own tab, whether the
// row was asked for (`url`) or opened inside the frame (only its observed `loc` says so).
const loc = t.loc || t.url
const stale = parsePageItemRoute(url) || pageItemLocation(loc) !== loc
tabs.push({ id: t.id, url, loc: stale ? url : loc })
const item = [t.url, loc].map(pageItemLocation).find((u) => parsePageItemRoute(u))
tabs.push(item ? { id: t.id, url: item, loc: item } : { id: t.id, url: t.url, loc })
}
if (tabs.length > 0) {
const wantActive = session.activePreviewTabId
@@ -245,13 +245,13 @@ describe('page item tabs', () => {
const snap = hydratePreviewTabs({
previewTabs: [
{ id: 'a', url: '/schedules#u/me/daily', loc: '/schedules?path=u#u/me/daily' },
// A drawer opened inside the frame, with the command still on the list.
// A drawer opened inside the frame: the command is still the bare list.
{ id: 'b', url: '/variables', loc: '/variables#u/me/token' }
]
})
expect(snap.tabs).toEqual([
{ id: 'a', url: 'pageitem:schedule/u%2Fme%2Fdaily', loc: 'pageitem:schedule/u%2Fme%2Fdaily' },
{ id: 'b', url: '/variables', loc: '/variables' }
{ id: 'b', url: 'pageitem:variable/u%2Fme%2Ftoken', loc: 'pageitem:variable/u%2Fme%2Ftoken' }
])
})
})