mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
fix(sessions): send the removed item's own tab back to the list, not the active one
A discard resolves asynchronously — the schedule's awaits a fetch of the runnable — so by the time it lands the user may be looking at another tab. Routing removal through `onBack` moved whichever tab was active, taking that one to the list while the tab whose item was gone stayed on it. Removal now retargets by tab id, as the deletion and rename paths already do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KcyBRxE8j3ujCYkum9KrC4
This commit is contained in:
co-authored by
Claude Opus 5
parent
420beeadd2
commit
3f8485060d
@@ -179,6 +179,16 @@
|
||||
: undefined
|
||||
)
|
||||
|
||||
// The item is gone (a draft-only one whose draft was discarded), so the tab has
|
||||
// the same destination as `backToList` — but bound to this tab by id, not to
|
||||
// whichever is active: a discard completes asynchronously, and by then the user
|
||||
// may be looking at another tab, which must not be the one sent to the list.
|
||||
const returnToList = $derived(
|
||||
slot.kind === 'entity' && runtime
|
||||
? () => runtime.previewTabs.retargetTabTo(tab.id, entityListHref(whereIs(tab)))
|
||||
: undefined
|
||||
)
|
||||
|
||||
// Follow a rename: the editor stays mounted and keeps editing the item, but the
|
||||
// tab, its label, the chat's ACTIVE PREVIEW and the draft key all address it by
|
||||
// path — so they have to move with it, or they name an item that no longer exists.
|
||||
@@ -377,6 +387,7 @@
|
||||
path={slot.path}
|
||||
{workspaceId}
|
||||
onBack={backToList}
|
||||
onRemoved={returnToList}
|
||||
onRenamed={retargetTo}
|
||||
/>
|
||||
{/await}
|
||||
@@ -388,6 +399,7 @@
|
||||
path={slot.path}
|
||||
{workspaceId}
|
||||
onBack={backToList}
|
||||
onRemoved={returnToList}
|
||||
onRenamed={retargetTo}
|
||||
/>
|
||||
{/await}
|
||||
@@ -399,6 +411,7 @@
|
||||
path={slot.path}
|
||||
{workspaceId}
|
||||
onBack={backToList}
|
||||
onRemoved={returnToList}
|
||||
onRenamed={retargetTo}
|
||||
/>
|
||||
{/await}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
path,
|
||||
workspaceId,
|
||||
onBack,
|
||||
onRemoved,
|
||||
onRenamed
|
||||
}: {
|
||||
/** The resource this tab edits (the row its location deep-links). */
|
||||
@@ -15,6 +16,10 @@
|
||||
workspaceId: string
|
||||
/** Back to the list this editor was reached through; the tab replaced it. */
|
||||
onBack?: () => void
|
||||
/** The item is gone — a draft-only one whose draft was discarded. Distinct from
|
||||
* `onBack`, which moves whichever tab is active: a discard can complete after
|
||||
* the user has moved on, and only this tab is the one to send back. */
|
||||
onRemoved?: () => void
|
||||
/** The item was saved under a different path. The tab addresses it by path —
|
||||
* as do its label, the chat's ACTIVE PREVIEW and the draft key — so the tab
|
||||
* has to follow, or all four keep naming an item that no longer exists. */
|
||||
@@ -38,7 +43,7 @@
|
||||
useDrawer={false}
|
||||
workspace={workspaceId}
|
||||
{onBack}
|
||||
onRemoved={onBack}
|
||||
{onRemoved}
|
||||
onSaved={(saved) => {
|
||||
if (saved && saved !== path) onRenamed?.(saved)
|
||||
}}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
path,
|
||||
workspaceId,
|
||||
onBack,
|
||||
onRemoved,
|
||||
onRenamed
|
||||
}: {
|
||||
/** The schedule this tab edits (the row its location deep-links). */
|
||||
@@ -18,6 +19,10 @@
|
||||
workspaceId: string
|
||||
/** Back to the list this editor was reached through; the tab replaced it. */
|
||||
onBack?: () => void
|
||||
/** The item is gone — a draft-only one whose draft was discarded. Distinct from
|
||||
* `onBack`, which moves whichever tab is active: the discard awaits a reload of
|
||||
* the runnable, by which time the user may be looking at another tab. */
|
||||
onRemoved?: () => void
|
||||
/** The item was saved under a different path. The tab addresses it by path —
|
||||
* as do its label, the chat's ACTIVE PREVIEW and the draft key — so the tab
|
||||
* has to follow, or all four keep naming an item that no longer exists. */
|
||||
@@ -64,7 +69,7 @@
|
||||
bind:this={editor}
|
||||
useDrawer={false}
|
||||
showDraftBanner
|
||||
onRemoved={onBack}
|
||||
{onRemoved}
|
||||
onUpdate={(saved: string | undefined) => {
|
||||
generation++
|
||||
// A draft-only schedule opens with its path editable (saving CREATEs),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
path,
|
||||
workspaceId,
|
||||
onBack,
|
||||
onRemoved,
|
||||
onRenamed
|
||||
}: {
|
||||
/** The variable this tab edits (the row its location deep-links). */
|
||||
@@ -15,6 +16,10 @@
|
||||
workspaceId: string
|
||||
/** Back to the list this editor was reached through; the tab replaced it. */
|
||||
onBack?: () => void
|
||||
/** The item is gone — a draft-only one whose draft was discarded. Distinct from
|
||||
* `onBack`, which moves whichever tab is active: a discard can complete after
|
||||
* the user has moved on, and only this tab is the one to send back. */
|
||||
onRemoved?: () => void
|
||||
/** The item was saved under a different path. The tab addresses it by path —
|
||||
* as do its label, the chat's ACTIVE PREVIEW and the draft key — so the tab
|
||||
* has to follow, or all four keep naming an item that no longer exists. */
|
||||
@@ -38,7 +43,7 @@
|
||||
useDrawer={false}
|
||||
workspace={workspaceId}
|
||||
{onBack}
|
||||
onRemoved={onBack}
|
||||
{onRemoved}
|
||||
onSaved={(saved) => {
|
||||
if (saved && saved !== path) onRenamed?.(saved)
|
||||
}}
|
||||
|
||||
@@ -130,11 +130,9 @@ const seedMisses = new Set<string>()
|
||||
// Cells whose last discard removed the item itself (see `takeDraftOnlyDiscard`).
|
||||
const draftOnlyDiscards = new Set<string>()
|
||||
|
||||
// Both sets above are read by the tool-completion listener right after the write
|
||||
// that wrote them — but only while something is listening, and nothing is when the
|
||||
// user is not in a session. Capped so an unread marker cannot accumulate: the
|
||||
// oldest is dropped, since a marker is only ever meaningful to the action that
|
||||
// immediately follows it.
|
||||
// Nothing reads either set outside a session, so unread markers would accumulate.
|
||||
// Capped by dropping the oldest: a marker means something only to the action that
|
||||
// immediately follows the write that left it.
|
||||
const MAX_WRITE_MARKERS = 64
|
||||
function noteMarker(set: Set<string>, key: string): void {
|
||||
set.add(key)
|
||||
|
||||
Reference in New Issue
Block a user