From e4453e642c96b8fbaf086ffd62ec62fb690dc2f5 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 5 Jun 2026 21:32:20 +0200 Subject: [PATCH] fix: defer script restartSync until script.path lands (Path widget gated on $userStore + $workspaceStore) --- .../src/lib/components/ScriptBuilder.svelte | 30 +++++++++++++++++-- .../scripts/edit/[...path]/+page.svelte | 24 ++------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 5842f4be1e..bc5a07cead 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -380,8 +380,34 @@ } console.log('[draft-sync] ScriptBuilder: calling initContent', userDraftPath) initContent(script.language, script.kind, template).finally(() => { - console.log('[draft-sync] ScriptBuilder: initContent finally → restartSync', userDraftPath) - UserDraft.restartSync('script', userDraftPath) + console.log('[draft-sync] ScriptBuilder: initContent finally', userDraftPath, { + pathAlreadySet: !!script.path + }) + // The `Path` widget assigns `script.path` from its + // `$effect.pre` gated on `$workspaceStore + $userStore` — + // which on a HARD-REFRESH-then-first-nav can be unset when + // we get here, so we can't `restartSync` yet (its eventual + // path mutation would look like the user's first edit and + // POST). Wait for `script.path` to land instead; on + // subsequent in-app navs the stores are warm and this + // fires synchronously on the very first tick. + if (script.path) { + console.log('[draft-sync] ScriptBuilder: path already set → restartSync', userDraftPath) + UserDraft.restartSync('script', userDraftPath) + return + } + const stopWatch = $effect.root(() => { + $effect(() => { + if (!script.path) return + console.log('[draft-sync] ScriptBuilder: path settled → restartSync', userDraftPath, { + path: script.path + }) + UserDraft.restartSync('script', userDraftPath) + // Tear down the scope so the watcher itself doesn't + // keep firing on future path edits. + queueMicrotask(stopWatch) + }) + }) }) } else { console.log('[draft-sync] ScriptBuilder: bootstrap SKIPPED (content non-empty)', userDraftPath) 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 fd770fec04..05a87f2c59 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte @@ -22,7 +22,6 @@ type UserDraftHandle } from '$lib/userDraft.svelte' import { notifyDraftLoaded, notifyRestoredFromLocal } from '$lib/userDraftToast' - import { random_adj } from '$lib/components/random_positive_adjetive' type EditableScript = NewScript & { draft_triggers?: Trigger[] } @@ -163,34 +162,15 @@ const url = new URL(window.location.href) url.searchParams.delete('new_draft') window.history.replaceState(window.history.state, '', url.toString()) - // Seed the path synchronously with the same `u/{user}/ - // {adj}_script` shape the `Path` widget would auto-generate. - // Otherwise the widget mutates `script.path` on its own a - // few ticks AFTER our `restartSync` (the mutation chain runs - // in an `$effect.pre`), and that mutation looks like the - // user's first edit — firing an unwanted POST. With a path - // already set the widget's `initPath` branch that calls - // `reset()` is skipped entirely. - const username = $userStore?.username ?? '' - const ownerPrefix = - username && !username.includes('@') - ? `u/${username}/` - : username - ? `u/${username.split('@')[0].replace(/[^a-zA-Z0-9_]/g, '')}/` - : '' - const seededPath = ownerPrefix ? `${ownerPrefix}${random_adj()}_script` : '' const empty: EditableScript = { - path: seededPath, + path: '', summary: '', description: '', content: '', language: 'bun', schema: {} } as unknown as EditableScript - // Path widget's `initPath` checks `initialPath` for the - // "reset to auto-name" decision — pass the same seeded path - // so it takes the "use initialPath" branch instead. - initialPath = seededPath + initialPath = '' savedScript = structuredClone(empty) console.log('[draft-sync] route: about to setDraftAndMeta(empty)', draftPath) scriptHandle.setDraftAndMeta(empty, {})