mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 00:02:03 +00:00
improve search windows and remove fuse.js
This commit is contained in:
Generated
-14
@@ -20,7 +20,6 @@
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-plugin-zoom": "^1.2.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"fuse.js": "^6.4.6",
|
||||
"highlight.js": "^11.5.1",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"monaco-editor-workers": "^0.34.2",
|
||||
@@ -3070,14 +3069,6 @@
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fuse.js": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
|
||||
"integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -9131,11 +9122,6 @@
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"fuse.js": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
|
||||
"integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA=="
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-plugin-zoom": "^1.2.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"fuse.js": "^6.4.6",
|
||||
"highlight.js": "^11.5.1",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"monaco-editor-workers": "^0.34.2",
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { ResourceService, ScriptService, VariableService } from '$lib/gen'
|
||||
import { getScriptByPath, loadHubScripts, sendUserToast } from '$lib/utils'
|
||||
import { ResourceService, VariableService } from '$lib/gen'
|
||||
import { getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
|
||||
import {
|
||||
faBuilding,
|
||||
faCube,
|
||||
faDollarSign,
|
||||
faEye,
|
||||
faRotate,
|
||||
faRotateLeft,
|
||||
faWallet
|
||||
faRotateLeft
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
import { hubScripts, workspaceStore } from '$lib/stores'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import type Editor from './Editor.svelte'
|
||||
import ItemPicker from './ItemPicker.svelte'
|
||||
import ResourceEditor from './ResourceEditor.svelte'
|
||||
@@ -24,23 +24,27 @@
|
||||
import HighlightCode from './HighlightCode.svelte'
|
||||
import DrawerContent from './common/drawer/DrawerContent.svelte'
|
||||
import { Badge, Drawer } from './common'
|
||||
import WorkspaceScriptPicker from './flows/pickers/WorkspaceScriptPicker.svelte'
|
||||
import PickHubScript from './flows/pickers/PickHubScript.svelte'
|
||||
import ToggleHubWorkspace from './ToggleHubWorkspace.svelte'
|
||||
import Skeleton from './common/skeleton/Skeleton.svelte'
|
||||
|
||||
export let lang: 'python3' | 'deno' | 'go' | 'bash'
|
||||
export let editor: Editor
|
||||
export let websocketAlive: { pyright: boolean; black: boolean; deno: boolean; go: boolean }
|
||||
export let iconOnly: boolean = false
|
||||
export let validCode: boolean = true
|
||||
export let kind: 'script' | 'trigger' | 'approval' = 'script'
|
||||
|
||||
let contextualVariablePicker: ItemPicker
|
||||
let variablePicker: ItemPicker
|
||||
let resourcePicker: ItemPicker
|
||||
let scriptPicker: ItemPicker
|
||||
let variableEditor: VariableEditor
|
||||
let resourceEditor: ResourceEditor
|
||||
|
||||
let codeViewer: Drawer
|
||||
let codeLang: 'python3' | 'deno' | 'go' | 'bash' = 'deno'
|
||||
let codeContent: string = ''
|
||||
let codeObj: { language: 'python3' | 'deno' | 'go' | 'bash'; content: string } | undefined =
|
||||
undefined
|
||||
|
||||
function addEditorActions() {
|
||||
editor.addAction('insert-variable', 'Windmill: Insert variable', () => {
|
||||
@@ -63,36 +67,38 @@
|
||||
})
|
||||
}
|
||||
|
||||
async function loadScripts(): Promise<{ path: string; summary?: string }[]> {
|
||||
const workspaceScripts: { path: string; summary?: string }[] = await ScriptService.listScripts({
|
||||
workspace: $workspaceStore ?? 'NO_W'
|
||||
})
|
||||
if (!$hubScripts) {
|
||||
await loadHubScripts()
|
||||
}
|
||||
const hubScripts_ = $hubScripts ?? []
|
||||
let scriptPicker: Drawer
|
||||
let pick_existing: 'hub' | 'workspace' = 'hub'
|
||||
let filter = ''
|
||||
|
||||
return workspaceScripts.concat(hubScripts_)
|
||||
async function onScriptPick(e: { detail: { path: string } }) {
|
||||
codeObj = undefined
|
||||
codeViewer?.openDrawer?.()
|
||||
codeObj = await getScriptByPath(e.detail.path ?? '')
|
||||
}
|
||||
</script>
|
||||
|
||||
<ItemPicker
|
||||
bind:this={scriptPicker}
|
||||
pickCallback={async (path, _) => {
|
||||
const { language, content } = await getScriptByPath(path ?? '')
|
||||
codeContent = content
|
||||
codeLang = language
|
||||
codeViewer.openDrawer()
|
||||
}}
|
||||
closeOnClick={false}
|
||||
itemName="script"
|
||||
extraField="summary"
|
||||
loadItems={loadScripts}
|
||||
/>
|
||||
<Drawer bind:this={scriptPicker} size="900px">
|
||||
<DrawerContent title="Code" on:close={scriptPicker.closeDrawer}>
|
||||
{#if pick_existing == 'hub'}
|
||||
<PickHubScript bind:filter {kind} on:pick={onScriptPick}>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} /></PickHubScript
|
||||
>
|
||||
{:else}
|
||||
<WorkspaceScriptPicker bind:filter {kind} on:pick={onScriptPick}
|
||||
><ToggleHubWorkspace bind:selected={pick_existing} /></WorkspaceScriptPicker
|
||||
>
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:this={codeViewer} size="600px">
|
||||
<DrawerContent title="Code" on:close={codeViewer.closeDrawer}>
|
||||
<HighlightCode language={codeLang} code={codeContent} />
|
||||
{#if codeObj}
|
||||
<HighlightCode language={codeObj?.language} code={codeObj?.content} />
|
||||
{:else}
|
||||
<Skeleton layout={[[40]]} />
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -197,6 +203,7 @@
|
||||
itemName="Resource"
|
||||
buttons={{ 'Edit/View': (x) => resourceEditor.initEdit(x) }}
|
||||
extraField="description"
|
||||
extraField2="resource_type"
|
||||
loadItems={async () =>
|
||||
await ResourceService.listResource({ workspace: $workspaceStore ?? 'NO_W' })}
|
||||
>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import Fuse from 'fuse.js'
|
||||
import { Drawer, Skeleton } from './common'
|
||||
import DrawerContent from './common/drawer/DrawerContent.svelte'
|
||||
import IconedResourceType from './IconedResourceType.svelte'
|
||||
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
|
||||
export let extraField: string = 'path'
|
||||
export let extraField2: string | undefined
|
||||
export let itemName: string
|
||||
export let closeOnClick = true
|
||||
/** Displayed if the load function returns no items. */
|
||||
@@ -19,32 +20,33 @@
|
||||
let loading = false
|
||||
let items: Item[] | undefined = []
|
||||
let filteredItems: Item[] | undefined = []
|
||||
let itemsFilter = ''
|
||||
|
||||
const fuseOptions = {
|
||||
includeScore: false,
|
||||
keys: ['path', extraField]
|
||||
}
|
||||
const fuse: Fuse<Item> = new Fuse(items, fuseOptions)
|
||||
let filter = ''
|
||||
|
||||
export function openDrawer() {
|
||||
loading = true
|
||||
loadItems().then((v) => {
|
||||
items = v
|
||||
if (items) {
|
||||
fuse.setCollection(items)
|
||||
}
|
||||
loading = false
|
||||
})
|
||||
drawer.openDrawer()
|
||||
drawer.openDrawer?.()
|
||||
}
|
||||
|
||||
$: filteredItems =
|
||||
itemsFilter.length > 0 && items ? fuse.search(itemsFilter).map((value) => value.item) : items
|
||||
|
||||
let drawer: Drawer
|
||||
</script>
|
||||
|
||||
<SearchItems
|
||||
{filter}
|
||||
{items}
|
||||
bind:filteredItems
|
||||
f={(x) =>
|
||||
(extraField2 ? x[extraField2] + ' ' : '') +
|
||||
(x[extraField] ?? '') +
|
||||
' ' +
|
||||
(x['path'] && x['path'] != x[extraField] ? '(' + x['path'] + ')' ?? '' : '') +
|
||||
' ' +
|
||||
(x['description'] != x[extraField] ? x['description'] ?? '' : '')}
|
||||
/>
|
||||
|
||||
<Drawer bind:this={drawer} size="600px">
|
||||
<DrawerContent title="Search a {itemName}" on:close={drawer.closeDrawer}>
|
||||
<div>
|
||||
@@ -52,7 +54,7 @@
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search {itemName}"
|
||||
bind:value={itemsFilter}
|
||||
bind:value={filter}
|
||||
class="search-item"
|
||||
/>
|
||||
</div>
|
||||
@@ -89,17 +91,24 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-col">
|
||||
<div class="text-sm font-semibold flex flex-col">
|
||||
<span class="mr-2 text-left">{obj[extraField] ?? ''}</span>
|
||||
{#if extraField != 'path'}
|
||||
<span class="font-normal text-xs text-left italic overflow-hidden"
|
||||
>{obj['path'] ?? ''}</span
|
||||
{#if obj.marked}
|
||||
<div class="text-sm font-semibold">
|
||||
{@html obj.marked}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-sm font-semibold flex flex-col">
|
||||
<span class="mr-2 text-left">{obj[extraField] ?? ''}</span>
|
||||
{#if extraField != 'path'}
|
||||
<span class="font-normal text-xs text-left italic overflow-hidden"
|
||||
>{obj['path'] ?? ''}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if extraField != 'description'}
|
||||
<div class="text-xs font-light italic text-left"
|
||||
>{obj['description'] ?? ''}</div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if extraField != 'description'}
|
||||
<div class="text-xs font-light italic text-left">{obj['description'] ?? ''}</div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -260,6 +260,7 @@
|
||||
/>{/if}
|
||||
<div class="border rounded p-2">
|
||||
<SimpleEditor
|
||||
fixedOverflowWidgets={false}
|
||||
on:change={() => {
|
||||
try {
|
||||
schema = JSON.parse(schemaString)
|
||||
|
||||
@@ -290,6 +290,7 @@
|
||||
bind:code={script.content}
|
||||
lang={script.language}
|
||||
{initialArgs}
|
||||
kind={script.kind}
|
||||
/>
|
||||
{:else if step === 3}
|
||||
<CenteredPage>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
export let code: string
|
||||
export let path: string | undefined
|
||||
export let lang: Preview.language
|
||||
export let kind: 'script' | 'trigger' | 'approval' | undefined = undefined
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let fixedOverflowWidgets = true
|
||||
|
||||
@@ -118,6 +119,7 @@
|
||||
{editor}
|
||||
{lang}
|
||||
{websocketAlive}
|
||||
{kind}
|
||||
/>
|
||||
<div class="py-1">
|
||||
<Button
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { faBuilding } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ToggleButton, ToggleButtonGroup } from './common'
|
||||
import { WindmillIcon } from './icons'
|
||||
export let selected: 'hub' | 'workspace' = 'hub'
|
||||
</script>
|
||||
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected>
|
||||
<ToggleButton position="left" value="hub" size="sm">
|
||||
<WindmillIcon white={selected == 'hub'} height="16px" width="16px" /><div class="mr-1" />Hub
|
||||
</ToggleButton>
|
||||
<ToggleButton position="right" value="workspace" size="sm" startIcon={{ icon: faBuilding }}>
|
||||
Workspace
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
@@ -186,11 +186,23 @@
|
||||
/>
|
||||
{:else if editorKind == 'json'}
|
||||
<div class="border rounded mb-4 w-full border-gray-700">
|
||||
<SimpleEditor bind:this={editor} autoHeight lang="json" bind:code={variable.value} />
|
||||
<SimpleEditor
|
||||
bind:this={editor}
|
||||
autoHeight
|
||||
lang="json"
|
||||
bind:code={variable.value}
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
</div>
|
||||
{:else if editorKind == 'yaml'}
|
||||
<div class="border rounded mb-4 w-full border-gray-700">
|
||||
<SimpleEditor bind:this={editor} autoHeight lang="yaml" bind:code={variable.value} />
|
||||
<SimpleEditor
|
||||
bind:this={editor}
|
||||
autoHeight
|
||||
lang="yaml"
|
||||
bind:code={variable.value}
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { Preview } from '$lib/gen'
|
||||
import { DENO_INIT_CODE_CLEAR } from '$lib/script_helpers'
|
||||
import { classNames, emptySchema } from '$lib/utils'
|
||||
import { faEdit, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faEdit, faPlus, faSave, faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
@@ -115,6 +115,9 @@
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
{/if}
|
||||
<div slot="submission" class="flex items-center gap-4">
|
||||
<Button startIcon={{ icon: faSave }} disabled>Automatically Saved</Button>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
export let language: Script.language
|
||||
|
||||
const languageLabel = {
|
||||
[Script.language.PYTHON3]: 'Python 3',
|
||||
[Script.language.PYTHON3]: 'Python',
|
||||
[Script.language.DENO]: 'TypeScript',
|
||||
[Script.language.GO]: 'Go',
|
||||
[Script.language.BASH]: 'Bash'
|
||||
|
||||
@@ -1,48 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
import { loadHubFlows } from '$lib/utils'
|
||||
import { Button, ButtonPopup, ButtonPopupItem } from '$lib/components/common'
|
||||
import ItemPicker from '../ItemPicker.svelte'
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { flowStore, initFlow } from '$lib/components/flows/flowStore'
|
||||
|
||||
let hubFlows: any[] | undefined = undefined
|
||||
|
||||
const drawers: {
|
||||
hub: ItemPicker | undefined
|
||||
json: Drawer | undefined
|
||||
} = {
|
||||
hub: undefined,
|
||||
json: undefined
|
||||
}
|
||||
|
||||
let drawer: Drawer | undefined = undefined
|
||||
let pendingJson: string
|
||||
let hubFilter = ''
|
||||
|
||||
const flowHubFuse: Fuse<any> = new Fuse([], {
|
||||
includeScore: false,
|
||||
keys: ['summary']
|
||||
})
|
||||
|
||||
$: filteredHubFlows =
|
||||
hubFilter.length > 0 ? flowHubFuse.search(hubFilter).map((value) => value.item) : hubFlows ?? []
|
||||
|
||||
async function importJson() {
|
||||
await goto('/flows/add')
|
||||
Object.assign($flowStore, JSON.parse(pendingJson))
|
||||
|
||||
initFlow($flowStore)
|
||||
drawers.json?.closeDrawer()
|
||||
}
|
||||
|
||||
async function loadHubFlowsWFuse(): Promise<void> {
|
||||
hubFlows = await loadHubFlows()
|
||||
flowHubFuse.setCollection(hubFlows ?? [])
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -50,30 +24,16 @@
|
||||
<div class="flex flex-row gap-2">
|
||||
<ButtonPopup size="sm" spacingSize="xl" startIcon={{ icon: faPlus }} href="/flows/add">
|
||||
<svelte:fragment slot="main">New Flow</svelte:fragment>
|
||||
<ButtonPopupItem on:click={() => drawers.json?.toggleDrawer()}>
|
||||
<ButtonPopupItem on:click={() => drawer?.toggleDrawer?.()}>
|
||||
Import from raw JSON
|
||||
</ButtonPopupItem>
|
||||
</ButtonPopup>
|
||||
</div>
|
||||
|
||||
<!-- WindmillHub script list -->
|
||||
<ItemPicker
|
||||
bind:this={drawers.hub}
|
||||
pickCallback={(path) => {
|
||||
goto('/flows/add?hub=' + path)
|
||||
}}
|
||||
itemName={'Flow'}
|
||||
extraField="summary"
|
||||
loadItems={async () => {
|
||||
await loadHubFlowsWFuse()
|
||||
return filteredHubFlows.map((x) => ({ ...x, path: x.flow_id }))
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- Raw JSON -->
|
||||
<Drawer bind:this={drawers.json} size="800px">
|
||||
<DrawerContent title="Import flow from JSON" on:close={() => drawers.json?.toggleDrawer()}>
|
||||
<SimpleEditor bind:code={pendingJson} lang="json" class="h-full" />
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
<DrawerContent title="Import flow from JSON" on:close={() => drawer?.toggleDrawer?.()}>
|
||||
<SimpleEditor bind:code={pendingJson} lang="json" class="h-full" fixedOverflowWidgets={false} />
|
||||
<span slot="submission"><Button size="sm" on:click={importJson}>Import</Button></span>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { ToggleButton, ToggleButtonGroup } from '$lib/components/common'
|
||||
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
|
||||
import ToggleHubWorkspace from '$lib/components/ToggleHubWorkspace.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { RawScript, Script } from '$lib/gen'
|
||||
|
||||
@@ -24,11 +25,14 @@
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected={kind}>
|
||||
<ToggleButton position="left" value="script" size="sm" startIcon={{ icon: faCode }}>
|
||||
Common
|
||||
Common <Tooltip>
|
||||
A common script is simply a script that is neither a trigger nor an approval script.
|
||||
Those are the majority of the scripts.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
{#if !shouldDisableTriggerScripts}
|
||||
<ToggleButton position="center" value="trigger" size="sm" startIcon={{ icon: faBolt }}>
|
||||
Trigger <Tooltip>
|
||||
Trigger <Tooltip>
|
||||
Used as a first step most commonly with a state and a schedule to watch for changes
|
||||
on an external system, compute the diff since last time, set the new state. The
|
||||
diffs are then treated one by one with a for-loop.
|
||||
@@ -36,11 +40,11 @@
|
||||
</ToggleButton>
|
||||
{/if}
|
||||
<ToggleButton position="right" value="approval" size="sm" startIcon={{ icon: faCheck }}>
|
||||
Approval <Tooltip>
|
||||
Approval <Tooltip>
|
||||
An approval step will suspend the execution of a flow until it has been approved
|
||||
through the resume endpoints or the approval page by and solely by the recipients of
|
||||
those secret urls. Use getResumeEndpoints from the wmill client to generate those
|
||||
URLs.
|
||||
those secret urls. Use `wmill.getResumeUrls()` in Typescript or
|
||||
`wmill.get_resume_urls()` in Python from the wmill client to generate those URLs.
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
@@ -48,10 +52,10 @@
|
||||
</div>
|
||||
{/if}
|
||||
<h3 class="pb-2">
|
||||
Inline new {kind == 'script' ? 'common' : kind} script
|
||||
Inline new <span class="text-blue-500">{kind == 'script' ? 'common' : kind}</span> script
|
||||
<Tooltip>
|
||||
Embed a script directly inside a flow instead of saving the script into your workspace for
|
||||
reuse. You can always save an inline script later.
|
||||
reuse. You can always save an inline script to your workspace later.
|
||||
</Tooltip>
|
||||
</h3>
|
||||
<div class="flex flex-row">
|
||||
@@ -119,24 +123,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-2 mt-8 mb-2"
|
||||
><h3>Use pre-made</h3>
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected={pick_existing}>
|
||||
<ToggleButton position="left" value="hub" size="xs">
|
||||
<WindmillIcon white={pick_existing == 'hub'} height="16px" width="16px" /><div
|
||||
class="mr-1"
|
||||
/>Hub
|
||||
</ToggleButton>
|
||||
<ToggleButton position="right" value="workspace" size="xs" startIcon={{ icon: faBuilding }}>
|
||||
Workspace
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</div></div
|
||||
<h3 class="mb-2 mt-6"
|
||||
>Use pre-made <span class="text-blue-500">{kind == 'script' ? 'common' : kind}</span> script</h3
|
||||
>
|
||||
{#if pick_existing == 'hub'}
|
||||
<PickHubScript bind:filter {kind} on:pick />
|
||||
<PickHubScript bind:filter {kind} on:pick
|
||||
><ToggleHubWorkspace bind:selected={pick_existing} /></PickHubScript
|
||||
>
|
||||
{:else}
|
||||
<WorkspaceScriptPicker bind:filter {kind} on:pick />
|
||||
<WorkspaceScriptPicker bind:filter {kind} on:pick
|
||||
><ToggleHubWorkspace bind:selected={pick_existing} /></WorkspaceScriptPicker
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
<Tab value="advanced-early-stop">Early Stop</Tab>
|
||||
<Tab value="advanced-suspend">Suspend</Tab>
|
||||
<Tab value="advanced-sleep">Sleep</Tab>
|
||||
<Tab value="advanced-same_worker">Same Worker/Shared dir</Tab>
|
||||
<Tab value="advanced-same_worker">Shared Directory</Tab>
|
||||
{/if}
|
||||
</Tabs>
|
||||
{#if selected === 'advanced-retries'}
|
||||
@@ -241,15 +241,15 @@
|
||||
</div>
|
||||
{:else if selected === 'advanced-same_worker'}
|
||||
<div class="p-4 h-full overflow-auto">
|
||||
<Alert type="info" title="Share a directory using same worker">
|
||||
If same worker is set, all steps will be run on the same worker and will share
|
||||
the folder `./shared` to pass data between each other.
|
||||
<Alert type="info" title="Share a directory between steps">
|
||||
If shared directory is set, will share a folder that will be mounted on
|
||||
`./shared` for each of them to pass data between each other.
|
||||
</Alert>
|
||||
<Button
|
||||
btnClasses="mt-4"
|
||||
on:click={() => {
|
||||
$selectedId = 'settings-same-worker'
|
||||
}}>Set same worker in the flow settings</Button
|
||||
}}>Set shared directory in the flow settings</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<Tabs selected={defaultTab}>
|
||||
<Tab value="metadata">Metadata</Tab>
|
||||
<Tab value="schedule">Schedule</Tab>
|
||||
<Tab value="same-worker">Same Worker</Tab>
|
||||
<Tab value="same-worker">Shared Directory</Tab>
|
||||
<Tab value="graph">Graph</Tab>
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
@@ -84,19 +84,16 @@
|
||||
</TabContent>
|
||||
|
||||
<TabContent value="same-worker" class="p-4 flex flex-col">
|
||||
<Alert
|
||||
type="info"
|
||||
title="Toggle Same Worker to have all steps be ran on the same worker"
|
||||
>
|
||||
Steps will be run one after the other on the same worker, and will share a folder at
|
||||
`./shared` in which they can store heavier data and pass them to the next step. <br
|
||||
/><br />Beware that the `./shared` folder is not preserved across suspends and sleeps.
|
||||
<Alert type="info" title="Shared Directory">
|
||||
Steps will share a folder at `./shared` in which they can store heavier data and pass
|
||||
them to the next step. <br /><br />Beware that the `./shared` folder is not preserved
|
||||
across suspends and sleeps.
|
||||
</Alert>
|
||||
<span class="my-2 text-sm font-bold">Same Worker</span>
|
||||
<span class="my-2 text-sm font-bold">Shared Directoryr</span>
|
||||
<Toggle
|
||||
bind:checked={$flowStore.value.same_worker}
|
||||
options={{
|
||||
right: 'Same Worker'
|
||||
right: 'Shared Directory on `./shared`'
|
||||
}}
|
||||
/>
|
||||
</TabContent>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if $flowStore.value.same_worker}
|
||||
<Badge color="blue" baseClass="truncate">same worker</Badge>
|
||||
<Badge color="blue" baseClass="truncate">./shared</Badge>
|
||||
{/if}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -31,14 +31,15 @@
|
||||
<SearchItems {filter} items={prefilteredItems} bind:filteredItems f={(x) => x.summary} />
|
||||
|
||||
<div class="flex flex-col min-h-0">
|
||||
<div class="w-12/12 pb-2 flex flex-row my-1 gap-1">
|
||||
<div class="w-full flex mt-1 items-center gap-2 mb-5">
|
||||
<slot />
|
||||
<input type="text" placeholder="Search Hub Scripts" bind:value={filter} class="text-2xl grow" />
|
||||
</div>
|
||||
|
||||
<div class="gap-2 w-full flex flex-wrap pb-2">
|
||||
<div class="gap-2 w-full flex flex-wrap pb-3">
|
||||
{#each apps as app}
|
||||
<Badge
|
||||
class="cursor-pointer hover:bg-gray-200"
|
||||
class="cursor-pointer hover:bg-gray-200 border"
|
||||
on:click={() => {
|
||||
appFilter = appFilter == app ? undefined : app
|
||||
}}
|
||||
|
||||
@@ -1,62 +1,116 @@
|
||||
<script lang="ts">
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Fuse from 'fuse.js'
|
||||
import { ScriptService } from '$lib/gen'
|
||||
import SearchItems from '$lib/components/SearchItems.svelte'
|
||||
import { Badge, Skeleton } from '$lib/components/common'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { emptyString } from '$lib/utils'
|
||||
|
||||
export let kind: 'script' | 'trigger' | 'approval' | 'failure' = 'script'
|
||||
export let isTemplate: boolean | undefined = undefined
|
||||
|
||||
let items: Item[] = []
|
||||
|
||||
let filteredItems: Item[] | undefined = []
|
||||
export let filter = ''
|
||||
|
||||
const fuseOptions = {
|
||||
includeScore: false,
|
||||
keys: ['path', 'summay']
|
||||
type Item = {
|
||||
path: string
|
||||
summary?: string
|
||||
description?: string
|
||||
}
|
||||
const fuse: Fuse<Item> = new Fuse(items, fuseOptions)
|
||||
|
||||
let items: Item[] | undefined = undefined
|
||||
|
||||
let filteredItems: (Item & { marked?: string })[] | undefined = undefined
|
||||
export let filter = ''
|
||||
|
||||
$: $workspaceStore && kind && loadItems()
|
||||
|
||||
$: filteredItems =
|
||||
filter.length > 0 && items ? fuse.search(filter).map((value) => value.item) : items
|
||||
|
||||
async function loadItems(): Promise<void> {
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, kind })
|
||||
fuse.setCollection(items)
|
||||
items = await ScriptService.listScripts({ workspace: $workspaceStore!, kind, isTemplate })
|
||||
}
|
||||
|
||||
let ownerFilter: string | undefined = undefined
|
||||
$: prefilteredItems = ownerFilter ? items?.filter((x) => x.path.startsWith(ownerFilter!)) : items
|
||||
|
||||
$: owners = Array.from(
|
||||
new Set(filteredItems?.map((x) => x.path.split('/').slice(0, 2).join('/')) ?? [])
|
||||
).sort()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<SearchItems
|
||||
{filter}
|
||||
items={prefilteredItems}
|
||||
bind:filteredItems
|
||||
f={(x) => (emptyString(x.summary) ? x.path : x.summary + ' (' + x.path + ')')}
|
||||
/>
|
||||
<div class="flex flex-col min-h-0">
|
||||
<div class="w-12/12 pb-4 mt-1">
|
||||
<input type="text" placeholder="Search script" bind:value={filter} class="text-2xl grow" />
|
||||
<div class="w-full flex mt-1 items-center gap-2 mb-3">
|
||||
<slot />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Workspace Scripts"
|
||||
bind:value={filter}
|
||||
class="text-2xl grow"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if filteredItems}
|
||||
{#if owners.length > 0}
|
||||
<div class="gap-2 w-full flex flex-wrap my-2">
|
||||
{#each owners as owner (owner)}
|
||||
<div in:fade={{ duration: 50 }} animate:flip={{ duration: 100 }}>
|
||||
<Badge
|
||||
class="cursor-pointer hover:bg-gray-200"
|
||||
on:click={() => {
|
||||
ownerFilter = ownerFilter == owner ? undefined : owner
|
||||
}}
|
||||
color={owner === ownerFilter ? 'blue' : 'gray'}
|
||||
baseClass={owner === ownerFilter ? 'border border-blue-500' : 'border'}
|
||||
>
|
||||
{owner}
|
||||
{#if owner === ownerFilter}✗{/if}
|
||||
</Badge>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if filter.length > 0 && filteredItems.length == 0}
|
||||
<p class="py-5">No items found</p>
|
||||
{/if}
|
||||
<ul class="divide-y divide-gray-200 overflow-auto">
|
||||
{#each filteredItems as obj}
|
||||
{#each filteredItems as { path, summary, description, marked }}
|
||||
<li class="flex flex-row w-full">
|
||||
<button
|
||||
class="py-4 px-1 gap-1 flex flex-row grow hover:bg-blue-50 bg-white transition-all text-black"
|
||||
on:click={() => {
|
||||
dispatch('pick', obj)
|
||||
dispatch('pick', { path })
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="text-sm font-semibold flex flex-col">
|
||||
<span class="mr-2 text-left">{obj['summary'] ?? ''}</span>
|
||||
<span class="mr-2 text-left">
|
||||
{#if marked}
|
||||
{@html marked}
|
||||
{:else}
|
||||
{!summary || summary.length == 0 ? path : summary}
|
||||
{/if}</span
|
||||
>
|
||||
<span class="font-normal text-xs text-left italic overflow-hidden"
|
||||
>{obj['path'] ?? ''}</span
|
||||
>{path ?? ''}</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-xs font-light italic text-left">{obj['description'] ?? ''}</div>
|
||||
<div class="text-xs font-light italic text-left">{description ?? ''}</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="mt-6" />
|
||||
|
||||
{#each new Array(6) as _}
|
||||
<Skeleton layout={[[4], 0.7]} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
{#if owners.length > 0}
|
||||
<div class="gap-2 w-full flex flex-wrap my-4">
|
||||
{#each owners as owner (owner)}
|
||||
<div in:fade animate:flip={{ duration: 200 }}>
|
||||
<div in:fade={{ duration: 50 }} animate:flip={{ duration: 100 }}>
|
||||
<Badge
|
||||
class="cursor-pointer hover:bg-gray-200"
|
||||
on:click={() => {
|
||||
|
||||
@@ -1,84 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { faPlus, faScroll } from '@fortawesome/free-solid-svg-icons'
|
||||
import Fuse from 'fuse.js'
|
||||
import { Script } from '$lib/gen'
|
||||
import { ScriptService } from '$lib/gen'
|
||||
import { workspaceStore, hubScripts } from '$lib/stores'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ButtonPopup, ButtonPopupItem } from '$lib/components/common'
|
||||
import type { HubItem } from '../flows/pickers/model'
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import WorkspaceScriptPicker from '../flows/pickers/WorkspaceScriptPicker.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
const drawers: {
|
||||
template: Drawer | undefined
|
||||
} = {
|
||||
template: undefined
|
||||
}
|
||||
|
||||
let hubItems: HubItem[]
|
||||
let templateScripts: Script[] = []
|
||||
let templateFilter = ''
|
||||
let filteredTemplates: Script[] | undefined
|
||||
const fuseOptions = {
|
||||
includeScore: false,
|
||||
keys: ['description', 'path', 'content', 'hash', 'summary']
|
||||
}
|
||||
const templateFuse: Fuse<Script> = new Fuse(templateScripts, fuseOptions)
|
||||
|
||||
$: hubItems = $hubScripts?.filter((x) => x.kind == Script.kind.SCRIPT) || []
|
||||
|
||||
$: filteredTemplates =
|
||||
templateFilter.length > 0
|
||||
? templateFuse.search(templateFilter).map((value) => value.item)
|
||||
: templateScripts
|
||||
|
||||
async function loadTemplateScripts(): Promise<void> {
|
||||
templateScripts = await ScriptService.listScripts({
|
||||
workspace: $workspaceStore!,
|
||||
isTemplate: true
|
||||
})
|
||||
templateFuse.setCollection(templateScripts)
|
||||
}
|
||||
let drawer: Drawer | undefined = undefined
|
||||
</script>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="flex flex-row gap-2">
|
||||
<ButtonPopup size="sm" spacingSize="xl" startIcon={{ icon: faPlus }} href="/scripts/add">
|
||||
<svelte:fragment slot="main">New Script</svelte:fragment>
|
||||
<ButtonPopupItem on:click={() => drawers.template?.toggleDrawer()}>
|
||||
<ButtonPopupItem on:click={() => drawer?.toggleDrawer?.()}>
|
||||
Import from template
|
||||
</ButtonPopupItem>
|
||||
</ButtonPopup>
|
||||
</div>
|
||||
|
||||
<!-- Template script list -->
|
||||
<Drawer bind:this={drawers.template} size="800px" on:open={loadTemplateScripts}>
|
||||
<DrawerContent title="Pick a template" on:close={() => drawers.template?.toggleDrawer()}>
|
||||
<div class="pt-2 pb-4">
|
||||
<input placeholder="Search templates" bind:value={templateFilter} class="search-bar" />
|
||||
</div>
|
||||
<div class="flex flex-col mb-2 md:mb-6">
|
||||
{#if filteredTemplates && filteredTemplates.length > 0}
|
||||
{#each filteredTemplates as { summary, path, hash }}
|
||||
<a
|
||||
class="p-1 flex flex-row items-baseline gap-2 selected text-gray-700"
|
||||
href="/scripts/add?template={path}"
|
||||
>
|
||||
{#if summary}
|
||||
<p class="text-sm font-semibold">{summary}</p>
|
||||
{/if}
|
||||
|
||||
<p class="text-sm">{path}</p>
|
||||
<p class="text-gray-400 text-xs text-right grow">
|
||||
Last version: {hash}
|
||||
</p>
|
||||
</a>
|
||||
{/each}
|
||||
{:else}
|
||||
<p class="text-sm text-gray-700">No templates</p>
|
||||
{/if}
|
||||
</div>
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
<DrawerContent title="Pick a template" on:close={() => drawer?.toggleDrawer?.()}>
|
||||
<WorkspaceScriptPicker
|
||||
isTemplate
|
||||
on:pick={(e) => goto(`/scripts/add?template=${e.detail.path}`)}
|
||||
/>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
Reference in New Issue
Block a user