diff --git a/frontend/src/lib/components/DraftBadge.svelte b/frontend/src/lib/components/DraftBadge.svelte index a5bcf73df5..dc5839fa75 100644 --- a/frontend/src/lib/components/DraftBadge.svelte +++ b/frontend/src/lib/components/DraftBadge.svelte @@ -4,26 +4,16 @@ interface Props { has_draft?: boolean - draft_only?: boolean } - let { has_draft = false, draft_only = false }: Props = $props() + let { has_draft = false }: Props = $props() {#if has_draft} - {#if draft_only} - - {#snippet text()} - Never deployed and is only a draft - {/snippet} - Draft only - - {:else} - - {#snippet text()} - Is deployed and has a draft - {/snippet} - +Draft - - {/if} + + {#snippet text()} + Is deployed and has a draft + {/snippet} + +Draft + {/if} diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 8c36de8b11..37e7e9e5bc 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -131,7 +131,6 @@ onDeployError, onDetails, onSaveDraftError, - onSaveDraftOnlyAtNewPath, onHistoryRestore, onNavigate }: FlowBuilderProps = $props() @@ -299,14 +298,7 @@ loadingDraft = true try { const flow = cleanFlow(flowStore.val) - if (newFlow || savedFlow?.draft_only) { - if (savedFlow?.draft_only) { - await FlowService.deleteFlowByPath({ - workspace: $workspaceStore!, - path: initialPath, - keepCaptures: true - }) - } + if (newFlow) { if (!initialPath || $pathStore != initialPath) { await CaptureService.moveCapturesAndConfigs({ workspace: $workspaceStore!, @@ -326,7 +318,6 @@ value: flow.value, schema: flow.schema, tag: flow.tag, - draft_only: true, ws_error_handler_muted: flow.ws_error_handler_muted, visible_to_runner_only: flow.visible_to_runner_only, on_behalf_of_email: flow.on_behalf_of_email, @@ -337,7 +328,7 @@ await DraftService.createDraft({ workspace: $workspaceStore!, requestBody: { - path: newFlow || savedFlow?.draft_only ? $pathStore : initialPath, + path: newFlow ? $pathStore : initialPath, typ: 'flow', value: { ...flow, @@ -348,11 +339,10 @@ }) savedFlow = { - ...(newFlow || savedFlow?.draft_only + ...(newFlow ? { ...structuredClone($state.snapshot(flowStore.val)), - path: $pathStore, - draft_only: true + path: $pathStore } : savedFlow), draft: { @@ -365,11 +355,6 @@ let savedAtNewPath = false if (newFlow) { onSaveInitial?.({ path: $pathStore, id: getSelectedId() ?? 'settings' }) - } else if (savedFlow?.draft_only && $pathStore !== initialPath) { - savedAtNewPath = true - initialPath = $pathStore - onSaveDraftOnlyAtNewPath?.({ path: $pathStore, selectedId: getSelectedId() ?? 'settings' }) - // this is so we can use the flow builder outside of sveltekit } onSaveDraft?.({ path: $pathStore, savedAtNewPath, newFlow }) sendUserToast('Saved as draft') @@ -400,7 +385,7 @@ async function handleSaveFlowInternal(deploymentMsg?: string) { await compareVersions() - if (onLatest || initialPath == '' || savedFlow?.draft_only) { + if (onLatest || initialPath == '') { // Handle directly await saveFlow(deploymentMsg) } else { @@ -800,15 +785,13 @@ }> = [] if (untrack(() => customUi).topBar?.extraDeployOptions != false) { - if (savedFlow?.draft_only === false || savedFlow?.draft_only === undefined) { - dropdownItems.push({ - label: 'Exit & see details', - // Use the deployed path, not the live `$pathStore` — the latter - // reflects local rename edits that haven't been deployed yet, - // which would land the user on a 404 details page. - onClick: () => onDetails?.({ path: initialPath }) - }) - } + dropdownItems.push({ + label: 'Exit & see details', + // Use the deployed path, not the live `$pathStore` — the latter + // reflects local rename edits that haven't been deployed yet, + // which would land the user on a 404 details page. + onClick: () => onDetails?.({ path: initialPath }) + }) if (!untrack(() => newFlow)) { dropdownItems.push({ diff --git a/frontend/src/lib/components/ResourceEditor.svelte b/frontend/src/lib/components/ResourceEditor.svelte index 126ab0dc7f..ea30840fb3 100644 --- a/frontend/src/lib/components/ResourceEditor.svelte +++ b/frontend/src/lib/components/ResourceEditor.svelte @@ -13,8 +13,7 @@ import { deepEqual } from 'fast-equals' import { getUserExt } from '$lib/user' import type { UserExt } from '$lib/stores' - import { UserDraft, checkStaleness, type UserDraftHandle } from '$lib/userDraft.svelte' - import LocalDraftStaleModal from './common/confirmationModal/LocalDraftStaleModal.svelte' + import { UserDraft, type UserDraftHandle } from '$lib/userDraft.svelte' interface Props { canSave?: boolean @@ -80,42 +79,6 @@ // no DB-draft concept, so only `remoteRev` is ever populated. let fetchedRev: Record = $state({}) - // Local-draft staleness modal: opened when the backend resource moved - // on (someone else edited it) since the local autosave was written. - let staleModalOpen = $state(false) - let pendingStale: { ws: string; backend: ResourceState } | undefined = undefined - - function onStaleLoadLatest(): void { - if (!pendingStale) { - staleModalOpen = false - return - } - const { ws, backend } = pendingStale - // Drop the divergent autosave and reset the handle to the freshly - // fetched backend state. A later edit re-creates the autosave and - // the seeding effect records the new rev. - UserDraft.discard('resource', initialPath ?? '', backend, { workspace: ws }) - initialStates[ws] = $state.snapshot(backend) as ResourceState - pendingStale = undefined - staleModalOpen = false - } - - function onStaleKeepDraft(): void { - if (pendingStale) { - const { ws } = pendingStale - // Ack the new backend rev so the modal doesn't fire again until - // the backend moves once more. Keeps the local autosave intact. - UserDraft.saveMeta( - 'resource', - initialPath ?? '', - { remoteRev: fetchedRev[ws] }, - { workspace: ws } - ) - } - pendingStale = undefined - staleModalOpen = false - } - const handlesArray = UserDraft.useMany(() => workspaceSpecs.map((s) => ({ itemKind: 'resource' as const, @@ -262,34 +225,6 @@ labels: r.labels ?? undefined, wsSpecific: r.ws_specific ?? false } - // Reconcile the local autosave with the backend before the - // handle is registered. If the backend moved on since the - // autosave was written (recorded rev != current rev) surface - // the staleness modal; otherwise the form is just showing the - // user's unsaved work — a toast with a "Reset to deployed" - // escape is enough. - const persisted = UserDraft.get('resource', initialPath ?? '', { - workspace: ws - }) - const previousMeta = UserDraft.getMeta('resource', initialPath ?? '', { workspace: ws }) - if (persisted !== undefined && !deepEqual(persisted, s)) { - const cause = checkStaleness(previousMeta, r.edited_at) - if (cause) { - pendingStale = { ws, backend: s } - staleModalOpen = true - } else { - if (previousMeta.remoteRev === undefined && previousMeta.remoteDraftRev === undefined) { - // Legacy autosave (no rev recorded) — backfill so the - // next backend change is detectable as drift. - UserDraft.saveMeta( - 'resource', - initialPath ?? '', - { remoteRev: r.edited_at }, - { workspace: ws } - ) - } - } - } ensureHandle(ws, s) initialStates[ws] = structuredClone(s) existedInitially[ws] = true @@ -438,13 +373,6 @@ } - -
{#if otherDirty.length > 0} diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 8f4e75ff3d..ee003ede11 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -696,15 +696,7 @@ sendUserToast(`Could not parse code, are you sure it is valid?`, true) } let newHash = '' - if (initialPath == '' || savedScript?.draft_only) { - if (savedScript?.draft_only) { - await ScriptService.deleteScriptByPath({ - workspace: $workspaceStore!, - path: initialPath, - keepCaptures: true - }) - script.parent_hash = undefined - } + if (initialPath == '') { if (!initialPath || script.path != initialPath) { await CaptureService.moveCapturesAndConfigs({ workspace: $workspaceStore!, @@ -727,7 +719,6 @@ language: script.language, kind: script.kind, tag: script.tag, - draft_only: true, envs: script.envs, concurrent_limit: script.concurrent_limit, concurrency_time_window_s: script.concurrency_time_window_s, @@ -762,7 +753,7 @@ await DraftService.createDraft({ workspace: $workspaceStore!, requestBody: { - path: initialPath == '' || savedScript?.draft_only ? script.path : initialPath, + path: initialPath == '' ? script.path : initialPath, typ: 'script', value: { ...script, @@ -773,9 +764,7 @@ const clonedScript = structuredClone($state.snapshot(script)) savedScript = { - ...(initialPath == '' || savedScript?.draft_only - ? { ...clonedScript, draft_only: true } - : savedScript), + ...(initialPath == '' ? clonedScript : savedScript), draft: { ...clonedScript, draft_triggers: draftTriggers @@ -783,7 +772,7 @@ } as NewScriptWithDraftAndDraftTriggers let savedAtNewPath = false - if (initialPath == '' || (savedScript?.draft_only && script.path !== initialPath)) { + if (initialPath == '') { savedAtNewPath = true initialPath = script.path onSaveInitial?.({ path: script.path, hash: newHash }) @@ -862,10 +851,7 @@ : []) ] : []), - ...(!inSessionPane && - !script.draft_only && - script.kind === 'script' && - !script.auto_kind + ...(!inSessionPane && script.kind === 'script' && !script.auto_kind ? [ { label: 'Exit & See details', @@ -1960,7 +1946,7 @@ {hasPreprocessor} canHavePreprocessor={canHavePreprocessor(script.language)} args={hasPreprocessor && selectedInputTab !== 'preprocessor' ? {} : args} - isDeployed={savedScript && !savedScript?.draft_only} + isDeployed={!!savedScript} schema={script.schema} runnableVersion={script.parent_hash} onDeployTrigger={handleDeployTrigger} @@ -2122,7 +2108,7 @@ {template} tag={script.tag} lastSavedCode={savedScript?.draft?.content} - lastDeployedCode={savedScript?.draft_only ? undefined : savedScript?.content} + lastDeployedCode={savedScript?.content} bind:args bind:hasPreprocessor bind:captureTable diff --git a/frontend/src/lib/components/VariableEditor.svelte b/frontend/src/lib/components/VariableEditor.svelte index 2421738fb4..d9a234ea1b 100644 --- a/frontend/src/lib/components/VariableEditor.svelte +++ b/frontend/src/lib/components/VariableEditor.svelte @@ -16,8 +16,7 @@ import { deepEqual } from 'fast-equals' import { getUserExt } from '$lib/user' import type { UserExt } from '$lib/stores' - import { UserDraft, checkStaleness, type UserDraftHandle } from '$lib/userDraft.svelte' - import LocalDraftStaleModal from './common/confirmationModal/LocalDraftStaleModal.svelte' + import { UserDraft, type UserDraftHandle } from '$lib/userDraft.svelte' import LocalDraftBanner from './LocalDraftBanner.svelte' const dispatch = createEventDispatcher() @@ -48,37 +47,6 @@ // no DB-draft concept, so only `remoteRev` is ever populated. let fetchedRev: Record = $state({}) - // Local-draft staleness modal: opened when the backend variable moved - // on (someone else edited it) since the local autosave was written. - let staleModalOpen = $state(false) - let pendingStale: { ws: string; backend: VariableState } | undefined = undefined - - function onStaleLoadLatest(): void { - if (!pendingStale) { - staleModalOpen = false - return - } - const { ws, backend } = pendingStale - UserDraft.discard('variable', editPath ?? '', backend, { workspace: ws }) - initialStates[ws] = $state.snapshot(backend) as VariableState - pendingStale = undefined - staleModalOpen = false - } - - function onStaleKeepDraft(): void { - if (pendingStale) { - const { ws } = pendingStale - UserDraft.saveMeta( - 'variable', - editPath ?? '', - { remoteRev: fetchedRev[ws] }, - { workspace: ws } - ) - } - pendingStale = undefined - staleModalOpen = false - } - const handlesArray = UserDraft.useMany(() => workspaceSpecs.map((s) => ({ itemKind: 'variable' as const, @@ -173,23 +141,6 @@ labels: v.labels ?? undefined, wsSpecific: v.ws_specific ?? false } - // See ResourceEditor for the same pattern: a backend that - // moved on since the autosave was written → staleness modal; - // otherwise just a "showing your local autosave" toast with - // a "Reset to deployed" escape. - const persisted = UserDraft.get('variable', p, { workspace: ws }) - const previousMeta = UserDraft.getMeta('variable', p, { workspace: ws }) - if (persisted !== undefined && !deepEqual(persisted, s)) { - const cause = checkStaleness(previousMeta, v.edited_at) - if (cause) { - pendingStale = { ws, backend: s } - staleModalOpen = true - } else { - if (previousMeta.remoteRev === undefined && previousMeta.remoteDraftRev === undefined) { - UserDraft.saveMeta('variable', p, { remoteRev: v.edited_at }, { workspace: ws }) - } - } - } ensureHandle(ws, s) initialStates[ws] = structuredClone(s) existedInitially[ws] = true @@ -315,13 +266,6 @@ } - - { appReportingDrawerOpen = true }, - disabled: !savedApp || savedApp.draft_only + disabled: !savedApp }, { displayName: 'Diff', @@ -905,7 +876,7 @@
- -
-
-
- - -{/if} diff --git a/frontend/src/lib/components/common/table/AppRow.svelte b/frontend/src/lib/components/common/table/AppRow.svelte index 1c0c5b08d1..6bb72584b5 100644 --- a/frontend/src/lib/components/common/table/AppRow.svelte +++ b/frontend/src/lib/components/common/table/AppRow.svelte @@ -14,7 +14,6 @@ import { ExternalLink, Eye, - File, FileJson, FolderOpen, GitFork, @@ -37,7 +36,7 @@ import { isCloudHosted } from '$lib/cloud' interface Props { - app: ListableApp & { has_draft?: boolean; draft_only?: boolean; canWrite: boolean } + app: ListableApp & { has_draft?: boolean; canWrite: boolean } marked: string | undefined shareModal: ShareModal moveDrawer: MoveDrawer @@ -86,7 +85,7 @@ path={app.path} summary={app.summary} workspaceId={app.workspace_id ?? $workspaceStore ?? ''} - canFavorite={!app.draft_only} + canFavorite={true} {depth} {keyboardSelected} > @@ -98,7 +97,7 @@ Raw {/if} - + {#if app.labels?.length}
{#each app.labels.slice(0, 3) as label} @@ -155,40 +154,9 @@ aiId={`app-row-dropdown-${app.summary?.length > 0 ? app.summary : app.path}`} aiDescription={`Open dropdown for app ${app.summary?.length > 0 ? app.summary : app.path} options`} items={async () => { - let { draft_only, canWrite, summary, execution_mode, path, has_draft } = app + let { canWrite, summary, execution_mode, path, has_draft } = app const canEdit = canWrite && showEditButton - if (draft_only) { - return [ - { - displayName: 'Delete', - icon: Trash, - action: async (event) => { - // TODO - // @ts-ignore - if (event?.shiftKey) { - await AppService.deleteApp({ workspace: $workspaceStore ?? '', path }) - dispatch('change') - } else { - deleteConfirmedCallback = async () => { - await AppService.deleteApp({ workspace: $workspaceStore ?? '', path }) - dispatch('change') - } - } - }, - type: 'delete', - disabled: !canEdit, - hide: $userStore?.operator - }, - { - displayName: $userStore?.operator ? 'View JSON' : 'View/Edit JSON', - icon: File, - action: () => { - loadAppJson() - } - } - ] - } return [ { displayName: 'Duplicate/Fork', diff --git a/frontend/src/lib/components/common/table/FlowRow.svelte b/frontend/src/lib/components/common/table/FlowRow.svelte index 14ee1a3287..695f70883f 100644 --- a/frontend/src/lib/components/common/table/FlowRow.svelte +++ b/frontend/src/lib/components/common/table/FlowRow.svelte @@ -39,7 +39,7 @@ import { isCloudHosted } from '$lib/cloud' interface Props { - flow: Flow & { has_draft?: boolean; draft_only?: boolean; canWrite: boolean } + flow: Flow & { has_draft?: boolean; canWrite: boolean } marked: string | undefined shareModal: ShareModal moveDrawer: MoveDrawer @@ -103,16 +103,14 @@ @@ -121,7 +119,7 @@ archived {/if} - + {#if flow.labels?.length}
{#each flow.labels.slice(0, 3) as label} @@ -180,30 +178,9 @@ aiId={`flow-row-dropdown-${flow.summary?.length > 0 ? flow.summary : flow.path}`} aiDescription={`Open dropdown for flow ${flow.summary?.length > 0 ? flow.summary : flow.path} options`} items={async () => { - let { draft_only, path, archived, has_draft } = flow + let { path, archived, has_draft } = flow let owner = isOwner(path, $userStore, $workspaceStore) const canEdit = flow.canWrite && showEditButton - if (draft_only) { - return [ - { - displayName: 'Delete', - icon: Trash, - action: (event) => { - // @ts-ignore - if (event?.shiftKey) { - deleteFlow(path) - } else { - deleteConfirmedCallback = () => { - deleteFlow(path) - } - } - }, - type: 'delete', - disabled: !owner, - hide: $userStore?.operator - } - ] - } return [ { displayName: 'View runs', diff --git a/frontend/src/lib/components/common/table/ScriptRow.svelte b/frontend/src/lib/components/common/table/ScriptRow.svelte index 3d0aec796b..ba67d54ffb 100644 --- a/frontend/src/lib/components/common/table/ScriptRow.svelte +++ b/frontend/src/lib/components/common/table/ScriptRow.svelte @@ -121,7 +121,7 @@ @@ -168,7 +168,7 @@ > {/if} - + {#if script.labels?.length}
{#each script.labels.slice(0, 3) as label} @@ -239,34 +239,6 @@ items={async () => { let owner = isOwner(script.path, $userStore, $workspaceStore) const canEdit = script.canWrite && showEditButton - if (script.draft_only) { - return [ - { - displayName: 'View code', - icon: Code, - action: () => { - showCode(script.path, script.summary) - } - }, - { - displayName: 'Delete', - icon: Trash, - action: (event) => { - // TODO - // @ts-ignore - if (event?.shiftKey) { - deleteScript(script.path) - } else { - deleteConfirmedCallback = () => { - deleteScript(script.path) - } - } - }, - type: dlt, - disabled: !canEdit - } - ] - } return [ { displayName: 'View code', diff --git a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts index a23a8da905..b076949404 100644 --- a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts +++ b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts @@ -424,10 +424,7 @@ export class AIChatManager { return { kind: 'flow', path: this.flowOptions?.path, - deployed: - !!this.flowOptions?.path && - !!this.flowOptions.lastDeployedFlow && - !this.flowOptions.lastDeployedFlow.draft_only + deployed: !!this.flowOptions?.path && !!this.flowOptions.lastDeployedFlow } } @@ -519,8 +516,7 @@ export class AIChatManager { this.tools = globalToolsFor({ sessionPreview: this.isSessionChat }) this.helpers = { ...(this.isSessionChat ? { sessionId: this.sessionId } : {}), - testActiveFlow: async (args?: Record) => - this.flowAiChatHelpers?.testFlow(args) + testActiveFlow: async (args?: Record) => this.flowAiChatHelpers?.testFlow(args) } satisfies GlobalToolHelpers } else if (mode === AIMode.APP) { const customPrompt = getCombinedCustomPrompt(mode) diff --git a/frontend/src/lib/components/copilot/chat/global/core.ts b/frontend/src/lib/components/copilot/chat/global/core.ts index db8ecb871f..e9c0fa7f42 100644 --- a/frontend/src/lib/components/copilot/chat/global/core.ts +++ b/frontend/src/lib/components/copilot/chat/global/core.ts @@ -128,10 +128,7 @@ const INSTRUCTION_SUBJECTS = [ // `datatable` is not a workspace item type, but the model can request the // datatable SDK reference (the wmill.datatable() runnable API) the same way. const INSTRUCTION_SUBJECTS_EXTRA = ['datatable'] as const -const ALL_INSTRUCTION_SUBJECTS = [ - ...INSTRUCTION_SUBJECTS, - ...INSTRUCTION_SUBJECTS_EXTRA -] as const +const ALL_INSTRUCTION_SUBJECTS = [...INSTRUCTION_SUBJECTS, ...INSTRUCTION_SUBJECTS_EXTRA] as const const MAX_LIST_LIMIT = 100 type ActiveGlobalEditorType = Extract type LiveEditorDraftKind = Parameters[0] @@ -163,7 +160,7 @@ const scriptLangSchema = z.enum($ScriptLang.enum) const getInstructionsSchema = z.object({ subject: instructionSubjectSchema.describe( - "What to get authoring instructions for: a workspace item type (script, flow, resource, app) or \"datatable\" for the wmill.datatable() SQL SDK used inside runnables. Schedules, triggers, and variables don't need instructions — their tool schemas describe everything." + 'What to get authoring instructions for: a workspace item type (script, flow, resource, app) or "datatable" for the wmill.datatable() SQL SDK used inside runnables. Schedules, triggers, and variables don\'t need instructions — their tool schemas describe everything.' ), language: scriptLangSchema .optional() @@ -1208,7 +1205,6 @@ async function listWorkspaceItems( workspace, pathStart: pathPrefix, perPage, - includeDraftOnly: true, withoutDescription: true }) for (const script of scripts) items.push(scriptToItem(script, false)) @@ -1219,7 +1215,6 @@ async function listWorkspaceItems( workspace, pathStart: pathPrefix, perPage, - includeDraftOnly: true, withoutDescription: true }) for (const flow of flows) items.push(flowToItem(flow, false)) @@ -2208,13 +2203,7 @@ async function writeScriptDraft( content: args.content, language: args.language } - UserDraft.setDraftAndMeta( - 'script', - storagePath, - draft, - { remoteRev: existing.hash, remoteDraftRev: existing.draft_created_at }, - { workspace } - ) + UserDraft.save('script', storagePath, draft, { workspace }) } else { const draft: NewScript = { path: args.path, @@ -2265,10 +2254,7 @@ async function writeFlowDraft( } UserDraft.save('flow', storagePath, draft, { workspace }) } else if (backendExists) { - const [existing, latestVersion] = await Promise.all([ - FlowService.getFlowByPathWithDraft({ workspace, path: args.path }), - FlowService.getFlowLatestVersion({ workspace, path: args.path }) - ]) + const existing = await FlowService.getFlowByPathWithDraft({ workspace, path: args.path }) const base = (existing.draft ?? existing) as Flow const draft: Flow = { ...structuredClone(base), @@ -2277,13 +2263,7 @@ async function writeFlowDraft( value, schema: draftValue.schema ?? base.schema } - UserDraft.setDraftAndMeta( - 'flow', - storagePath, - draft, - { remoteRev: latestVersion.id, remoteDraftRev: existing.draft_created_at }, - { workspace } - ) + UserDraft.save('flow', storagePath, draft, { workspace }) } else { const draft: Flow = { path: args.path, @@ -2381,11 +2361,10 @@ async function writeResourceDraft(args: CreateResource, ctx: WriteDraftCtx): Pro }) } else if (backendExists) { const existing = await ResourceService.getResource({ workspace, path: args.path }) - UserDraft.setDraftAndMeta( + UserDraft.save( 'resource', args.path, createResourceToDraftState(args, resourceToDraftState(existing)), - { remoteRev: existing.edited_at }, { workspace } ) } else { @@ -2418,11 +2397,10 @@ async function writeVariableDraft(args: CreateVariable, ctx: WriteDraftCtx): Pro path: args.path, decryptSecret: false }) - UserDraft.setDraftAndMeta( + UserDraft.save( 'variable', args.path, createVariableToDraftState(args, variableToDraftState(existing)), - { remoteRev: existing.edited_at }, { workspace } ) } else { diff --git a/frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts b/frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts index 9a6d9125f4..553fad9440 100644 --- a/frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts +++ b/frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts @@ -354,11 +354,9 @@ export function saveGlobalAppDraft( ): WorkspaceItem { const storagePath = resolveDraftStoragePath(workspace, 'raw_app', path) const normalized = normalizeAppDraftValue(value) - if (meta) { - UserDraft.setDraftAndMeta('raw_app', storagePath, normalized, meta, { workspace }) - } else { - UserDraft.save('raw_app', storagePath, normalized, { workspace }) - } + // Persist (and update any live editor) regardless of meta — meta tracking + // (staleness) was removed. + UserDraft.save('raw_app', storagePath, normalized, { workspace }) const stored = getGlobalDraft(workspace, 'app', path) if (!stored) throw new Error(`Could not read written app draft "${path}".`) return stored diff --git a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte index 471790d539..2b613c42a3 100644 --- a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte +++ b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte @@ -171,7 +171,7 @@ hasPreprocessor={!!flowStore.val.value.preprocessor_module} canHavePreprocessor={true} args={previewArgs.val} - isDeployed={savedFlow && !savedFlow?.draft_only} + isDeployed={!!savedFlow} schema={flowStore.val.schema} {onDeployTrigger} /> diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index d1950697f9..f3aa79691f 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -90,7 +90,6 @@ workspace: $workspaceStore!, showArchived: archived ? true : undefined, includeWithoutMain: includeWithoutMain ? true : undefined, - includeDraftOnly: true, withoutDescription: true }) @@ -108,7 +107,6 @@ await FlowService.listFlows({ workspace: $workspaceStore!, showArchived: archived ? true : undefined, - includeDraftOnly: true, withoutDescription: true }) ).map((x: Flow) => { @@ -124,17 +122,15 @@ } async function loadApps(): Promise { - apps = (await AppService.listApps({ workspace: $workspaceStore!, includeDraftOnly: true })).map( - (app: ListableApp) => { - return { - canWrite: - canWrite(app.path!, app.extra_perms!, $userStore) && - app.workspace_id == $workspaceStore && - !$userStore?.operator, - ...app - } + apps = (await AppService.listApps({ workspace: $workspaceStore! })).map((app: ListableApp) => { + return { + canWrite: + canWrite(app.path!, app.extra_perms!, $userStore) && + app.workspace_id == $workspaceStore && + !$userStore?.operator, + ...app } - ) + }) loading = false } diff --git a/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte b/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte index dedb7b5d11..8781aedd41 100644 --- a/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppEditorHeader.svelte @@ -107,7 +107,6 @@ path: string summary: string policy: any - draft_only?: boolean custom_path?: string } | undefined @@ -474,7 +473,6 @@ path: newEditedPath, summary: summary, policy, - draft_only: true, custom_path: customPath }, js, @@ -500,7 +498,6 @@ value: structuredClone(stateSnapshot(app)), path: newEditedPath, policy, - draft_only: true, draft: { summary: summary, value: structuredClone(stateSnapshot(app)), @@ -558,33 +555,10 @@ try { await computeTriggerables() let path = appPath - if (savedApp.draft_only) { - await AppService.deleteApp({ - workspace: $workspaceStore!, - path: path - }) - let { css, js } = await getBundle() - - await AppService.createAppRaw({ - workspace: $workspaceStore!, - formData: { - app: { - value: app!, - summary: summary, - policy, - path: newEditedPath || path, - draft_only: true, - custom_path: customPath - }, - js, - css - } - }) - } await DraftService.createDraft({ workspace: $workspaceStore!, requestBody: { - path: savedApp.draft_only ? newEditedPath || path : path, + path: path, typ: 'app', value: { value: app!, @@ -596,16 +570,7 @@ }) savedApp = { - ...(savedApp?.draft_only - ? { - summary: summary, - value: structuredClone(stateSnapshot(app)), - path: savedApp.draft_only ? newEditedPath || path : path, - policy, - draft_only: true, - custom_path: customPath - } - : savedApp), + ...savedApp, draft: { summary: summary, value: structuredClone(stateSnapshot(app)), @@ -618,7 +583,7 @@ sendUserToast('Draft saved') if (!inSessionPane) UserDraft.remove('raw_app', path) loading.saveDraft = false - if (newApp || savedApp.draft_only) { + if (newApp) { dispatch('savedNewAppPath', newEditedPath || path) } } catch (e) { @@ -788,7 +753,7 @@