fix(drafts): hide LocalDraftBanner when deployed and current match the DiffDrawer's compare

Earlier I gated the banner on `getDeployed() != null`, but the user
still saw it fire on entries where 'Show diff' opens to 'No changes
detected'. That means `show` (the caller's coarse dirty check) flagged
a difference the DiffDrawer treats as a no-op — typically toggle
defaults (`false ↔ undefined`), removed empty arrays, or key-ordering
noise that `cleanValueProperties + orderedYamlStringify` collapses.

Replicate the drawer's comparison inside the banner: stringify both
sides through the same pipeline and only render when the keys differ.
A single `diffKey()` helper keeps the logic local; the catch-and-empty
fallback survives a non-serializable side rather than throwing.
This commit is contained in:
Diego Imbert
2026-06-09 14:50:57 +02:00
parent 2b61c052b4
commit 30c48ab82e
@@ -2,7 +2,12 @@
import { Button } from '$lib/components/common'
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
import { classes } from '$lib/components/common/alert/model'
import { type Value } from '$lib/utils'
import {
cleanValueProperties,
orderedYamlStringify,
replaceFalseWithUndefined,
type Value
} from '$lib/utils'
import { AlertCircle, Diff } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
import { slide } from 'svelte/transition'
@@ -31,14 +36,33 @@
title = 'Deployed <> Local changes'
}: Props = $props()
// Suppress the banner when there's no deployed baseline to diff
// against — a brand-new entity (variable/resource/trigger with no
// deployed row yet) has deployed == null, so "Show diff" would do
// nothing (the drawer early-returns) and "Discard changes" is
// semantically backwards (there's nothing to revert to). The
// callers' own `show` is computed off `current != deployed` which
// is trivially true in that case, so the gate has to live here.
let visible = $derived(show && getDeployed() != null)
/** Same cleaning + YAML serialization the DiffDrawer applies before
* comparing. Without it the banner would fire on differences the
* drawer treats as no-op (toggle defaults, `false ↔ undefined`,
* key ordering noise) — exactly the case where the user clicks
* "Show diff" and sees the "No changes detected" empty state. */
function diffKey(value: unknown): string {
try {
return orderedYamlStringify(cleanValueProperties(replaceFalseWithUndefined(value as Value)))
} catch {
return ''
}
}
// Suppress the banner when:
// • There's no deployed baseline (brand-new entity — "Show diff"
// would early-return and "Discard" is semantically backwards),
// OR
// • Deployed and current are equal under the DiffDrawer's own
// comparison. The callers' `show` is a coarser "form differs
// from baseline" check that can stale-fire after a save lands
// or when `false`/`undefined` toggle noise flips a field.
let visible = $derived.by(() => {
if (!show) return false
const deployed = getDeployed()
if (deployed == null) return false
return diffKey(deployed) !== diffKey(getCurrent())
})
let diffDrawer: DiffDrawer | undefined = $state()