From 3c3bd5414828003bc79ff6695ada165983c39947 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 13 May 2026 19:32:54 +0200 Subject: [PATCH] feat(frontend): load URL-encoded scripts on /scripts/edit + consume-once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the URL-hash seed mechanism from /scripts/add to /scripts/edit for parity: decode the base64-JSON-encoded NewScript payload from the URL hash, apply it over the bakedBaseline as the editor's initial state, send "Loaded from URL", and strip the hash immediately via window.history.replaceState so a reload restores the user's autosave rather than re-injecting the seed. The seed wins over local autosave + backend draft + deployed — UserDraft.remove(script, draftPath) drops the stale autosave on disk before setDraftAndMeta writes the seeded value, so the user's subsequent edits will overwrite cleanly. Skipped when ?hash= is in the URL (historical-version view, which is read-only relative to drafts) and when the hash fragment isn't a parseable encoded payload. No callers build /scripts/edit# URLs today — this lands the mechanism for future symmetry with /scripts/add. --- .../scripts/edit/[...path]/+page.svelte | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte index 827bb35805..d9c1db317a 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte @@ -34,6 +34,28 @@ const draftPath = hash ? '' : (page.params.path ?? '') const scriptHandle = UserDraft.use('script', draftPath) + /** Some pages base64-JSON-encode a NewScript-like payload into the URL + * hash on `/scripts/edit/#…`. 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 | 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 + } catch { + // Hash isn't a valid encoded script — ignore. + } + return undefined + } + let urlScriptSeed = decodeUrlScriptSeed() + let initialPath: string = $state('') let scriptBuilder: ScriptBuilder | undefined = $state(undefined) @@ -124,7 +146,24 @@ parent_hash: topHash ?? scriptWithDraft.hash } - if (localDraft != undefined) { + if (urlScriptSeed) { + // URL hash seed wins over local autosave + backend draft + deployed. + // Wipe the stale autosave on disk so the in-memory seed becomes the + // only source — the user's subsequent edits will overwrite cleanly. + const seeded = { ...bakedBaseline, ...urlScriptSeed } as EditableScript + UserDraft.remove('script', draftPath) + scriptHandle.setDraftAndMeta(seeded, newRevs) + sendUserToast('Loaded from URL') + // Consume-once: strip the hash so a reload restores the user's + // autosave (which now reflects the seed + edits) instead of + // re-applying the seed. + if (typeof window !== 'undefined') { + const url = new URL(window.location.href) + url.hash = '' + window.history.replaceState(window.history.state, '', url.toString()) + } + urlScriptSeed = undefined + } else if (localDraft != undefined) { const reference = backendDraft ?? scriptWithDraft const referenceClean = cleanValueProperties(reference) const localClean = cleanValueProperties(localDraft)