mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
fix(editors): keep an edit made while a save is in flight, and land baselines per workspace
The forms stay editable during a save, and the post-write reset dropped the local draft back to the state that was sent — so anything typed in between vanished when the request returned. The draft is now dropped only while it still holds what was written; an edit on top of the save survives as the unsaved change it is, and the hosts that remount pick it back up. A multi-workspace variable save also adopted its baselines only after every write had succeeded, so a later workspace throwing left an earlier one's draft dropped below a baseline that never moved: dirty forever, and retried as a create it had already made. Each lands as its own write does. 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
8df960ebd2
commit
0635901e7f
@@ -366,10 +366,14 @@
|
||||
})
|
||||
}
|
||||
// Reset the handle to the new deployed baseline via `discard`, not
|
||||
// `remove`. See VariableEditor for the full rationale.
|
||||
initialStates[ws] = $state.snapshot(s) as ResourceState
|
||||
// `remove`. See VariableEditor for the full rationale — including why the
|
||||
// cell is only dropped while it still holds what was written.
|
||||
const written = $state.snapshot(s) as ResourceState
|
||||
initialStates[ws] = written
|
||||
existedInitially[ws] = true
|
||||
UserDraft.discard('resource', initialPath ?? '', s, { workspace: ws })
|
||||
if (draftValuesEqual(states[ws]?.draft, written)) {
|
||||
UserDraft.discard('resource', initialPath ?? '', written, { workspace: ws })
|
||||
}
|
||||
// Path now exists server-side — drop the autocomplete cache so
|
||||
// it shows up immediately instead of after the 60s TTL.
|
||||
invalidateWorkspacePaths(ws)
|
||||
|
||||
@@ -322,11 +322,22 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
// Per workspace, as each write lands: a later one throwing must not leave
|
||||
// this one's draft dropped below a baseline that never moved, which reads
|
||||
// as dirty and retries the create it already made.
|
||||
if (editPath === from) {
|
||||
initialStates[ws] = s
|
||||
existedInitially[ws] = true
|
||||
}
|
||||
// The just-saved state is the new deployed baseline; reset the
|
||||
// handle to it via `discard` (not `remove` — blanking the cell to
|
||||
// `undefined` reads as dirty). The `value: null` POST also deletes
|
||||
// the server draft row so `is_draft` clears on refetch.
|
||||
UserDraft.discard('variable', from ?? '', s, { workspace: ws })
|
||||
// the server draft row so `is_draft` clears on refetch. Only while the
|
||||
// cell still holds what was written: the form stays editable during the
|
||||
// request, and an edit made then is a change on top of the save.
|
||||
if (draftValuesEqual(states[ws]?.draft, s)) {
|
||||
UserDraft.discard('variable', from ?? '', s, { workspace: ws })
|
||||
}
|
||||
// Path now exists server-side — drop the autocomplete cache so
|
||||
// it shows up immediately instead of after the 60s TTL.
|
||||
invalidateWorkspacePaths(ws)
|
||||
@@ -336,12 +347,8 @@
|
||||
)
|
||||
dispatch('create')
|
||||
// Only while this editor is still the one that was saved: re-pointed, the
|
||||
// baseline and path below are the variable it moved to.
|
||||
// path below is the variable it moved to.
|
||||
if (editPath === from) {
|
||||
for (const { ws, s } of payloads) {
|
||||
initialStates[ws] = s
|
||||
existedInitially[ws] = true
|
||||
}
|
||||
// A rename moved the item; the drawer host closes over it, but an inline one
|
||||
// stays mounted, so follow the new path here and tell the host about it.
|
||||
if (savedPath && savedPath !== editPath) editPath = savedPath
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
import { runScheduleNow } from '../scheduled/utils'
|
||||
import { handleConfigChange } from '../utils'
|
||||
import { withForkConflictRetry } from '$lib/utils/forkConflict'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { normalizeDraftForCompare } from '$lib/userDraft.svelte'
|
||||
import { useTriggerDraftSync } from '../useTriggerDraftSync.svelte'
|
||||
import LocalDraftBanner from '$lib/components/LocalDraftBanner.svelte'
|
||||
import TextInput from '$lib/components/text_input/TextInput.svelte'
|
||||
@@ -620,7 +622,17 @@
|
||||
deploymentLoading = true
|
||||
const isSaved = await saveScheduleFromCfg(scheduleCfg, edit, wsId!)
|
||||
if (isSaved) {
|
||||
draftSync.discard(previousPath, scheduleCfg)
|
||||
// Drop the local draft only while the form still holds what was saved: it
|
||||
// stays editable during the request, and an edit made then is a change on
|
||||
// top of the save, which the reset would silently swallow.
|
||||
if (
|
||||
deepEqual(
|
||||
normalizeDraftForCompare($state.snapshot(getScheduleCfg())),
|
||||
normalizeDraftForCompare(scheduleCfg)
|
||||
)
|
||||
) {
|
||||
draftSync.discard(previousPath, scheduleCfg)
|
||||
}
|
||||
onUpdate?.(scheduleCfg.path, previousPath, previousWs)
|
||||
drawer?.closeDrawer()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user