mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
feat: take latest adopts the head the diff shows, and is offered while the drawer sees the draft behind
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
194c3218d8
commit
062adc284b
@@ -51,7 +51,8 @@
|
||||
* way to tell what their draft is being compared against. */
|
||||
deployedLabel?: string
|
||||
versions?: DiffVersionOption[]
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
draftBase?: string
|
||||
draft: DiffData | undefined
|
||||
current: DiffData
|
||||
path?: string
|
||||
@@ -139,12 +140,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** The version this drawer presents as the deployed head, when it has a list to say
|
||||
* so. Both the action's gate and the base it adopts hang off it, so "take latest"
|
||||
* means the version the reader is looking at. */
|
||||
const headShown = $derived.by(() =>
|
||||
data?.mode === 'normal' ? data.versions?.find((v) => v.isHead)?.id : undefined
|
||||
)
|
||||
/** Behind as the drawer can see it: a base that is not the head on display. With no
|
||||
* version list there is nothing to compare, so the editor's own gate stands. */
|
||||
const behindShown = $derived.by(
|
||||
() => data?.mode === 'normal' && (headShown == null || data.draftBase !== headShown)
|
||||
)
|
||||
|
||||
let takingLatest = $state(false)
|
||||
async function takeLatest() {
|
||||
if (!data || data.mode !== 'normal' || !data.onTakeLatest || takingLatest) return
|
||||
takingLatest = true
|
||||
try {
|
||||
await data.onTakeLatest()
|
||||
await data.onTakeLatest(headShown)
|
||||
diffViewer?.closeDrawer()
|
||||
} finally {
|
||||
takingLatest = false
|
||||
@@ -159,7 +172,8 @@
|
||||
deployedLabel?: string
|
||||
versions?: DiffVersionOption[]
|
||||
loadVersion?: (id: string) => Promise<Value | undefined>
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
draftBase?: string
|
||||
draft?: Value | undefined
|
||||
current: Value
|
||||
defaultDiffType?: 'deployed' | 'draft'
|
||||
@@ -180,6 +194,7 @@
|
||||
versions,
|
||||
loadVersion,
|
||||
onTakeLatest,
|
||||
draftBase,
|
||||
draft,
|
||||
current,
|
||||
button
|
||||
@@ -197,6 +212,7 @@
|
||||
deployedLabel,
|
||||
versions,
|
||||
onTakeLatest,
|
||||
draftBase,
|
||||
draft: draft ? prepareDiff(draft) : undefined,
|
||||
current: prepareDiff(current),
|
||||
path: draft?.path || deployed?.path,
|
||||
@@ -333,7 +349,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#snippet actions()}
|
||||
{#if data?.mode === 'normal' && data.onTakeLatest}
|
||||
{#if data?.mode === 'normal' && data.onTakeLatest && behindShown}
|
||||
<Button unifiedSize="sm" variant="default" loading={takingLatest} onClick={takeLatest}>
|
||||
Take latest, keep my edits
|
||||
</Button>
|
||||
|
||||
@@ -1175,6 +1175,7 @@
|
||||
deployedLabel,
|
||||
versions: await deployedVersionOptions(),
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseVersion,
|
||||
loadVersion: async (id) => {
|
||||
const v = await FlowService.getFlowVersion({
|
||||
workspace: opWorkspace!,
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
initialPath = $bindable(''),
|
||||
userDraftPath = '',
|
||||
onTakeLatest = undefined,
|
||||
draftBaseHash = undefined,
|
||||
autosaveWorkspace = undefined,
|
||||
autosavePath = undefined,
|
||||
template = $bindable('script'),
|
||||
@@ -898,6 +899,7 @@
|
||||
deployedLabel: deployedVersionLabel(deployed),
|
||||
versions: await deployedVersionOptions(headHash),
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseHash,
|
||||
loadVersion: async (hash) => {
|
||||
const v = await ScriptService.getScriptByHash({ workspace: opWorkspace!, hash })
|
||||
return replaceFalseWithUndefined({
|
||||
|
||||
@@ -27,10 +27,14 @@ export type DiffDrawerDiff =
|
||||
/** Loads one version's payload. Returning `undefined` leaves the current
|
||||
* comparison in place rather than blanking the diff. */
|
||||
loadVersion?: (id: string) => Promise<Value | undefined>
|
||||
/** Moves the draft's base to the head and keeps its content. Passed only
|
||||
* while the draft is behind; rendered as a header action so the user
|
||||
* takes the latest with the diff in front of them. */
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
/** Moves the draft's base to the head and keeps its content, rendered as a
|
||||
* header action so the user takes the latest with the diff in front of them.
|
||||
* Called with the version the drawer is showing as head (from `versions`), so
|
||||
* the base adopted is the one the reader just looked at. */
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
/** The version the draft forked from. The drawer offers `onTakeLatest` only
|
||||
* while it differs from the head on display. */
|
||||
draftBase?: string
|
||||
draft?: Value | undefined
|
||||
current: Value
|
||||
defaultDiffType?: 'deployed' | 'draft'
|
||||
|
||||
@@ -139,7 +139,9 @@
|
||||
version?: number | undefined
|
||||
/** Moves the draft's base to the deployed head and keeps its content;
|
||||
* offered in the diff drawer while the draft is behind. */
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
/** The app_version the draft forked from, threaded to the topbar's diff drawer. */
|
||||
draftBaseVersion?: string | undefined
|
||||
// See ScriptBuilderProps — same indicator semantics.
|
||||
loadedFromDraft?: boolean
|
||||
othersDraftsCount?: number
|
||||
@@ -213,7 +215,8 @@
|
||||
onSavedNewAppPath,
|
||||
condensedHeader = false,
|
||||
version = undefined,
|
||||
onTakeLatest = undefined
|
||||
onTakeLatest = undefined,
|
||||
draftBaseVersion = undefined
|
||||
}: Props = $props()
|
||||
|
||||
// Workspace this editor operates on: the session's acting workspace when
|
||||
@@ -2309,6 +2312,7 @@
|
||||
bind:pendingDraftPath
|
||||
{version}
|
||||
{onTakeLatest}
|
||||
{draftBaseVersion}
|
||||
{onRestore}
|
||||
{onSavedNewAppPath}
|
||||
{policy}
|
||||
|
||||
@@ -111,7 +111,10 @@
|
||||
version?: number | undefined
|
||||
/** Moves the draft's base to the deployed head and keeps its content;
|
||||
* offered in the diff drawer while the draft is behind. */
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
/** The app_version the draft forked from; the drawer offers `onTakeLatest` only
|
||||
* while it differs from the head on display. */
|
||||
draftBaseVersion?: string | undefined
|
||||
newApp: boolean
|
||||
newPath?: string
|
||||
/** Initial labels for the app, threaded from the loaded app data. */
|
||||
@@ -183,6 +186,7 @@
|
||||
savedApp = $bindable(undefined),
|
||||
version = $bindable(undefined),
|
||||
onTakeLatest = undefined,
|
||||
draftBaseVersion = undefined,
|
||||
newApp,
|
||||
newPath = '',
|
||||
labels: initialLabels = undefined,
|
||||
@@ -460,6 +464,7 @@
|
||||
deployed: deployedValue ?? stripRawAppDiffNoise(savedApp),
|
||||
versions: await deployedVersionOptions(),
|
||||
onTakeLatest,
|
||||
draftBase: draftBaseVersion,
|
||||
loadVersion: async (id) => {
|
||||
const v = await AppService.getAppByVersion({ workspace: opWorkspace!, id: Number(id) })
|
||||
// Same normalization as `syncWithDeployed`, so switching versions doesn't
|
||||
|
||||
@@ -36,10 +36,13 @@ export interface ScriptBuilderProps {
|
||||
* stop/restart pair is a no-op on a non-live entry.
|
||||
*/
|
||||
userDraftPath?: string
|
||||
/** Moves the draft's base to the deployed head and keeps its content; offered
|
||||
* in the diff drawer while the draft is behind. The route owns it because
|
||||
* the base lives in a per-kind field of the value. */
|
||||
onTakeLatest?: () => void | Promise<void>
|
||||
/** Moves the draft's base to the head and keeps its content; offered in the diff
|
||||
* drawer while the draft is behind. Called with the version the drawer shows as
|
||||
* head. The route owns it because the base lives in a per-kind field of the value. */
|
||||
onTakeLatest?: (head?: string) => void | Promise<void>
|
||||
/** The hash the draft forked from, so the drawer offers `onTakeLatest` only while
|
||||
* it differs from the head on display. */
|
||||
draftBaseHash?: string
|
||||
/**
|
||||
* Workspace + path the AutosaveIndicator watches for sync state. Default
|
||||
* (undefined) falls back to `$workspaceStore` / `userDraftPath` — the
|
||||
|
||||
@@ -625,19 +625,20 @@
|
||||
newApp={isNewApp}
|
||||
version={parentVersion ??
|
||||
(deployedHeadVersion != null ? Number(deployedHeadVersion) : undefined)}
|
||||
onTakeLatest={draftBaseVersion &&
|
||||
deployedHeadVersion &&
|
||||
draftBaseVersion !== deployedHeadVersion
|
||||
? async () => {
|
||||
// Re-read rather than trusting the head this page loaded with; see
|
||||
{draftBaseVersion}
|
||||
onTakeLatest={draftBaseVersion
|
||||
? async (shown?: string) => {
|
||||
// The version the drawer showed as head, else a fresh read; see
|
||||
// /scripts/edit.
|
||||
const head =
|
||||
(shown != null ? Number(shown) : undefined) ??
|
||||
(
|
||||
await AppService.getAppLatestVersion({
|
||||
workspace: $workspaceStore!,
|
||||
path: page.params.path ?? ''
|
||||
}).catch(() => undefined)
|
||||
)?.version ?? Number(deployedHeadVersion)
|
||||
)?.version ??
|
||||
Number(deployedHeadVersion)
|
||||
parentVersion = head
|
||||
if (deployedBaseline) deployedBaseline = { ...deployedBaseline, parent_version: head }
|
||||
draftBaseVersion = String(head)
|
||||
|
||||
@@ -544,17 +544,18 @@
|
||||
</div>
|
||||
{:else if renderEditor}
|
||||
<FlowBuilder
|
||||
onTakeLatest={draftBaseVersion && version != null && draftBaseVersion !== String(version)
|
||||
? async () => {
|
||||
// Re-read rather than trusting the head this page loaded with; see
|
||||
// /scripts/edit.
|
||||
onTakeLatest={draftBaseVersion
|
||||
? async (shown?: string) => {
|
||||
// The version the drawer showed as head, else a fresh read; see /scripts/edit.
|
||||
const head =
|
||||
(shown != null ? Number(shown) : undefined) ??
|
||||
(
|
||||
await FlowService.getFlowLatestVersion({
|
||||
workspace: $workspaceStore!,
|
||||
path: flowDraftPath
|
||||
}).catch(() => undefined)
|
||||
)?.id ?? version
|
||||
)?.id ??
|
||||
version
|
||||
if (!draftSync.draft || head == null || !$workspaceStore) return
|
||||
draftSync.draft = { ...draftSync.draft, version_id: head }
|
||||
draftBaseVersion = String(head)
|
||||
|
||||
@@ -525,18 +525,21 @@
|
||||
bind:this={scriptBuilder}
|
||||
{initialPath}
|
||||
userDraftPath={draftPath}
|
||||
onTakeLatest={draftBaseHash && deployedHeadHash && draftBaseHash !== deployedHeadHash
|
||||
? async () => {
|
||||
// Re-read rather than trusting the head this page loaded with: taking a
|
||||
// stale one would say the draft is up to date with a version that is not
|
||||
// the latest any more.
|
||||
{draftBaseHash}
|
||||
onTakeLatest={draftBaseHash
|
||||
? async (shown?: string) => {
|
||||
// The version the drawer showed as head, else a fresh read: taking the one
|
||||
// this page loaded with would say the draft is up to date with a version
|
||||
// that is not the latest any more.
|
||||
const head =
|
||||
shown ??
|
||||
(
|
||||
await ScriptService.getScriptLatestVersion({
|
||||
workspace: $workspaceStore!,
|
||||
path: draftPath
|
||||
}).catch(() => undefined)
|
||||
)?.script_hash ?? deployedHeadHash
|
||||
)?.script_hash ??
|
||||
deployedHeadHash
|
||||
if (!draftSync.draft || !head || !$workspaceStore) return
|
||||
draftSync.draft = { ...draftSync.draft, parent_hash: head }
|
||||
// The baseline mirrors the draft's base so an unedited draft still
|
||||
|
||||
Reference in New Issue
Block a user