Files
windmill/frontend/src/lib/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte
T
3c99b3fdc7 feat: migrate to svelte5 + vite6 (#4813)
* runs on svelte 5

* Line component from svelte-chartjs

* Replaced all svelte-chartjs occurrences with custom wrapper

* Fix props mistake

* Fix illegal table structures

* self-closing-tags fix

* aria labels

* Fixed trivial warnings and errors

* @tanstack/svelte-table fix

* upgrade to vite 6

* svelte-kit sync before running svelte-check

* Remove on:clear which is actually on:removeAll and already handled by on:change

* fix worker tags not displaying in Autoscaling

* Try to fix svelte-kit sync not working during CI

* remove warnings

* Fix add flow page crashing

* access worldStore before assignment fix

* fix infinite recursions in App Editor

* Replaced JSON.stringify with proper deepEqual

* component mount api changed (no longer classes)

* fix ci errors

* Fix infinite loops in background runnable panel

* factored effect on deep equal logic in onObjChange

* fix "Add" not working in AgGrid Table

* Replaced legacy component.$set api

* Fix multiselect infinite value reaction

* Fix flow input fields resetting when opening their edit tab

* fix date input resetting when typing year

* Remove !p-0 affecting subgrid dotted borders

* fix missing debounceTemplate causing hundreds of updates

* Fix AgGrid action refreshes and disppearing

* resolve getItems generating random ids every rerun

* fix cannot access items before init

* fix sort lambda arguments being undefined

* Revert "Remove !p-0 affecting subgrid dotted borders"

This reverts commit c62809bb45d682a48376b071680645ed4e1c601b.

* fix input not updating in decision tree editor

* Update frontend/src/lib/components/schema/EditableSchemaWrapper.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Re-added padding affecting subgrid dotted borders (#5479)

* remove !p-0 in preset components

* removed extra padding on accordion tabs subgrid

* Fix non-reactive SchemaForm

* dirty fix for the oneOf bug

* Fix warnings and update svelte-exmarkdown for svelte 5

* fix dnd not working

* don't mount component like objects

---------

Co-authored-by: Diego Imbert <diegoimbert@protonmail.com>
Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-04-02 21:33:50 +00:00

249 lines
7.4 KiB
Svelte

<script lang="ts">
import type { Schema } from '$lib/common'
import { Button, Drawer, DrawerContent, Tab, Tabs } from '$lib/components/common'
import FlowScriptPicker from '$lib/components/flows/pickers/FlowScriptPicker.svelte'
import PickHubScript from '$lib/components/flows/pickers/PickHubScript.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { emptySchema } from '$lib/utils'
import { defaultScriptLanguages, getScriptByPath, processLangs } from '$lib/scripts'
import { Building, GitFork, Globe2 } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { fly } from 'svelte/transition'
import type { AppViewerContext } from '../../types'
import { defaultCode } from '../component'
import InlineScriptList from '../settingsPanel/mainInput/InlineScriptList.svelte'
import WorkspaceScriptList from '../settingsPanel/mainInput/WorkspaceScriptList.svelte'
import RunnableSelector from '../settingsPanel/mainInput/RunnableSelector.svelte'
import { defaultScripts } from '$lib/stores'
import DefaultScripts from '$lib/components/DefaultScripts.svelte'
import type { Preview } from '$lib/gen'
export let name: string
export let componentType: string | undefined = undefined
export let showScriptPicker = false
let tab = 'inlinescripts'
let filter: string = ''
let picker: Drawer
const { appPath, app } = getContext<AppViewerContext>('AppViewerContext')
const dispatch = createEventDispatcher()
async function inferInlineScriptSchema(
language: Preview['language'],
content: string,
schema: Schema
): Promise<Schema> {
try {
await inferArgs(language, content, schema)
} catch (e) {
console.error("Couldn't infer args", e)
}
return schema
}
async function createInlineScriptByLanguage(
language: Preview['language'],
path: string,
subkind: 'pgsql' | 'mysql' | 'fetch' | undefined = undefined
) {
const content =
defaultCode(componentType ?? '', (subkind || language) ?? '') ??
initialCode(language, 'script', subkind ?? 'flow')
return newInlineScript(content, language, path)
}
async function newInlineScript(content: string, language: Preview['language'], path: string) {
const fullPath = `${$appPath}/${path}`
let schema: Schema = emptySchema()
schema = await inferInlineScriptSchema(language, content, schema)
const newInlineScript = {
content,
language,
path: fullPath,
schema
}
dispatch('new', newInlineScript)
}
async function pickScript(path: string) {
const script = await getScriptByPath(path)
newInlineScript(script.content, script.language, path)
}
async function pickHubScript(path: string) {
const script = await getScriptByPath(path)
newInlineScript(script.content, script.language, path)
}
function pickInlineScript(name: string) {
const unusedInlineScriptIndex = $app.unusedInlineScripts?.findIndex(
(script) => script.name === name
)
const unusedInlineScript = $app.unusedInlineScripts?.[unusedInlineScriptIndex]
$app.unusedInlineScripts.splice(unusedInlineScriptIndex, 1)
$app = $app
dispatch('new', unusedInlineScript.inlineScript)
}
$: langs = processLangs(undefined, $defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) =>
x[1] != 'docker' &&
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
) as [string, Preview['language']][]
</script>
<Drawer bind:this={picker} size="1000px">
<DrawerContent title="Script/Flow Picker" on:close={picker.closeDrawer}>
<div>
<div class="max-w-6xl">
<Tabs bind:selected={tab}>
<Tab size="sm" value="inlinescripts">
<div class="flex gap-2 items-center my-1">
<Building size={18} />
Detached Inline Scripts
</div>
</Tab>
<Tab size="sm" value="workspacescripts">
<div class="flex gap-2 items-center my-1">
<Building size={18} />
Workspace Scripts
</div>
</Tab>
<Tab size="sm" value="hubscripts">
<div class="flex gap-2 items-center my-1">
<Globe2 size={18} />
Hub Scripts
</div>
</Tab>
</Tabs>
<div class="my-2"></div>
<div class="flex flex-col gap-y-16">
<div class="flex flex-col">
{#if tab == 'inlinescripts'}
<InlineScriptList
on:pick={(e) => pickInlineScript(e.detail)}
inlineScripts={$app.unusedInlineScripts
? $app.unusedInlineScripts.map((uis) => uis.name)
: []}
/>
{:else if tab == 'workspacescripts'}
<WorkspaceScriptList on:pick={(e) => pickScript(e.detail)} />
{:else if tab == 'hubscripts'}
<PickHubScript bind:filter on:pick={(e) => pickHubScript(e.detail.path)} />
{/if}
</div>
</div>
</div>
</div>
</DrawerContent>
</Drawer>
<div
class="flex flex-col px-4 gap-2 text-sm"
in:fly={{ duration: 50 }}
id="app-editor-empty-runnable"
>
<div class="mt-2 flex justify-between gap-4" id="app-editor-runnable-header">
<div class="font-bold items-baseline truncate">Choose a language</div>
<div class="flex gap-2">
{#if showScriptPicker}
<RunnableSelector on:pick hideCreateScript />
{/if}
<Button
on:click={() => picker?.openDrawer()}
size="xs"
variant="border"
color="light"
startIcon={{ icon: GitFork }}
btnClasses="truncate"
>
Fork other script
</Button>
<Button
on:click={() => dispatch('delete')}
size="xs"
color="red"
variant="border"
btnClasses="truncate"
>
Clear
</Button>
</div>
</div>
<div class="flex flex-row w-full gap-8">
<div id="app-editor-backend-runnables">
<div class="mb-1 text-sm font-semibold flex gap-4">Backend <DefaultScripts /> </div>
<div class="flex flex-row flex-wrap gap-2">
{#each langs as [label, lang] (lang)}
<FlowScriptPicker
{label}
{lang}
on:click={() => {
createInlineScriptByLanguage(lang, name)
}}
id={`create-${lang}-script`}
/>
{/each}
</div>
</div>
<div id="app-editor-frontend-runnables">
<div class="mb-1 text-sm font-semibold">
Frontend
<Tooltip
documentationLink="https://www.windmill.dev/docs/apps/app-runnable-panel#frontend-scripts"
>
Frontend scripts are executed in the browser and can manipulate the app context directly.
</Tooltip>
</div>
<div>
<FlowScriptPicker
label={`JavaScript`}
lang="javascript"
on:click={() => {
const newInlineScript = {
content: `// read outputs and ctx
console.log(ctx.email)
// access a global state store
if (!state.foo) { state.foo = 0 }
state.foo += 1
// for reactivity to work, you need to assign a value and not modify it in place
// e.g: state.foo.push(1) will not work but 'state.foo = [...state.foo, 1]' will.
// you may also just reassign as next statement 'state.foo = state.foo'
// you can also navigate (goto), recompute a script (recompute), or set a tab (setTab)
// Inputs and display components support settings their value directly
// setValue('a', "Bar")
// Tables support setting their selected index (setSelectedIndex)
// all helpers can be found at https://www.windmill.dev/docs/apps/app-runnable-panel#frontend-scripts-helpers
return state.foo`,
language: 'frontend',
path: 'frontend script',
schema: undefined
}
dispatch('new', newInlineScript)
}}
/>
</div>
</div>
</div>
</div>