Compare commits
3 Commits
rf/pythonI
...
rf/whitela
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57b02e6103 | ||
|
|
a7ed1c83c5 | ||
|
|
360a371d0e |
3
frontend/package-lock.json
generated
3
frontend/package-lock.json
generated
@@ -7,7 +7,6 @@
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.496.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^4.0.0",
|
||||
@@ -151,7 +150,7 @@
|
||||
"fsevents": "^2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^4.0.0"
|
||||
"svelte": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"postinstall": "node scripts/untar_ui_builder.js && node scripts/patch_files.js",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold warning",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
@@ -161,7 +160,7 @@
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^4.0.0"
|
||||
"svelte": "^5.0.0"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
@@ -381,11 +380,16 @@
|
||||
"./tailwindUtils": {
|
||||
"types": "./package/components/apps/editor/componentsPanel/tailwindUtils.d.ts",
|
||||
"default": "./package/components/apps/editor/componentsPanel/tailwindUtils.js"
|
||||
},
|
||||
"./components/custom_ui": {
|
||||
"types": "./package/components/custom_ui.d.ts",
|
||||
"default": "./package/components/custom_ui.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"package"
|
||||
"package",
|
||||
"scripts"
|
||||
],
|
||||
"license": "AGPL-3.0",
|
||||
"svelte": "./dist/index.js",
|
||||
@@ -519,6 +523,9 @@
|
||||
],
|
||||
"tailwindUtils": [
|
||||
"./package/components/apps/editor/componentsPanel/tailwindUtils.d.ts"
|
||||
],
|
||||
"components/custom_ui": [
|
||||
"./package/components/custom_ui.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
// Check if this script is being run from the package root
|
||||
const isTopLevel = !process.env.npm_config_global && process.env.INIT_CWD === process.cwd()
|
||||
|
||||
if (isTopLevel) {
|
||||
console.log('Running postinstall: direct install')
|
||||
// Your postinstall logic here
|
||||
} else {
|
||||
console.log('Skipping postinstall: installed as dependency')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
@@ -18,17 +29,6 @@ const response = await fetch(tarUrl)
|
||||
const buffer = await response.arrayBuffer()
|
||||
await fs.promises.writeFile(outputTarPath, Buffer.from(buffer))
|
||||
|
||||
// Check if this script is being run from the package root
|
||||
const isRootInstall = process.cwd() + '/scripts' === __dirname
|
||||
|
||||
if (isRootInstall) {
|
||||
console.log('Running postinstall: direct install')
|
||||
// Your postinstall logic here
|
||||
} else {
|
||||
console.log('Skipping postinstall: installed as dependency')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Create extract directory if it doesn't exist
|
||||
try {
|
||||
await fs.promises.mkdir(extractTo, { recursive: true })
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script context="module" lang="ts">
|
||||
<script module lang="ts">
|
||||
export const EDITOR_BAR_WIDTH_THRESHOLD = 1044
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import { ResourceService, VariableService, type Script } from '$lib/gen'
|
||||
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
@@ -47,110 +49,145 @@
|
||||
import type { EditorBarUi } from './custom_ui'
|
||||
import EditorSettings from './EditorSettings.svelte'
|
||||
|
||||
export let lang: SupportedLanguage | 'bunnative' | undefined
|
||||
export let editor: Editor | undefined
|
||||
export let websocketAlive: {
|
||||
pyright: boolean
|
||||
ruff: boolean
|
||||
deno: boolean
|
||||
go: boolean
|
||||
shellcheck: boolean
|
||||
interface Props {
|
||||
lang: SupportedLanguage | 'bunnative' | undefined
|
||||
editor: Editor | undefined
|
||||
websocketAlive: {
|
||||
pyright: boolean
|
||||
ruff: boolean
|
||||
deno: boolean
|
||||
go: boolean
|
||||
shellcheck: boolean
|
||||
}
|
||||
iconOnly?: boolean
|
||||
validCode?: boolean
|
||||
kind?: 'script' | 'trigger' | 'approval'
|
||||
template?: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' | 'bunnative'
|
||||
collabMode?: boolean
|
||||
collabLive?: boolean
|
||||
collabUsers?: { name: string }[]
|
||||
scriptPath?: string | undefined
|
||||
diffEditor?: DiffEditor | undefined
|
||||
args: Record<string, any>
|
||||
noHistory?: boolean
|
||||
saveToWorkspace?: boolean
|
||||
customUi?: EditorBarUi
|
||||
lastDeployedCode?: string | undefined
|
||||
diffMode?: boolean
|
||||
showHistoryDrawer?: boolean
|
||||
right?: import('svelte').Snippet
|
||||
}
|
||||
export let iconOnly: boolean = false
|
||||
export let validCode: boolean = true
|
||||
export let kind: 'script' | 'trigger' | 'approval' = 'script'
|
||||
export let template: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' | 'bunnative' =
|
||||
'script'
|
||||
export let collabMode = false
|
||||
export let collabLive = false
|
||||
export let collabUsers: { name: string }[] = []
|
||||
export let scriptPath: string | undefined = undefined
|
||||
export let diffEditor: DiffEditor | undefined = undefined
|
||||
export let args: Record<string, any>
|
||||
export let noHistory = false
|
||||
export let saveToWorkspace = false
|
||||
export let customUi: EditorBarUi = {}
|
||||
export let lastDeployedCode: string | undefined = undefined
|
||||
export let diffMode: boolean = false
|
||||
export let showHistoryDrawer: boolean = false
|
||||
|
||||
let contextualVariablePicker: ItemPicker
|
||||
let variablePicker: ItemPicker
|
||||
let resourcePicker: ItemPicker
|
||||
let resourceTypePicker: ItemPicker
|
||||
let variableEditor: VariableEditor
|
||||
let resourceEditor: ResourceEditorDrawer
|
||||
let showContextVarPicker = false
|
||||
let showVarPicker = false
|
||||
let showResourcePicker = false
|
||||
let showResourceTypePicker = false
|
||||
let {
|
||||
lang,
|
||||
editor,
|
||||
websocketAlive,
|
||||
iconOnly = false,
|
||||
validCode = true,
|
||||
kind = 'script',
|
||||
template = 'script',
|
||||
collabMode = false,
|
||||
collabLive = false,
|
||||
collabUsers = [],
|
||||
scriptPath = undefined,
|
||||
diffEditor = undefined,
|
||||
args,
|
||||
noHistory = false,
|
||||
saveToWorkspace = false,
|
||||
customUi = {},
|
||||
lastDeployedCode = undefined,
|
||||
diffMode = false,
|
||||
showHistoryDrawer = $bindable(false),
|
||||
right
|
||||
}: Props = $props()
|
||||
|
||||
$: showContextVarPicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
$: showVarPicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
$: showResourcePicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
$: showResourceTypePicker =
|
||||
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) ||
|
||||
lang === 'python3' ||
|
||||
lang === 'php'
|
||||
let contextualVariablePicker: ItemPicker | undefined = $state()
|
||||
let variablePicker: ItemPicker | undefined = $state()
|
||||
let resourcePicker: ItemPicker | undefined = $state()
|
||||
let resourceTypePicker: ItemPicker | undefined = $state()
|
||||
let variableEditor: VariableEditor | undefined = $state()
|
||||
let resourceEditor: ResourceEditorDrawer | undefined = $state()
|
||||
let showContextVarPicker = $state(false)
|
||||
let showVarPicker = $state(false)
|
||||
let showResourcePicker = $state(false)
|
||||
let showResourceTypePicker = $state(false)
|
||||
|
||||
let codeViewer: Drawer
|
||||
let codeObj: { language: SupportedLanguage; content: string } | undefined = undefined
|
||||
run(() => {
|
||||
showContextVarPicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
})
|
||||
run(() => {
|
||||
showVarPicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
})
|
||||
run(() => {
|
||||
showResourcePicker = [
|
||||
'python3',
|
||||
'bash',
|
||||
'powershell',
|
||||
'go',
|
||||
'deno',
|
||||
'bun',
|
||||
'bunnative',
|
||||
'nativets',
|
||||
'php',
|
||||
'rust',
|
||||
'csharp',
|
||||
'nu',
|
||||
'java'
|
||||
// for related places search: ADD_NEW_LANG
|
||||
].includes(lang ?? '')
|
||||
})
|
||||
run(() => {
|
||||
showResourceTypePicker =
|
||||
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) ||
|
||||
lang === 'python3' ||
|
||||
lang === 'php'
|
||||
})
|
||||
|
||||
let codeViewer: Drawer | undefined = $state()
|
||||
let codeObj: { language: SupportedLanguage; content: string } | undefined = $state(undefined)
|
||||
|
||||
function addEditorActions() {
|
||||
editor?.addAction('insert-variable', 'Windmill: Insert variable', () => {
|
||||
variablePicker.openDrawer()
|
||||
variablePicker?.openDrawer()
|
||||
})
|
||||
editor?.addAction('insert-resource', 'Windmill: Insert resource', () => {
|
||||
resourcePicker.openDrawer()
|
||||
resourcePicker?.openDrawer()
|
||||
})
|
||||
}
|
||||
|
||||
$: editor && addEditorActions()
|
||||
run(() => {
|
||||
editor && addEditorActions()
|
||||
})
|
||||
|
||||
async function loadVariables() {
|
||||
return await VariableService.listVariable({ workspace: $workspaceStore ?? '' })
|
||||
@@ -162,9 +199,9 @@
|
||||
})
|
||||
}
|
||||
|
||||
let scriptPicker: Drawer
|
||||
let pick_existing: 'hub' | 'workspace' = 'hub'
|
||||
let filter = ''
|
||||
let scriptPicker: Drawer | undefined = $state()
|
||||
let pick_existing: 'hub' | 'workspace' = $state('hub')
|
||||
let filter = $state('')
|
||||
|
||||
async function onScriptPick(e: { detail: { path: string } }) {
|
||||
codeObj = undefined
|
||||
@@ -462,21 +499,23 @@ string ${windmillPathToCamelCaseName(path)} = await client.GetStringAsync(uri);
|
||||
itemName="Variable"
|
||||
extraField="path"
|
||||
loadItems={loadVariables}
|
||||
buttons={{ 'Edit/View': (x) => variableEditor.editVariable(x) }}
|
||||
buttons={{ 'Edit/View': (x) => variableEditor?.editVariable(x) }}
|
||||
>
|
||||
<div slot="submission" class="flex flex-row">
|
||||
<Button
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
startIcon={{ icon: Plus }}
|
||||
on:click={() => {
|
||||
variableEditor.initNew()
|
||||
}}
|
||||
>
|
||||
New variable
|
||||
</Button>
|
||||
</div>
|
||||
{#snippet submission()}
|
||||
<div class="flex flex-row">
|
||||
<Button
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
startIcon={{ icon: Plus }}
|
||||
on:click={() => {
|
||||
variableEditor?.initNew()
|
||||
}}
|
||||
>
|
||||
New variable
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</ItemPicker>
|
||||
|
||||
<ItemPicker
|
||||
@@ -548,24 +587,26 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
tooltip="Resources represent connections to third party systems. Resources are a good way to define a connection to a frequently used third party system such as a database."
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/resources_and_types"
|
||||
itemName="Resource"
|
||||
buttons={{ 'Edit/View': (x) => resourceEditor.initEdit(x) }}
|
||||
buttons={{ 'Edit/View': (x) => resourceEditor?.initEdit(x) }}
|
||||
extraField="description"
|
||||
extraField2="resource_type"
|
||||
loadItems={async () =>
|
||||
await ResourceService.listResource({ workspace: $workspaceStore ?? 'NO_W' })}
|
||||
>
|
||||
<div slot="submission" class="flex flex-row gap-x-1 mr-2">
|
||||
<Button
|
||||
startIcon={{ icon: Plus }}
|
||||
target="_blank"
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
href="{base}/resources?connect_app=undefined"
|
||||
>
|
||||
Add resource
|
||||
</Button>
|
||||
</div>
|
||||
{#snippet submission()}
|
||||
<div class="flex flex-row gap-x-1 mr-2">
|
||||
<Button
|
||||
startIcon={{ icon: Plus }}
|
||||
target="_blank"
|
||||
variant="border"
|
||||
color="blue"
|
||||
size="sm"
|
||||
href="{base}/resources?connect_app=undefined"
|
||||
>
|
||||
Add resource
|
||||
</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
</ItemPicker>
|
||||
|
||||
{#if showResourceTypePicker}
|
||||
@@ -650,7 +691,7 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={resourceTypePicker.openDrawer}
|
||||
on:click={() => resourceTypePicker?.openDrawer}
|
||||
{iconOnly}
|
||||
startIcon={{ icon: Package }}
|
||||
>
|
||||
@@ -724,7 +765,9 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
}}
|
||||
/>
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">Toggle diff mode</svelte:fragment>
|
||||
{#snippet text()}
|
||||
Toggle diff mode
|
||||
{/snippet}
|
||||
<DiffIcon class="ml-1 text-tertiary" size={14} />
|
||||
</Popover>
|
||||
</div>
|
||||
@@ -739,14 +782,16 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
on:change={() => dispatch('toggleCollabMode')}
|
||||
/>
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">Multiplayer</svelte:fragment>
|
||||
{#snippet text()}
|
||||
Multiplayer
|
||||
{/snippet}
|
||||
<Users class="ml-1 text-tertiary" size={14} />
|
||||
</Popover>
|
||||
{#if collabLive}
|
||||
<button
|
||||
title="Show invite link"
|
||||
class="p-1 rounded hover:bg-gray-400 mx-1 border"
|
||||
on:click={() => dispatch('collabPopup')}><Link size={14} /></button
|
||||
onclick={() => dispatch('collabPopup')}><Link size={14} /></button
|
||||
>
|
||||
<div class="isolate flex -space-x-2 pl-2">
|
||||
{#each collabUsers as user}
|
||||
@@ -773,7 +818,7 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<slot name="right" />
|
||||
{@render right?.()}
|
||||
{#if scriptPath && !noHistory}
|
||||
<Button
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
import type { FlowEditorContext, FlowInput, FlowInputEditorState } from './flows/types'
|
||||
import { cleanInputs } from './flows/utils'
|
||||
import { Calendar, Pen, Save, DiffIcon, HistoryIcon, FileJson, type Icon } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Awareness from './Awareness.svelte'
|
||||
import { getAllModules } from './flows/flowExplorer'
|
||||
import { type FlowCopilotContext } from './copilot/flow'
|
||||
@@ -89,6 +88,24 @@
|
||||
export let setSavedraftCb: ((cb: () => void) => void) | undefined = undefined
|
||||
export let draftTriggersFromUrl: Trigger[] | undefined = undefined
|
||||
export let selectedTriggerIndexFromUrl: number | undefined = undefined
|
||||
export let onevent: {
|
||||
deploy?: (path: string) => void
|
||||
deployError?: (error: Error) => void
|
||||
saveInitial?: (path: string) => void
|
||||
saveDraft?: ({
|
||||
path,
|
||||
savedAtNewPath,
|
||||
newFlow
|
||||
}: {
|
||||
path: string
|
||||
savedAtNewPath: boolean
|
||||
newFlow: boolean
|
||||
}) => void
|
||||
saveDraftError?: (error: Error) => void
|
||||
saveDraftOnlyAtNewPath?: ({ path, selectedId }: { path: string; selectedId: string }) => void
|
||||
seeDetails?: (path: string) => void
|
||||
historyRestore?: () => void
|
||||
} = {}
|
||||
|
||||
let initialPathStore = writable(initialPath)
|
||||
$: initialPathStore.set(initialPath)
|
||||
@@ -152,8 +169,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const primaryScheduleStore = writable<ScheduleTrigger | undefined | false>(savedPrimarySchedule) // kept for legacy reasons
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
const simplifiedPoll = writable(false)
|
||||
@@ -275,18 +290,19 @@
|
||||
|
||||
let savedAtNewPath = false
|
||||
if (newFlow) {
|
||||
dispatch('saveInitial', $pathStore)
|
||||
onevent.saveInitial?.($pathStore)
|
||||
} else if (savedFlow?.draft_only && $pathStore !== initialPath) {
|
||||
savedAtNewPath = true
|
||||
initialPath = $pathStore
|
||||
// this is so we can use the flow builder outside of sveltekit
|
||||
dispatch('saveDraftOnlyAtNewPath', { path: $pathStore, selectedId: getSelectedId() })
|
||||
onevent.saveDraftOnlyAtNewPath?.({ path: $pathStore, selectedId: getSelectedId() })
|
||||
}
|
||||
dispatch('saveDraft', { path: $pathStore, savedAtNewPath, newFlow })
|
||||
|
||||
onevent.saveDraft?.({ path: $pathStore, savedAtNewPath, newFlow })
|
||||
sendUserToast('Saved as draft')
|
||||
} catch (error) {
|
||||
sendUserToast(`Error while saving the flow as a draft: ${error.body || error.message}`, true)
|
||||
dispatch('saveDraftError', error)
|
||||
onevent.saveDraftError?.(error)
|
||||
}
|
||||
loadingDraft = false
|
||||
}
|
||||
@@ -450,9 +466,9 @@
|
||||
} as Flow
|
||||
setDraftTriggers([])
|
||||
loadingSave = false
|
||||
dispatch('deploy', $pathStore)
|
||||
onevent.deploy?.($pathStore)
|
||||
} catch (err) {
|
||||
dispatch('deployError', err)
|
||||
onevent.deployError?.(err)
|
||||
sendUserToast(`The flow could not be saved: ${err.body}`, true)
|
||||
loadingSave = false
|
||||
}
|
||||
@@ -651,7 +667,7 @@
|
||||
if (savedFlow?.draft_only === false || savedFlow?.draft_only === undefined) {
|
||||
dropdownItems.push({
|
||||
label: 'Exit & see details',
|
||||
onClick: () => dispatch('details', $pathStore)
|
||||
onClick: () => onevent.seeDetails?.($pathStore)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -777,7 +793,11 @@
|
||||
{#key renderCount}
|
||||
{#if !$userStore?.operator}
|
||||
{#if $pathStore}
|
||||
<FlowHistory bind:this={flowHistory} path={$pathStore} on:historyRestore />
|
||||
<FlowHistory
|
||||
bind:this={flowHistory}
|
||||
path={$pathStore}
|
||||
onHistoryRestore={() => onevent.historyRestore?.()}
|
||||
/>
|
||||
{/if}
|
||||
<FlowYamlEditor bind:drawer={yamlEditorDrawer} />
|
||||
<FlowImportExportMenu bind:drawer={jsonViewerDrawer} />
|
||||
|
||||
@@ -7,23 +7,41 @@
|
||||
import SearchItems from './SearchItems.svelte'
|
||||
|
||||
type Item = Record<string, any>
|
||||
export let pickCallback: (path: string, f: string) => void
|
||||
export let loadItems: () => Promise<Item[] | undefined>
|
||||
export let extraField: string = 'path'
|
||||
export let extraField2: string | undefined = undefined
|
||||
export let itemName: string
|
||||
export let closeOnClick = true
|
||||
/** Displayed if the load function returns no items. */
|
||||
export let noItemMessage = 'There are no items in the list'
|
||||
/** Displayed if the search returns no items. */
|
||||
export let buttons: Record<string, (x: string) => void> = {}
|
||||
export let tooltip: string = ''
|
||||
export let documentationLink: string | undefined = undefined
|
||||
|
||||
let loading = false
|
||||
let items: Item[] | undefined = []
|
||||
let filteredItems: Item[] | undefined = []
|
||||
let filter = ''
|
||||
interface Props {
|
||||
pickCallback: (path: string, f: string) => void
|
||||
loadItems: () => Promise<Item[] | undefined>
|
||||
extraField?: string
|
||||
extraField2?: string | undefined
|
||||
itemName: string
|
||||
closeOnClick?: boolean
|
||||
/** Displayed if the load function returns no items. */
|
||||
noItemMessage?: string
|
||||
/** Displayed if the search returns no items. */
|
||||
buttons?: Record<string, (x: string) => void>
|
||||
tooltip?: string
|
||||
documentationLink?: string | undefined
|
||||
submission?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
pickCallback,
|
||||
loadItems,
|
||||
extraField = 'path',
|
||||
extraField2 = undefined,
|
||||
itemName,
|
||||
closeOnClick = true,
|
||||
noItemMessage = 'There are no items in the list',
|
||||
buttons = {},
|
||||
tooltip = '',
|
||||
documentationLink = undefined,
|
||||
submission
|
||||
}: Props = $props()
|
||||
|
||||
let loading = $state(false)
|
||||
let items: Item[] | undefined = $state([])
|
||||
let filteredItems: Item[] | undefined = $state([])
|
||||
let filter = $state('')
|
||||
|
||||
export function openDrawer() {
|
||||
loading = true
|
||||
@@ -34,12 +52,12 @@
|
||||
.finally(() => {
|
||||
loading = false
|
||||
})
|
||||
drawer.openDrawer?.()
|
||||
drawer?.openDrawer?.()
|
||||
}
|
||||
|
||||
let drawer: Drawer
|
||||
let drawer: Drawer | undefined = $state()
|
||||
|
||||
let refreshing = false
|
||||
let refreshing = $state(false)
|
||||
</script>
|
||||
|
||||
<SearchItems
|
||||
@@ -65,7 +83,7 @@
|
||||
>
|
||||
<div class="w-full h-full flex flex-col">
|
||||
<div class="flex flex-row gap-2 pb-4">
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search {itemName}s"
|
||||
@@ -110,9 +128,9 @@
|
||||
<button
|
||||
class="py-2 px-1 gap-1 flex grow border-gray-300 border-opacity-0
|
||||
text-primary"
|
||||
on:click={() => {
|
||||
onclick={() => {
|
||||
if (closeOnClick) {
|
||||
drawer.closeDrawer()
|
||||
drawer?.closeDrawer()
|
||||
}
|
||||
pickCallback(obj['path'], obj[extraField])
|
||||
}}
|
||||
@@ -174,8 +192,8 @@
|
||||
<NoItemFound />
|
||||
{/if}
|
||||
</div>
|
||||
<svelte:fragment slot="actions">
|
||||
<slot name="submission" />
|
||||
</svelte:fragment>
|
||||
{#snippet actions()}
|
||||
{@render submission?.()}
|
||||
{/snippet}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
import { writable } from 'svelte/store'
|
||||
import { defaultScriptLanguages, processLangs } from '$lib/scripts'
|
||||
import DefaultScripts from './DefaultScripts.svelte'
|
||||
import { createEventDispatcher, onMount, setContext } from 'svelte'
|
||||
import { onMount, setContext } from 'svelte'
|
||||
import Summary from './Summary.svelte'
|
||||
import type { ScriptBuilderWhitelabelCustomUi } from './custom_ui'
|
||||
import DeployOverrideConfirmationModal from '$lib/components/common/confirmationModal/DeployOverrideConfirmationModal.svelte'
|
||||
@@ -112,6 +112,22 @@
|
||||
export let savedPrimarySchedule: ScheduleTrigger | undefined = undefined
|
||||
export let functionExports: ((exports: ScriptBuilderFunctionExports) => void) | undefined =
|
||||
undefined
|
||||
export let onevent: {
|
||||
deploy?: (newHash: string) => void
|
||||
deployError?: (error: Error) => void
|
||||
saveInitial?: (path: string) => void
|
||||
saveDraft?: ({
|
||||
path,
|
||||
savedAtNewPath,
|
||||
script
|
||||
}: {
|
||||
path: string
|
||||
savedAtNewPath: boolean
|
||||
script: NewScript
|
||||
}) => void
|
||||
saveDraftError?: (error: Error) => void
|
||||
seeDetails?: (path: string) => void
|
||||
} = {}
|
||||
|
||||
export function getInitialAndModifiedValues(): SavedAndModifiedValue {
|
||||
return {
|
||||
@@ -183,8 +199,6 @@
|
||||
loadTriggers()
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: initialPath != '' && loadTriggers()
|
||||
|
||||
onMount(() => {
|
||||
@@ -566,10 +580,10 @@
|
||||
script.parent_hash = newHash
|
||||
sendUserToast('Deployed')
|
||||
} else {
|
||||
dispatch('deploy', newHash)
|
||||
onevent.deploy?.(newHash)
|
||||
}
|
||||
} catch (error) {
|
||||
dispatch('deployError', error)
|
||||
onevent.deployError?.(error)
|
||||
sendUserToast(`Error while saving the script: ${error.body || error.message}`, true)
|
||||
}
|
||||
loadingSave = false
|
||||
@@ -701,9 +715,9 @@
|
||||
if (initialPath == '' || (savedScript?.draft_only && script.path !== initialPath)) {
|
||||
savedAtNewPath = true
|
||||
initialPath = script.path
|
||||
dispatch('saveInitial', script.path)
|
||||
onevent.saveInitial?.(script.path)
|
||||
}
|
||||
dispatch('saveDraft', { path: script.path, savedAtNewPath, script })
|
||||
onevent.saveDraft?.({ path: script.path, savedAtNewPath, script })
|
||||
|
||||
sendUserToast('Saved as draft')
|
||||
} catch (error) {
|
||||
@@ -711,7 +725,7 @@
|
||||
`Error while saving the script as a draft: ${error.body || error.message}`,
|
||||
true
|
||||
)
|
||||
dispatch('saveDraftError', error)
|
||||
onevent.saveDraftError?.(error)
|
||||
}
|
||||
loadingDraft = false
|
||||
}
|
||||
@@ -769,7 +783,7 @@
|
||||
{
|
||||
label: 'Exit & See details',
|
||||
onClick: () => {
|
||||
dispatch('seeDetails', initialPath)
|
||||
onevent.seeDetails?.(initialPath)
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1686,12 +1700,8 @@
|
||||
{customUi}
|
||||
collabMode
|
||||
edit={initialPath != ''}
|
||||
on:format={() => {
|
||||
saveDraft()
|
||||
}}
|
||||
on:saveDraft={() => {
|
||||
saveDraft()
|
||||
}}
|
||||
onFormat={() => saveDraft()}
|
||||
onSaveDraft={() => saveDraft()}
|
||||
on:openTriggers={openTriggers}
|
||||
on:applyArgs={applyArgs}
|
||||
on:addPreprocessor={addPreprocessor}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import EditorBar, { EDITOR_BAR_WIDTH_THRESHOLD } from './EditorBar.svelte'
|
||||
import TestJobLoader from './TestJobLoader.svelte'
|
||||
import JobProgressBar from '$lib/components/jobs/JobProgressBar.svelte'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import { Button } from './common'
|
||||
import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte'
|
||||
import WindmillIcon from './icons/WindmillIcon.svelte'
|
||||
@@ -42,78 +42,114 @@
|
||||
import HideButton from './apps/editor/settingsPanel/HideButton.svelte'
|
||||
import { base } from '$lib/base'
|
||||
import { SUPPORTED_CHAT_SCRIPT_LANGUAGES } from './copilot/chat/script/core'
|
||||
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
|
||||
import { getStringError } from './copilot/chat/utils'
|
||||
import type { ScriptOptions } from './copilot/chat/ContextManager.svelte'
|
||||
import { aiChatManager, AIMode } from './copilot/chat/AIChatManager.svelte'
|
||||
import TriggerableByAI from './TriggerableByAI.svelte'
|
||||
|
||||
// Exported
|
||||
export let schema: Schema | any = emptySchema()
|
||||
export let code: string
|
||||
export let path: string | undefined
|
||||
export let lang: Preview['language']
|
||||
export let kind: string | undefined = undefined
|
||||
export let template: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' | 'bunnative' =
|
||||
'script'
|
||||
export let tag: string | undefined
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let fixedOverflowWidgets = true
|
||||
export let noSyncFromGithub = false
|
||||
export let editor: Editor | undefined = undefined
|
||||
export let diffEditor: DiffEditor | undefined = undefined
|
||||
export let collabMode = false
|
||||
export let edit = true
|
||||
export let noHistory = false
|
||||
export let saveToWorkspace = false
|
||||
export let watchChanges = false
|
||||
export let customUi: ScriptEditorWhitelabelCustomUi | undefined = undefined
|
||||
export let args: Record<string, any> = initialArgs
|
||||
export let selectedTab: 'main' | 'preprocessor' = 'main'
|
||||
export let hasPreprocessor = false
|
||||
export let captureTable: CaptureTable | undefined = undefined
|
||||
export let showCaptures: boolean = true
|
||||
export let stablePathForCaptures: string = ''
|
||||
export let lastSavedCode: string | undefined = undefined
|
||||
export let lastDeployedCode: string | undefined = undefined
|
||||
interface Props {
|
||||
// Exported
|
||||
schema?: Schema | any
|
||||
code: string
|
||||
path: string | undefined
|
||||
lang: Preview['language']
|
||||
kind?: string | undefined
|
||||
template?: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' | 'bunnative'
|
||||
tag: string | undefined
|
||||
initialArgs?: Record<string, any>
|
||||
fixedOverflowWidgets?: boolean
|
||||
noSyncFromGithub?: boolean
|
||||
editor?: Editor | undefined
|
||||
diffEditor?: DiffEditor | undefined
|
||||
collabMode?: boolean
|
||||
edit?: boolean
|
||||
noHistory?: boolean
|
||||
saveToWorkspace?: boolean
|
||||
watchChanges?: boolean
|
||||
customUi?: ScriptEditorWhitelabelCustomUi | undefined
|
||||
args?: Record<string, any>
|
||||
selectedTab?: 'main' | 'preprocessor'
|
||||
hasPreprocessor?: boolean
|
||||
captureTable?: CaptureTable | undefined
|
||||
showCaptures?: boolean
|
||||
stablePathForCaptures?: string
|
||||
lastSavedCode?: string | undefined
|
||||
lastDeployedCode?: string | undefined
|
||||
editor_bar_right?: import('svelte').Snippet
|
||||
onChange?: (code: string, schema: Schema) => void
|
||||
onFormat?: () => void
|
||||
onSaveDraft?: () => void
|
||||
}
|
||||
|
||||
let showHistoryDrawer = false
|
||||
let {
|
||||
schema = $bindable(emptySchema()),
|
||||
code = $bindable(),
|
||||
path,
|
||||
lang,
|
||||
kind = undefined,
|
||||
template = 'script',
|
||||
tag,
|
||||
initialArgs = {},
|
||||
fixedOverflowWidgets = true,
|
||||
noSyncFromGithub = false,
|
||||
editor = $bindable(undefined),
|
||||
diffEditor = $bindable(undefined),
|
||||
collabMode = false,
|
||||
edit = true,
|
||||
noHistory = false,
|
||||
saveToWorkspace = false,
|
||||
watchChanges = false,
|
||||
customUi = undefined,
|
||||
args = $bindable(initialArgs),
|
||||
selectedTab = $bindable('main'),
|
||||
hasPreprocessor = $bindable(false),
|
||||
captureTable = $bindable(undefined),
|
||||
showCaptures = true,
|
||||
stablePathForCaptures = '',
|
||||
lastSavedCode = undefined,
|
||||
lastDeployedCode = undefined,
|
||||
editor_bar_right,
|
||||
onChange,
|
||||
onFormat,
|
||||
onSaveDraft
|
||||
}: Props = $props()
|
||||
// export let onC
|
||||
|
||||
let jobProgressReset: () => void
|
||||
let diffMode = false
|
||||
let showHistoryDrawer = $state(false)
|
||||
|
||||
let websocketAlive = {
|
||||
let jobProgressReset: (() => void) | undefined = $state()
|
||||
let diffMode = $state(false)
|
||||
|
||||
let websocketAlive = $state({
|
||||
pyright: false,
|
||||
deno: false,
|
||||
go: false,
|
||||
ruff: false,
|
||||
shellcheck: false
|
||||
}
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
|
||||
$effect(() => {
|
||||
watchChanges && (code != undefined || schema != undefined) && onChange?.(code, schema)
|
||||
})
|
||||
|
||||
$: watchChanges &&
|
||||
(code != undefined || schema != undefined) &&
|
||||
dispatchIfMounted('change', { code, schema })
|
||||
let width = $state(1200)
|
||||
|
||||
let width = 1200
|
||||
let testJobLoader: TestJobLoader | undefined = $state()
|
||||
|
||||
let testJobLoader: TestJobLoader
|
||||
|
||||
let isValid: boolean = true
|
||||
let scriptProgress = undefined
|
||||
let isValid: boolean = $state(true)
|
||||
let scriptProgress = $state(undefined)
|
||||
|
||||
// Test
|
||||
let testIsLoading = false
|
||||
let testJob: Job | undefined
|
||||
let pastPreviews: CompletedJob[] = []
|
||||
let validCode = true
|
||||
let testIsLoading = $state(false)
|
||||
let testJob: Job | undefined = $state()
|
||||
let pastPreviews: CompletedJob[] = $state([])
|
||||
let validCode = $state(true)
|
||||
let logPanel: LogPanel | undefined = $state()
|
||||
|
||||
let wsProvider: WebsocketProvider | undefined = undefined
|
||||
let yContent: Y.Text | undefined = undefined
|
||||
let peers: { name: string }[] = []
|
||||
let showCollabPopup = false
|
||||
let wsProvider: WebsocketProvider | undefined = $state(undefined)
|
||||
let yContent: Y.Text | undefined = $state(undefined)
|
||||
let peers: { name: string }[] = $state([])
|
||||
let showCollabPopup = $state(false)
|
||||
|
||||
const url = new URL(window.location.toString())
|
||||
let initialCollab = /true|1/i.test(url.searchParams.get('collab') ?? '0')
|
||||
@@ -292,11 +328,15 @@
|
||||
return `${url}?collab=1` + (edit ? '' : `&path=${path}`)
|
||||
}
|
||||
|
||||
$: showTabs = hasPreprocessor
|
||||
$: !hasPreprocessor && (selectedTab = 'main')
|
||||
$: selectedTab && inferSchema(code)
|
||||
let showTabs = $derived(hasPreprocessor)
|
||||
$effect(() => {
|
||||
!hasPreprocessor && (selectedTab = 'main')
|
||||
})
|
||||
$effect(() => {
|
||||
selectedTab && inferSchema(code)
|
||||
})
|
||||
|
||||
let argsRender = 0
|
||||
let argsRender = $state(0)
|
||||
export async function updateArgs(newArgs: Record<string, any>) {
|
||||
if (Object.keys(newArgs).length > 0) {
|
||||
args = { ...newArgs }
|
||||
@@ -304,17 +344,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
let setFocusToLogs = () => {}
|
||||
function setFocusToLogs() {
|
||||
logPanel?.setFocusToLogs()
|
||||
}
|
||||
|
||||
setContext('disableTooltips', customUi?.disableTooltips === true)
|
||||
|
||||
let codePanelSize = 70
|
||||
let testPanelSize = 30
|
||||
let codePanelSize = $state(70)
|
||||
let testPanelSize = $state(30)
|
||||
let storedTestPanelSize = testPanelSize
|
||||
|
||||
$: !SUPPORTED_CHAT_SCRIPT_LANGUAGES.includes(lang ?? '') &&
|
||||
!aiChatManager.open &&
|
||||
aiChatManager.toggleOpen()
|
||||
$effect(() => {
|
||||
!SUPPORTED_CHAT_SCRIPT_LANGUAGES.includes(lang ?? '') &&
|
||||
!aiChatManager.open &&
|
||||
aiChatManager.toggleOpen()
|
||||
})
|
||||
|
||||
function toggleTestPanel() {
|
||||
if (testPanelSize > 0) {
|
||||
@@ -348,9 +392,9 @@
|
||||
editor?.show()
|
||||
}
|
||||
|
||||
$: error = getError(testJob)
|
||||
let error = $derived(getError(testJob))
|
||||
|
||||
$: {
|
||||
$effect(() => {
|
||||
const options: ScriptOptions = {
|
||||
code,
|
||||
lang: lang as ScriptLang,
|
||||
@@ -367,7 +411,7 @@
|
||||
editor?.reviewAndApplyCode(code)
|
||||
}
|
||||
aiChatManager.scriptEditorShowDiffMode = showDiffMode
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<TestJobLoader
|
||||
@@ -378,7 +422,7 @@
|
||||
bind:job={testJob}
|
||||
/>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
<svelte:window onkeydown={onKeyDown} />
|
||||
|
||||
<TriggerableByAI id="script-editor" description="Component to edit a script" />
|
||||
|
||||
@@ -431,7 +475,9 @@
|
||||
{diffMode}
|
||||
bind:showHistoryDrawer
|
||||
>
|
||||
<slot name="editor-bar-right" slot="right" />
|
||||
{#snippet right()}
|
||||
{@render editor_bar_right?.()}
|
||||
{/snippet}
|
||||
</EditorBar>
|
||||
{#if !noSyncFromGithub && customUi?.editorBar?.useVsCode != false}
|
||||
<div class="py-1">
|
||||
@@ -490,7 +536,7 @@
|
||||
aiChatManager.toggleOpen()
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="popoverOverride">
|
||||
{#snippet popoverOverride()}
|
||||
<div class="text-sm">
|
||||
Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
@@ -500,7 +546,7 @@
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</HideButton>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -518,7 +564,7 @@
|
||||
on:change={(e) => {
|
||||
inferSchema(e.detail)
|
||||
}}
|
||||
on:saveDraft
|
||||
on:saveDraft={() => onSaveDraft?.()}
|
||||
on:toggleAiPanel={() => aiChatManager.toggleOpen()}
|
||||
on:addSelectedLinesToAiChat={(e) => {
|
||||
const { lines, startLine, endLine } = e.detail
|
||||
@@ -536,7 +582,7 @@
|
||||
} catch (e) {
|
||||
console.error('Could not save last_save to local storage', e)
|
||||
}
|
||||
dispatch('format')
|
||||
onFormat?.()
|
||||
}}
|
||||
class="flex flex-1 h-full !overflow-visible"
|
||||
scriptLang={lang}
|
||||
@@ -670,7 +716,7 @@
|
||||
</Pane>
|
||||
<Pane size={67} class="relative">
|
||||
<LogPanel
|
||||
bind:setFocusToLogs
|
||||
bind:this={logPanel}
|
||||
{lang}
|
||||
previewJob={testJob}
|
||||
{pastPreviews}
|
||||
@@ -690,7 +736,7 @@
|
||||
compact={true}
|
||||
/>
|
||||
{/if}
|
||||
<svelte:fragment slot="capturesTab">
|
||||
{#snippet capturesTab()}
|
||||
<div class="h-full p-2">
|
||||
<CaptureTable
|
||||
bind:this={captureTable}
|
||||
@@ -704,7 +750,7 @@
|
||||
on:addPreprocessor
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
</LogPanel>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
|
||||
@@ -79,6 +79,10 @@
|
||||
path: string,
|
||||
opt?: Record<string, any>
|
||||
) => window.history.pushState(null, '', path)
|
||||
export let onevent: {
|
||||
savedNewAppPath?: (path: string) => void
|
||||
restore?: (app: App) => void
|
||||
} = {}
|
||||
|
||||
migrateApp(app)
|
||||
|
||||
@@ -805,7 +809,7 @@
|
||||
<AppEditorHeader
|
||||
{newPath}
|
||||
{newApp}
|
||||
on:restore
|
||||
on:restore={(e) => onevent.restore?.(e.detail)}
|
||||
{policy}
|
||||
{fromHub}
|
||||
bind:this={appEditorHeader}
|
||||
@@ -815,7 +819,7 @@
|
||||
leftPanelHidden={leftPanelSize === 0}
|
||||
rightPanelHidden={rightPanelSize === 0}
|
||||
bottomPanelHidden={runnablePanelSize === 0}
|
||||
on:savedNewAppPath
|
||||
on:savedNewAppPath={(e) => onevent.savedNewAppPath?.(e.detail)}
|
||||
on:showLeftPanel={() => showLeftPanel()}
|
||||
on:showRightPanel={() => showRightPanel()}
|
||||
on:hideLeftPanel={() => hideLeftPanel()}
|
||||
|
||||
@@ -13,17 +13,33 @@
|
||||
} from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let btnClasses: string | undefined = undefined
|
||||
export let size: ButtonType.Size = 'xs'
|
||||
interface Props {
|
||||
btnClasses?: string | undefined
|
||||
size?: ButtonType.Size
|
||||
variant?: ButtonType.Variant
|
||||
color?: ButtonType.Color
|
||||
direction?: 'left' | 'right' | 'bottom'
|
||||
hidden?: boolean
|
||||
shortcut?: string | undefined
|
||||
panelName?: string | undefined
|
||||
customHiddenIcon?: ButtonType.Icon | undefined
|
||||
usePopoverOverride?: boolean
|
||||
popoverOverride?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
export let variant: ButtonType.Variant = 'contained'
|
||||
export let color: ButtonType.Color = 'light'
|
||||
export let direction: 'left' | 'right' | 'bottom' = 'right'
|
||||
export let hidden: boolean = false
|
||||
export let shortcut: string | undefined = undefined
|
||||
export let panelName: string | undefined = undefined
|
||||
export let customHiddenIcon: ButtonType.Icon | undefined = undefined
|
||||
export let usePopoverOverride: boolean = false
|
||||
let {
|
||||
btnClasses = undefined,
|
||||
size = 'xs',
|
||||
variant = 'contained',
|
||||
color = 'light',
|
||||
direction = 'right',
|
||||
hidden = false,
|
||||
shortcut = undefined,
|
||||
panelName = undefined,
|
||||
customHiddenIcon = undefined,
|
||||
usePopoverOverride = false,
|
||||
popoverOverride
|
||||
}: Props = $props()
|
||||
|
||||
const OpenIconMap = {
|
||||
left: PanelLeftOpen,
|
||||
@@ -45,9 +61,9 @@
|
||||
</script>
|
||||
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">
|
||||
{#if usePopoverOverride && $$slots.popoverOverride}
|
||||
<slot name="popoverOverride" />
|
||||
{#snippet text()}
|
||||
{#if usePopoverOverride && popoverOverride}
|
||||
{@render popoverOverride?.()}
|
||||
{:else}
|
||||
<div class="flex flex-row gap-1">
|
||||
{hidden ? 'Show' : 'Hide '} the {panelName ?? direction} panel.
|
||||
@@ -57,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{/snippet}
|
||||
<Button
|
||||
iconOnly
|
||||
startIcon={hidden
|
||||
|
||||
@@ -5,17 +5,35 @@
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let title: string | undefined = undefined
|
||||
export let overflow_y = true
|
||||
export let noPadding = false
|
||||
export let forceOverflowVisible = false
|
||||
export let tooltip: string = ''
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let CloseIcon: any | undefined = undefined
|
||||
interface Props {
|
||||
aiId?: string | undefined
|
||||
aiDescription?: string | undefined
|
||||
title?: string | undefined
|
||||
overflow_y?: boolean
|
||||
noPadding?: boolean
|
||||
forceOverflowVisible?: boolean
|
||||
tooltip?: string
|
||||
documentationLink?: string | undefined
|
||||
CloseIcon?: any | undefined
|
||||
fullScreen?: boolean
|
||||
actions?: import('svelte').Snippet
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
export let fullScreen: boolean = true
|
||||
let {
|
||||
aiId = undefined,
|
||||
aiDescription = undefined,
|
||||
title = undefined,
|
||||
overflow_y = true,
|
||||
noPadding = false,
|
||||
forceOverflowVisible = false,
|
||||
tooltip = '',
|
||||
documentationLink = undefined,
|
||||
CloseIcon = undefined,
|
||||
fullScreen = true,
|
||||
actions,
|
||||
children
|
||||
}: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
@@ -39,9 +57,9 @@
|
||||
{/if}</span
|
||||
>
|
||||
</div>
|
||||
{#if $$slots.actions}
|
||||
{#if actions}
|
||||
<div class="flex gap-2 items-center justify-end">
|
||||
<slot name="actions" />
|
||||
{@render actions?.()}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -54,6 +72,6 @@
|
||||
)}
|
||||
class:overflow-y-auto={overflow_y}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
76
frontend/src/lib/components/common/tabs/TabsV2.svelte
Normal file
76
frontend/src/lib/components/common/tabs/TabsV2.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { TabsContext } from './tabs.svelte'
|
||||
|
||||
interface Props {
|
||||
selected: string
|
||||
hideTabs?: boolean
|
||||
class?: string
|
||||
wrapperClass?: string
|
||||
style?: string
|
||||
hashNavigation?: boolean
|
||||
values?: string[] | undefined
|
||||
children?: import('svelte').Snippet<[any]>
|
||||
content?: import('svelte').Snippet
|
||||
onSelectedChange?: (value: string) => void
|
||||
}
|
||||
|
||||
let {
|
||||
selected = $bindable(),
|
||||
hideTabs = false,
|
||||
class: c = '',
|
||||
wrapperClass = '',
|
||||
style = '',
|
||||
hashNavigation = false,
|
||||
values = undefined,
|
||||
children,
|
||||
content,
|
||||
onSelectedChange
|
||||
}: Props = $props()
|
||||
|
||||
const selectedStore = writable(selected)
|
||||
|
||||
setContext<TabsContext>('Tabs', {
|
||||
selected: selectedStore,
|
||||
update: (value: string) => {
|
||||
selectedStore.set(value)
|
||||
selected = value
|
||||
},
|
||||
hashNavigation
|
||||
})
|
||||
|
||||
function updateSelected() {
|
||||
selectedStore.set(selected)
|
||||
}
|
||||
|
||||
function hashChange() {
|
||||
if (hashNavigation) {
|
||||
const hash = window.location.hash
|
||||
if (hash && hashValues?.includes(hash)) {
|
||||
const id = hash.replace('#', '')
|
||||
selectedStore.set(id)
|
||||
selected = id
|
||||
}
|
||||
}
|
||||
}
|
||||
$effect(() => {
|
||||
selected && updateSelected()
|
||||
})
|
||||
$effect(() => {
|
||||
$selectedStore && onSelectedChange?.($selectedStore)
|
||||
})
|
||||
|
||||
let hashValues = $derived(values ? values.map((x) => '#' + x) : undefined)
|
||||
</script>
|
||||
|
||||
<svelte:window onhashchange={hashChange} />
|
||||
{#if !hideTabs}
|
||||
<div class="overflow-x-auto {wrapperClass}">
|
||||
<div class={twMerge('border-b flex flex-row whitespace-nowrap scrollbar-hidden', c)} {style}>
|
||||
{@render children?.({ selected })}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{@render content?.()}
|
||||
7
frontend/src/lib/components/common/tabs/tabs.svelte.ts
Normal file
7
frontend/src/lib/components/common/tabs/tabs.svelte.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Writable } from 'svelte/store'
|
||||
|
||||
export type TabsContext = {
|
||||
selected: Writable<string>
|
||||
update: (value: string) => void
|
||||
hashNavigation: boolean
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Drawer from '../common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '../common/drawer/DrawerContent.svelte'
|
||||
|
||||
import FlowHistoryInner from './FlowHistoryInner.svelte'
|
||||
|
||||
export let path: string
|
||||
let drawer: Drawer
|
||||
|
||||
export function open() {
|
||||
drawer.openDrawer()
|
||||
interface Props {
|
||||
path: string
|
||||
onHistoryRestore: () => void
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let { path, onHistoryRestore }: Props = $props()
|
||||
let drawer: Drawer | undefined = undefined
|
||||
|
||||
export function open() {
|
||||
drawer?.openDrawer()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={drawer} size="1200px">
|
||||
@@ -26,8 +28,8 @@
|
||||
<FlowHistoryInner
|
||||
allowFork
|
||||
on:historyRestore={() => {
|
||||
drawer.closeDrawer()
|
||||
dispatch('historyRestore')
|
||||
drawer?.closeDrawer()
|
||||
onHistoryRestore()
|
||||
}}
|
||||
{path}
|
||||
/>
|
||||
|
||||
@@ -198,9 +198,7 @@
|
||||
{#key script.hash}
|
||||
<ScriptEditor
|
||||
showCaptures={false}
|
||||
on:saveDraft={() => {
|
||||
saveScript()
|
||||
}}
|
||||
onSaveDraft={() => saveScript()}
|
||||
noSyncFromGithub
|
||||
lang={script.language}
|
||||
path={script.path}
|
||||
@@ -209,7 +207,7 @@
|
||||
bind:code={script.content}
|
||||
bind:schema={script.schema}
|
||||
>
|
||||
<div slot="editor-bar-right">
|
||||
<div slot="editor_bar_right">
|
||||
<WorkerTagSelect bind:tag={script.tag} />
|
||||
</div>
|
||||
</ScriptEditor>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy'
|
||||
|
||||
import {
|
||||
type CompletedJob,
|
||||
type Job,
|
||||
@@ -10,11 +12,9 @@
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { base } from '$lib/base'
|
||||
import { displayDate } from '$lib/utils'
|
||||
import Tabs from '../common/tabs/Tabs.svelte'
|
||||
import Tab from '../common/tabs/Tab.svelte'
|
||||
import DisplayResult from '../DisplayResult.svelte'
|
||||
import Drawer from '../common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '../common/drawer/DrawerContent.svelte'
|
||||
import HighlightCode from '../HighlightCode.svelte'
|
||||
import LogViewer from '../LogViewer.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
@@ -30,33 +30,54 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import type { PreviewPanelUi } from '../custom_ui'
|
||||
import { getStringError } from '../copilot/chat/utils'
|
||||
import { DrawerContent } from '../common'
|
||||
import TabsV2 from '../common/tabs/TabsV2.svelte'
|
||||
|
||||
export let lang: Preview['language'] | undefined
|
||||
export let previewIsLoading = false
|
||||
export let previewJob: Job | undefined
|
||||
export let pastPreviews: CompletedJob[] = []
|
||||
export let editor: Editor | undefined = undefined
|
||||
export let diffEditor: DiffEditor | undefined = undefined
|
||||
export let args: Record<string, any> | undefined = undefined
|
||||
export let workspace: string | undefined = undefined
|
||||
export let showCaptures: boolean = false
|
||||
export let customUi: PreviewPanelUi | undefined = undefined
|
||||
interface Props {
|
||||
lang: Preview['language'] | undefined
|
||||
previewIsLoading?: boolean
|
||||
previewJob: Job | undefined
|
||||
pastPreviews?: CompletedJob[]
|
||||
editor?: Editor | undefined
|
||||
diffEditor?: DiffEditor | undefined
|
||||
args?: Record<string, any> | undefined
|
||||
workspace?: string | undefined
|
||||
showCaptures?: boolean
|
||||
customUi?: PreviewPanelUi | undefined
|
||||
children?: import('svelte').Snippet
|
||||
capturesTab?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
type DrawerContent = {
|
||||
let {
|
||||
lang,
|
||||
previewIsLoading = false,
|
||||
previewJob,
|
||||
pastPreviews = [],
|
||||
editor = undefined,
|
||||
diffEditor = undefined,
|
||||
args = undefined,
|
||||
workspace = undefined,
|
||||
showCaptures = false,
|
||||
customUi = undefined,
|
||||
children,
|
||||
capturesTab
|
||||
}: Props = $props()
|
||||
|
||||
type DContent = {
|
||||
mode: 'json' | Preview['language'] | 'plain'
|
||||
title: string
|
||||
content: any
|
||||
}
|
||||
|
||||
let selectedTab = 'logs'
|
||||
let drawerOpen: boolean = false
|
||||
let drawerContent: DrawerContent | undefined = undefined
|
||||
let selectedTab = $state('logs')
|
||||
let drawerOpen: boolean = $state(false)
|
||||
let drawerContent: DContent | undefined = $state(undefined)
|
||||
|
||||
export function setFocusToLogs() {
|
||||
selectedTab = 'logs'
|
||||
}
|
||||
|
||||
function openDrawer(newContent: DrawerContent) {
|
||||
function openDrawer(newContent: DContent) {
|
||||
drawerContent = newContent
|
||||
drawerOpen = true
|
||||
}
|
||||
@@ -69,7 +90,7 @@
|
||||
return x as Record<string, WorkflowStatus>
|
||||
}
|
||||
|
||||
let forceJson = false
|
||||
let forceJson = $state(false)
|
||||
</script>
|
||||
|
||||
<Drawer bind:open={drawerOpen} size="800px">
|
||||
@@ -94,7 +115,7 @@
|
||||
</Drawer>
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<Tabs bind:selected={selectedTab} class="pt-1" wrapperClass="flex-none">
|
||||
<TabsV2 bind:selected={selectedTab} class="pt-1" wrapperClass="flex-none">
|
||||
<Tab value="logs" size="xs">Logs & Result</Tab>
|
||||
{#if customUi?.disableHistory !== true}
|
||||
<Tab value="history" size="xs">History</Tab>
|
||||
@@ -103,7 +124,7 @@
|
||||
<Tab value="captures" size="xs">Trigger captures</Tab>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
{#snippet content()}
|
||||
<div class="grow min-h-0">
|
||||
{#if selectedTab === 'logs'}
|
||||
<SplitPanesWrapper>
|
||||
@@ -128,7 +149,7 @@
|
||||
/>
|
||||
</Pane>
|
||||
<Pane>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
{#if previewJob != undefined && 'result' in previewJob}
|
||||
<div class="relative w-full h-full p-2">
|
||||
<div class="relative">
|
||||
@@ -140,6 +161,7 @@
|
||||
customUi={customUi?.displayResult}
|
||||
language={lang}
|
||||
>
|
||||
<!-- @migration-task: migrate this slot by hand, `copilot-fix` is an invalid identifier -->
|
||||
<svelte:fragment slot="copilot-fix">
|
||||
{#if lang && editor && diffEditor && args && previewJob && !previewJob.success && getStringError(previewJob.result)}
|
||||
<ScriptFix {lang} />
|
||||
@@ -203,7 +225,7 @@
|
||||
<Cell>
|
||||
<button
|
||||
class="text-xs"
|
||||
on:click|preventDefault={() => {
|
||||
onclick={preventDefault(() => {
|
||||
openDrawer({ mode: 'json', content: undefined, title: 'Result' })
|
||||
JobService.getCompletedJobResult({
|
||||
workspace: workspace ?? $workspaceStore ?? 'NO_W',
|
||||
@@ -211,7 +233,7 @@
|
||||
}).then((res) => {
|
||||
drawerContent && (drawerContent.content = res)
|
||||
})
|
||||
}}
|
||||
})}
|
||||
>
|
||||
See Result
|
||||
</button>
|
||||
@@ -219,7 +241,7 @@
|
||||
<Cell>
|
||||
<button
|
||||
class="text-xs"
|
||||
on:click|preventDefault={async () => {
|
||||
onclick={preventDefault(async () => {
|
||||
const code = (
|
||||
await JobService.getCompletedJob({
|
||||
workspace: workspace ?? $workspaceStore ?? 'NO_W',
|
||||
@@ -231,7 +253,7 @@
|
||||
content: String(code),
|
||||
title: `Code ${lang}`
|
||||
})
|
||||
}}
|
||||
})}
|
||||
>
|
||||
View code
|
||||
</button>
|
||||
@@ -239,7 +261,7 @@
|
||||
<Cell last>
|
||||
<button
|
||||
class="text-xs"
|
||||
on:click|preventDefault={async () => {
|
||||
onclick={preventDefault(async () => {
|
||||
const logs = await (
|
||||
await fetch(
|
||||
OpenAPI.BASE +
|
||||
@@ -252,7 +274,7 @@
|
||||
content: String(logs),
|
||||
title: `Logs for ${id}`
|
||||
})
|
||||
}}
|
||||
})}
|
||||
>
|
||||
View logs
|
||||
</button>
|
||||
@@ -264,9 +286,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if selectedTab === 'captures'}
|
||||
<slot name="capturesTab" />
|
||||
{@render capturesTab?.()}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
{/snippet}
|
||||
</TabsV2>
|
||||
</div>
|
||||
|
||||
@@ -165,14 +165,16 @@
|
||||
<!-- <div id="monaco-widgets-root" class="monaco-editor" style="z-index: 1200;" /> -->
|
||||
|
||||
<FlowBuilder
|
||||
on:saveInitial={(e) => {
|
||||
goto(`/flows/edit/${e.detail}?selected=${getSelectedId?.()}`)
|
||||
}}
|
||||
on:deploy={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:details={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
onevent={{
|
||||
saveInitial: (path) => {
|
||||
goto(`/flows/edit/${path}?selected=${getSelectedId?.()}`)
|
||||
},
|
||||
deploy: (path) => {
|
||||
goto(`/flows/get/${path}?workspace=${$workspaceStore}`)
|
||||
},
|
||||
seeDetails: (path) => {
|
||||
goto(`/flows/get/${path}?workspace=${$workspaceStore}`)
|
||||
}
|
||||
}}
|
||||
{initialPath}
|
||||
{pathStoreInit}
|
||||
|
||||
@@ -244,18 +244,19 @@
|
||||
|
||||
<DiffDrawer bind:this={diffDrawer} {restoreDeployed} {restoreDraft} />
|
||||
<FlowBuilder
|
||||
on:deploy={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:details={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:saveDraftOnlyAtNewPath={(e) => {
|
||||
const { path, selectedId } = e.detail
|
||||
goto(`/flows/edit/${path}?selected=${selectedId}`)
|
||||
}}
|
||||
on:historyRestore={() => {
|
||||
loadFlow()
|
||||
onevent={{
|
||||
deploy: (path) => {
|
||||
goto(`/flows/get/${path}?workspace=${$workspaceStore}`)
|
||||
},
|
||||
seeDetails: (path) => {
|
||||
goto(`/flows/get/${path}?workspace=${$workspaceStore}`)
|
||||
},
|
||||
saveDraftOnlyAtNewPath: ({ path, selectedId }) => {
|
||||
goto(`/flows/edit/${path}?selected=${selectedId}`)
|
||||
},
|
||||
historyRestore: () => {
|
||||
loadFlow()
|
||||
}
|
||||
}}
|
||||
{flowStore}
|
||||
{flowStateStore}
|
||||
|
||||
@@ -108,13 +108,13 @@
|
||||
{initialArgs}
|
||||
bind:this={scriptBuilder}
|
||||
lockedLanguage={templatePath != null || hubPath != null}
|
||||
on:deploy={(e) => {
|
||||
let newHash = e.detail
|
||||
goto(`/scripts/get/${newHash}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:saveInitial={(e) => {
|
||||
let path = e.detail
|
||||
goto(`/scripts/edit/${path}`)
|
||||
onevent={{
|
||||
deploy: (newHash) => {
|
||||
goto(`/scripts/get/${newHash}?workspace=${$workspaceStore}`)
|
||||
},
|
||||
saveInitial: (path) => {
|
||||
goto(`/scripts/edit/${path}`)
|
||||
}
|
||||
}}
|
||||
bind:getInitialAndModifiedValues
|
||||
searchParams={$page.url.searchParams}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
let scriptLoadedFromUrl = initialState != undefined ? decodeState(initialState) : undefined
|
||||
|
||||
|
||||
let script: (NewScript & { draft_triggers?: Trigger[] }) | undefined = undefined
|
||||
|
||||
let initialPath: string = ''
|
||||
@@ -218,19 +217,18 @@
|
||||
{diffDrawer}
|
||||
{savedPrimarySchedule}
|
||||
searchParams={$page.url.searchParams}
|
||||
on:deploy={(e) => {
|
||||
let newHash = e.detail
|
||||
goto(`/scripts/get/${newHash}?workspace=${$workspaceStore}`)
|
||||
onevent={{
|
||||
deploy: (newHash) => {
|
||||
goto(`/scripts/get/${newHash}?workspace=${$workspaceStore}`)
|
||||
},
|
||||
saveInitial: (path) => {
|
||||
goto(`/scripts/edit/${path}`)
|
||||
},
|
||||
saveDraft: ({ path, savedAtNewPath, script }) => {
|
||||
goto(`/scripts/edit/${path}`)
|
||||
}
|
||||
}}
|
||||
bind:getInitialAndModifiedValues
|
||||
on:saveInitial={(e) => {
|
||||
let path = e.detail
|
||||
goto(`/scripts/edit/${path}`)
|
||||
}}
|
||||
on:seeDetails={(e) => {
|
||||
let path = e.detail
|
||||
goto(`/scripts/get/${path}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
replaceStateFn={(path) => {
|
||||
replaceState(path, $page.state)
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user