Compare commits
6 Commits
preprocess
...
tutorials
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b76426ba83 | ||
|
|
1cb2a177c6 | ||
|
|
db87577a1b | ||
|
|
c219eb0ee9 | ||
|
|
bfe5b56c99 | ||
|
|
4453690521 |
42
frontend/package-lock.json
generated
42
frontend/package-lock.json
generated
@@ -64,6 +64,7 @@
|
|||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"prettier": "^2.8.3",
|
"prettier": "^2.8.3",
|
||||||
"prettier-plugin-svelte": "^2.9.0",
|
"prettier-plugin-svelte": "^2.9.0",
|
||||||
|
"shepherd.js": "^10.0.1",
|
||||||
"simple-svelte-autocomplete": "^2.5.1",
|
"simple-svelte-autocomplete": "^2.5.1",
|
||||||
"stylelint-config-recommended": "^9.0.0",
|
"stylelint-config-recommended": "^9.0.0",
|
||||||
"svelte": "^3.55.1",
|
"svelte": "^3.55.1",
|
||||||
@@ -6653,6 +6654,24 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/shepherd.js": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/shepherd.js/-/shepherd.js-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-R32v4b4b0N1gK/vxvRtdhty+SBZlBPcPTSYCwcaAQvFd0n6Xki7cRH6Sx0oD9WB/HCkqCNM1msyIyylXmq635w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.11.5",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"smoothscroll-polyfill": "^0.4.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "12.* || 14.* || >= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/rwwagner90"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
@@ -6739,6 +6758,12 @@
|
|||||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/smoothscroll-polyfill": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/sorcery": {
|
"node_modules/sorcery": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
||||||
@@ -12989,6 +13014,17 @@
|
|||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"shepherd.js": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/shepherd.js/-/shepherd.js-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-R32v4b4b0N1gK/vxvRtdhty+SBZlBPcPTSYCwcaAQvFd0n6Xki7cRH6Sx0oD9WB/HCkqCNM1msyIyylXmq635w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@popperjs/core": "^2.11.5",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"smoothscroll-polyfill": "^0.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"signal-exit": {
|
"signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
@@ -13049,6 +13085,12 @@
|
|||||||
"is-fullwidth-code-point": "^3.0.0"
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"smoothscroll-polyfill": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"sorcery": {
|
"sorcery": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"prettier": "^2.8.3",
|
"prettier": "^2.8.3",
|
||||||
"prettier-plugin-svelte": "^2.9.0",
|
"prettier-plugin-svelte": "^2.9.0",
|
||||||
|
"shepherd.js": "^10.0.1",
|
||||||
"simple-svelte-autocomplete": "^2.5.1",
|
"simple-svelte-autocomplete": "^2.5.1",
|
||||||
"stylelint-config-recommended": "^9.0.0",
|
"stylelint-config-recommended": "^9.0.0",
|
||||||
"svelte": "^3.55.1",
|
"svelte": "^3.55.1",
|
||||||
|
|||||||
@@ -232,8 +232,14 @@
|
|||||||
<Badge color={validCode ? 'green' : 'red'} class="min-w-[60px] mr-3">
|
<Badge color={validCode ? 'green' : 'red'} class="min-w-[60px] mr-3">
|
||||||
{validCode ? 'Valid' : 'Invalid'}
|
{validCode ? 'Valid' : 'Invalid'}
|
||||||
</Badge>
|
</Badge>
|
||||||
<div class="flex items-center divide-x">
|
<div id="script-tutorial-3" class="flex items-center divide-x">
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="pr-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="pr-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
color="light"
|
color="light"
|
||||||
btnClasses="!font-medium !h-full"
|
btnClasses="!font-medium !h-full"
|
||||||
@@ -245,11 +251,15 @@
|
|||||||
>
|
>
|
||||||
+Context Var
|
+Context Var
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Add context variable</svelte:fragment>
|
||||||
Add context variable
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
color="light"
|
color="light"
|
||||||
btnClasses="!font-medium !h-full"
|
btnClasses="!font-medium !h-full"
|
||||||
@@ -261,11 +271,15 @@
|
|||||||
>
|
>
|
||||||
+Variable
|
+Variable
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Add variable</svelte:fragment>
|
||||||
Add variable
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
btnClasses="!font-medium !h-full"
|
btnClasses="!font-medium !h-full"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -277,11 +291,15 @@
|
|||||||
>
|
>
|
||||||
+Resource
|
+Resource
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Add resource</svelte:fragment>
|
||||||
Add resource
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
btnClasses="!font-medium !h-full"
|
btnClasses="!font-medium !h-full"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -293,11 +311,15 @@
|
|||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Reset</svelte:fragment>
|
||||||
Reset
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
btnClasses="!font-medium !h-full"
|
btnClasses="!font-medium !h-full"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -320,11 +342,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Reload assistant</svelte:fragment>
|
||||||
Reload assistant
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
btnClasses="!font-medium"
|
btnClasses="!font-medium"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -342,7 +368,13 @@
|
|||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Popover notClickable placement="bottom" disapperTimoout={0} class="px-1" disablePopup={!iconOnly}>
|
<Popover
|
||||||
|
notClickable
|
||||||
|
placement="bottom"
|
||||||
|
disapperTimoout={0}
|
||||||
|
class="px-1"
|
||||||
|
disablePopup={!iconOnly}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
btnClasses="!font-medium"
|
btnClasses="!font-medium"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -354,9 +386,7 @@
|
|||||||
>
|
>
|
||||||
Script
|
Script
|
||||||
</Button>
|
</Button>
|
||||||
<svelte:fragment slot="text">
|
<svelte:fragment slot="text">Script</svelte:fragment>
|
||||||
Script
|
|
||||||
</svelte:fragment>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
|
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
|
||||||
import type { FlowEditorContext } from './flows/types'
|
import type { FlowEditorContext } from './flows/types'
|
||||||
import { cleanInputs } from './flows/utils'
|
import { cleanInputs } from './flows/utils'
|
||||||
|
import { Tour } from './tutorial'
|
||||||
|
|
||||||
export let initialPath: string = ''
|
export let initialPath: string = ''
|
||||||
export let selectedId: string | undefined
|
export let selectedId: string | undefined
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
export let loading = false
|
export let loading = false
|
||||||
export let flowStore: Writable<Flow>
|
export let flowStore: Writable<Flow>
|
||||||
export let flowStateStore: Writable<FlowState>
|
export let flowStateStore: Writable<FlowState>
|
||||||
|
export let tour = false
|
||||||
|
|
||||||
async function createSchedule(path: string) {
|
async function createSchedule(path: string) {
|
||||||
const { cron, args, enabled } = $scheduleStore
|
const { cron, args, enabled } = $scheduleStore
|
||||||
@@ -246,6 +248,10 @@
|
|||||||
|
|
||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
|
||||||
|
{#if tour}
|
||||||
|
<Tour tutorial="flow" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if !$userStore?.operator}
|
{#if !$userStore?.operator}
|
||||||
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
|
<ScriptEditorDrawer bind:this={$scriptEditorDrawer} />
|
||||||
|
|
||||||
|
|||||||
@@ -346,6 +346,7 @@
|
|||||||
lang={script.language}
|
lang={script.language}
|
||||||
{initialArgs}
|
{initialArgs}
|
||||||
{kind}
|
{kind}
|
||||||
|
tour
|
||||||
/>
|
/>
|
||||||
{:else if step === 3}
|
{:else if step === 3}
|
||||||
<CenteredPage>
|
<CenteredPage>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
import { Button, Kbd } from './common'
|
import { Button, Kbd } from './common'
|
||||||
import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte'
|
import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte'
|
||||||
import WindmillIcon from './icons/WindmillIcon.svelte'
|
import WindmillIcon from './icons/WindmillIcon.svelte'
|
||||||
|
import { Tour } from './tutorial'
|
||||||
|
|
||||||
// Exported
|
// Exported
|
||||||
export let schema: Schema = emptySchema()
|
export let schema: Schema = emptySchema()
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
export let initialArgs: Record<string, any> = {}
|
export let initialArgs: Record<string, any> = {}
|
||||||
export let fixedOverflowWidgets = true
|
export let fixedOverflowWidgets = true
|
||||||
export let noSyncFromGithub = false
|
export let noSyncFromGithub = false
|
||||||
|
export let tour = false
|
||||||
|
|
||||||
let websocketAlive = { pyright: false, black: false, deno: false, go: false }
|
let websocketAlive = { pyright: false, black: false, deno: false, go: false }
|
||||||
|
|
||||||
@@ -114,6 +116,10 @@
|
|||||||
|
|
||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
|
||||||
|
{#if tour}
|
||||||
|
<Tour tutorial="script" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="border-b-2 shadow-sm px-1 pr-4" bind:clientWidth={width}>
|
<div class="border-b-2 shadow-sm px-1 pr-4" bind:clientWidth={width}>
|
||||||
<div class="flex justify-between space-x-2">
|
<div class="flex justify-between space-x-2">
|
||||||
<EditorBar
|
<EditorBar
|
||||||
@@ -146,6 +152,7 @@
|
|||||||
<Splitpanes class="!overflow-visible">
|
<Splitpanes class="!overflow-visible">
|
||||||
<Pane size={60} minSize={10} class="!overflow-visible">
|
<Pane size={60} minSize={10} class="!overflow-visible">
|
||||||
<div
|
<div
|
||||||
|
id="script-tutorial-1"
|
||||||
class="pl-2 h-full !overflow-visible"
|
class="pl-2 h-full !overflow-visible"
|
||||||
on:mouseleave={() => {
|
on:mouseleave={() => {
|
||||||
inferSchema(code)
|
inferSchema(code)
|
||||||
@@ -180,7 +187,7 @@
|
|||||||
</Pane>
|
</Pane>
|
||||||
<Pane size={40} minSize={10}>
|
<Pane size={40} minSize={10}>
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<div class="px-2 w-full border-b py-1">
|
<div id="script-tutorial-4" class="px-2 w-full border-b py-1">
|
||||||
{#if testIsLoading}
|
{#if testIsLoading}
|
||||||
<Button on:click={testJobLoader?.cancelJob} btnClasses="w-full" color="red" size="xs">
|
<Button on:click={testJobLoader?.cancelJob} btnClasses="w-full" color="red" size="xs">
|
||||||
<WindmillIcon
|
<WindmillIcon
|
||||||
@@ -211,7 +218,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Splitpanes horizontal class="!max-h-[calc(100%-43px)]">
|
<Splitpanes horizontal class="!max-h-[calc(100%-43px)]">
|
||||||
<Pane size={33}>
|
<Pane size={33}>
|
||||||
<div class="px-2">
|
<div id="script-tutorial-2" class="px-2">
|
||||||
<div class="break-words relative font-sans">
|
<div class="break-words relative font-sans">
|
||||||
<SchemaForm compact {schema} bind:args bind:isValid />
|
<SchemaForm compact {schema} bind:args bind:isValid />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="{$$props.class} z-auto">
|
<span class="{$$props.class ?? ''} z-auto">
|
||||||
<label
|
<label
|
||||||
for={id}
|
for={id}
|
||||||
class="inline-flex items-center mt-2 duration-200 {disabled
|
class="inline-flex items-center mt-2 duration-200 {disabled
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
import { page } from '$app/stores'
|
import { page } from '$app/stores'
|
||||||
import CssSettings from './componentsPanel/CssSettings.svelte'
|
import CssSettings from './componentsPanel/CssSettings.svelte'
|
||||||
import { initHistory } from '$lib/history'
|
import { initHistory } from '$lib/history'
|
||||||
|
import { Tour } from '../../tutorial'
|
||||||
import ComponentNavigation from './component/ComponentNavigation.svelte'
|
import ComponentNavigation from './component/ComponentNavigation.svelte'
|
||||||
import ItemPicker from '$lib/components/ItemPicker.svelte'
|
import ItemPicker from '$lib/components/ItemPicker.svelte'
|
||||||
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
export let policy: Policy
|
export let policy: Policy
|
||||||
export let summary: string
|
export let summary: string
|
||||||
export let fromHub: boolean = false
|
export let fromHub: boolean = false
|
||||||
|
export let tour: boolean = false
|
||||||
|
|
||||||
const appStore = writable<App>(app)
|
const appStore = writable<App>(app)
|
||||||
const worldStore = writable<World | undefined>(undefined)
|
const worldStore = writable<World | undefined>(undefined)
|
||||||
@@ -152,6 +154,10 @@
|
|||||||
|
|
||||||
<svelte:window on:hashchange={hashchange} />
|
<svelte:window on:hashchange={hashchange} />
|
||||||
|
|
||||||
|
{#if tour}
|
||||||
|
<Tour tutorial="app" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if $connectingInput.opened}
|
{#if $connectingInput.opened}
|
||||||
<div
|
<div
|
||||||
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
class="absolute w-full h-screen bg-black border-2 bg-opacity-25 z-20 flex justify-center items-center"
|
||||||
@@ -196,6 +202,7 @@
|
|||||||
$selectedComponent = undefined
|
$selectedComponent = undefined
|
||||||
$focusedGrid = undefined
|
$focusedGrid = undefined
|
||||||
}}
|
}}
|
||||||
|
id="app-tutorial-3"
|
||||||
class={twMerge(
|
class={twMerge(
|
||||||
'bg-gray-100 h-full w-full',
|
'bg-gray-100 h-full w-full',
|
||||||
$appStore.css?.['app']?.['viewer']?.class
|
$appStore.css?.['app']?.['viewer']?.class
|
||||||
@@ -221,7 +228,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
<Pane size={$connectingInput?.opened ? 0 : 30}>
|
<Pane size={$connectingInput?.opened ? 0 : 30}>
|
||||||
<div class="relative h-full w-full">
|
<div id="app-tutorial-4" class="relative h-full w-full">
|
||||||
<InlineScriptsPanel />
|
<InlineScriptsPanel />
|
||||||
</div>
|
</div>
|
||||||
</Pane>
|
</Pane>
|
||||||
|
|||||||
@@ -65,7 +65,11 @@
|
|||||||
No components found
|
No components found
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div in:fade|local={{ duration: 50, delay: 50 }} out:fade|local={{ duration: 50 }}>
|
<div
|
||||||
|
id="app-tutorial-1"
|
||||||
|
in:fade|local={{ duration: 50, delay: 50 }}
|
||||||
|
out:fade|local={{ duration: 50 }}
|
||||||
|
>
|
||||||
{#each componentsFiltered as { title, components }, index (index)}
|
{#each componentsFiltered as { title, components }, index (index)}
|
||||||
{#if components.length}
|
{#if components.length}
|
||||||
<div transition:slide|local={{ duration: 300 }}>
|
<div transition:slide|local={{ duration: 300 }}>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative p-2">
|
<div id="app-tutorial-2" class="relative p-2">
|
||||||
{#each filteredPanels as [componentId, outputs] (componentId)}
|
{#each filteredPanels as [componentId, outputs] (componentId)}
|
||||||
<div
|
<div
|
||||||
animate:flip={{ duration: 300 }}
|
animate:flip={{ duration: 300 }}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
}[kind]
|
}[kind]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<li
|
||||||
class="hover:bg-gray-50 w-full inline-flex items-center p-4 gap-4 first-of-type:!border-t-0
|
class="hover:bg-gray-50 w-full inline-flex items-center p-4 gap-4 first-of-type:!border-t-0
|
||||||
first-of-type:rounded-t-md last-of-type:rounded-b-md {color}"
|
first-of-type:rounded-t-md last-of-type:rounded-b-md {color}"
|
||||||
>
|
>
|
||||||
@@ -61,4 +61,4 @@ first-of-type:rounded-t-md last-of-type:rounded-b-md {color}"
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</li>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
export { c as class }
|
export { c as class }
|
||||||
export let wrapperClass = ''
|
export let wrapperClass = ''
|
||||||
export let style = ''
|
export let style = ''
|
||||||
|
export let id = ''
|
||||||
|
|
||||||
$: selected && updateSelected()
|
$: selected && updateSelected()
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
<div
|
<div
|
||||||
class={twMerge('border-b border-gray-200 flex flex-row whitespace-nowrap scrollbar-hidden', c)}
|
class={twMerge('border-b border-gray-200 flex flex-row whitespace-nowrap scrollbar-hidden', c)}
|
||||||
{style}
|
{style}
|
||||||
|
{id}
|
||||||
>
|
>
|
||||||
<slot {selected} />
|
<slot {selected} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<div class="h-full overflow-hidden">
|
<div class="h-full overflow-hidden">
|
||||||
<FlowCard title="Settings">
|
<FlowCard title="Settings">
|
||||||
<div class="h-full flex-1">
|
<div class="h-full flex-1">
|
||||||
<Tabs bind:selected={$selectedId}>
|
<Tabs bind:selected={$selectedId} id="flow-tutorial-3">
|
||||||
<Tab value="settings-metadata">Metadata</Tab>
|
<Tab value="settings-metadata">Metadata</Tab>
|
||||||
<Tab value="settings-schedule">Schedule</Tab>
|
<Tab value="settings-schedule">Schedule</Tab>
|
||||||
<Tab value="settings-same-worker">Shared Directory</Tab>
|
<Tab value="settings-same-worker">Shared Directory</Tab>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
let is_owner = false
|
let is_owner = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-row-reverse justify-between items-center gap-x-2">
|
<div id="flow-tutorial-4" class="flex flex-row-reverse justify-between items-center gap-x-2">
|
||||||
<Button
|
<Button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
previewMode = 'whole'
|
previewMode = 'whole'
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
<FlowConstantsItem />
|
<FlowConstantsItem />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-auto grow" bind:clientHeight={minHeight}>
|
<div id="flow-tutorial-1" class="flex-auto grow" bind:clientHeight={minHeight}>
|
||||||
<FlowGraph
|
<FlowGraph
|
||||||
insertable
|
insertable
|
||||||
scroll
|
scroll
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
|
|
||||||
{#if insertable && modules && (label != 'Input' || modules.length == 0)}
|
{#if insertable && modules && (label != 'Input' || modules.length == 0)}
|
||||||
<div
|
<div
|
||||||
|
id="flow-tutorial-2"
|
||||||
class="{openMenu ? 'z-10' : ''} w-7 absolute {whereInsert == 'after'
|
class="{openMenu ? 'z-10' : ''} w-7 absolute {whereInsert == 'after'
|
||||||
? 'top-12'
|
? 'top-12'
|
||||||
: '-top-10'} left-[50%] right-[50%] -translate-x-1/2"
|
: '-top-10'} left-[50%] right-[50%] -translate-x-1/2"
|
||||||
|
|||||||
@@ -245,69 +245,74 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<CenteredPage>
|
<CenteredPage>
|
||||||
<div class="flex flex-wrap gap-2 items-center justify-between w-full">
|
<div id="welcome-tutorial-1">
|
||||||
<div class="flex justify-start">
|
<div class="flex flex-wrap gap-2 items-center justify-between w-full">
|
||||||
<ToggleButtonGroup bind:selected={itemKind}>
|
<div class="flex justify-start">
|
||||||
<ToggleButton light position="left" value="all" size="sm">All</ToggleButton>
|
<ToggleButtonGroup bind:selected={itemKind}>
|
||||||
<ToggleButton light position="center" value="script" size="sm">
|
<ToggleButton light position="left" value="all" size="sm">All</ToggleButton>
|
||||||
<div class="flex gap-1 items-center">
|
<ToggleButton light position="center" value="script" size="sm">
|
||||||
<Code2 size={16} />
|
<div class="flex gap-1 items-center">
|
||||||
Scripts
|
<Code2 size={16} />
|
||||||
</div>
|
Scripts
|
||||||
</ToggleButton>
|
</div>
|
||||||
<ToggleButton light position="center" value="flow" size="sm">
|
</ToggleButton>
|
||||||
<div class="flex gap-1 items-center">
|
<ToggleButton light position="center" value="flow" size="sm">
|
||||||
<Icon data={faBarsStaggered} scale={0.8} class="mr-1" />
|
<div class="flex gap-1 items-center">
|
||||||
Flows
|
<Icon data={faBarsStaggered} scale={0.8} class="mr-1" />
|
||||||
</div>
|
Flows
|
||||||
</ToggleButton>
|
</div>
|
||||||
<ToggleButton light position="right" value="app" size="sm">
|
</ToggleButton>
|
||||||
<div class="flex gap-1 items-center">
|
<ToggleButton light position="right" value="app" size="sm">
|
||||||
<LayoutDashboard size={16} />
|
<div class="flex gap-1 items-center">
|
||||||
Apps
|
<LayoutDashboard size={16} />
|
||||||
</div>
|
Apps
|
||||||
</ToggleButton>
|
</div>
|
||||||
</ToggleButtonGroup>
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
</div>
|
||||||
|
<div class="relative text-gray-600 grow min-w-[100px]">
|
||||||
|
<!-- svelte-ignore a11y-autofocus -->
|
||||||
|
<input
|
||||||
|
autofocus
|
||||||
|
placeholder="Search Scripts, Flows & Apps"
|
||||||
|
bind:value={filter}
|
||||||
|
class="bg-white !h-10 !px-4 !pr-10 !rounded-lg text-sm focus:outline-none"
|
||||||
|
/>
|
||||||
|
<button type="submit" class="absolute right-0 top-0 mt-3 mr-4">
|
||||||
|
<svg
|
||||||
|
class="h-4 w-4 fill-current"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 56.966 56.966"
|
||||||
|
style="enable-background:new 0 0 56.966 56.966;"
|
||||||
|
xml:space="preserve"
|
||||||
|
width="512px"
|
||||||
|
height="512px"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M55.146,51.887L41.588,37.786c3.486-4.144,5.396-9.358,5.396-14.786c0-12.682-10.318-23-23-23s-23,10.318-23,23 s10.318,23,23,23c4.761,0,9.298-1.436,13.177-4.162l13.661,14.208c0.571,0.593,1.339,0.92,2.162,0.92 c0.779,0,1.518-0.297,2.079-0.837C56.255,54.982,56.293,53.08,55.146,51.887z M23.984,6c9.374,0,17,7.626,17,17s-7.626,17-17,17 s-17-7.626-17-17S14.61,6,23.984,6z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="relative flex justify-between items-start">
|
||||||
<div class="relative text-gray-600 grow min-w-[100px]">
|
<ListFilters bind:selectedFilter={ownerFilter} filters={owners} />
|
||||||
<!-- svelte-ignore a11y-autofocus -->
|
{#if !loading}
|
||||||
<input
|
<Toggle
|
||||||
autofocus
|
size="xs"
|
||||||
placeholder="Search Scripts, Flows & Apps"
|
bind:checked={archived}
|
||||||
bind:value={filter}
|
options={{ right: 'Show archived' }}
|
||||||
class="bg-white !h-10 !px-4 !pr-10 !rounded-lg text-sm focus:outline-none"
|
class="pt-2"
|
||||||
/>
|
textClass="whitespace-nowrap"
|
||||||
<button type="submit" class="absolute right-0 top-0 mt-3 mr-4">
|
/>
|
||||||
<svg
|
{/if}
|
||||||
class="h-4 w-4 fill-current"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
version="1.1"
|
|
||||||
id="Capa_1"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 56.966 56.966"
|
|
||||||
style="enable-background:new 0 0 56.966 56.966;"
|
|
||||||
xml:space="preserve"
|
|
||||||
width="512px"
|
|
||||||
height="512px"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M55.146,51.887L41.588,37.786c3.486-4.144,5.396-9.358,5.396-14.786c0-12.682-10.318-23-23-23s-23,10.318-23,23 s10.318,23,23,23c4.761,0,9.298-1.436,13.177-4.162l13.661,14.208c0.571,0.593,1.339,0.92,2.162,0.92 c0.779,0,1.518-0.297,2.079-0.837C56.255,54.982,56.293,53.08,55.146,51.887z M23.984,6c9.374,0,17,7.626,17,17s-7.626,17-17,17 s-17-7.626-17-17S14.61,6,23.984,6z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
|
||||||
<ListFilters bind:selectedFilter={ownerFilter} filters={owners} />
|
|
||||||
{#if !loading}
|
|
||||||
<div class="absolute -bottom-2 right-0 bg-white/90">
|
|
||||||
<Toggle size="xs" bind:checked={archived} options={{ right: 'Show archived' }} /></div
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
{#if filteredItems == undefined}
|
{#if filteredItems == undefined}
|
||||||
<div class="mt-4" />
|
<div class="mt-4" />
|
||||||
@@ -318,7 +323,7 @@
|
|||||||
{:else if filteredItems.length === 0}
|
{:else if filteredItems.length === 0}
|
||||||
<NoItemFound />
|
<NoItemFound />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="border rounded-md divide-y divide-gray-200">
|
<ul class="border rounded-md divide-y divide-gray-200">
|
||||||
<!-- <VirtualList {items} let:item bind:start bind:end> -->
|
<!-- <VirtualList {items} let:item bind:start bind:end> -->
|
||||||
{#each (items ?? []).slice(0, nbDisplayed) as item, i (item.type + '/' + item.path + (item.summary ?? ''))}
|
{#each (items ?? []).slice(0, nbDisplayed) as item, i (item.type + '/' + item.path + (item.summary ?? ''))}
|
||||||
{#if item.type == 'script'}
|
{#if item.type == 'script'}
|
||||||
@@ -354,7 +359,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<!-- </VirtualList> -->
|
<!-- </VirtualList> -->
|
||||||
</div>
|
</ul>
|
||||||
{#if items && items?.length > 30}
|
{#if items && items?.length > 30}
|
||||||
<span class="text-xs"
|
<span class="text-xs"
|
||||||
>{nbDisplayed} items out of {items.length}
|
>{nbDisplayed} items out of {items.length}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
export let icon: IconDefinition
|
export let icon: IconDefinition
|
||||||
export let isCollapsed: boolean
|
export let isCollapsed: boolean
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
|
export let id: string = ''
|
||||||
|
|
||||||
let isSelected = false
|
let isSelected = false
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
$$props.class
|
$$props.class
|
||||||
)}
|
)}
|
||||||
target={href.includes('http') ? '_blank' : null}
|
target={href.includes('http') ? '_blank' : null}
|
||||||
|
{id}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
data={icon}
|
data={icon}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
const mainMenuLinks = [
|
const mainMenuLinks = [
|
||||||
{ label: 'Home', href: '/', icon: faHomeAlt },
|
{ label: 'Home', href: '/', icon: faHomeAlt },
|
||||||
{ label: 'Runs', href: '/runs', icon: faPlay },
|
{ label: 'Runs', href: '/runs', icon: faPlay, id: 'welcome-tutorial-4' },
|
||||||
{ label: 'Variables', href: '/variables', icon: faDollarSign, disabled: $userStore?.operator },
|
{ label: 'Variables', href: '/variables', icon: faDollarSign, disabled: $userStore?.operator },
|
||||||
{ label: 'Resources', href: '/resources', icon: faCubes, disabled: $userStore?.operator }
|
{ label: 'Resources', href: '/resources', icon: faCubes, disabled: $userStore?.operator }
|
||||||
]
|
]
|
||||||
|
|||||||
143
frontend/src/lib/components/tutorial/Tour.svelte
Normal file
143
frontend/src/lib/components/tutorial/Tour.svelte
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Shepherd from 'shepherd.js'
|
||||||
|
import { steps, tourStore, type TutorialName } from './'
|
||||||
|
|
||||||
|
export let tutorial: TutorialName
|
||||||
|
/**
|
||||||
|
* If the index of a step is added to a tour, the custom footer will not be added
|
||||||
|
* (ie. the steps won't be shown and the buttons won't be positioned)
|
||||||
|
*/
|
||||||
|
const noPageNumber: Partial<Record<TutorialName, number[]>> = {
|
||||||
|
welcome: [0]
|
||||||
|
}
|
||||||
|
let tour: Shepherd.Tour | undefined
|
||||||
|
|
||||||
|
$: $tourStore.includes(tutorial) && initiate()
|
||||||
|
|
||||||
|
function initiate() {
|
||||||
|
if (!tourStore.isTourActive(tutorial)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tour?.cancel()
|
||||||
|
tour = new Shepherd.Tour({
|
||||||
|
tourName: tutorial,
|
||||||
|
useModalOverlay: true,
|
||||||
|
defaultStepOptions: {
|
||||||
|
classes: 'max-w-[460px] shadow-xl bg-white border rounded px-6 py-4 z-[1100]',
|
||||||
|
scrollTo: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
tour.addSteps(steps[tutorial](tour))
|
||||||
|
tour.steps.forEach((step, i) => {
|
||||||
|
if (noPageNumber[tutorial]?.includes(i)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
step.on('show', () => {
|
||||||
|
const currentStep = tour?.getCurrentStep()
|
||||||
|
const currentStepElement = currentStep?.getElement()
|
||||||
|
const header = currentStepElement?.querySelector('.shepherd-header')
|
||||||
|
if (currentStep && currentStepElement && header) {
|
||||||
|
const progress = document.createElement('span')
|
||||||
|
progress.className = 'shepherd-progress text-sm text-gray-500 pr-2'
|
||||||
|
progress.innerText = `${(tour?.steps.indexOf(currentStep) ?? 0) + 1}/${
|
||||||
|
tour?.steps.length
|
||||||
|
}`
|
||||||
|
header.prepend(progress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
;['complete', 'cancel'].forEach((event) => {
|
||||||
|
tour?.on(event, () => {
|
||||||
|
tourStore.markTourAsDone(tutorial)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
tour.start()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style global>
|
||||||
|
.shepherd-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple primary button */
|
||||||
|
.shepherd-button:not(.shepherd-button-secondary.special) {
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button:not(.shepherd-button-secondary.special):hover,
|
||||||
|
.shepherd-button:not(.shepherd-button-secondary.special):focus {
|
||||||
|
background-color: #eff6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple secondary button */
|
||||||
|
.shepherd-button.shepherd-button-secondary:not(.special) {
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button.shepherd-button-secondary:not(.special):hover,
|
||||||
|
.shepherd-button.shepherd-button-secondary:not(.special):focus {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special primary button */
|
||||||
|
.shepherd-button.special:not(.shepherd-button-secondary) {
|
||||||
|
background-color: #3b82f6;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button.special:not(.shepherd-button-secondary):hover,
|
||||||
|
.shepherd-button.special:not(.shepherd-button-secondary):focus {
|
||||||
|
background-color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special secondary button */
|
||||||
|
.shepherd-button.special.shepherd-button-secondary {
|
||||||
|
background-color: #eff6ff;
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-button.special.shepherd-button-secondary:hover,
|
||||||
|
.shepherd-button.special.shepherd-button-secondary:focus {
|
||||||
|
background-color: #dbeafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-cancel-icon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-cancel-icon:hover,
|
||||||
|
.shepherd-cancel-icon:focus {
|
||||||
|
background-color: #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shepherd-highlighted-element {
|
||||||
|
box-shadow: 0 0 0 6px #93c4fdbb;
|
||||||
|
background-color: #93c4fd3a;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1090;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
113
frontend/src/lib/components/tutorial/app-steps.ts
Normal file
113
frontend/src/lib/components/tutorial/app-steps.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import type Shepherd from 'shepherd.js'
|
||||||
|
|
||||||
|
export default (tour: Shepherd.Tour) => {
|
||||||
|
const steps: object[] | Shepherd.Step[] = [
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
text: `<div class="text-xl font-bold mb-6">Windmill App Editor</div>
|
||||||
|
<p class="mb-6">Build your own UI quick and easy with drag-and-drop components. Connect your scripts and data to them and deploy a fully functioning application.</p>`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'See by myself',
|
||||||
|
action: tour.cancel,
|
||||||
|
secondary: true,
|
||||||
|
classes: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Give me an overview',
|
||||||
|
action: tour.next,
|
||||||
|
classes: 'special'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'components',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Components</div>
|
||||||
|
<p class="mb-2">Click to add and move them around by dragging. Configure the inputs, settings and styling, even with Tailwind classes.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#app-tutorial-1', on: 'left-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'outputs',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Outputs</div>
|
||||||
|
<p class="mb-2">Each component has their outputs, which can be used to easily hook into the app state.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#app-tutorial-2', on: 'right-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'parameters',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Canvas</div>
|
||||||
|
<p class="mb-2">It allows you to position, scale and group the components.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#app-tutorial-3', on: 'bottom' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'test',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Runnable editor</div>
|
||||||
|
<p class="mb-2">Here you can create, edit and manage the scripts of your app.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#app-tutorial-4', on: 'top-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Finish',
|
||||||
|
action: tour.complete
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return steps
|
||||||
|
}
|
||||||
113
frontend/src/lib/components/tutorial/flow-steps.ts
Normal file
113
frontend/src/lib/components/tutorial/flow-steps.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import type Shepherd from 'shepherd.js'
|
||||||
|
|
||||||
|
export default (tour: Shepherd.Tour) => {
|
||||||
|
const steps: object[] | Shepherd.Step[] = [
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
text: `<div class="text-xl font-bold mb-6">Windmill Flow Editor</div>
|
||||||
|
<p class="mb-6">Build complex workflows from your scripts, configure them and get a clear visualization.</p>`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'See by myself',
|
||||||
|
action: tour.cancel,
|
||||||
|
secondary: true,
|
||||||
|
classes: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Give me an overview',
|
||||||
|
action: tour.next,
|
||||||
|
classes: 'special'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'graph',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Flow graph</div>
|
||||||
|
<p class="mb-2">Know how your flow is structured at a glimpse.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#flow-tutorial-1', on: 'right-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'modules',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Insert modules</div>
|
||||||
|
<p class="mb-2">Start building by adding modules. A module can be a simple script, a loop or a branching for example.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#flow-tutorial-2 button', on: 'right-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'settings',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Settings</div>
|
||||||
|
<p class="mb-2">Configure schedules, authorizations, sharing parameters and many more.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#flow-tutorial-3', on: 'bottom-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'test',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Test flow</div>
|
||||||
|
<p class="mb-2">Time to try your work! Test the flow and iterate as much as you want.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#flow-tutorial-4', on: 'left-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Finish',
|
||||||
|
action: tour.complete
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return steps
|
||||||
|
}
|
||||||
51
frontend/src/lib/components/tutorial/index.ts
Normal file
51
frontend/src/lib/components/tutorial/index.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { browser } from '$app/environment'
|
||||||
|
import type Shepherd from 'shepherd.js'
|
||||||
|
import welcome from './welcome-steps'
|
||||||
|
import script from './script-steps'
|
||||||
|
import flow from './flow-steps'
|
||||||
|
import app from './app-steps'
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
|
export { default as Tour } from './Tour.svelte'
|
||||||
|
|
||||||
|
export const TUTORIAL_NAMES = ['welcome', 'script', 'flow', 'app'] as const
|
||||||
|
export type TutorialName = (typeof TUTORIAL_NAMES)[number]
|
||||||
|
|
||||||
|
export const steps: Record<TutorialName, (tour: Shepherd.Tour) => object[] | Shepherd.Step[]> = {
|
||||||
|
welcome,
|
||||||
|
script,
|
||||||
|
flow,
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'tutorials-to-show' as const
|
||||||
|
const store = writable<Readonly<TutorialName[]>>([])
|
||||||
|
export const tourStore = {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
initiateTours: () => {
|
||||||
|
if (!browser) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(TUTORIAL_NAMES))
|
||||||
|
store.set(TUTORIAL_NAMES)
|
||||||
|
},
|
||||||
|
isTourActive: (name: TutorialName) => {
|
||||||
|
if (!browser) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const toursToShow = JSON.parse(
|
||||||
|
window.localStorage.getItem(STORAGE_KEY) || '[]'
|
||||||
|
) as TutorialName[]
|
||||||
|
return toursToShow.includes(name)
|
||||||
|
},
|
||||||
|
markTourAsDone: (name: TutorialName) => {
|
||||||
|
if (!browser) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const toursToShow = (
|
||||||
|
JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]') as TutorialName[]
|
||||||
|
).filter((t) => t !== name)
|
||||||
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(toursToShow))
|
||||||
|
store.set(toursToShow)
|
||||||
|
}
|
||||||
|
}
|
||||||
113
frontend/src/lib/components/tutorial/script-steps.ts
Normal file
113
frontend/src/lib/components/tutorial/script-steps.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import type Shepherd from 'shepherd.js'
|
||||||
|
|
||||||
|
export default (tour: Shepherd.Tour) => {
|
||||||
|
const steps: object[] | Shepherd.Step[] = [
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
text: `<div class="text-xl font-bold mb-6">Windmill Script Builder</div>
|
||||||
|
<p class="mb-6">Build scripts with our own web editor, then use them anywhere in your flows, apps or as standalone computations with their auto-generated UI.</p>`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'See by myself',
|
||||||
|
action: tour.cancel,
|
||||||
|
secondary: true,
|
||||||
|
classes: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Give me an overview',
|
||||||
|
action: tour.next,
|
||||||
|
classes: 'special'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'editor',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Web editor</div>
|
||||||
|
<p class="mb-2">Code directly from the web editor.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#script-tutorial-1', on: 'right-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'preview',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">UI preview</div>
|
||||||
|
<p class="mb-2">Preview the user interface generated from the signature of the script.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#script-tutorial-2', on: 'left-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'parameters',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Parameters</div>
|
||||||
|
<p class="mb-2">Add variables, resources and configure your script.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#script-tutorial-3', on: 'bottom-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'test',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Test script</div>
|
||||||
|
<p class="mb-2">Time to try your work! Test the script and iterate as much as you want.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#script-tutorial-4', on: 'left-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Finish',
|
||||||
|
action: tour.complete
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return steps
|
||||||
|
}
|
||||||
114
frontend/src/lib/components/tutorial/welcome-steps.ts
Normal file
114
frontend/src/lib/components/tutorial/welcome-steps.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import type Shepherd from 'shepherd.js'
|
||||||
|
|
||||||
|
export default (tour: Shepherd.Tour) => {
|
||||||
|
const steps: object[] | Shepherd.Step[] = [
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
text: `<div class="text-xl font-bold mb-6">Welcome to Windmill Cloud App!</div>
|
||||||
|
<p class="mb-6">Thank your for your interest in Windmill. Let us guide you through the home screen in strictly less than a minute.</p>`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'See by myself',
|
||||||
|
action: tour.cancel,
|
||||||
|
secondary: true,
|
||||||
|
classes: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Give me an overview',
|
||||||
|
action: tour.next,
|
||||||
|
classes: 'special'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'workspace',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Workspace</div>
|
||||||
|
<p>You can see all the scripts, flows and apps here.</p>
|
||||||
|
<p class="mb-2">Use the search and filters to find just what you are looking for.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#welcome-tutorial-1', on: 'bottom-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'create',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Create</div>
|
||||||
|
<p class="mb-2">Build your own tools in minutes.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#welcome-tutorial-2', on: 'bottom-end' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'inspiration',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Take inspiration</div>
|
||||||
|
<p class="mb-2">Explore templates built by the community and import them directly from <a href="https://hub.windmill.dev" target="_blank">Windmill Hub</a>.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#welcome-tutorial-3', on: 'bottom-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Next',
|
||||||
|
action: tour.next
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'monitor',
|
||||||
|
text: `<div class="text-xl font-bold mb-2">Monitor</div>
|
||||||
|
<p class="mb-2">Keep an eye an all past and scheduled executions of scripts and flows.</p>`,
|
||||||
|
cancelIcon: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
attachTo: { element: '#welcome-tutorial-4-wrapper #welcome-tutorial-4', on: 'right-start' },
|
||||||
|
highlightClass: 'shepherd-highlighted-element',
|
||||||
|
scrollTo: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Back',
|
||||||
|
action: tour.back,
|
||||||
|
secondary: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Finish',
|
||||||
|
action: tour.complete
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return steps
|
||||||
|
}
|
||||||
@@ -202,7 +202,10 @@
|
|||||||
isCollapsed ? 'md:w-12' : 'md:w-40'
|
isCollapsed ? 'md:w-12' : 'md:w-40'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="flex-1 flex flex-col min-h-0 h-screen shadow-lg bg-[#2e3440]">
|
<div
|
||||||
|
id="welcome-tutorial-4-wrapper"
|
||||||
|
class="flex-1 flex flex-col min-h-0 h-screen shadow-lg bg-[#2e3440]"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
goto('/')
|
goto('/')
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
|
import AppPreview from '$lib/components/apps/editor/AppPreview.svelte'
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
import type { EditorBreakpoint } from '$lib/components/apps/types'
|
import type { EditorBreakpoint } from '$lib/components/apps/types'
|
||||||
|
import { Tour, tourStore } from '../../../lib/components/tutorial'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
type Tab = 'hubscripts' | 'hubflows' | 'hubapps' | 'workspace'
|
type Tab = 'hubscripts' | 'hubflows' | 'hubapps' | 'workspace'
|
||||||
|
|
||||||
@@ -60,8 +62,12 @@
|
|||||||
appViewerApp = hub
|
appViewerApp = hub
|
||||||
appViewer.openDrawer?.()
|
appViewer.openDrawer?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(tourStore.initiateTours)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<Tour tutorial="welcome" />
|
||||||
|
|
||||||
<Drawer bind:this={codeViewer} size="900px">
|
<Drawer bind:this={codeViewer} size="900px">
|
||||||
<DrawerContent title={codeViewerObj?.summary ?? ''} on:close={codeViewer.closeDrawer}>
|
<DrawerContent title={codeViewerObj?.summary ?? ''} on:close={codeViewer.closeDrawer}>
|
||||||
<svelte:fragment slot="actions">
|
<svelte:fragment slot="actions">
|
||||||
@@ -181,7 +187,7 @@
|
|||||||
</Alert>
|
</Alert>
|
||||||
{/if}
|
{/if}
|
||||||
<PageHeader title="Home">
|
<PageHeader title="Home">
|
||||||
<div class="flex flex-row gap-4 flex-wrap justify-end items-center">
|
<div id="welcome-tutorial-2" class="flex flex-row gap-4 flex-wrap justify-end items-center">
|
||||||
{#if !$userStore?.operator}
|
{#if !$userStore?.operator}
|
||||||
<span class="text-sm text-gray-500">Create a new:</span>
|
<span class="text-sm text-gray-500">Create a new:</span>
|
||||||
<CreateActionsScript />
|
<CreateActionsScript />
|
||||||
@@ -192,7 +198,7 @@
|
|||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
{#if !$userStore?.operator}
|
{#if !$userStore?.operator}
|
||||||
<div class="w-full overflow-auto scrollbar-hidden">
|
<div id="welcome-tutorial-3" class="w-full">
|
||||||
<Tabs bind:selected={tab}>
|
<Tabs bind:selected={tab}>
|
||||||
<Tab size="md" value="workspace">
|
<Tab size="md" value="workspace">
|
||||||
<div class="flex gap-2 items-center my-1">
|
<div class="flex gap-2 items-center my-1">
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
{#if value}
|
{#if value}
|
||||||
<div class="h-screen">
|
<div class="h-screen">
|
||||||
{#key value}
|
{#key value}
|
||||||
<AppEditor {summary} app={value} path={''} {policy} fromHub={hubId != null} />
|
<AppEditor {summary} app={value} path={''} {policy} fromHub={hubId != null} tour />
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -89,4 +89,4 @@
|
|||||||
$dirtyStore = true
|
$dirtyStore = true
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FlowBuilder {flowStore} {flowStateStore} {selectedId} {loading} />
|
<FlowBuilder {flowStore} {flowStateStore} {selectedId} {loading} tour />
|
||||||
|
|||||||
Reference in New Issue
Block a user