mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-12 16:02:56 +00:00
fix: review — compare every deployed field, passive-read filter only with its toggle, drag cancel, asset escape hatch
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
112f33df80
commit
1b159d91f5
@@ -377,8 +377,21 @@
|
||||
onDrag.over = nodeUnder(ev.clientX, ev.clientY)
|
||||
onDrag.blank = !onDrag.over && overBlank(ev.clientX, ev.clientY)
|
||||
}
|
||||
const up = (ev: PointerEvent) => {
|
||||
// A drag that ends without a release (pointer cancelled, window left) drops
|
||||
// nothing, and must not leave its line following the cursor.
|
||||
const teardown = () => {
|
||||
window.removeEventListener('pointermove', move)
|
||||
window.removeEventListener('pointerup', up)
|
||||
window.removeEventListener('pointercancel', cancel)
|
||||
window.removeEventListener('blur', cancel)
|
||||
document.removeEventListener('visibilitychange', cancel)
|
||||
}
|
||||
const cancel = () => {
|
||||
teardown()
|
||||
onDrag = undefined
|
||||
}
|
||||
const up = (ev: PointerEvent) => {
|
||||
teardown()
|
||||
const drag = onDrag
|
||||
onDrag = undefined
|
||||
if (!started || !drag) return
|
||||
@@ -394,7 +407,10 @@
|
||||
drop(target && drag.eligible.has(target) ? target : undefined, at, !target && drag.blank)
|
||||
}
|
||||
window.addEventListener('pointermove', move)
|
||||
window.addEventListener('pointerup', up, { once: true })
|
||||
window.addEventListener('pointerup', up)
|
||||
window.addEventListener('pointercancel', cancel)
|
||||
window.addEventListener('blur', cancel)
|
||||
document.addEventListener('visibilitychange', cancel)
|
||||
}
|
||||
function startOnDrag(sourceId: string, scripts: string[], e: PointerEvent) {
|
||||
if (!onAddAssetTrigger || scripts.length === 0) return
|
||||
@@ -1488,8 +1504,10 @@
|
||||
showPassiveReads = localStorage.getItem(SHOW_PASSIVE_READS_KEY) !== 'false'
|
||||
} catch {}
|
||||
let withoutPassive = $derived(withoutPassiveReads(model.nodes, model.edges))
|
||||
// Only where the toggle is shown: the preference is global, and a graph
|
||||
// without the toggle would have no way to show the reads again.
|
||||
let shownModel = $derived(
|
||||
showPassiveReads || withoutPassive.dropped === 0
|
||||
!assetsOnlyToggle || showPassiveReads || withoutPassive.dropped === 0
|
||||
? model
|
||||
: { ...model, nodes: withoutPassive.nodes, edges: withoutPassive.edges }
|
||||
)
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
onStartOnDrag?: (e: PointerEvent) => void
|
||||
/** The assets-only node for the scripts that build no asset. */
|
||||
noAsset?: boolean
|
||||
/** Nothing in the workspace writes it. */
|
||||
/** No script in the workspace writes it; a flow's write fires no asset trigger. */
|
||||
neverWritten?: boolean
|
||||
/** Card width in the assets-only view, sized to its text. */
|
||||
width?: number
|
||||
|
||||
@@ -8,8 +8,9 @@ export interface AssetGraphAssetNode {
|
||||
path: string
|
||||
// Filled in client-side: the node is misconfigured (see `pipelineNodeErrors`).
|
||||
error?: string
|
||||
// Filled in client-side: nothing in the workspace writes it, so a step
|
||||
// downstream of it would never run on its writes.
|
||||
// Filled in client-side: no script in the workspace writes it, so a step
|
||||
// downstream of it would never run on its writes (a flow's write fires no
|
||||
// asset trigger).
|
||||
never_written?: boolean
|
||||
// Fork workspaces only: 'fork' when this ducklake asset was materialized in
|
||||
// the fork itself, 'deferred' when reads fall back to the parent workspace's
|
||||
|
||||
@@ -1180,10 +1180,27 @@
|
||||
// (same rationale as the live-callback handlers above) and so the
|
||||
// template can gate them per-mode with simple ternaries — the canvas
|
||||
// hides each affordance when its callback is undefined.
|
||||
// Every field Save all deploys: a draft differing from the live script in any
|
||||
// of them still has something to save.
|
||||
const DEPLOYED_FIELDS = [
|
||||
'content',
|
||||
'language',
|
||||
'summary',
|
||||
'description',
|
||||
'tag',
|
||||
'kind',
|
||||
'labels'
|
||||
] as const
|
||||
async function matchesDeployed(path: string, d: Draft, ws: string): Promise<boolean> {
|
||||
try {
|
||||
const live = await ScriptService.getScriptByPath({ workspace: ws, path })
|
||||
return live.content === d.script.content && live.language === d.script.language
|
||||
const norm = (v: unknown) =>
|
||||
JSON.stringify(Array.isArray(v) && v.length === 0 ? null : (v ?? null))
|
||||
return DEPLOYED_FIELDS.every(
|
||||
(f) =>
|
||||
norm(f === 'description' ? (live[f] ?? '') : live[f]) ===
|
||||
norm(f === 'description' ? (d.script[f] ?? '') : d.script[f])
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
@@ -1216,9 +1233,14 @@
|
||||
opts?: { soleScript?: string }
|
||||
) {
|
||||
if (s?.kind === 'asset' && opts?.soleScript) {
|
||||
const script = opts.soleScript
|
||||
if (!(await isMaterialized({ kind: s.asset_kind, path: s.path }))) {
|
||||
s = { kind: 'runnable', runnable_kind: 'script', path: script }
|
||||
const asset = s
|
||||
if (!(await isMaterialized({ kind: asset.asset_kind, path: asset.path }))) {
|
||||
s = { kind: 'runnable', runnable_kind: 'script', path: opts.soleScript }
|
||||
// Job history expires, so "no write on record" may be an asset built long
|
||||
// ago: its own pane stays one click away.
|
||||
sendUserToast(`No build of ${asset.path} on record: opened its script.`, false, [
|
||||
{ label: 'Show the asset', callback: () => void handleCanvasSelect(asset) }
|
||||
])
|
||||
}
|
||||
}
|
||||
// Clicking a node while the pane is explicitly hidden is a request
|
||||
|
||||
Reference in New Issue
Block a user