mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 08:01:25 +00:00
f4bb80ce8a
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
124 lines
4.6 KiB
Svelte
124 lines
4.6 KiB
Svelte
<script lang="ts">
|
|
import ScriptBuilder from '$lib/components/ScriptBuilder.svelte'
|
|
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
|
|
import type { WorkspaceItem } from '$lib/components/workspacePicker'
|
|
import type { SessionRuntime } from './sessionRuntime.svelte'
|
|
import type { NewScript } from '$lib/gen'
|
|
import { UserDraft } from '$lib/userDraft.svelte'
|
|
import { UserDraftDbSyncer } from '$lib/userDraftDbSyncer.svelte'
|
|
import SessionEditorTarget from './SessionEditorTarget.svelte'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { invalidateWorkspaceDrafts } from '$lib/workspaceDrafts.svelte'
|
|
|
|
let {
|
|
runtime,
|
|
path,
|
|
workspaceId,
|
|
onNavigate,
|
|
initialTestPanelCollapsed = false,
|
|
isActiveSession = true
|
|
}: {
|
|
runtime: SessionRuntime
|
|
path: string
|
|
workspaceId: string
|
|
onNavigate?: (item: WorkspaceItem) => void
|
|
initialTestPanelCollapsed?: boolean
|
|
/** Forwarded to SessionEditorTarget — only the visible session claims the
|
|
* workspace's single live-editor slot. */
|
|
isActiveSession?: boolean
|
|
} = $props()
|
|
|
|
let diffDrawer: DiffDrawer | undefined = $state()
|
|
|
|
// Restore actions for the diff drawer. The previous shared
|
|
// `loadScript`-based handler was a no-op: loadScript early-returns on the
|
|
// already-loaded path (and would re-read the local draft anyway). Instead
|
|
// reset the live UserDraft handle to the target baseline — the inbound
|
|
// effect then syncs the editor preview. Mirrors /scripts/edit's restore.
|
|
async function restoreDeployed() {
|
|
const saved = runtime.savedScript.val
|
|
if (!saved) {
|
|
sendUserToast('Could not restore to deployed', true)
|
|
return
|
|
}
|
|
diffDrawer?.closeDrawer()
|
|
// Drop the user's per-user draft too, so "deployed" sticks across
|
|
// a reload. The overlay sets `is_draft: true` when a draft exists
|
|
// for the authed user; the syncer's `value: null` POST is the
|
|
// canonical per-user delete.
|
|
//
|
|
// Fire-and-forget: every read here (`saved`, the snapshot we build
|
|
// below, the UserDraft.discard write) is purely in-memory, so we
|
|
// don't need the DELETE to have landed to finish the restore. We
|
|
// flip `is_draft` optimistically so the UI matches the new intent
|
|
// immediately. A failed DELETE only matters across a hard reload
|
|
// before it lands — log and move on.
|
|
if (saved.is_draft) {
|
|
saved.is_draft = false
|
|
UserDraftDbSyncer.save({
|
|
workspace: workspaceId,
|
|
itemKind: 'script',
|
|
path: saved.path,
|
|
value: null
|
|
}).catch((e) => console.error('restoreDeployed: draft delete failed', e))
|
|
// Per-user draft gone — refresh the session draft-bar count immediately
|
|
// instead of waiting for an AI turn-end / tab-refocus signal.
|
|
invalidateWorkspaceDrafts(workspaceId)
|
|
}
|
|
const deployed = structuredClone($state.snapshot(saved)) as NewScript & { draft?: unknown }
|
|
delete deployed.draft
|
|
UserDraft.discard<NewScript>('script', path, deployed, { workspace: workspaceId })
|
|
}
|
|
</script>
|
|
|
|
{#if runtime.savedScript.val}
|
|
<DiffDrawer bind:this={diffDrawer} {restoreDeployed} />
|
|
{/if}
|
|
<SessionEditorTarget
|
|
{runtime}
|
|
kind="script"
|
|
{path}
|
|
{workspaceId}
|
|
{onNavigate}
|
|
{isActiveSession}
|
|
effectivePath={() => runtime.scriptStore.val?.path ?? path}
|
|
>
|
|
{#snippet editor()}
|
|
{#if runtime.scriptStore.val}
|
|
<!--
|
|
A script with no backend version yet (AI-created, never saved or deployed
|
|
→ savedScript undefined) is a *new* script: pass an empty initialPath so
|
|
ScriptBuilder behaves exactly like /scripts/add — Save draft is enabled and
|
|
creates it on first save. On that save ScriptBuilder writes savedScript back
|
|
through the bind and sets its own initialPath to the path, flipping us into
|
|
edit mode (Save draft + Diff) without navigating away.
|
|
-->
|
|
<ScriptBuilder
|
|
bind:script={runtime.scriptStore.val}
|
|
bind:savedScript={runtime.savedScript.val}
|
|
initialPath={runtime.savedScript.val ? path : ''}
|
|
autosaveWorkspace={workspaceId}
|
|
autosavePath={path}
|
|
initialPathChosen={true}
|
|
neverShowMeta={true}
|
|
fullyLoaded={!runtime.slot('script').loading}
|
|
disableHistoryChange={true}
|
|
condensedHeader={true}
|
|
{diffDrawer}
|
|
{onNavigate}
|
|
{initialTestPanelCollapsed}
|
|
onDeploy={(e) => {
|
|
// Fires on every deploy (primary, "Deploy & Stay here", and lib — we
|
|
// ignore e.stay since the session always stays). Toast, then sync the
|
|
// preview to the deployed version.
|
|
sendUserToast('Deployed')
|
|
runtime.syncPreviewWithDeployed(workspaceId, 'script', e.path)
|
|
// Deploying clears the item's pending draft — refresh the workspace
|
|
// Draft Count so the session bar / compare page drop it immediately.
|
|
invalidateWorkspaceDrafts(workspaceId)
|
|
}}
|
|
/>
|
|
{/if}
|
|
{/snippet}
|
|
</SessionEditorTarget>
|