mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 16:02:23 +00:00
* feat(frontend): adapt AI-chat/sessions drafts to DB-backed model PR #9351 dropped UserDraft's localStorage layer; the chat adapter's synchronous save->read-back threw "Could not read written draft". The adapter now treats the backend as source of truth (in-tab cell used opportunistically for live-preview coherence) with conflict-on-save, and read tools fall back to the backend. Collapses the six writeXDraft functions onto one generic writeDraft + typed per-kind WriteSpec constants. Terminology: "local draft" -> "draft" (drafts are server-side). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): autosave indicator + draft-only diff guard in session editors Thread an explicit (workspace, path) autosave target to the cloud AutosaveIndicator in the Script/Flow/RawApp session previews so it watches the same key saves land on (it previously watched an empty path and never animated). Disable the Diff button with a hint for draft-only (no_deployed) items consistently across the three editors. Adjust the script topbar compact breakpoint/layout so the cloud icon is part of the bar, and stop splitpanes over-constraining session panes on reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): session draft diff viewer for schedule/resource/variable Canonicalize both sides of the draft diff onto one field set and strip runtime-only fields so rows aren't spuriously marked all-changed; mask secret values. Map draft itemKinds to deploy-style kinds so the DiffRow shows the correct icon/label. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): uniform diff-viewer row height regardless of summary Diff-viewer leaf rows (WorkspaceItemRow) drew two lines when an item had a summary and one line otherwise, giving unequal heights. Add an opt-in `uniformHeight` prop that gives the text wrapper a shared min-height and vertically centers the one-line case; enable it only from the diff viewer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(frontend): address review nits on the drafts diff/guard changes - Reuse the exported TRIGGER_RUNTIME_IGNORE from utils_deployable instead of a verbatim copy, so the runtime-field ignore list has one source of truth. - Drop the now-redundant `(savedApp as any)` cast in RawAppEditorHeader; the prop type already carries `no_deployed`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): add description parameter to the write_flow chat tool write_flow had no way to set a flow's top-level description (the sibling of summary in OpenFlow); patch_flow_json only edits the compact value, so the field was unreachable from the AI chat. Thread an optional description end-to-end: tool schema -> persisted draft -> read-back -> deploy body. Structural patches (patch_flow_json/set_flow_module_code) pass no description, so a previously-set description is preserved. Adds a deployRequests regression test asserting a draft description reaches the deploy body, overriding the deployed one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): round-trip top-level fields in session preview draft sync The session preview's two-way draft sync dedups on a per-kind signature and mirrors fields between the editor store and the shared UserDraft cell. Both omitted fields the chat can set, so with the preview open a change to only that field was swallowed (identical signature) and then clobbered by the editor's outbound save: - flow: the signature and applyDraftToStore ignored top-level `description`. - script: the signature keyed on `content` alone, dropping `summary`/`language`. Add the missing fields to flowDraftSig and the script codec signature, and copy `description` in the flow codec's applyDraftToStore (mirroring `summary`). Raw-app already stringifies the whole draft, so it was unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): deploy draft-only flow from the session preview Deploying a draft-only flow (a draft with no deployed row) from the session preview hit two gaps the full-page flow editor already handled: - create vs update: newFlow keyed on `!savedFlow.val`, but a draft-only flow has a synthesized savedFlow (no_deployed=true), so deploy took updateFlow against the draft path and 404'd "Flow not found". Key it on no_deployed too. - friendly name: a brand-new flow is stored under a `draft_<uuid>` path with its intended name in `draft_path`. Seed the builder's initialPath from `draft_path` (as the full-page editor does) so the Path widget and deploy use the friendly name instead of creating a flow named draft_<uuid>. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): deploy draft-only raw app from the session preview Same create-vs-update bug as the flow session preview: newApp keyed on `!savedRawApp.val`, but a draft-only app has a truthy synthesized savedApp (getAppByPath with rawApp:true resolves to the draft kind instead of 404ing, carrying no_deployed=true), so deploy took updateApp against a path with no deployed row and 404'd "not found". Key newApp on no_deployed too so a never-deployed app deploys via createApp. More reachable than the flow case: it hit any never-deployed app, including chat-created ones at friendly paths. Keying newApp on no_deployed also exposed that newEditedPath (the breadcrumb path AND the createApp target) used newApp to mean "brand-new, generate a random name". A draft-only app is newApp=true but already has a real path (empty newPath at init, but appPath is set), so it showed and would deploy a random `*_app` name. Prefer the real appPath before the random fallback, so only a genuinely new app (appPath === '') still gets a generated suggestion; the full-page editor is unaffected (it always sets newPath). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): don't re-save a draft after deploying from the session preview Deploying from a session preview reloaded the editor (expected) but then immediately POSTed a fresh draft. The full-page editor guards deploy with discardDraftAfterDeploy (stopSync + arm-restart-on-first-interaction), but the shared editor header skips that in a session pane (inSessionPane) and routes post-deploy cleanup through sessionRuntime.syncPreviewWithDeployed, which did discard + reload without the stopSync guard. UserDraft.discard keeps the cell entry, so the reload's UserDraft.save fired the cell's reactive effect and re-POSTed the just-deployed value as a draft. Wrap the discard + reload in the same UserDraft.stopSync + armRestartOnFirst- Interaction bracket. One place fixes all three kinds (script/flow/raw_app), since they all funnel through syncPreviewWithDeployed; autosave resumes on the next genuine edit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(frontend): address review findings on the session-preview drafts work - Type `no_deployed` via the GetXByPathResponse/UserDraftOverlay types instead of `(result as any)`/`(saved as any)` casts at the sites this branch added (sessionRuntime, ScriptBuilder, FlowBuilder, + widened the script/flow builder prop types). Pre-existing trigger/variable/resource-editor casts left untouched. - Drop a history-narrating comment parenthetical per the AGENTS.md comment policy (RawAppEditorView). - Add a unit test covering persistGlobalDraft's conflict-on-save / override path (conflict-capable updateDraft mock; inert for existing tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): keep the friendly generated path for a brand-new raw app The earlier draft-only newApp fix made newEditedPath prefer `appPath` before the random suggestion, but a brand-new app is parked at the storage placeholder `u/{user}/draft_{uuid}` (the /apps_raw/add redirect target), so it surfaced that uuid instead of a friendly `<adjective>_app` suggestion. Reject a `draft_` placeholder segment when choosing the path: a real named/draft-only path is still kept, a placeholder falls through to the generated suggestion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): show the Diff-button tooltip when it's disabled A disabled <button> fires no pointer events and browsers suppress its native title, so the "deploy once to compare" explanation never showed on hover for a draft-only item's Diff button. Wrap the button in a titled element and set pointer-events-none on the button when disabled, so the hover reaches the wrapper. Applied in ScriptBuilder, FlowBuilder, and RawAppEditorHeader (covers both the full-page editors and the session preview). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): surface draft save failures and conflicts in the AI chat tools Addresses Codex + Pi review findings on PR #9601 (three P1s, all in the DB-backed draft adapter reporting success when the write didn't land): - persistGlobalDraft reported {status:'saved'} even when UserDraftDbSyncer.save failed (it records network/5xx into a failure map instead of throwing). Check getState().state==='failed' after the save and return a new 'error' status; finishDraftWrite now emits success:false with a retry hint. - saveGlobalAppDraft dropped the conflict/error status (returned only the item), so write_app_file/patch_app_file/write_app_runnable reported every stale or failed write as saved. It now returns the full DraftPersistResult, and the six app write tools route through a shared finishAppDraftWrite helper. - fetchBackendDraftValue's catch{} swallowed non-404 errors (403/500/network), collapsing them to "no draft" so the write merged from the deployed item and lost in-progress draft edits. Narrow the catch to status===404; propagate the rest. Adds unit coverage: save-failure -> 'error', non-404 read -> propagates, raw-app stale write -> 'conflict'. 71/71 pass, check:fast + full build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): surface failed draft deletes + strip tool-only override from schedule drafts Addresses Codex's second-round review on PR #9601: - [P1] deleteGlobalDraft reported success even when the server delete failed or conflicted (UserDraftDbSyncer.save records failure state instead of throwing) — so discard_local_draft / deploy_workspace_item / delete_workspace_item / the /global_drafts delete could report a draft removed while the DB still had it. Check getState().state and getConflict() after the awaited null save and throw, mirroring the write-path guard. - [P2] writeScheduleDraft persisted the tool-only `override` conflict flag into the schedule draft value (mergeDraftConfig cloned every arg field). Strip `override` in SCHEDULE_SPEC.buildDraft before merging. Tests: failed server delete -> throws; schedule draft no longer contains `override`. 73/73 pass, check:fast + full build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(frontend): /global_drafts "Clear all" deletes persisted drafts, not just cells Codex review nit (P2): the dev-only global-drafts inspector's "Clear all" called clearGlobalDrafts(), which only iterates in-tab UserDraft cells — any persisted backend draft row not currently mounted as a cell survived, so the list re-showed it after refresh. Iterate the listed drafts and delete each via the backend-aware deleteGlobalDraft() (continue past per-row failures), matching the per-row delete, then clear local cells + ephemeral secrets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
96 lines
4.3 KiB
TypeScript
96 lines
4.3 KiB
TypeScript
import type { NewScript, Script } from '$lib/gen'
|
|
import type { AssetWithAltAccessType } from './assets/lib'
|
|
import type { ScriptBuilderWhitelabelCustomUi } from './custom_ui'
|
|
import type { DiffDrawerI } from './diff_drawer'
|
|
import type { ScriptBuilderFunctionExports } from './scriptBuilder'
|
|
import type { ScheduleTrigger } from './triggers'
|
|
import type { Trigger } from './triggers/utils'
|
|
import type { WorkspaceItem } from './workspacePicker'
|
|
|
|
export interface ScriptBuilderProps {
|
|
script: NewScript & {
|
|
draft_triggers?: Trigger[]
|
|
assets?: AssetWithAltAccessType[]
|
|
}
|
|
disableAi?: boolean
|
|
fullyLoaded?: boolean
|
|
initialPath?: string
|
|
/**
|
|
* Path the route's `UserDraft.use<EditableScript>('script', ...)`
|
|
* handle is keyed by. Distinct from `initialPath` for new drafts —
|
|
* `initialPath` is the displayed/editor path (empty for new), while
|
|
* this is the URL path the draft is persisted under (`u/{user}/
|
|
* draft_{uuid}`). Used to bracket the bootstrap `initContent` write
|
|
* with `UserDraft.stopSync` / `restartSync` so the template seed
|
|
* doesn't POST before the user's first real edit. Default to `''`
|
|
* for backwards compat with callers that don't manage drafts; the
|
|
* stop/restart pair is a no-op on a non-live entry.
|
|
*/
|
|
userDraftPath?: string
|
|
/**
|
|
* Workspace + path the AutosaveIndicator watches for sync state. Default
|
|
* (undefined) falls back to `$workspaceStore` / `userDraftPath` — the
|
|
* full-page editor. The sessions preview sets these to the session's
|
|
* (possibly forked) workspace and target path, where autosave is owned by
|
|
* `SessionEditorTarget`/`useUserDraftSync`, so the indicator must watch that
|
|
* key rather than the global store + the unset `userDraftPath`.
|
|
*/
|
|
autosaveWorkspace?: string
|
|
autosavePath?: string
|
|
template?:
|
|
| 'docker'
|
|
| 'bunnative'
|
|
| 'claudesandbox'
|
|
| 'wac_python'
|
|
| 'wac_typescript'
|
|
| 'ci_test_bun'
|
|
| 'ci_test_python'
|
|
| 'script'
|
|
initialArgs?: Record<string, any>
|
|
lockedLanguage?: boolean
|
|
showMeta?: boolean
|
|
neverShowMeta?: boolean
|
|
diffDrawer?: DiffDrawerI | undefined
|
|
savedScript?: (Script | NewScript) & { no_deployed?: boolean }
|
|
searchParams?: URLSearchParams
|
|
disableHistoryChange?: boolean
|
|
customUi?: ScriptBuilderWhitelabelCustomUi
|
|
savedPrimarySchedule?: ScheduleTrigger | undefined
|
|
functionExports?: ((exports: ScriptBuilderFunctionExports) => void) | undefined
|
|
children?: import('svelte').Snippet
|
|
// Fires on every successful deploy. `stay` is true for "Deploy & Stay here"
|
|
// and for lib scripts (the editor stays in place rather than navigating to
|
|
// the deployed item) — consumers should skip post-deploy navigation when set.
|
|
onDeploy?: (e: { path: string; hash: string; stay: boolean }) => void
|
|
onDeployError?: (e: { path: string; error: any }) => void
|
|
onHistoryRestore?: () => void
|
|
onSeeDetails?: (e: { path: string }) => void
|
|
onNavigate?: (item: WorkspaceItem) => void
|
|
// Fired whenever a test run is started from the script editor, with the
|
|
// preview job id. Used by whitelabel embedders to track test jobs.
|
|
onTestJob?: (e: { jobId: string }) => void
|
|
// Forwarded to the underlying ScriptEditor. When true, the right-hand
|
|
// test/run pane opens collapsed. Used by the session preview.
|
|
initialTestPanelCollapsed?: boolean
|
|
// Treat the path as already chosen (seeds the path "dirty" flag) so the
|
|
// summary→path auto-slug for new scripts (initialPath == '') doesn't
|
|
// overwrite it. Used by the session preview, which opens AI-created scripts
|
|
// as new but with a path the AI already assigned.
|
|
initialPathChosen?: boolean
|
|
// Threaded to the `AutosaveIndicator` popover so its "Reset to
|
|
// deployed" button can do the same thing the load-time toast offers.
|
|
// Routes pass their own re-load-without-draft callback here; omit on
|
|
// callers (session preview, embedded SDK) that shouldn't surface the
|
|
// action at all.
|
|
onResetToDeployed?: () => void | Promise<void>
|
|
// Triggers the AutosaveIndicator's on-mount "Loaded from draft" hint
|
|
// (with a one-shot green flash) the first time it flips to true.
|
|
loadedFromDraft?: boolean
|
|
// Non-zero when other workspace users have a draft at this path.
|
|
// Drives both the indicator's hint label ("Others are working on
|
|
// this script") and the popover's "See others' drafts" button.
|
|
othersDraftsCount?: number
|
|
// Wired by the route to flip the OtherUsersDraftsModal open.
|
|
onOpenOthersDrafts?: () => void
|
|
}
|