mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-26 00:01:37 +00:00
feat: add yaml for import/export/diff (#2631)
* feat: add yaml for import/export/diff * fix: speeeeeeed
This commit is contained in:
Generated
+40
-16
@@ -27,6 +27,7 @@
|
||||
"driver.js": "^1.3.0",
|
||||
"esm-env": "^1.0.0",
|
||||
"fast-equals": "^5.0.1",
|
||||
"fsevents": "*",
|
||||
"graphql": "^16.7.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -52,6 +53,7 @@
|
||||
"windmill-parser-wasm": "^1.202.1",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
"yaml": "^2.3.4",
|
||||
"yjs": "^13.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -109,6 +111,9 @@
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@sveltejs/kit": "^1.25.0",
|
||||
"svelte": "^4.0.0"
|
||||
@@ -1198,6 +1203,20 @@
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.21",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz",
|
||||
@@ -4332,10 +4351,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -10226,10 +10244,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
|
||||
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
|
||||
"dev": true,
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
|
||||
"integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
@@ -11004,6 +11021,15 @@
|
||||
"@types/node": "*",
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.34.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@polka/url": {
|
||||
@@ -13295,10 +13321,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
@@ -17372,10 +17397,9 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"yaml": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
|
||||
"integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
|
||||
"dev": true
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
|
||||
"integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA=="
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte": "^2.33.1",
|
||||
"monaco-editor-workers": "~0.44.0",
|
||||
"ol": "^7.4.0",
|
||||
"openapi-typescript-codegen": "^0.25.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
@@ -68,8 +69,7 @@
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-circular-dependency": "^0.2.1",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"yootils": "^0.3.1",
|
||||
"monaco-editor-workers": "~0.44.0"
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@rgossiaux/svelte-headlessui": {
|
||||
@@ -136,6 +136,7 @@
|
||||
"windmill-parser-wasm": "^1.202.1",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
"yaml": "^2.3.4",
|
||||
"yjs": "^13.6.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -390,5 +391,8 @@
|
||||
"./package/gen/core/OpenAPI.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
import Tabs from './common/tabs/Tabs.svelte'
|
||||
import Tab from './common/tabs/Tab.svelte'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { cleanValueProperties, orderedJsonStringify, type Value } from '$lib/utils'
|
||||
import {
|
||||
cleanValueProperties,
|
||||
orderedJsonStringify,
|
||||
orderedYamlStringify,
|
||||
type Value
|
||||
} from '$lib/utils'
|
||||
import type { Script } from '$lib/gen'
|
||||
|
||||
type DiffData = {
|
||||
@@ -60,7 +65,7 @@
|
||||
return {
|
||||
lang: data.language ? scriptLangToEditorLang(data.language as Script.language) : undefined,
|
||||
content,
|
||||
metadata: orderedJsonStringify(metadata, 2)
|
||||
metadata: orderedYamlStringify(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +222,7 @@
|
||||
<DiffEditor
|
||||
automaticLayout
|
||||
class="h-full"
|
||||
defaultLang="json"
|
||||
defaultLang="yaml"
|
||||
defaultOriginal={metadata}
|
||||
defaultModified={data.current.metadata}
|
||||
readOnly
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
type SvelvetSettingsContext
|
||||
} from './graph/svelvet/container/models'
|
||||
import { Clipboard } from 'lucide-svelte'
|
||||
import YAML from 'yaml'
|
||||
import { yaml } from 'svelte-highlight/languages'
|
||||
|
||||
setContext<SvelvetSettingsContext>(SVELVET_CONTEXT_KEY, {
|
||||
fullHeight: true
|
||||
@@ -36,9 +38,11 @@
|
||||
|
||||
export let noGraph: boolean = false
|
||||
|
||||
export let tab: 'ui' | 'json' | 'schema' = noGraph ? 'schema' : 'ui'
|
||||
export let tab: 'ui' | 'raw' | 'schema' = noGraph ? 'schema' : 'ui'
|
||||
export let noSummary = false
|
||||
|
||||
let rawType: 'json' | 'yaml' = 'yaml'
|
||||
|
||||
let open: { [id: number]: boolean } = {}
|
||||
if (initialOpen) {
|
||||
open[initialOpen] = true
|
||||
@@ -53,7 +57,7 @@
|
||||
{#if !noGraph}
|
||||
<Tab value="ui">Graph</Tab>
|
||||
{/if}
|
||||
<Tab value="json">JSON</Tab>
|
||||
<Tab value="raw">Raw</Tab>
|
||||
<Tab value="schema">Input Schema</Tab>
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
@@ -91,24 +95,37 @@
|
||||
<FlowGraphViewer download {noSide} {flow} overflowAuto />
|
||||
</div>
|
||||
</TabContent>
|
||||
<TabContent value="json">
|
||||
<div class="relative pt-2">
|
||||
<Button
|
||||
on:click={() => copyToClipboard(JSON.stringify(flowFiltered, null, 4))}
|
||||
color="light"
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight
|
||||
language={json}
|
||||
code={JSON.stringify(flowFiltered, null, 4)}
|
||||
class="overflow-auto"
|
||||
/>
|
||||
</div>
|
||||
<TabContent value="raw"
|
||||
><Tabs bind:selected={rawType} wrapperClass="mt-4">
|
||||
<Tab value="yaml">YAML</Tab>
|
||||
<Tab value="json">JSON</Tab>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="relative pt-2">
|
||||
<Button
|
||||
on:click={() =>
|
||||
copyToClipboard(
|
||||
rawType === 'yaml'
|
||||
? YAML.stringify(flowFiltered)
|
||||
: JSON.stringify(flowFiltered, null, 4)
|
||||
)}
|
||||
color="light"
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight
|
||||
class="overflow-auto px-1"
|
||||
language={rawType === 'yaml' ? yaml : json}
|
||||
code={rawType === 'yaml'
|
||||
? YAML.stringify(flowFiltered)
|
||||
: JSON.stringify(flowFiltered, null, 4)}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
</TabContent>
|
||||
<TabContent value="schema">
|
||||
<div class="my-4" />
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
classNames,
|
||||
cleanValueProperties,
|
||||
copyToClipboard,
|
||||
orderedJsonStringify,
|
||||
truncateRev
|
||||
truncateRev,
|
||||
orderedJsonStringify
|
||||
} from '../../../utils'
|
||||
import type {
|
||||
AppInput,
|
||||
@@ -484,7 +484,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'JSON',
|
||||
displayName: 'Export',
|
||||
icon: FileJson,
|
||||
action: () => {
|
||||
appExport.open($app)
|
||||
|
||||
@@ -9,11 +9,17 @@
|
||||
import { Button } from '../../common'
|
||||
import type { App } from '../types'
|
||||
import { Clipboard } from 'lucide-svelte'
|
||||
import { yaml } from 'svelte-highlight/languages'
|
||||
import YAML from 'yaml'
|
||||
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
|
||||
import Tab from '$lib/components/common/tabs/Tab.svelte'
|
||||
|
||||
let jsonViewerDrawer: Drawer
|
||||
|
||||
let app: App | undefined = undefined
|
||||
|
||||
let rawType: 'json' | 'yaml' = 'yaml'
|
||||
|
||||
export function open(app_l: App) {
|
||||
app = app_l
|
||||
jsonViewerDrawer?.toggleDrawer()
|
||||
@@ -21,19 +27,38 @@
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={jsonViewerDrawer} size="800px">
|
||||
<DrawerContent title="App JSON" on:close={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
<div class="relative">
|
||||
<Button
|
||||
on:click={() => copyToClipboard(JSON.stringify(app, null, 4))}
|
||||
color="dark"
|
||||
variant="border"
|
||||
size="sm"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight language={json} code={JSON.stringify(app ?? {}, null, 4)} />
|
||||
</div>
|
||||
</DrawerContent>
|
||||
<DrawerContent title="App Export" on:close={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
<div>
|
||||
<Tabs bind:selected={rawType}>
|
||||
<Tab value="yaml">YAML</Tab>
|
||||
<Tab value="json">JSON</Tab>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="relative pt-2">
|
||||
<Button
|
||||
on:click={() =>
|
||||
copyToClipboard(
|
||||
rawType === 'yaml'
|
||||
? YAML.stringify(app ?? {})
|
||||
: JSON.stringify(app ?? {}, null, 4)
|
||||
)}
|
||||
color="dark"
|
||||
variant="border"
|
||||
size="sm"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight
|
||||
class="overflow-auto px-1 flex-1"
|
||||
language={rawType === 'yaml' ? yaml : json}
|
||||
code={rawType === 'yaml'
|
||||
? YAML.stringify(app ?? {})
|
||||
: JSON.stringify(app ?? {}, null, 4)}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
</div></DrawerContent
|
||||
>
|
||||
</Drawer>
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
import { copyToClipboard } from '$lib/utils'
|
||||
import { CalendarCheck2, Clipboard, Terminal, Webhook } from 'lucide-svelte'
|
||||
import { Highlight } from 'svelte-highlight'
|
||||
import { yaml } from 'svelte-highlight/languages'
|
||||
import json from 'svelte-highlight/languages/json'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import YAML from 'yaml'
|
||||
|
||||
let triggerSelected: 'webhooks' | 'schedule' | 'cli' = 'webhooks'
|
||||
export let flow_json: any | undefined = undefined
|
||||
@@ -13,6 +15,7 @@
|
||||
export let isOperator: boolean = false
|
||||
|
||||
let selected = 'saved_inputs'
|
||||
let rawType: 'json' | 'yaml' = 'yaml'
|
||||
|
||||
$: if (hasStepDetails) {
|
||||
selected = 'flow_step'
|
||||
@@ -30,7 +33,7 @@
|
||||
<Tab value="details">Details & Triggers</Tab>
|
||||
{/if}
|
||||
{#if flow_json}
|
||||
<Tab value="flow_json">JSON</Tab>
|
||||
<Tab value="raw">Raw</Tab>
|
||||
{/if}
|
||||
{#if hasStepDetails}
|
||||
<Tab value="flow_step">Step</Tab>
|
||||
@@ -81,20 +84,37 @@
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</TabContent>
|
||||
<TabContent value="flow_json" class="flex flex-col flex-1 h-full overflow-auto">
|
||||
<div class="relative pt-2">
|
||||
<Button
|
||||
on:click={() => copyToClipboard(JSON.stringify(flow_json, null, 4))}
|
||||
color="light"
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight language={json} code={JSON.stringify(flow_json, null, 4)} />
|
||||
</div>
|
||||
<TabContent value="raw" class="flex flex-col flex-1 h-full overflow-auto">
|
||||
<Tabs bind:selected={rawType} wrapperClass="mt-4">
|
||||
<Tab value="yaml">YAML</Tab>
|
||||
<Tab value="json">JSON</Tab>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="relative pt-2">
|
||||
<Button
|
||||
on:click={() =>
|
||||
copyToClipboard(
|
||||
rawType === 'yaml'
|
||||
? YAML.stringify(flow_json)
|
||||
: JSON.stringify(flow_json, null, 4)
|
||||
)}
|
||||
color="light"
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: Clipboard }}
|
||||
btnClasses="absolute top-2 right-2 w-min"
|
||||
>
|
||||
Copy content
|
||||
</Button>
|
||||
<Highlight
|
||||
class="overflow-auto px-1"
|
||||
language={rawType === 'yaml' ? yaml : json}
|
||||
code={rawType === 'yaml'
|
||||
? YAML.stringify(flow_json)
|
||||
: JSON.stringify(flow_json, null, 4)}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
</TabContent>
|
||||
<TabContent value="flow_step" class="flex flex-col flex-1 h-full">
|
||||
<slot name="flow_step" />
|
||||
|
||||
@@ -11,17 +11,20 @@
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import Path from '../Path.svelte'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
import YAML from 'yaml'
|
||||
|
||||
let drawer: Drawer | undefined = undefined
|
||||
let rawAppDrawer: Drawer | undefined = undefined
|
||||
let pendingJson: string = ''
|
||||
let pendingRaw: string = ''
|
||||
let pendingCode: string = ''
|
||||
let summary: string = ''
|
||||
let path: string = ''
|
||||
let pathError: string = ''
|
||||
|
||||
async function importJson() {
|
||||
$importStore = JSON.parse(pendingJson)
|
||||
let importType: 'yaml' | 'json' = 'yaml'
|
||||
|
||||
async function importRaw() {
|
||||
$importStore = importType === 'yaml' ? YAML.parse(pendingRaw) : JSON.parse(pendingRaw)
|
||||
await goto('/apps/add?nodraft=true')
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
@@ -48,9 +51,19 @@
|
||||
startIcon={{ icon: Plus }}
|
||||
href="/apps/add?nodraft=true"
|
||||
dropdownItems={[
|
||||
{
|
||||
label: 'Import low-code app from YAML',
|
||||
onClick: () => {
|
||||
drawer?.toggleDrawer?.()
|
||||
importType = 'yaml'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Import low-code app from JSON',
|
||||
onClick: () => drawer?.toggleDrawer?.()
|
||||
onClick: () => {
|
||||
drawer?.toggleDrawer?.()
|
||||
importType = 'json'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Import app in React/Vue/Svelte',
|
||||
@@ -66,10 +79,18 @@
|
||||
|
||||
<!-- Raw JSON -->
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
<DrawerContent title="Import low-code app from JSON" on:close={() => drawer?.toggleDrawer?.()}>
|
||||
<SimpleEditor bind:code={pendingJson} lang="json" class="h-full" fixedOverflowWidgets={false} />
|
||||
<DrawerContent
|
||||
title={'Import low-code app from ' + (importType === 'yaml' ? 'YAML' : 'JSON')}
|
||||
on:close={() => drawer?.toggleDrawer?.()}
|
||||
>
|
||||
<SimpleEditor
|
||||
bind:code={pendingRaw}
|
||||
lang={importType}
|
||||
class="h-full"
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button size="sm" on:click={importJson}>Import</Button>
|
||||
<Button size="sm" on:click={importRaw}>Import</Button>
|
||||
</svelte:fragment>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { importFlowStore } from '$lib/components/flows/flowStore'
|
||||
import { Plus } from 'lucide-svelte'
|
||||
import YAML from 'yaml'
|
||||
|
||||
let drawer: Drawer | undefined = undefined
|
||||
let pendingJson: string
|
||||
let pendingRaw: string
|
||||
let importType: 'yaml' | 'json' = 'yaml'
|
||||
|
||||
async function importJson() {
|
||||
$importFlowStore = JSON.parse(pendingJson)
|
||||
async function importRaw() {
|
||||
$importFlowStore = importType === 'yaml' ? YAML.parse(pendingRaw) : JSON.parse(pendingRaw)
|
||||
await goto('/flows/add')
|
||||
drawer?.closeDrawer?.()
|
||||
}
|
||||
@@ -28,9 +30,19 @@
|
||||
endIcon={{ icon: BarsStaggered }}
|
||||
href="/flows/add?nodraft=true"
|
||||
dropdownItems={[
|
||||
{
|
||||
label: 'Import from YAML',
|
||||
onClick: () => {
|
||||
drawer?.toggleDrawer?.()
|
||||
importType = 'yaml'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Import from JSON',
|
||||
onClick: () => drawer?.toggleDrawer?.()
|
||||
onClick: () => {
|
||||
drawer?.toggleDrawer?.()
|
||||
importType = 'json'
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
@@ -40,10 +52,18 @@
|
||||
|
||||
<!-- Raw JSON -->
|
||||
<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} />
|
||||
<DrawerContent
|
||||
title={'Import flow from ' + (importType === 'yaml' ? 'YAML' : 'JSON')}
|
||||
on:close={() => drawer?.toggleDrawer?.()}
|
||||
>
|
||||
<SimpleEditor
|
||||
bind:code={pendingRaw}
|
||||
lang={importType}
|
||||
class="h-full"
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button size="sm" on:click={importJson}>Import</Button>
|
||||
<Button size="sm" on:click={importRaw}>Import</Button>
|
||||
</svelte:fragment>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
on:click={() => jsonViewerDrawer.toggleDrawer()}
|
||||
startIcon={{ icon: FileJson }}
|
||||
>
|
||||
JSON
|
||||
Export
|
||||
</Button>
|
||||
|
||||
<Drawer bind:this={jsonViewerDrawer} size="800px">
|
||||
<DrawerContent title="OpenFlow JSON" on:close={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
<DrawerContent title="OpenFlow" on:close={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
{#if $flowStore}
|
||||
<FlowViewer flow={cleanInputs($flowStore)} tab="json" />
|
||||
<FlowViewer flow={cleanInputs($flowStore)} tab="raw" />
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// import { get } from 'svelte/store'
|
||||
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import YAML from 'yaml'
|
||||
import type { UserExt } from './stores'
|
||||
import { sendUserToast } from './toast'
|
||||
import type { Script } from './gen'
|
||||
@@ -692,3 +693,9 @@ export function orderedJsonStringify(obj: any, space?: string | number) {
|
||||
JSON.stringify(obj, (key, value) => (allKeys.add(key), value))
|
||||
return JSON.stringify(obj, (Array.from(allKeys) as string[]).sort(), space)
|
||||
}
|
||||
|
||||
export function orderedYamlStringify(obj: any) {
|
||||
const allKeys = new Set()
|
||||
YAML.stringify(obj, (key, value) => (allKeys.add(key), value))
|
||||
return YAML.stringify(obj, (Array.from(allKeys) as string[]).sort())
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
const templatePath = $page.url.searchParams.get('template')
|
||||
const templateId = $page.url.searchParams.get('template_id')
|
||||
|
||||
const importJson = $importStore
|
||||
const importRaw = $importStore
|
||||
if ($importStore) {
|
||||
$importStore = undefined
|
||||
}
|
||||
@@ -50,14 +50,14 @@
|
||||
loadApp()
|
||||
|
||||
async function loadApp() {
|
||||
if (importJson) {
|
||||
sendUserToast('Loaded from JSON')
|
||||
if ('value' in importJson) {
|
||||
summary = importJson.summary
|
||||
value = importJson.value
|
||||
policy = importJson.policy
|
||||
if (importRaw) {
|
||||
sendUserToast('Loaded from YAML/JSON')
|
||||
if ('value' in importRaw) {
|
||||
summary = importRaw.summary
|
||||
value = importRaw.value
|
||||
policy = importRaw.policy
|
||||
} else {
|
||||
value = importJson
|
||||
value = importRaw
|
||||
}
|
||||
} else if (templatePath) {
|
||||
const template = await AppService.getAppByPath({
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
if ($importFlowStore) {
|
||||
flow = $importFlowStore
|
||||
$importFlowStore = undefined
|
||||
sendUserToast('Flow loaded from JSON')
|
||||
sendUserToast('Flow loaded from YAML/JSON')
|
||||
} else if (!templatePath && !hubId && state) {
|
||||
sendUserToast('Flow restored from draft', false, [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user