fix: remove URL-hash sync from script editor (already marked TEMP)

This commit is contained in:
Diego Imbert
2026-06-02 17:08:02 +02:00
parent a6091d91cd
commit 938c2d5336
@@ -4,12 +4,7 @@
import { initialArgsStore, userStore, workspaceStore } from '$lib/stores'
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
import { editPathFor, invalidate } from '$lib/components/workspacePicker'
import {
cleanValueProperties,
encodeState,
orderedJsonStringify,
readFieldsRecursively
} from '$lib/utils'
import { cleanValueProperties, orderedJsonStringify } from '$lib/utils'
import { goto } from '$lib/navigation'
import { sendUserToast } from '$lib/toast'
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
@@ -80,28 +75,6 @@
return () => UserDraft.clearLiveEditorDraft('script', { workspace, storagePath: draftPath })
})
/** Some pages base64-JSON-encode a NewScript-like payload into the URL
* hash on `/scripts/edit/<path>#…`. Treat it as a one-shot seed that
* wins over local autosave + backend draft + deployed: apply, toast,
* strip from the URL. Same logic as /scripts/add, kept in this file for
* a faithful mirror of its decoder.
*
* Can't reuse `decodeState` from utils.ts — it fires its own error toast
* on parse failure, which would noise up the UI for unrelated anchors.
*/
function decodeUrlScriptSeed(): Partial<EditableScript> | undefined {
const fragment = page.url.hash.startsWith('#') ? page.url.hash.slice(1) : ''
if (!fragment) return undefined
try {
const decoded = JSON.parse(decodeURIComponent(atob(fragment)))
if (decoded && typeof decoded === 'object') return decoded as Partial<EditableScript>
} catch {
// Hash isn't a valid encoded script — ignore.
}
return undefined
}
let urlScriptSeed = decodeUrlScriptSeed()
// Seed from the URL so ScriptBuilder mounts with a populated `initialPath`
// even when `scriptHandle.draft` is already defined synchronously from a
// local autosave. An empty initialPath flips ScriptBuilder's
@@ -127,18 +100,6 @@
let staleModalCause = $state<'draft' | 'version'>('version')
let pendingBaseline: { baseline: EditableScript; revs: UserDraftMeta } | undefined = undefined
// === BEGIN TEMP URL-HASH SYNC (remove with future PR) ===
// Legacy behavior: URL hash both seeds the editor and stays in sync with
// edits. Asks the user via modal when the URL value would clobber an
// existing local autosave that differs from it.
let urlConflictModalOpen = $state(false)
let urlConflictPending: { seed: EditableScript; revs: UserDraftMeta } | undefined = undefined
// Gates the URL-sync effect until the initial URL-seed has been resolved
// (silent apply OR modal closed) so it doesn't overwrite the URL payload
// before the user has decided.
let initialUrlSeedResolved = $state(!urlScriptSeed)
// === END TEMP URL-HASH SYNC ===
function applyBaseline(baseline: EditableScript): void {
initialPath = baseline.path
scriptBuilder?.setDraftTriggers(baseline.draft_triggers)
@@ -170,26 +131,6 @@
staleModalOpen = false
}
// === BEGIN TEMP URL-HASH SYNC (remove with future PR) ===
function onUrlConflictUseUrl(): void {
if (urlConflictPending) {
const { seed, revs } = urlConflictPending
UserDraft.remove('script', draftPath)
scriptHandle.setDraftAndMeta(seed, revs)
applyBaseline(seed)
sendUserToast('Loaded from URL')
}
urlConflictPending = undefined
urlConflictModalOpen = false
initialUrlSeedResolved = true
}
function onUrlConflictKeepLocal(): void {
urlConflictPending = undefined
urlConflictModalOpen = false
initialUrlSeedResolved = true
}
// === END TEMP URL-HASH SYNC ===
/** Increments per `loadScript` call. Stale loads (e.g. when picker
* navigation races a draft-discard reload) bail at the next checkpoint
* after their captured token no longer matches. */
@@ -262,32 +203,7 @@
parent_hash: topHash ?? backendScript.hash
}
if (urlScriptSeed) {
// === TEMP URL-HASH SYNC branch (remove with future PR) ===
// URL hash seed competes with the local autosave on load.
// When they differ, defer to a user-facing modal instead of
// silently overwriting.
const seeded = { ...bakedBaseline, ...urlScriptSeed } as EditableScript
if (localDraft != undefined) {
const localClean = orderedJsonStringify(cleanValueProperties(localDraft))
const seededClean = orderedJsonStringify(cleanValueProperties(seeded))
if (localClean === seededClean) {
UserDraft.remove('script', draftPath)
scriptHandle.setDraftAndMeta(seeded, newRevs)
initialUrlSeedResolved = true
} else {
urlConflictPending = { seed: seeded, revs: newRevs }
urlConflictModalOpen = true
}
} else {
UserDraft.remove('script', draftPath)
scriptHandle.setDraftAndMeta(seeded, newRevs)
sendUserToast('Loaded from URL')
initialUrlSeedResolved = true
}
urlScriptSeed = undefined
// === END TEMP URL-HASH SYNC branch ===
} else if (localDraft != undefined) {
if (localDraft != undefined) {
const referenceClean = cleanValueProperties(backendScript)
const localClean = cleanValueProperties(localDraft)
if (orderedJsonStringify(referenceClean) === orderedJsonStringify(localClean)) {
@@ -362,31 +278,6 @@
}
})
// === BEGIN TEMP URL-HASH SYNC (remove with future PR) ===
// Mirror the current draft to the URL hash on every edit (debounced).
let _urlHashSyncTimeout: number | undefined
$effect(() => {
const draft = scriptHandle.draft
if (!draft) return
// Wait until the initial URL-seed has been resolved (silent apply or
// modal closed) so we don't clobber the URL payload prematurely.
if (!initialUrlSeedResolved) {
if (_urlHashSyncTimeout) clearTimeout(_urlHashSyncTimeout)
return
}
readFieldsRecursively(draft)
if (typeof window === 'undefined') return
if (_urlHashSyncTimeout) clearTimeout(_urlHashSyncTimeout)
_urlHashSyncTimeout = setTimeout(() => {
const snapshot = $state.snapshot(scriptHandle.draft)
if (!snapshot) return
const url = new URL(window.location.href)
url.hash = encodeState(snapshot)
window.history.replaceState(window.history.state, '', url.toString())
}, 500)
})
// === END TEMP URL-HASH SYNC ===
let diffDrawer: DiffDrawer | undefined = $state()
async function restoreDeployed() {
@@ -424,13 +315,6 @@
}}
/>
{/if}
<!-- TEMP URL-HASH SYNC: conflict modal (remove with future PR) -->
<LocalDraftStaleModal
open={urlConflictModalOpen}
cause="url"
onLoadLatest={onUrlConflictUseUrl}
onKeepDraft={onUrlConflictKeepLocal}
/>
{#if scriptHandle.draft && renderEditor}
<ScriptBuilder
bind:this={scriptBuilder}