mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-15 08:02:27 +00:00
Compare commits
113
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
424cb4e7db | ||
|
|
fe5e8d8bdb | ||
|
|
d2c868156b | ||
|
|
8ed283f812 | ||
|
|
f7b7ff06ff | ||
|
|
af186df512 | ||
|
|
673e02315f | ||
|
|
cc7b819012 | ||
|
|
6bf388d7d7 | ||
|
|
ac6a337a00 | ||
|
|
b292f54db7 | ||
|
|
70a016eb6b | ||
|
|
e16344dd19 | ||
|
|
375228d14d | ||
|
|
1b18794a40 | ||
|
|
e1a414d3ef | ||
|
|
b2f2e55425 | ||
|
|
5cad0bfe70 | ||
|
|
851e2f29f7 | ||
|
|
effcccf30f | ||
|
|
0568474ba4 | ||
|
|
e8cf9457a1 | ||
|
|
170d8f7cda | ||
|
|
ee0ef7d8b0 | ||
|
|
9e999106f0 | ||
|
|
be1fcc016e | ||
|
|
731802a186 | ||
|
|
8641a7e95f | ||
|
|
3eb1af6c81 | ||
|
|
a2f0be9d6f | ||
|
|
c79227079e | ||
|
|
60f8924a4c | ||
|
|
d6271a18b8 | ||
|
|
5f696f12f9 | ||
|
|
4c0177c255 | ||
|
|
5e73d41e85 | ||
|
|
d483940297 | ||
|
|
4960f3ce6b | ||
|
|
5684035098 | ||
|
|
a6bad016bc | ||
|
|
f793080828 | ||
|
|
4c48665314 | ||
|
|
762e58b5cd | ||
|
|
76f826a222 | ||
|
|
6b8ee38caa | ||
|
|
7fd59292eb | ||
|
|
db4c02785f | ||
|
|
215c48aa53 | ||
|
|
d93e3422cf | ||
|
|
dec070458f | ||
|
|
7438e94df0 | ||
|
|
590c325607 | ||
|
|
0c431b0b2c | ||
|
|
6c5cde4532 | ||
|
|
b0c4783b66 | ||
|
|
3b40f40a8b | ||
|
|
d37d090b07 | ||
|
|
fa197262cc | ||
|
|
b1f4a8e40a | ||
|
|
f37659207a | ||
|
|
b97af4dbf1 | ||
|
|
a038513bfa | ||
|
|
69ebe10851 | ||
|
|
eff81bcae0 | ||
|
|
3e277fa5ec | ||
|
|
51549db78b | ||
|
|
0aae35c74a | ||
|
|
bdce1b75cb | ||
|
|
526e659dc7 | ||
|
|
4135784719 | ||
|
|
cc5c50beed | ||
|
|
10a5eabf5c | ||
|
|
dc32bcb2d6 | ||
|
|
7df118e7d6 | ||
|
|
335ae41c39 | ||
|
|
6add112a54 | ||
|
|
02567839a3 | ||
|
|
8ef175e77b | ||
|
|
e2de8f43ee | ||
|
|
e19c24224c | ||
|
|
17279bf83e | ||
|
|
1f159dd212 | ||
|
|
8df6b6fdfe | ||
|
|
7a64644b9f | ||
|
|
0c395ed4d1 | ||
|
|
0594838c36 | ||
|
|
805478790c | ||
|
|
47fdcc126f | ||
|
|
27ba2f72a0 | ||
|
|
987990ee41 | ||
|
|
b728b0ec01 | ||
|
|
cae8f5bbd4 | ||
|
|
e10e5e74d1 | ||
|
|
70b44528bb | ||
|
|
7a178c7fc7 | ||
|
|
c770fab977 | ||
|
|
d886f59cd3 | ||
|
|
5c3ea422e9 | ||
|
|
c584efd2f6 | ||
|
|
c868c27d9e | ||
|
|
767b6fe41b | ||
|
|
0e58d96a4a | ||
|
|
582f2cd8f9 | ||
|
|
b891a40ac1 | ||
|
|
68d9a2bb40 | ||
|
|
eb885752f4 | ||
|
|
87a5a6fdbd | ||
|
|
2029f6afbc | ||
|
|
a612320087 | ||
|
|
1fb8a24f69 | ||
|
|
f5fc11a51d | ||
|
|
67417de142 | ||
|
|
41e46238d3 |
Vendored
+5
-5
@@ -1,7 +1,7 @@
|
||||
declare namespace svelte.JSX {
|
||||
interface DOMAttributes<T> {
|
||||
onclick_outside?: CompositionEventHandler<T>
|
||||
onpointerdown_outside?: (event: CustomEvent) => void
|
||||
onpointerdown_connecting?: (event: CustomEvent) => void
|
||||
declare namespace svelteHTML {
|
||||
interface HTMLAttributes<T> {
|
||||
'on:click_outside'?: (event: CustomEvent) => void
|
||||
'on:pointerdown_outside'?: (event: CustomEvent) => void
|
||||
'on:pointerdown_connecting'?: (event: CustomEvent) => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { globalEmailInvite, superadmin, workspaceStore } from '$lib/stores'
|
||||
import { SettingService, UserService, WorkspaceService } from '$lib/gen'
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { goto } from '$lib/navigation'
|
||||
@@ -72,64 +73,66 @@
|
||||
let selected: 'operator' | 'developer' | 'admin' = 'developer'
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover placement="bottom-end">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true} startIcon={{ icon: UserPlus }}>
|
||||
Add new user
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col w-72 p-2">
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">Add a new user</span>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="flex flex-col w-72 p-4">
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">Add a new user</span>
|
||||
|
||||
<span class="text-xs mb-1 leading-6">Email</span>
|
||||
<input type="email mb-1" on:keyup={handleKeyUp} placeholder="email" bind:value={email} />
|
||||
<span class="text-xs mb-1 leading-6">Email</span>
|
||||
<input type="email mb-1" on:keyup={handleKeyUp} placeholder="email" bind:value={email} />
|
||||
|
||||
{#if !automateUsernameCreation}
|
||||
<span class="text-xs mb-1 pt-2 leading-6">Username</span>
|
||||
<input type="text" on:keyup={handleKeyUp} placeholder="username" bind:value={username} />
|
||||
{/if}
|
||||
{#if !automateUsernameCreation}
|
||||
<span class="text-xs mb-1 pt-2 leading-6">Username</span>
|
||||
<input type="text" on:keyup={handleKeyUp} placeholder="username" bind:value={username} />
|
||||
{/if}
|
||||
|
||||
<span class="text-xs mb-1 pt-2 leading-6">Role</span>
|
||||
<ToggleButtonGroup bind:selected class="mb-4">
|
||||
<ToggleButton
|
||||
value="operator"
|
||||
<span class="text-xs mb-1 pt-2 leading-6">Role</span>
|
||||
<ToggleButtonGroup bind:selected class="mb-4" let:item>
|
||||
<ToggleButton
|
||||
value="operator"
|
||||
size="sm"
|
||||
label="Operator"
|
||||
tooltip="An operator can only execute and view scripts/flows/apps from your workspace, and only those that he has visibility on."
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="developer"
|
||||
size="sm"
|
||||
label="Developer"
|
||||
tooltip="A Developer can execute and view scripts/flows/apps, but they can also create new ones and edit those they are allowed to by their path (either u/ or Writer or Admin of their folder found at /f)."
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="admin"
|
||||
size="sm"
|
||||
label="Admin"
|
||||
tooltip="An admin has full control over a specific Windmill workspace, including the ability to manage users, edit entities, and control permissions within the workspace."
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="blue"
|
||||
size="sm"
|
||||
label="Operator"
|
||||
tooltip="An operator can only execute and view scripts/flows/apps from your workspace, and only those that he has visibility on."
|
||||
/>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="developer"
|
||||
size="sm"
|
||||
label="Developer"
|
||||
tooltip="A Developer can execute and view scripts/flows/apps, but they can also create new ones and edit those they are allowed to by their path (either u/ or Writer or Admin of their folder found at /f)."
|
||||
/>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="admin"
|
||||
size="sm"
|
||||
label="Admin"
|
||||
tooltip="An admin has full control over a specific Windmill workspace, including the ability to manage users, edit entities, and control permissions within the workspace."
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="blue"
|
||||
size="sm"
|
||||
on:click={() => {
|
||||
addUser().then(() => {
|
||||
// @ts-ignore
|
||||
email = undefined
|
||||
// @ts-ignore
|
||||
username = undefined
|
||||
})
|
||||
}}
|
||||
disabled={email === undefined || (!automateUsernameCreation && username === undefined)}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</Popup>
|
||||
on:click={() => {
|
||||
addUser().then(() => {
|
||||
// @ts-ignore
|
||||
email = undefined
|
||||
// @ts-ignore
|
||||
username = undefined
|
||||
})
|
||||
}}
|
||||
disabled={email === undefined || (!automateUsernameCreation && username === undefined)}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import Toggle from './Toggle.svelte'
|
||||
import TestConnection from './TestConnection.svelte'
|
||||
import SupabaseIcon from './icons/SupabaseIcon.svelte'
|
||||
import Popup from './common/popup/Popup.svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
|
||||
@@ -129,13 +129,12 @@
|
||||
/>
|
||||
<TestConnection {resourceType} {args} />
|
||||
{#if resourceType == 'postgresql'}
|
||||
<Popup
|
||||
let:close
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
placement: 'bottom'
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
spacingSize="sm"
|
||||
size="xs"
|
||||
@@ -147,32 +146,34 @@
|
||||
From connection string
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary">
|
||||
<div class="w-[550px] flex flex-col items-start gap-1">
|
||||
<div class="flex flex-row gap-1 w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={connectionString}
|
||||
placeholder="postgres://user:password@host:5432/dbname?sslmode=disable"
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
on:click={() => {
|
||||
parseConnectionString(close)
|
||||
}}
|
||||
disabled={connectionString.length <= 0}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="block text-primary p-4">
|
||||
<div class="w-[550px] flex flex-col items-start gap-1">
|
||||
<div class="flex flex-row gap-1 w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={connectionString}
|
||||
placeholder="postgres://user:password@host:5432/dbname?sslmode=disable"
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
on:click={() => {
|
||||
parseConnectionString(close)
|
||||
}}
|
||||
disabled={connectionString.length <= 0}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
</div>
|
||||
{#if !validConnectionString}
|
||||
<p class="text-red-500 text-xs">Could not parse connection string</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !validConnectionString}
|
||||
<p class="text-red-500 text-xs">Could not parse connection string</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if resourceType == 'postgresql' && supabaseWizard}
|
||||
<a
|
||||
|
||||
@@ -759,9 +759,10 @@
|
||||
value = { label: oneOfSelected }
|
||||
redraw += 1
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
{#each oneOf as obj}
|
||||
<ToggleButton value={obj.title} label={obj.title} />
|
||||
<ToggleButton value={obj.title} label={obj.title} {item} />
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
{#if oneOfSelected}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import AssignableTagsInner from './AssignableTagsInner.svelte'
|
||||
@@ -11,12 +12,13 @@
|
||||
export let showWorkspaceRestriction = false
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'absolute', placement: placement }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
{disabled}
|
||||
closeButton
|
||||
usePointerDownOutside
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button {color} size="xs" nonCaptureEvent={true} {disabled}>
|
||||
<div class="flex flex-row gap-1 items-center"
|
||||
><Pen size={14} /> Custom tags <Tooltip light
|
||||
@@ -28,5 +30,7 @@
|
||||
>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<AssignableTagsInner {showWorkspaceRestriction} on:refresh />
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<AssignableTagsInner {showWorkspaceRestriction} on:refresh />
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
loadCustomTags()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-72 p-2 gap-2">
|
||||
<div class="flex flex-col w-72 p-4 gap-2">
|
||||
{#if customTags == undefined}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:else}
|
||||
|
||||
@@ -52,9 +52,15 @@
|
||||
<label class="block pb-2">
|
||||
<div class="flex gap-2 items-end">
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={value['custom']}>
|
||||
<ToggleButton value={false} label={'Org'} />
|
||||
<ToggleButton value={true} label="Custom" />
|
||||
<ToggleButtonGroup
|
||||
selected={value['custom'] ? 'custom' : 'org'}
|
||||
on:selected={({ detail }) => {
|
||||
value['custom'] = detail === 'custom'
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="org" label={'Org'} {item} />
|
||||
<ToggleButton value="custom" label="Custom" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
<div class="grow">
|
||||
|
||||
@@ -264,28 +264,23 @@
|
||||
on:selected={(e) => dispatch('dirty')}
|
||||
bind:selected={config.integration.type}
|
||||
class="mb-4 mt-2"
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
value="dryrun"
|
||||
size="sm"
|
||||
label="Dry run"
|
||||
tooltip="See autoscaling events but not actual scaling actions will be performed"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
value="script"
|
||||
size="sm"
|
||||
label="Custom script"
|
||||
tooltip="Run a custom script to scale your worker group"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton position="center" disabled value="ecs" size="sm" label="ECS (soon)" />
|
||||
<ToggleButton position="right" disabled value="nomad" size="sm" label="Nomad (soon)" />
|
||||
<ToggleButton
|
||||
position="right"
|
||||
disabled
|
||||
value="kubernetes"
|
||||
size="sm"
|
||||
label="Kubernetes (soon)"
|
||||
/>
|
||||
<ToggleButton disabled value="ecs" label="ECS (soon)" {item} />
|
||||
<ToggleButton disabled value="nomad" label="Nomad (soon)" {item} />
|
||||
<ToggleButton disabled value="kubernetes" label="Kubernetes (soon)" {item} />
|
||||
</ToggleButtonGroup>
|
||||
|
||||
{#if config.integration.type === 'script'}
|
||||
@@ -347,12 +342,12 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<ToggleButtonGroup selected={'script'} disabled class="mb-4 mt-2">
|
||||
<ToggleButton value="dryrun" size="sm" label="Dry run" />
|
||||
<ToggleButton value="script" size="sm" label="Custom script" />
|
||||
<ToggleButton position="center" value="ecs" size="sm" label="ECS (soon)" />
|
||||
<ToggleButton position="right" value="nomad" size="sm" label="Nomad (soon)" />
|
||||
<ToggleButton position="right" value="kubernetes" size="sm" label="Kubernetes (soon)" />
|
||||
<ToggleButtonGroup selected={'script'} disabled class="mb-4 mt-2" let:item>
|
||||
<ToggleButton value="dryrun" label="Dry run" {item} />
|
||||
<ToggleButton value="script" label="Custom script" {item} />
|
||||
<ToggleButton value="ecs" label="ECS (soon)" {item} />
|
||||
<ToggleButton value="nomad" label="Nomad (soon)" {item} />
|
||||
<ToggleButton value="kubernetes" label="Kubernetes (soon)" {item} />
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<label>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import ChangeInstanceUsernameInner from './ChangeInstanceUsernameInner.svelte'
|
||||
|
||||
@@ -8,7 +9,7 @@
|
||||
export let isConflict = false
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -16,19 +17,20 @@
|
||||
})
|
||||
]
|
||||
}}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
closeButton
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color={isConflict ? 'red' : 'light'} size="xs" spacingSize="xs2" nonCaptureEvent={true}
|
||||
>{isConflict ? 'Fix username conflict' : 'Change username'}</Button
|
||||
>
|
||||
</svelte:fragment>
|
||||
<ChangeInstanceUsernameInner
|
||||
{email}
|
||||
{username}
|
||||
{isConflict}
|
||||
on:close={() => close(null)}
|
||||
on:renamed
|
||||
/>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<ChangeInstanceUsernameInner
|
||||
{email}
|
||||
{username}
|
||||
{isConflict}
|
||||
on:close={() => close()}
|
||||
on:renamed
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -71,11 +71,9 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col max-w-2xl p-2">
|
||||
{#if isConflict}
|
||||
<span class="text-sm mb-2 leading-6 font-semibold"
|
||||
>Fix username conflict</span
|
||||
>
|
||||
<div class="flex flex-col max-w-2xl p-4">
|
||||
{#if isConflict}
|
||||
<span class="text-sm mb-2 leading-6 font-semibold">Fix username conflict</span>
|
||||
{/if}
|
||||
|
||||
<span class="text-sm font-semibold mb-1 leading-6"
|
||||
|
||||
@@ -157,15 +157,23 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2 flex-wrap sticky top-0 left-0 right-0 bg-surface">
|
||||
<div class="p-2">
|
||||
<ToggleButtonGroup bind:selected={searchKind} class="h-10 ">
|
||||
<ToggleButton small light value="all" label={'All' + counts.all} />
|
||||
<ToggleButton small light value="scripts" icon={Code2} label={'Scripts' + counts.scripts} />
|
||||
<ToggleButtonGroup bind:selected={searchKind} class="h-10 " let:item>
|
||||
<ToggleButton small light value="all" label={'All' + counts.all} {item} />
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
value="scripts"
|
||||
icon={Code2}
|
||||
label={'Scripts' + counts.scripts}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
value="resources"
|
||||
icon={Boxes}
|
||||
label={'Resources' + counts.resources}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
small
|
||||
@@ -174,6 +182,7 @@
|
||||
label={'Flows' + counts.flows}
|
||||
icon={FlowIcon}
|
||||
selectedColor="#14b8a6"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
small
|
||||
@@ -182,6 +191,7 @@
|
||||
label={'Apps' + counts.apps}
|
||||
icon={LayoutDashboard}
|
||||
selectedColor="#fb923c"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<script lang="ts">
|
||||
import Section from './Section.svelte'
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import { Clock } from 'lucide-svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }} closeButton>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true} startIcon={{ icon: Clock }}>
|
||||
Use simplified builder
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<Section label="CRON Builder">
|
||||
<div class="flex flex-col w-72">
|
||||
<slot {close} />
|
||||
</div>
|
||||
</Section>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<Section label="CRON Builder" wrapperClass="p-4">
|
||||
<div class="flex flex-col w-72">
|
||||
<slot {close} />
|
||||
</div>
|
||||
</Section>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -385,7 +385,7 @@
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
schedule = nschedule
|
||||
close(null)
|
||||
close()
|
||||
}}
|
||||
>
|
||||
Set cron schedule
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
$: dbSchema = resourcePath && resourcePath in $dbSchemas ? $dbSchemas[resourcePath] : undefined
|
||||
|
||||
$: shouldDisplayError = resourcePath && resourcePath in $dbSchemas && !$dbSchemas[resourcePath]
|
||||
|
||||
function handleSelected({ detail }: CustomEvent<string>) {
|
||||
if (dbSchema && dbSchema.lang !== 'graphql') {
|
||||
dbSchema.publicOnly = detail === 'dbo'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
@@ -86,9 +92,14 @@
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{#if dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)}
|
||||
<ToggleButtonGroup class="mb-4" bind:selected={dbSchema.publicOnly}>
|
||||
<ToggleButton value={true} label={dbSchema.schema.dbo ? 'Dbo' : 'Public'} />
|
||||
<ToggleButton value={false} label="All" />
|
||||
<ToggleButtonGroup
|
||||
class="mb-4"
|
||||
selected={dbSchema.publicOnly ? 'dbo' : 'all'}
|
||||
on:selected={handleSelected}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="dbo" label={dbSchema.schema.dbo ? 'Dbo' : 'Public'} {item} />
|
||||
<ToggleButton value="all" label="All" {item} />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if dbSchema.lang === 'graphql'}
|
||||
|
||||
@@ -146,9 +146,9 @@
|
||||
</Button>
|
||||
{/if}
|
||||
<!-- <div>
|
||||
<ToggleButtonGroup bind:selected={format}>
|
||||
<ToggleButton light small value={'local'} label="local" />
|
||||
<ToggleButton light small value={'utc'} label="utc" />
|
||||
<ToggleButtonGroup bind:selected={format} let:item>
|
||||
<ToggleButton light small value={'local'} label="local" {item} />
|
||||
<ToggleButton light small value={'utc'} label="utc" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
|
||||
import DefaultTagsInner from './DefaultTagsInner.svelte'
|
||||
|
||||
@@ -11,11 +12,8 @@
|
||||
let placement: 'bottom-end' | 'top-end' = 'bottom-end'
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: placement }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: placement }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row gap-1 items-center"
|
||||
><Pen size={14} /> Default tags <Tooltip light
|
||||
@@ -25,5 +23,9 @@
|
||||
>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<DefaultTagsInner bind:defaultTagPerWorkspace bind:defaultTagWorkspaces />
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<DefaultTagsInner bind:defaultTagPerWorkspace bind:defaultTagWorkspaces />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -379,10 +379,11 @@
|
||||
on:selected={(ev) => {
|
||||
globalForceJson = ev.detail === 'json'
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton class="px-1.5" value="pretty" label="Pretty" icon={Highlighter} />
|
||||
<ToggleButton class="px-1.5" value="pretty" label="Pretty" icon={Highlighter} {item} />
|
||||
|
||||
<ToggleButton class="px-1.5" value="json" label="JSON" icon={Braces} />
|
||||
<ToggleButton class="px-1.5" value="json" label="JSON" icon={Braces} {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -412,16 +413,23 @@
|
||||
{#if !hideAsJson && !['json', 's3object'].includes(resultKind ?? '') && typeof result === 'object'}<ToggleButtonGroup
|
||||
class="h-6"
|
||||
selected={forceJson ? 'json' : resultKind?.startsWith('table-') ? 'table' : 'pretty'}
|
||||
let:item
|
||||
on:selected={(ev) => {
|
||||
forceJson = ev.detail === 'json'
|
||||
}}
|
||||
>
|
||||
{#if ['table-col', 'table-row', 'table-row-object'].includes(resultKind ?? '')}
|
||||
<ToggleButton class="px-1.5" value="table" label="Table" icon={Table2} />
|
||||
<ToggleButton class="px-1.5" value="table" label="Table" icon={Table2} {item} />
|
||||
{:else}
|
||||
<ToggleButton class="px-1.5" value="pretty" label="Pretty" icon={Highlighter} />
|
||||
<ToggleButton
|
||||
class="px-1.5"
|
||||
value="pretty"
|
||||
label="Pretty"
|
||||
icon={Highlighter}
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
<ToggleButton class="px-1.5" value="json" label="JSON" icon={Braces} />
|
||||
<ToggleButton class="px-1.5" value="json" label="JSON" icon={Braces} {item} />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { MoreVertical } from 'lucide-svelte'
|
||||
import Menu from './common/menu/MenuV2.svelte'
|
||||
|
||||
import { Menu, Menubar } from '$lib/components/meltComponents'
|
||||
import { melt } from '@melt-ui/svelte'
|
||||
import type { Placement } from '@floating-ui/core'
|
||||
import type { Item } from '$lib/utils'
|
||||
import DropdownV2Inner from './DropdownV2Inner.svelte'
|
||||
|
||||
type Item = {
|
||||
displayName: string
|
||||
action?: (e: CustomEvent<any>) => void
|
||||
icon?: any
|
||||
href?: string
|
||||
disabled?: boolean
|
||||
type?: 'action' | 'delete'
|
||||
hide?: boolean | undefined
|
||||
}
|
||||
|
||||
export let items: Item[] | (() => Item[]) | (() => Promise<Item[]>) = []
|
||||
export let justifyEnd: boolean = true
|
||||
export let disabled = false
|
||||
export let placement: Placement = 'bottom-end'
|
||||
export let usePointerDownOutside = false
|
||||
|
||||
async function computeItems(): Promise<Item[]> {
|
||||
if (typeof items === 'function') {
|
||||
@@ -27,17 +21,31 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu placement="bottom-end" {justifyEnd} on:close on:open {disabled}>
|
||||
<div slot="trigger">
|
||||
{#if $$slots.buttonReplacement}
|
||||
<slot name="buttonReplacement" />
|
||||
{:else}
|
||||
<MoreVertical
|
||||
size={16}
|
||||
class="w-8 h-8 p-2 hover:bg-surface-hover cursor-pointer rounded-md"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<Menubar let:createMenu>
|
||||
<Menu
|
||||
{createMenu}
|
||||
{placement}
|
||||
{justifyEnd}
|
||||
on:close
|
||||
on:open
|
||||
{disabled}
|
||||
let:item
|
||||
class={$$props.class}
|
||||
{usePointerDownOutside}
|
||||
>
|
||||
<svelte:fragment slot="trigger" let:trigger>
|
||||
<button use:melt={trigger} on:click={(e) => e?.stopPropagation()}>
|
||||
{#if $$slots.buttonReplacement}
|
||||
<slot name="buttonReplacement" />
|
||||
{:else}
|
||||
<MoreVertical
|
||||
size={16}
|
||||
class="w-8 h-8 p-2 hover:bg-surface-hover cursor-pointer rounded-md"
|
||||
/>
|
||||
{/if}
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
|
||||
<DropdownV2Inner items={computeItems} />
|
||||
</Menu>
|
||||
<DropdownV2Inner items={computeItems} meltItem={item} />
|
||||
</Menu>
|
||||
</Menubar>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { MenuItem } from '$lib/components/meltComponents'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
displayName: string
|
||||
action?: (e: CustomEvent<any>) => void
|
||||
icon?: any
|
||||
iconColor?: string
|
||||
href?: string
|
||||
disabled?: boolean
|
||||
type?: 'action' | 'delete'
|
||||
@@ -14,6 +15,7 @@
|
||||
}
|
||||
|
||||
export let items: Item[] | (() => Item[]) | (() => Promise<Item[]>) = []
|
||||
export let meltItem: any
|
||||
|
||||
let computedItems: Item[] | undefined = undefined
|
||||
async function computeItems() {
|
||||
@@ -35,16 +37,18 @@
|
||||
href={item?.href}
|
||||
disabled={item?.disabled}
|
||||
class={twMerge(
|
||||
'px-4 py-2 text-primary hover:bg-surface-hover hover:text-primary cursor-pointer text-xs transition-all',
|
||||
'px-4 py-2 text-primary font-semibold hover:bg-surface-hover cursor-pointer text-xs transition-all',
|
||||
'data-[highlighted]:bg-surface-hover',
|
||||
'flex flex-row gap-2 items-center',
|
||||
item?.disabled && 'text-gray-400 cursor-not-allowed',
|
||||
item?.type === 'delete' &&
|
||||
!item?.disabled &&
|
||||
'text-red-500 hover:bg-red-100 hover:text-red-500'
|
||||
'text-red-500 hover:bg-red-100 hover:text-red-500 data-[highlighted]:text-red-500 data-[highlighted]:bg-red-100'
|
||||
)}
|
||||
item={meltItem}
|
||||
>
|
||||
{#if item.icon}
|
||||
<svelte:component this={item.icon} size={14} />
|
||||
<svelte:component this={item.icon} size={14} color={item.iconColor} />
|
||||
{/if}
|
||||
{item.displayName}
|
||||
</MenuItem>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import Popup from './common/popup/Popup.svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import SchemaFormDnd from './schema/SchemaFormDND.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { tweened } from 'svelte/motion'
|
||||
@@ -371,12 +371,11 @@
|
||||
{argName}
|
||||
{#if !uiOnly}
|
||||
<div on:click|stopPropagation|preventDefault>
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
<Popover
|
||||
placement="bottom-end"
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
@@ -385,34 +384,36 @@
|
||||
iconOnly
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<Label label="Name">
|
||||
<div class="flex flex-col gap-2">
|
||||
<input
|
||||
type="text"
|
||||
class="w-full !bg-surface"
|
||||
value={argName}
|
||||
id={argName + i}
|
||||
on:keydown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<Label label="Name" class="p-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<input
|
||||
type="text"
|
||||
class="w-full !bg-surface"
|
||||
value={argName}
|
||||
id={argName + i}
|
||||
on:keydown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
renameProperty(argName, argName + i)
|
||||
close()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
renameProperty(argName, argName + i)
|
||||
close(null)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
renameProperty(argName, argName + i)
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
</div>
|
||||
</Label>
|
||||
</Popup>
|
||||
close()
|
||||
}}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
</div>
|
||||
</Label>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -467,6 +468,7 @@
|
||||
<ToggleButtonGroup
|
||||
tabListClass="flex-wrap"
|
||||
class="h-auto"
|
||||
let:item
|
||||
bind:selected
|
||||
on:selected={(e) => {
|
||||
const isS3 = e.detail == 'S3'
|
||||
@@ -547,7 +549,7 @@
|
||||
}}
|
||||
>
|
||||
{#each [['String', 'string'], ['Number', 'number'], ['Integer', 'integer'], ['Object', 'object'], ['OneOf', 'oneOf'], ['Array', 'array'], ['Boolean', 'boolean'], ['S3 Object', 'S3']] as x}
|
||||
<ToggleButton value={x[1]} label={x[0]} />
|
||||
<ToggleButton value={x[1]} label={x[0]} {item} />
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
</Label>
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
import { Settings } from 'lucide-svelte'
|
||||
import FormatOnSave from './FormatOnSave.svelte'
|
||||
import VimMode from './VimMode.svelte'
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import CodeCompletionStatus from './copilot/CodeCompletionStatus.svelte'
|
||||
import type { EditorBarUi } from './custom_ui'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
|
||||
export let customUi: EditorBarUi = {}
|
||||
</script>
|
||||
|
||||
{#if customUi?.autoformatting != false || customUi?.vimMode != false || customUi?.aiCompletion != false}
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }} usePointerDownOutside>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
btnClasses="text-tertiary"
|
||||
color="light"
|
||||
@@ -26,22 +24,24 @@
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex flex-col gap-y-2">
|
||||
{#if customUi?.autoformatting != false}
|
||||
<div>
|
||||
<FormatOnSave />
|
||||
</div>
|
||||
{/if}
|
||||
{#if customUi?.vimMode != false}
|
||||
<div>
|
||||
<VimMode />
|
||||
</div>
|
||||
{/if}
|
||||
{#if customUi?.aiCompletion != false}
|
||||
<div>
|
||||
<CodeCompletionStatus />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="flex flex-col gap-y-2 p-4">
|
||||
{#if customUi?.autoformatting != false}
|
||||
<div>
|
||||
<FormatOnSave />
|
||||
</div>
|
||||
{/if}
|
||||
{#if customUi?.vimMode != false}
|
||||
<div>
|
||||
<VimMode />
|
||||
</div>
|
||||
{/if}
|
||||
{#if customUi?.aiCompletion != false}
|
||||
<div>
|
||||
<CodeCompletionStatus />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -73,14 +73,11 @@
|
||||
import { loadFlowModuleState, pickScript } from './flows/flowStateUtils'
|
||||
import FlowCopilotInputsModal from './copilot/FlowCopilotInputsModal.svelte'
|
||||
import FlowBuilderTutorials from './FlowBuilderTutorials.svelte'
|
||||
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import FlowTutorials from './FlowTutorials.svelte'
|
||||
import { ignoredTutorials } from './tutorials/ignoredTutorials'
|
||||
import type DiffDrawer from './DiffDrawer.svelte'
|
||||
import FlowHistory from './flows/FlowHistory.svelte'
|
||||
import ButtonDropdown from './common/button/ButtonDropdown.svelte'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import CustomPopover from './CustomPopover.svelte'
|
||||
import Summary from './Summary.svelte'
|
||||
import type { FlowBuilderWhitelabelCustomUi } from './custom_ui'
|
||||
@@ -1364,33 +1361,16 @@
|
||||
{/if}
|
||||
<div>
|
||||
{#if moreItems?.length > 0}
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<Dropdown items={moreItems} class="center-center">
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<Button nonCaptureEvent size="xs" color="light">
|
||||
<div class="flex flex-row items-center">
|
||||
<MoreVertical size={14} />
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: MoreVertical }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each moreItems as item}
|
||||
<MenuItem
|
||||
on:click={item.action}
|
||||
disabled={item.disabled}
|
||||
class={item.disabled ? 'opacity-50' : ''}
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
'text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<svelte:component this={item.icon} size={14} />
|
||||
{item.displayName}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</Dropdown>
|
||||
{/if}
|
||||
</div>
|
||||
{#if customUi?.topBar?.tutorials != false}
|
||||
|
||||
@@ -1,21 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { BookOpen } from 'lucide-svelte'
|
||||
import ButtonDropdown from './common/button/ButtonDropdown.svelte'
|
||||
import { BookOpen, CheckCircle, Circle, RefreshCw, CheckCheck } from 'lucide-svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
|
||||
import MenuItem from './common/menu/MenuItem.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { resetAllTodos, skipAllTodos } from '$lib/tutorialUtils'
|
||||
import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte'
|
||||
import TutorialItem from './tutorials/TutorialItem.svelte'
|
||||
import FlowTutorials from './FlowTutorials.svelte'
|
||||
import { tutorialsToDo } from '$lib/stores'
|
||||
|
||||
let targetTutorial: string | undefined = undefined
|
||||
let flowTutorials: FlowTutorials | undefined = undefined
|
||||
|
||||
async function getTutorialItems() {
|
||||
const tutorials = [
|
||||
{ displayName: 'Simple flow tutorials', id: 'action' },
|
||||
{ displayName: 'For loops tutorial', id: 'forloop' },
|
||||
{ displayName: 'Branch one tutorial', id: 'branchone' },
|
||||
{ displayName: 'Branch all tutorial', id: 'branchall' },
|
||||
{ displayName: 'Error handler', id: 'error-handler' }
|
||||
]
|
||||
|
||||
return [
|
||||
...tutorials.map((tutorial, index) => ({
|
||||
displayName: tutorial.displayName,
|
||||
action: () => flowTutorials?.runTutorialById(tutorial.id),
|
||||
icon: $tutorialsToDo.includes(index) ? Circle : CheckCircle,
|
||||
iconColor: $tutorialsToDo.includes(index) ? undefined : 'green'
|
||||
})),
|
||||
{
|
||||
displayName: 'Skip tutorials',
|
||||
action: () => skipAllTodos(),
|
||||
icon: CheckCheck
|
||||
},
|
||||
{
|
||||
displayName: 'Reset tutorials',
|
||||
action: () => resetAllTodos(),
|
||||
icon: RefreshCw
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:pointerdown|stopPropagation>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
{#key $tutorialsToDo}
|
||||
<Dropdown items={getTutorialItems}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
@@ -26,63 +52,8 @@
|
||||
startIcon={{ icon: BookOpen }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
<TutorialItem
|
||||
on:click={() => flowTutorials?.runTutorialById('action')}
|
||||
label="Simple flow tutorial"
|
||||
index={0}
|
||||
/>
|
||||
|
||||
<TutorialItem
|
||||
on:click={() => flowTutorials?.runTutorialById('forloop')}
|
||||
label="For loops tutorial"
|
||||
index={1}
|
||||
/>
|
||||
|
||||
<TutorialItem
|
||||
on:click={() => flowTutorials?.runTutorialById('branchone')}
|
||||
label="Branch one tutorial"
|
||||
index={2}
|
||||
/>
|
||||
|
||||
<TutorialItem
|
||||
on:click={() => flowTutorials?.runTutorialById('branchall')}
|
||||
label="Branch all tutorial"
|
||||
index={3}
|
||||
/>
|
||||
|
||||
<TutorialItem
|
||||
on:click={() => flowTutorials?.runTutorialById('error-handler')}
|
||||
label="Error handler"
|
||||
index={4}
|
||||
/>
|
||||
|
||||
<div class="border-t border-surface-hover" />
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
resetAllTodos()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-primary flex flex-row items-center text-left gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
Reset tutorials
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem on:click={() => skipAllTodos()}>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-primary flex flex-row items-center text-left gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
Skip tutorials
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
</Dropdown>
|
||||
{/key}
|
||||
|
||||
<FlowTutorials
|
||||
bind:this={flowTutorials}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { type Job, JobService, type Flow, type RestartedFrom, type OpenFlow } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Badge, Button, Popup } from './common'
|
||||
import { Badge, Button } from './common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import { runFlowPreview } from './flows/utils'
|
||||
@@ -261,7 +262,7 @@
|
||||
</Badge>
|
||||
</Button>
|
||||
{:else}
|
||||
<Popup floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
|
||||
<svelte:fragment slot="button">
|
||||
<Button
|
||||
title={`Re-start this flow from step ${selectedJobStep} (included).`}
|
||||
@@ -283,49 +284,51 @@
|
||||
</Badge>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-1 text-sm text-secondary"
|
||||
>{selectedJobStepType == 'forloop' ? 'From iteration #:' : 'From branch:'}</div
|
||||
>
|
||||
<div class="flex w-full">
|
||||
{#if selectedJobStepType === 'forloop'}
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
bind:value={branchOrIterationN}
|
||||
class="!w-32 grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
/>
|
||||
{:else}
|
||||
<select
|
||||
bind:value={branchOrIterationN}
|
||||
class="!w-32 grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
>
|
||||
{#each restartBranchNames as [branchIdx, branchName]}
|
||||
<option value={branchIdx}>{branchName}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="!p-1 !w-[34px] !ml-1"
|
||||
aria-label="Restart flow"
|
||||
on:click|once={() => {
|
||||
runPreview($previewArgs, {
|
||||
flow_job_id: jobId,
|
||||
step_id: selectedJobStep,
|
||||
branch_or_iteration_n: branchOrIterationN
|
||||
})
|
||||
}}
|
||||
<svelte:fragment slot="content">
|
||||
<label class="block text-primary p-4">
|
||||
<div class="pb-1 text-sm text-secondary"
|
||||
>{selectedJobStepType == 'forloop' ? 'From iteration #:' : 'From branch:'}</div
|
||||
>
|
||||
<ArrowRight size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
</label>
|
||||
</Popup>
|
||||
<div class="flex w-full">
|
||||
{#if selectedJobStepType === 'forloop'}
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
bind:value={branchOrIterationN}
|
||||
class="!w-32 grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
/>
|
||||
{:else}
|
||||
<select
|
||||
bind:value={branchOrIterationN}
|
||||
class="!w-32 grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
>
|
||||
{#each restartBranchNames as [branchIdx, branchName]}
|
||||
<option value={branchIdx}>{branchName}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="!p-1 !w-[34px] !ml-1"
|
||||
aria-label="Restart flow"
|
||||
on:click|once={() => {
|
||||
runPreview($previewArgs, {
|
||||
flow_job_id: jobId,
|
||||
step_id: selectedJobStep,
|
||||
branch_or_iteration_n: branchOrIterationN
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ArrowRight size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
</label>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{/if}
|
||||
<Button
|
||||
|
||||
@@ -184,9 +184,13 @@
|
||||
</Alert>
|
||||
<div class="flex items-center gap-1">
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={ownerKind} on:selected={() => (ownerItem = '')}>
|
||||
<ToggleButton value="user" size="xs" label="User" />
|
||||
<ToggleButton value="group" size="xs" label="Group" />
|
||||
<ToggleButtonGroup
|
||||
bind:selected={ownerKind}
|
||||
on:selected={() => (ownerItem = '')}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="user" size="xs" label="User" {item} />
|
||||
<ToggleButton value="group" size="xs" label="Group" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -271,6 +275,7 @@
|
||||
<div>
|
||||
<ToggleButtonGroup
|
||||
selected={role}
|
||||
let:item
|
||||
on:selected={async (e) => {
|
||||
const role = e.detail
|
||||
// const wasInFolder = (folder?.owners ?? []).includes(folder)
|
||||
@@ -312,6 +317,7 @@
|
||||
size="xs"
|
||||
label="Viewer"
|
||||
tooltip="A viewer of a folder has read-only access to all the elements (scripts/flows/apps/schedules/resources/variables) inside the folder"
|
||||
{item}
|
||||
/>
|
||||
|
||||
<ToggleButton
|
||||
@@ -320,6 +326,7 @@
|
||||
size="xs"
|
||||
label="Writer"
|
||||
tooltip="A writer of a folder has read AND write access to all the elements (scripts/flows/apps/schedules/resources/variables) inside the folder"
|
||||
{item}
|
||||
/>
|
||||
|
||||
<ToggleButton
|
||||
@@ -328,6 +335,7 @@
|
||||
size="xs"
|
||||
label="Admin"
|
||||
tooltip="An admin of a folder has read AND write access to all the elements inside the folders and can manage the permissions as well as add new admins"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -150,6 +150,7 @@
|
||||
<div>
|
||||
<ToggleButtonGroup
|
||||
selected={role}
|
||||
let:item
|
||||
on:selected={async (e) => {
|
||||
const role = e.detail
|
||||
// const wasInGroup = (group?.members ?? []).includes(group)
|
||||
@@ -215,6 +216,7 @@
|
||||
size="xs"
|
||||
label="Member"
|
||||
tooltip="A Member of a group can see everything the group can see, write to everything the group can write, and generally act on behalf of the group"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
@@ -222,6 +224,7 @@
|
||||
size="xs"
|
||||
label="Admin"
|
||||
tooltip="An admin of a group is a member of a group that can also add and remove members to the group, or make them admin."
|
||||
{item}
|
||||
/>
|
||||
{#if role === 'manager'}
|
||||
<ToggleButton
|
||||
@@ -230,6 +233,7 @@
|
||||
label="Manager"
|
||||
tooltip="A manager of a group can manage the group, adding and removing users and
|
||||
change their roles. Being a manager does not make you a member"
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -358,6 +358,7 @@
|
||||
<div>
|
||||
<ToggleButtonGroup
|
||||
selected={propertyType}
|
||||
let:item
|
||||
on:selected={(e) => {
|
||||
if (e.detail == propertyType) return
|
||||
const staticTemplate = isStaticTemplate(inputCat)
|
||||
@@ -417,9 +418,10 @@
|
||||
value="static"
|
||||
size="xs2"
|
||||
label={'${}'}
|
||||
{item}
|
||||
/>
|
||||
{:else}
|
||||
<ToggleButton small label="Static" value="static" />
|
||||
<ToggleButton small label="Static" value="static" {item} />
|
||||
{/if}
|
||||
|
||||
{#if codeInjectionDetected && propertyType == 'static'}
|
||||
@@ -441,6 +443,7 @@
|
||||
tooltip="JavaScript expression ('flow_input' or 'results')."
|
||||
value="javascript"
|
||||
icon={FunctionSquare}
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Pencil } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import Popup from './common/popup/Popup.svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import ChangeInstanceUsernameInner from './ChangeInstanceUsernameInner.svelte'
|
||||
import { UserService } from '$lib/gen'
|
||||
@@ -26,128 +26,124 @@
|
||||
sendUserToast('Password must be at least 5 characters long', true)
|
||||
return
|
||||
}
|
||||
await UserService.setPasswordForUser({user: email, requestBody:{ password }})
|
||||
await UserService.setPasswordForUser({ user: email, requestBody: { password } })
|
||||
sendUserToast(`Password updated for ${email}`)
|
||||
}
|
||||
|
||||
async function saveLoginType() {
|
||||
await UserService.setLoginTypeForUser({user: email, requestBody:{ login_type }})
|
||||
await UserService.setLoginTypeForUser({ user: email, requestBody: { login_type } })
|
||||
sendUserToast(`Login type updated for ${email}`)
|
||||
dispatch('refresh')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
let:close
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-end',
|
||||
middleware: [offset(8), flip(), shift()]
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button nonCaptureEvent={true} size="xs" color="light" endIcon={{ icon: Pencil }}>Edit</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col gap-8 max-w-sm">
|
||||
{#if automateUsernameCreation && username}
|
||||
<ChangeInstanceUsernameInner {email} {username} on:renamed />
|
||||
{/if}
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Name</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
saveName()
|
||||
close(null)
|
||||
}
|
||||
<svelte:fragment slot="content">
|
||||
<div class="flex flex-col gap-8 max-w-sm p-4">
|
||||
{#if automateUsernameCreation && username}
|
||||
<ChangeInstanceUsernameInner {email} {username} on:renamed />
|
||||
{/if}
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Name</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
saveName()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
saveName()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
saveName()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update name
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Password</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="password"
|
||||
bind:value={password}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
savePassword()
|
||||
close(null)
|
||||
}
|
||||
>
|
||||
Update name
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Password</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="password"
|
||||
bind:value={password}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
savePassword()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
savePassword()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
savePassword()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update password
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Login type</div>
|
||||
<div class="text-xs text-secondary mb-2">
|
||||
Must match exact SSO name, "password" or "saml". Examples: password, google, saml, microsoft
|
||||
</div>
|
||||
>
|
||||
Update password
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Login type</div>
|
||||
<div class="text-xs text-secondary mb-2">
|
||||
Must match exact SSO name, "password" or "saml". Examples: password, google, saml,
|
||||
microsoft
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={login_type}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
saveLoginType()
|
||||
close(null)
|
||||
}
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={login_type}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
saveLoginType()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save login type"
|
||||
on:click={() => {
|
||||
saveLoginType()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save login type"
|
||||
on:click={() => {
|
||||
saveLoginType()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update login type
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
</Popup>
|
||||
>
|
||||
Update login type
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -183,12 +183,13 @@
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
<ToggleButtonGroup bind:selected={$values[setting.key]}>
|
||||
<ToggleButtonGroup bind:selected={$values[setting.key]} let:item={toggleButton}>
|
||||
{#each setting.select_items ?? [] as item}
|
||||
<ToggleButton
|
||||
value={item.value ?? item.label}
|
||||
label={item.label}
|
||||
tooltip={item.tooltip}
|
||||
item={toggleButton}
|
||||
/>
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { globalEmailInvite, superadmin, workspaceStore } from '$lib/stores'
|
||||
import { UserService, WorkspaceService } from '$lib/gen'
|
||||
import { Button, Popup } from './common'
|
||||
import { Button } from './common'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { goto } from '$lib/navigation'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
@@ -64,53 +65,57 @@
|
||||
let selected: 'operator' | 'developer' | 'admin' = 'developer'
|
||||
</script>
|
||||
|
||||
<Popup floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true} startIcon={{ icon: MailPlus }}>
|
||||
Invite
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<input
|
||||
type="email"
|
||||
on:keyup={handleKeyUp}
|
||||
placeholder="email"
|
||||
bind:value={email}
|
||||
class="mr-4"
|
||||
/>
|
||||
<ToggleButtonGroup bind:selected>
|
||||
<ToggleButton
|
||||
value="operator"
|
||||
size="sm"
|
||||
label="Operator"
|
||||
tooltip="An operator can only execute and view scripts/flows/apps from your workspace, and only those that he has visibility on."
|
||||
<svelte:fragment slot="content">
|
||||
<div class="flex flex-col gap-2 p-4">
|
||||
<input
|
||||
type="email"
|
||||
on:keyup={handleKeyUp}
|
||||
placeholder="email"
|
||||
bind:value={email}
|
||||
class="mr-4"
|
||||
/>
|
||||
<ToggleButtonGroup bind:selected let:item>
|
||||
<ToggleButton
|
||||
value="operator"
|
||||
size="sm"
|
||||
label="Operator"
|
||||
tooltip="An operator can only execute and view scripts/flows/apps from your workspace, and only those that he has visibility on."
|
||||
{item}
|
||||
/>
|
||||
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="developer"
|
||||
size="sm"
|
||||
label="Developer"
|
||||
tooltip="A Developer can execute and view scripts/flows/apps, but they can also create new ones and edit those they are allowed to by their path (either u/ or Writer or Admin of their folder found at /f)."
|
||||
/>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="developer"
|
||||
size="sm"
|
||||
label="Developer"
|
||||
tooltip="A Developer can execute and view scripts/flows/apps, but they can also create new ones and edit those they are allowed to by their path (either u/ or Writer or Admin of their folder found at /f)."
|
||||
{item}
|
||||
/>
|
||||
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="admin"
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="admin"
|
||||
size="sm"
|
||||
label="Admin"
|
||||
tooltip="An admin has full control over a specific Windmill workspace, including the ability to manage users, edit entities, and control permissions within the workspace."
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="blue"
|
||||
size="sm"
|
||||
label="Admin"
|
||||
tooltip="An admin has full control over a specific Windmill workspace, including the ability to manage users, edit entities, and control permissions within the workspace."
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="blue"
|
||||
size="sm"
|
||||
on:click={inviteUser}
|
||||
disabled={email === undefined}
|
||||
>
|
||||
Invite
|
||||
</Button>
|
||||
</div>
|
||||
</Popup>
|
||||
on:click={inviteUser}
|
||||
disabled={email === undefined}
|
||||
>
|
||||
Invite
|
||||
</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -52,9 +52,15 @@
|
||||
<label class="block pb-2">
|
||||
<div class="flex gap-2 items-end">
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={value['custom']}>
|
||||
<ToggleButton value={false} label={'Org'} />
|
||||
<ToggleButton value={true} label="Custom" />
|
||||
<ToggleButtonGroup
|
||||
selected={value['custom'] ? 'custom' : 'org'}
|
||||
on:selected={({ detail }) => {
|
||||
value['custom'] = detail === 'custom'
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="org" label={'Org'} {item} />
|
||||
<ToggleButton value="custom" label="Custom" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
<div class="grow">
|
||||
|
||||
@@ -367,6 +367,7 @@
|
||||
}
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
icon={User}
|
||||
@@ -376,6 +377,7 @@
|
||||
value="user"
|
||||
position="left"
|
||||
label="User"
|
||||
{item}
|
||||
/>
|
||||
<!-- <ToggleButton light size="xs" value="group" position="center">Group</ToggleButton> -->
|
||||
<ToggleButton
|
||||
@@ -386,6 +388,7 @@
|
||||
value="folder"
|
||||
position="right"
|
||||
label="Folder"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
import { ResourceService } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import IconedResourceType from './IconedResourceType.svelte'
|
||||
import { Button, ClearableInput, Popup } from './common'
|
||||
import { Button, ClearableInput } from './common'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import Label from './Label.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
@@ -50,66 +51,65 @@
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-end',
|
||||
middleware: [offset(8), flip(), shift()]
|
||||
}}
|
||||
containerClasses="border rounded-lg shadow-lg bg-surface p-4 w-[500px] h-[500px] "
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button nonCaptureEvent size="xs" color="dark">Select resource type</Button>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="flex flex-col gap-2 h-full p-4">
|
||||
<ClearableInput bind:value={search} placeholder="Search resource..." />
|
||||
|
||||
<div class="flex flex-col gap-2 h-full">
|
||||
<ClearableInput bind:value={search} placeholder="Search resource..." />
|
||||
<div class="overflow-y-scroll h-full">
|
||||
<div
|
||||
class="grid sm:grid-cols-2 md:grid-cols-3 gap-x-2 gap-y-1 items-center overflow-x-hidden"
|
||||
>
|
||||
{#if nonePickable && search === ''}
|
||||
{@const isPicked = value === undefined}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={isPicked ? 'blue' : 'dark'}
|
||||
btnClasses={isPicked ? '!border-2' : 'm-[1px]'}
|
||||
disabled={notPickable}
|
||||
on:click={() => {
|
||||
onClick(undefined)
|
||||
close()
|
||||
}}
|
||||
>
|
||||
None
|
||||
</Button>
|
||||
{/if}
|
||||
{#each filteredResources as r}
|
||||
{@const isPicked = value === r}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={isPicked ? 'blue' : 'light'}
|
||||
btnClasses={isPicked ? '!border-2' : 'm-[1px]'}
|
||||
disabled={notPickable}
|
||||
on:click={() => {
|
||||
onClick(r)
|
||||
close()
|
||||
}}
|
||||
>
|
||||
<IconedResourceType name={r} after={true} width="20px" height="20px" />
|
||||
</Button>
|
||||
{/each}
|
||||
|
||||
<div class="overflow-y-scroll h-full">
|
||||
<div
|
||||
class="grid sm:grid-cols-2 md:grid-cols-3 gap-x-2 gap-y-1 items-center overflow-x-hidden"
|
||||
>
|
||||
{#if nonePickable && search === ''}
|
||||
{@const isPicked = value === undefined}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={isPicked ? 'blue' : 'dark'}
|
||||
btnClasses={isPicked ? '!border-2' : 'm-[1px]'}
|
||||
disabled={notPickable}
|
||||
on:click={() => {
|
||||
onClick(undefined)
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
None
|
||||
</Button>
|
||||
{/if}
|
||||
{#each filteredResources as r}
|
||||
{@const isPicked = value === r}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={isPicked ? 'blue' : 'light'}
|
||||
btnClasses={isPicked ? '!border-2' : 'm-[1px]'}
|
||||
disabled={notPickable}
|
||||
on:click={() => {
|
||||
onClick(r)
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
<IconedResourceType name={r} after={true} width="20px" height="20px" />
|
||||
</Button>
|
||||
{/each}
|
||||
|
||||
{#if filteredResources.length === 0 && search !== ''}
|
||||
<div class="text-tertiary text-sm">No resources found</div>
|
||||
{/if}
|
||||
{#if filteredResources.length === 0 && search !== ''}
|
||||
<div class="text-tertiary text-sm">No resources found</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-row items-center w-full justify-between">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import SharedBadge from './SharedBadge.svelte'
|
||||
|
||||
import TimeAgo from './TimeAgo.svelte'
|
||||
import Popup from './common/popup/Popup.svelte'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import { Calendar, CornerDownLeft } from 'lucide-svelte'
|
||||
import RunFormAdvancedPopup from './RunFormAdvancedPopup.svelte'
|
||||
@@ -58,7 +58,6 @@
|
||||
export let jsonView = false
|
||||
|
||||
let reloadArgs = 0
|
||||
let blockPopupOpen = false
|
||||
let jsonEditor: JsonInputs | undefined = undefined
|
||||
let schemaHeight = 0
|
||||
|
||||
@@ -206,7 +205,7 @@
|
||||
{scheduledForStr ? 'Schedule to run later' : buttonText}
|
||||
</Button>
|
||||
<div>
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -221,22 +220,23 @@
|
||||
})
|
||||
]
|
||||
}}
|
||||
let:close
|
||||
blockOpen={blockPopupOpen}
|
||||
closeButton
|
||||
usePointerDownOutside
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button nonCaptureEvent startIcon={{ icon: Calendar }} size="xs" color="light">
|
||||
Advanced
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<RunFormAdvancedPopup
|
||||
bind:scheduledForStr
|
||||
bind:invisible_to_owner
|
||||
bind:overrideTag
|
||||
bind:runnable
|
||||
on:close={() => close(null)}
|
||||
/>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<RunFormAdvancedPopup
|
||||
bind:scheduledForStr
|
||||
bind:invisible_to_owner
|
||||
bind:overrideTag
|
||||
bind:runnable
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
{#if overrideTag}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { SCRIPT_VIEW_SHOW_SCHEDULE_RUN_LATER } from '$lib/consts'
|
||||
import Label from './Label.svelte'
|
||||
import CloseButton from './common/CloseButton.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { userStore, workerTags, workspaceStore } from '$lib/stores'
|
||||
@@ -36,15 +35,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4 p-2">
|
||||
<div class="flex flex-col gap-4 p-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if SCRIPT_VIEW_SHOW_SCHEDULE_RUN_LATER}
|
||||
<div class="">
|
||||
<Label label="Schedule to run later">
|
||||
<svelte:fragment slot="action">
|
||||
<CloseButton on:close noBg />
|
||||
</svelte:fragment>
|
||||
</Label>
|
||||
<Label label="Schedule to run later" />
|
||||
|
||||
<div class="flex flex-row items-center my-1">
|
||||
<div>
|
||||
|
||||
@@ -335,27 +335,30 @@
|
||||
{#if linkedSecretCandidates?.includes(argName)}
|
||||
<div>
|
||||
<ToggleButtonGroup
|
||||
selected={linkedSecret == argName}
|
||||
selected={linkedSecret == argName ? 'secret' : 'inlined'}
|
||||
on:selected={(e) => {
|
||||
if (e.detail) {
|
||||
if (e.detail === 'secret') {
|
||||
linkedSecret = argName
|
||||
} else if (linkedSecret == argName) {
|
||||
linkedSecret = undefined
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
value={false}
|
||||
value="inlined"
|
||||
size="sm"
|
||||
label="Inlined"
|
||||
tooltip="The value is inlined in the resource and thus has no special treatment."
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value={true}
|
||||
value="secret"
|
||||
size="sm"
|
||||
label="Secret"
|
||||
tooltip="The value will be stored in a newly created linked secret variable at the same path. That variable can be permissioned differently, will be treated as a secret the UI, operators will not be able to load it and every access will generate a corresponding audit log."
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>{/if}</svelte:fragment
|
||||
|
||||
@@ -902,6 +902,7 @@
|
||||
script.kind = detail
|
||||
initContent(script.language, detail, template)
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
{#each scriptKindOptions as { value, title, desc, documentationLink, Icon }}
|
||||
<ToggleButton
|
||||
@@ -911,6 +912,7 @@
|
||||
{documentationLink}
|
||||
icon={Icon}
|
||||
showTooltipIcon={Boolean(desc)}
|
||||
{item}
|
||||
/>
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
items = (
|
||||
await ScriptService.listScripts({
|
||||
workspace: $workspaceStore!,
|
||||
kinds: kinds.join(','),
|
||||
kinds: kinds.join(',')
|
||||
})
|
||||
).map((script) => ({
|
||||
value: script.path,
|
||||
@@ -85,9 +85,9 @@
|
||||
<div class="flex flex-row items-center gap-4 w-full mt-2">
|
||||
{#if options.length > 1}
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={itemKind}>
|
||||
<ToggleButtonGroup bind:selected={itemKind} let:item>
|
||||
{#each options as [label, value, icon, selectedColor]}
|
||||
<ToggleButton {icon} {disabled} {value} {label} {selectedColor} />
|
||||
<ToggleButton {icon} {disabled} {value} {label} {selectedColor} {item} />
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -190,9 +190,15 @@
|
||||
{#if selectedVersionIndex !== undefined && versions?.slice(selectedVersionIndex + 1).length}
|
||||
<div class="p-2 flex flex-row items-center gap-2 h-8">
|
||||
<div class="w-min">
|
||||
<ToggleButtonGroup bind:selected={showDiff}>
|
||||
<ToggleButton light small value={false} label="Code" icon={Code} />
|
||||
<ToggleButton light small value={true} label="Diff" icon={Diff} />
|
||||
<ToggleButtonGroup
|
||||
selected={showDiff ? 'diff' : 'code'}
|
||||
let:item
|
||||
on:selected={({ detail }) => {
|
||||
showDiff = detail === 'diff'
|
||||
}}
|
||||
>
|
||||
<ToggleButton light small value="code" label="Code" icon={Code} {item} />
|
||||
<ToggleButton light small value="diff" label="Diff" icon={Diff} {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -122,9 +122,13 @@
|
||||
{#if own}
|
||||
<div class="flex flex-row flex-wrap gap-2 items-center">
|
||||
<div>
|
||||
<ToggleButtonGroup bind:selected={ownerKind} on:selected={() => (owner = '')}>
|
||||
<ToggleButton value="user" size="xs" label="User" />
|
||||
<ToggleButton value="group" size="xs" label="Group" />
|
||||
<ToggleButtonGroup
|
||||
bind:selected={ownerKind}
|
||||
on:selected={() => (owner = '')}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="user" size="xs" label="User" {item} />
|
||||
<ToggleButton value="group" size="xs" label="Group" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{#key ownerKind}
|
||||
@@ -162,9 +166,10 @@
|
||||
}
|
||||
loadAcls()
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="viewer" size="xs" label="Viewer" />
|
||||
<ToggleButton value="writer" size="xs" label="Writer" />
|
||||
<ToggleButton value="viewer" size="xs" label="Viewer" {item} />
|
||||
<ToggleButton value="writer" size="xs" label="Writer" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{:else}{write}{/if}</td
|
||||
|
||||
@@ -133,9 +133,16 @@
|
||||
disableVariablePicker = undefined
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
{#each FIELD_SETTINGS as x}
|
||||
<ToggleButton value={x[1]} label={x[0]} tooltip={x[2]} showTooltipIcon={Boolean(x[2])} />
|
||||
<ToggleButton
|
||||
value={x[1]}
|
||||
label={x[0]}
|
||||
tooltip={x[2]}
|
||||
showTooltipIcon={Boolean(x[2])}
|
||||
{item}
|
||||
/>
|
||||
{/each}
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
|
||||
@@ -292,15 +292,22 @@
|
||||
sendUserToast('User updated')
|
||||
listUsers(activeOnly)
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value={'user'} size="xs" label="User" />
|
||||
<ToggleButton value={'user'} size="xs" label="User" {item} />
|
||||
<ToggleButton
|
||||
value={'devops'}
|
||||
size="xs"
|
||||
label="Devops"
|
||||
tooltip="Devops is a role that grants visibilty similar to that of a super admin, but without giving all rights. For example devops users can see service logs and crtical alerts. You can think of it as a 'readonly' super admin"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
value={'super_admin'}
|
||||
size="xs"
|
||||
label="Superadmin"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton value={'super_admin'} size="xs" label="Superadmin" />
|
||||
</ToggleButtonGroup>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</script>
|
||||
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected>
|
||||
<ToggleButton value="hub" icon={WindmillIcon} label="Hub" />
|
||||
<ToggleButton value="workspace" icon={Building} label="Workspace" />
|
||||
<ToggleButtonGroup bind:selected let:item>
|
||||
<ToggleButton value="hub" icon={WindmillIcon} label="Hub" {item} />
|
||||
<ToggleButton value="workspace" icon={Building} label="Workspace" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
</script>
|
||||
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup id="flow-editor-flow-providers" bind:selected>
|
||||
<ToggleButton value="all" label="All" light small />
|
||||
<ToggleButton value="hub" icon={WindmillIcon2} label="Hub" light small />
|
||||
<ToggleButton value="workspace" icon={Building} label="Workspace" light small />
|
||||
<ToggleButtonGroup id="flow-editor-flow-providers" bind:selected let:item>
|
||||
<ToggleButton value="all" label="All" light small {item} />
|
||||
<ToggleButton value="hub" icon={WindmillIcon2} label="Hub" light small {item} />
|
||||
<ToggleButton value="workspace" icon={Building} label="Workspace" light small {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -183,10 +183,10 @@
|
||||
>{/if}{/if}
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<ToggleButtonGroup bind:selected={editorKind}>
|
||||
<ToggleButton value="plain" label="Plain" />
|
||||
<ToggleButton value="json" label="Json" />
|
||||
<ToggleButton value="yaml" label="YAML" />
|
||||
<ToggleButtonGroup bind:selected={editorKind} let:item>
|
||||
<ToggleButton value="plain" label="Plain" {item} />
|
||||
<ToggleButton value="json" label="Json" {item} />
|
||||
<ToggleButton value="yaml" label="YAML" {item} />
|
||||
</ToggleButtonGroup>
|
||||
{#if editorKind == 'plain'}
|
||||
<textarea
|
||||
|
||||
@@ -263,13 +263,21 @@
|
||||
}
|
||||
}}
|
||||
class="mb-4"
|
||||
let:item
|
||||
>
|
||||
<ToggleButton position="left" value="normal" size="sm" label="Any jobs within worker tags" />
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="normal"
|
||||
size="sm"
|
||||
label="Any jobs within worker tags"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
position="dedicated"
|
||||
value="dedicated"
|
||||
size="sm"
|
||||
label="Dedicated to a script/flow"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
{#if selected == 'normal'}
|
||||
@@ -682,9 +690,10 @@
|
||||
envvar.value = undefined
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton position="left" value="dynamic" label="Dynamic" />
|
||||
<ToggleButton position="right" value="static" label="Static" />
|
||||
<ToggleButton value="dynamic" label="Dynamic" {item} />
|
||||
<ToggleButton value="static" label="Static" {item} />
|
||||
</ToggleButtonGroup>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
import { Button } from '$lib/components/common'
|
||||
import { loadIcon } from '../icon'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
import Menu from '$lib/components/common/menu/MenuV2.svelte'
|
||||
import { AppButton } from '../buttons'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import { Menubar, Menu } from '$lib/components/meltComponents'
|
||||
import { melt } from '@melt-ui/svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -96,71 +97,83 @@
|
||||
|
||||
{#if render}
|
||||
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
|
||||
<Menu placement="bottom-end" justifyEnd={false} on:close on:open>
|
||||
<div slot="trigger">
|
||||
<Button
|
||||
on:pointerdown={(e) => e.stopPropagation()}
|
||||
btnClasses={twMerge(
|
||||
css?.button?.class,
|
||||
'wm-button',
|
||||
'wm-menu-button',
|
||||
resolvedConfig.fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
wrapperClasses={twMerge(
|
||||
'wm-button-container',
|
||||
'wm-menu-button-container',
|
||||
resolvedConfig.fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
style={css?.button?.style}
|
||||
size={resolvedConfig.size}
|
||||
color={resolvedConfig.color}
|
||||
nonCaptureEvent
|
||||
>
|
||||
<span class="truncate inline-flex gap-2 items-center">
|
||||
{#if resolvedConfig.beforeIcon}
|
||||
{#key resolvedConfig.beforeIcon}
|
||||
<div class="min-w-4" bind:this={beforeIconComponent} />
|
||||
{/key}
|
||||
{/if}
|
||||
{#if resolvedConfig.label && resolvedConfig.label?.length > 0}
|
||||
<div>{resolvedConfig.label}</div>
|
||||
{/if}
|
||||
{#if resolvedConfig.afterIcon}
|
||||
{#key resolvedConfig.afterIcon}
|
||||
<div class="min-w-4" bind:this={afterIconComponent} />
|
||||
{/key}
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Menubar let:createMenu class={resolvedConfig.fillContainer ? 'w-full h-full' : ''}>
|
||||
<Menu
|
||||
{createMenu}
|
||||
placement="bottom-end"
|
||||
justifyEnd={false}
|
||||
on:close
|
||||
on:open
|
||||
class={resolvedConfig.fillContainer ? 'w-full h-full' : ''}
|
||||
usePointerDownOutside={true}
|
||||
>
|
||||
<svelte:fragment slot="trigger" let:trigger>
|
||||
<div use:melt={trigger} class="w-full h-full">
|
||||
<Button
|
||||
on:pointerdown={(e) => e.stopPropagation()}
|
||||
btnClasses={twMerge(
|
||||
css?.button?.class,
|
||||
'wm-button',
|
||||
'wm-menu-button',
|
||||
resolvedConfig.fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
wrapperClasses={twMerge(
|
||||
'wm-button-container',
|
||||
'wm-menu-button-container',
|
||||
resolvedConfig.fillContainer ? 'w-full h-full' : ''
|
||||
)}
|
||||
style={css?.button?.style}
|
||||
size={resolvedConfig.size}
|
||||
color={resolvedConfig.color}
|
||||
nonCaptureEvent
|
||||
>
|
||||
<span class="truncate inline-flex gap-2 items-center">
|
||||
{#if resolvedConfig.beforeIcon}
|
||||
{#key resolvedConfig.beforeIcon}
|
||||
<div class="min-w-4" bind:this={beforeIconComponent} />
|
||||
{/key}
|
||||
{/if}
|
||||
{#if resolvedConfig.label && resolvedConfig.label?.length > 0}
|
||||
<div>{resolvedConfig.label}</div>
|
||||
{/if}
|
||||
{#if resolvedConfig.afterIcon}
|
||||
{#key resolvedConfig.afterIcon}
|
||||
<div class="min-w-4" bind:this={afterIconComponent} />
|
||||
{/key}
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="flex flex-col w-full p-1 gap-2">
|
||||
{#if menuItems.length > 0}
|
||||
{#each menuItems as actionButton, actionIndex (actionButton?.id)}
|
||||
{#if actionButton.type == 'buttoncomponent'}
|
||||
<div
|
||||
on:pointerup={() => {
|
||||
outputs?.result.set({
|
||||
latestButtonClicked: actionButton.id
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AppButton
|
||||
extraKey={'idx' + actionIndex}
|
||||
{render}
|
||||
id={actionButton.id}
|
||||
customCss={actionButton.customCss}
|
||||
configuration={actionButton.configuration}
|
||||
recomputeIds={actionButton.recomputeIds}
|
||||
componentInput={actionButton.componentInput}
|
||||
noWFull={false}
|
||||
isMenuItem={true}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</Menu>
|
||||
<div class="flex flex-col w-full p-1 gap-2 max-h-[50vh] overflow-y-auto">
|
||||
{#if menuItems.length > 0}
|
||||
{#each menuItems as actionButton, actionIndex (actionButton?.id)}
|
||||
{#if actionButton.type == 'buttoncomponent'}
|
||||
<div
|
||||
on:pointerup={() => {
|
||||
outputs?.result.set({
|
||||
latestButtonClicked: actionButton.id
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AppButton
|
||||
extraKey={'idx' + actionIndex}
|
||||
{render}
|
||||
id={actionButton.id}
|
||||
customCss={actionButton.customCss}
|
||||
configuration={actionButton.configuration}
|
||||
recomputeIds={actionButton.recomputeIds}
|
||||
componentInput={actionButton.componentInput}
|
||||
noWFull={false}
|
||||
isMenuItem={true}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</Menu>
|
||||
</Menubar>
|
||||
</AlignWrapper>
|
||||
{/if}
|
||||
|
||||
+21
-17
@@ -11,12 +11,12 @@
|
||||
import AppSelect from '../../inputs/AppSelect.svelte'
|
||||
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Popup } from '$lib/components/common'
|
||||
import { Plug2 } from 'lucide-svelte'
|
||||
import ComponentOutputViewer from '$lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte'
|
||||
import { connectOutput } from '$lib/components/apps/editor/appUtils'
|
||||
import RowWrapper from '../../layout/RowWrapper.svelte'
|
||||
import type { ICellRendererParams } from 'ag-grid-community'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
|
||||
export let p: ICellRendererParams<any>
|
||||
export let id: string
|
||||
@@ -124,34 +124,38 @@
|
||||
|
||||
{#if $connectingInput.opened}
|
||||
<div class="absolute z-50 left-8 -top-[10px]">
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom-start'
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<button
|
||||
class="bg-red-500/70 border border-red-600 px-1 py-0.5"
|
||||
title="Outputs"
|
||||
aria-label="Open output"><Plug2 size={12} /></button
|
||||
>
|
||||
</svelte:fragment>
|
||||
<ComponentOutputViewer
|
||||
suffix="table"
|
||||
on:select={({ detail }) => {
|
||||
const tableId = action.id.split('_')[0]
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<ComponentOutputViewer
|
||||
suffix="table"
|
||||
on:select={({ detail }) => {
|
||||
const tableId = action.id.split('_')[0]
|
||||
|
||||
connectOutput(
|
||||
connectingInput,
|
||||
action.type,
|
||||
tableId,
|
||||
`inputs.${action.id}[${rowIndex}].${detail}`
|
||||
)
|
||||
}}
|
||||
componentId={action.id}
|
||||
/>
|
||||
</Popup>
|
||||
connectOutput(
|
||||
connectingInput,
|
||||
action.type,
|
||||
tableId,
|
||||
`inputs.${action.id}[${rowIndex}].${detail}`
|
||||
)
|
||||
}}
|
||||
componentId={action.id}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -39,12 +39,12 @@
|
||||
import AppSelect from '../../inputs/AppSelect.svelte'
|
||||
import RowWrapper from '../../layout/RowWrapper.svelte'
|
||||
import ResolveStyle from '../../helpers/ResolveStyle.svelte'
|
||||
import { Popup } from '$lib/components/common'
|
||||
import ComponentOutputViewer from '$lib/components/apps/editor/contextPanel/ComponentOutputViewer.svelte'
|
||||
import { EyeIcon, Plug2 } from 'lucide-svelte'
|
||||
import AppCell from './AppCell.svelte'
|
||||
import sum from 'hash-sum'
|
||||
import RefreshButton from '$lib/components/apps/components/helpers/RefreshButton.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
@@ -553,31 +553,35 @@
|
||||
|
||||
{#if $connectingInput.opened}
|
||||
<div class="absolute z-50 left-8 -top-[10px]">
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom-start'
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<button
|
||||
class="bg-red-500/70 border border-red-600 px-1 py-0.5"
|
||||
title="Outputs"
|
||||
aria-label="Open output"><Plug2 size={12} /></button
|
||||
>
|
||||
</svelte:fragment>
|
||||
<ComponentOutputViewer
|
||||
suffix="table"
|
||||
on:select={({ detail }) =>
|
||||
connectOutput(
|
||||
connectingInput,
|
||||
'buttoncomponent',
|
||||
actionButton.id,
|
||||
detail
|
||||
)}
|
||||
componentId={actionButton.id}
|
||||
/>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<ComponentOutputViewer
|
||||
suffix="table"
|
||||
on:select={({ detail }) =>
|
||||
connectOutput(
|
||||
connectingInput,
|
||||
'buttoncomponent',
|
||||
actionButton.id,
|
||||
detail
|
||||
)}
|
||||
componentId={actionButton.id}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -70,8 +70,7 @@
|
||||
import DeploymentHistory from './DeploymentHistory.svelte'
|
||||
import Awareness from '$lib/components/Awareness.svelte'
|
||||
import { secondaryMenuLeftStore, secondaryMenuRightStore } from './settingsPanel/secondaryMenu'
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import AppEditorTutorial from './AppEditorTutorial.svelte'
|
||||
import AppTimeline from './AppTimeline.svelte'
|
||||
import type DiffDrawer from '$lib/components/DiffDrawer.svelte'
|
||||
@@ -86,7 +85,6 @@
|
||||
import { getDeleteInput } from '../components/display/dbtable/queries/delete'
|
||||
import { collectOneOfFields } from './appUtils'
|
||||
import Summary from '$lib/components/Summary.svelte'
|
||||
import ToggleEnable from '$lib/components/common/toggleButton-v2/ToggleEnable.svelte'
|
||||
import HideButton from './settingsPanel/HideButton.svelte'
|
||||
import DeployOverrideConfirmationModal from '$lib/components/common/confirmationModal/DeployOverrideConfirmationModal.svelte'
|
||||
import { computeS3FileInputPolicy, computeWorkspaceS3FileInputPolicy } from './appUtilsS3'
|
||||
@@ -1548,70 +1546,92 @@
|
||||
/>
|
||||
|
||||
{#if $app}
|
||||
<ToggleButtonGroup class="h-[30px]" bind:selected={$app.fullscreen}>
|
||||
<ToggleButtonGroup
|
||||
class="h-[30px]"
|
||||
selected={$app.fullscreen ? 'true' : 'false'}
|
||||
on:selected={({ detail }) => {
|
||||
$app.fullscreen = detail === 'true'
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
icon={AlignHorizontalSpaceAround}
|
||||
value={false}
|
||||
value={'false'}
|
||||
tooltip="The max width is 1168px and the content stay centered instead of taking the full page width"
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
tooltip="The width is of the app if the full width of its container"
|
||||
icon={Expand}
|
||||
value={true}
|
||||
value={'true'}
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if $app}
|
||||
<ToggleButtonGroup
|
||||
class="h-[30px]"
|
||||
on:selected={(e) => {
|
||||
setTheme(e.detail)
|
||||
on:selected={({ detail }) => {
|
||||
console.log('dbg detail', detail)
|
||||
const theme = detail === 'dark' ? true : detail === 'sun' ? false : undefined
|
||||
console.log('dbg settheme', theme)
|
||||
setTheme(theme)
|
||||
}}
|
||||
bind:selected={$app.darkMode}
|
||||
selected={$app.darkMode === undefined ? 'auto' : $app.darkMode ? 'dark' : 'sun'}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
icon={SunMoon}
|
||||
value={undefined}
|
||||
value={'auto'}
|
||||
tooltip="The app mode between dark/light is automatic"
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
icon={Sun}
|
||||
value={false}
|
||||
value={'sun'}
|
||||
tooltip="Force light mode"
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
tooltip="Force dark mode"
|
||||
icon={Moon}
|
||||
value={true}
|
||||
value={'dark'}
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2">
|
||||
<ToggleButtonGroup class="h-[30px]" bind:selected={$breakpoint}>
|
||||
<ToggleButtonGroup class="h-[30px]" bind:selected={$breakpoint} let:item>
|
||||
<ToggleButton
|
||||
tooltip="Computer View"
|
||||
icon={Laptop2}
|
||||
value={'lg'}
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
tooltip="Mobile View"
|
||||
icon={Smartphone}
|
||||
value={'sm'}
|
||||
iconProps={{ size: 16 }}
|
||||
{item}
|
||||
/>
|
||||
{#if $breakpoint === 'sm'}
|
||||
<ToggleEnable
|
||||
tooltip="Desktop view is enabled by default. Enable this to customize the layout of the components for the mobile view"
|
||||
label="Enable mobile view for smaller screens"
|
||||
<Toggle
|
||||
size="xs"
|
||||
options={{
|
||||
right: 'Enable mobile view for smaller screens',
|
||||
rightTooltip:
|
||||
'Desktop view is enabled by default. Enable this to customize the layout of the components for the mobile view'
|
||||
}}
|
||||
textClass="text-2xs whitespace-nowrap white !w-full"
|
||||
bind:checked={$app.mobileViewOnSmallerScreens}
|
||||
iconProps={{ size: 16 }}
|
||||
iconOnly={false}
|
||||
class="flex flex-row px-2 items-center"
|
||||
/>
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
@@ -1660,33 +1680,11 @@
|
||||
<Awareness />
|
||||
{/if}
|
||||
<div class="flex flex-row gap-2 justify-end items-center overflow-visible">
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<Dropdown items={moreItems} class="center-center">
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<Button nonCaptureEvent size="xs" color="light">
|
||||
<div class="flex flex-row items-center">
|
||||
<MoreVertical size={14} />
|
||||
</div>
|
||||
</Button>
|
||||
<Button nonCaptureEvent size="xs" color="light" startIcon={{ icon: MoreVertical }} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each moreItems as item}
|
||||
<MenuItem
|
||||
on:click={item.action}
|
||||
disabled={item.disabled}
|
||||
class={item.disabled ? 'opacity-50' : ''}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<svelte:component this={item.icon} size={14} />
|
||||
{item.displayName}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</Dropdown>
|
||||
<AppEditorTutorial bind:this={appEditorTutorial} />
|
||||
|
||||
<div class="hidden md:inline relative overflow-visible">
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import AppTutorials from '../../AppTutorials.svelte'
|
||||
import { BookOpen } from 'lucide-svelte'
|
||||
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import TutorialItem from '$lib/components/tutorials/TutorialItem.svelte'
|
||||
import MenuItem from '$lib/components/common/menu/MenuItem.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { BookOpen, CheckCircle, Circle, RefreshCw, CheckCheck } from 'lucide-svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { resetAllTodos, skipAllTodos } from '$lib/tutorialUtils'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
@@ -45,63 +41,60 @@
|
||||
appTutorials?.runTutorialById('simpleapptutorial')
|
||||
}
|
||||
}
|
||||
|
||||
async function getTutorialItems() {
|
||||
return [
|
||||
{
|
||||
displayName: 'App tutorial',
|
||||
action: () => appTutorials?.runTutorialById('simpleapptutorial'),
|
||||
index: 7,
|
||||
icon: $tutorialsToDo.includes(7) ? Circle : CheckCircle,
|
||||
iconColor: $tutorialsToDo.includes(7) ? undefined : 'green'
|
||||
},
|
||||
{
|
||||
displayName: 'Background runnables',
|
||||
action: () => appTutorials?.runTutorialById('backgroundrunnables'),
|
||||
index: 5,
|
||||
icon: $tutorialsToDo.includes(5) ? Circle : CheckCircle,
|
||||
iconColor: $tutorialsToDo.includes(5) ? undefined : 'green'
|
||||
},
|
||||
{
|
||||
displayName: 'Connection',
|
||||
action: () => appTutorials?.runTutorialById('connection'),
|
||||
index: 6,
|
||||
icon: $tutorialsToDo.includes(6) ? Circle : CheckCircle,
|
||||
iconColor: $tutorialsToDo.includes(6) ? undefined : 'green'
|
||||
},
|
||||
{
|
||||
displayName: 'Reset tutorials',
|
||||
action: () => resetAllTodos(),
|
||||
icon: RefreshCw
|
||||
},
|
||||
{
|
||||
displayName: 'Skip tutorials',
|
||||
action: () => skipAllTodos(),
|
||||
icon: CheckCheck
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
iconOnly
|
||||
startIcon={{
|
||||
icon: BookOpen
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
<TutorialItem
|
||||
on:click={() => appTutorials?.runTutorialById('simpleapptutorial')}
|
||||
label="App tutorial"
|
||||
index={7}
|
||||
/>
|
||||
<TutorialItem
|
||||
on:click={() => appTutorials?.runTutorialById('backgroundrunnables')}
|
||||
label="Background runnables"
|
||||
index={5}
|
||||
/>
|
||||
<TutorialItem
|
||||
on:click={() => appTutorials?.runTutorialById('connection')}
|
||||
label="Connection"
|
||||
index={6}
|
||||
/>
|
||||
|
||||
<div class="border-t border-surface-hover" />
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
resetAllTodos()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-primary flex flex-row items-center text-left gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
Reset tutorials
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem on:click={() => skipAllTodos()}>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-primary flex flex-row items-center text-left gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
Skip tutorials
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
{#key $tutorialsToDo}
|
||||
<Dropdown items={getTutorialItems} hasPadding={false}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
iconOnly
|
||||
startIcon={{
|
||||
icon: BookOpen
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Dropdown>
|
||||
{/key}
|
||||
|
||||
<AppTutorials
|
||||
bind:this={appTutorials}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import type { AppViewerContext } from '../types'
|
||||
import { Anchor, ArrowDownFromLine, Bug, Network, Pen, Plug } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { Button, Popup } from '$lib/components/common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { Button } from '$lib/components/common'
|
||||
import type { AppComponent } from './component'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { connectOutput } from './appUtils'
|
||||
@@ -69,11 +69,8 @@
|
||||
style="left: {id_width}px;"
|
||||
data-connection-button
|
||||
>
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'fixed', placement: 'bottom-start' }}
|
||||
bind:popupHover={connectingPopupHover}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'fixed', placement: 'bottom-start' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<AnimatedButton
|
||||
animate={true}
|
||||
baseRadius="9999px"
|
||||
@@ -89,13 +86,17 @@
|
||||
>
|
||||
</AnimatedButton>
|
||||
</svelte:fragment>
|
||||
<ComponentOutputViewer
|
||||
suffix="connect"
|
||||
on:select={({ detail }) =>
|
||||
connectOutput(connectingInput, component.type, component.id, detail)}
|
||||
componentId={component.id}
|
||||
/>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<ComponentOutputViewer
|
||||
suffix="connect"
|
||||
on:select={({ detail }) =>
|
||||
connectOutput(connectingInput, component.type, component.id, detail)}
|
||||
componentId={component.id}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -258,21 +259,25 @@
|
||||
'text-red-500 px-1 text-2xs py-0.5 font-bold w-fit absolute border border-red-500 -bottom-1 shadow left-1/2 transform -translate-x-1/2 z-50 cursor-pointer'
|
||||
)}
|
||||
>
|
||||
<Popover notClickable placement="bottom" popupClass="!bg-surface border w-96">
|
||||
<Bug size={14} />
|
||||
<span slot="text">
|
||||
<div class="bg-surface">
|
||||
<pre class=" whitespace-pre-wrap text-red-600 bg-surface border w-full p-4 text-xs mb-2"
|
||||
>{error ?? ''}
|
||||
</pre>
|
||||
<Popover notClickable placement="bottom" class="!bg-surface border w-96">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Bug size={14} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
<div class="bg-surface">
|
||||
<pre class="whitespace-pre-wrap text-red-600 bg-surface border w-full p-4 text-xs mb-2"
|
||||
>{error ?? ''}
|
||||
</pre>
|
||||
</div>
|
||||
<Button
|
||||
color="red"
|
||||
variant="border"
|
||||
on:click={() => $openDebugRun?.($errorByComponent[component.id]?.id ?? '')}
|
||||
>Open Debug Runs</Button
|
||||
>
|
||||
</div>
|
||||
<Button
|
||||
color="red"
|
||||
variant="border"
|
||||
on:click={() => $openDebugRun?.($errorByComponent[component.id]?.id ?? '')}
|
||||
>Open Debug Runs</Button
|
||||
>
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import type { DecisionTreeNode } from './component'
|
||||
@@ -69,81 +68,68 @@
|
||||
}
|
||||
|
||||
$: onNodesChange(nodes)
|
||||
|
||||
async function getDropdownItems() {
|
||||
return [
|
||||
// Debug node items
|
||||
...(nodes ?? []).map((node, index) => ({
|
||||
displayName: `Debug node ${node.label}`,
|
||||
action: () => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
$debuggingComponents[id] = index
|
||||
},
|
||||
type: 'action' as const
|
||||
})),
|
||||
// Reset debug mode item
|
||||
{
|
||||
displayName: 'Reset debug mode',
|
||||
action: () => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
},
|
||||
type: 'delete' as const
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key renderCount}
|
||||
<button
|
||||
title={'Debug tabs'}
|
||||
class={classNames(
|
||||
'px-1 text-2xs font-bold rounded cursor-pointer w-fit h-full',
|
||||
componentIsDebugging
|
||||
? ' hover:bg-red-300 hover:text-red-800'
|
||||
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div class="px-1 w-fit">
|
||||
{#if componentIsDebugging}
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{`${isSmall ? '' : 'Debugging node'} ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
|
||||
<button
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
<Dropdown items={getDropdownItems} class="w-fit h-auto" usePointerDownOutside={true}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<button
|
||||
title={'Debug tabs'}
|
||||
class={classNames(
|
||||
'px-1 text-2xs font-bold rounded cursor-pointer w-fit h-full',
|
||||
componentIsDebugging
|
||||
? ' hover:bg-red-300 hover:text-red-800'
|
||||
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
{#if componentIsDebugging}
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{`${isSmall ? '' : 'Debugging node'} ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
|
||||
<button
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<X size={11} />
|
||||
</button>
|
||||
</div>
|
||||
{:else if isSmall}
|
||||
<div class="flex h-full w-fit items-center"><Bug size={11} /></div>
|
||||
{:else}<div class="whitespace-nowrap h-full"
|
||||
>{`Debug nodes (current node: ${currentNodeId})`}</div
|
||||
>{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each nodes ?? [] as node, index}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
|
||||
$debuggingComponents[id] = index
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
{`Debug node ${node.label}`}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{`Reset debug mode`}
|
||||
<X size={11} />
|
||||
</button>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
{:else if isSmall}
|
||||
<div class="flex h-full w-fit items-center"><Bug size={11} /></div>
|
||||
{:else}<div class="whitespace-nowrap h-full"
|
||||
>{`Debug nodes (current node: ${currentNodeId})`}</div
|
||||
>{/if}
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
</Dropdown>
|
||||
{/key}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
jobsById.set({})
|
||||
jobs.set([])
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
label="Editor"
|
||||
@@ -26,6 +27,7 @@
|
||||
disabled={loading}
|
||||
iconProps={{ size: 16 }}
|
||||
class="gap-2"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
label="Preview"
|
||||
@@ -36,5 +38,6 @@
|
||||
id="app-editor-preview-toggle"
|
||||
iconProps={{ size: 16 }}
|
||||
class="gap-2"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Loader2, RefreshCw, TimerReset } from 'lucide-svelte'
|
||||
import Button from '../../common/button/Button.svelte'
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { classNames } from '$lib/utils'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -54,53 +52,20 @@
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<slot:fragment slot="buttonReplacement">
|
||||
<Dropdown {items} class="w-fit h-auto">
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div class="flex flex-row gap-2 text-xs hover:bg-surface-hover px-2 items-center h-7">
|
||||
{#if interval}
|
||||
<Badge color="blue" small>
|
||||
{interval ? `Every ${interval / 1000}s` : 'Once'}
|
||||
</Badge>
|
||||
{/if}
|
||||
|
||||
<div class="flex justify-center items-center">
|
||||
<TimerReset size={14} />
|
||||
</div>
|
||||
</div>
|
||||
</slot:fragment>
|
||||
<svelte:fragment slot="label">
|
||||
<span
|
||||
class={twMerge('text-xs min-w-[2rem] ', interval ? 'text-blue-500' : 'text-tertiary')}
|
||||
>
|
||||
{interval ? `${interval / 1000}s` : 'Once'}
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each items ?? [] as { }, index}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
if (index === 0) {
|
||||
dispatch('setInter', undefined)
|
||||
} else {
|
||||
dispatch('setInter', index * 5000)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{#if index === 0}
|
||||
Once
|
||||
{:else}
|
||||
{`Every ${index * 5} seconds`}
|
||||
{/if}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</Dropdown>
|
||||
</div>
|
||||
{#if interval}
|
||||
<div class="w-full bg-gray-200 rounded-full h-0.5 dark:bg-gray-700">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import { Bug } from 'lucide-svelte'
|
||||
@@ -17,22 +16,50 @@
|
||||
|
||||
export let isManuallySelected: boolean = false
|
||||
let selected: number | null = null
|
||||
|
||||
async function getItems() {
|
||||
return [
|
||||
...tabs.map((_, index) => ({
|
||||
displayName:
|
||||
index === tabs.length - 1
|
||||
? isConditionalDebugMode
|
||||
? `Debug default condition`
|
||||
: `Debug tab ${index + 1}`
|
||||
: `Debug ${isConditionalDebugMode ? 'condition' : 'tab'} ${index + 1}`,
|
||||
action: () => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
selected = index
|
||||
isManuallySelected = true
|
||||
},
|
||||
type: 'action' as const
|
||||
})),
|
||||
{
|
||||
displayName: 'Reset debug mode',
|
||||
action: () => {
|
||||
$componentControl?.[id]?.setTab?.(-1)
|
||||
selected = null
|
||||
isManuallySelected = false
|
||||
},
|
||||
type: 'delete' as const
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
title={isConditionalDebugMode ? 'Debug conditions' : 'Debug tabs'}
|
||||
class={classNames(
|
||||
'text-2xs font-bold w-fit h-full cursor-pointer rounded',
|
||||
isManuallySelected
|
||||
? 'hover:bg-red-200 hover:text-red-800'
|
||||
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
{#key tabs}
|
||||
<Dropdown items={getItems} class="w-fit h-auto" usePointerDownOutside>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div class="px-1">
|
||||
<button
|
||||
title={isConditionalDebugMode ? 'Debug conditions' : 'Debug tabs'}
|
||||
class={classNames(
|
||||
'px-1 text-2xs font-bold w-fit h-full cursor-pointer rounded',
|
||||
isManuallySelected
|
||||
? 'hover:bg-red-200 hover:text-red-800'
|
||||
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
{#if isManuallySelected}
|
||||
<div class="whitespace-nowrap">
|
||||
{#if selected === tabs.length - 1}
|
||||
@@ -51,45 +78,7 @@
|
||||
{:else if isSmall}<Bug size={11} />{:else}
|
||||
{isConditionalDebugMode ? `Debug conditions` : `Debug tabs`}
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each tabs ?? [] as { }, index}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
selected = index
|
||||
isManuallySelected = true
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{#if index === tabs.length - 1}
|
||||
{isConditionalDebugMode ? `Debug default condition` : `Debug tab ${index + 1}`}
|
||||
{:else}
|
||||
{`Debug ${isConditionalDebugMode ? 'condition' : 'tab'} ${index + 1}`}
|
||||
{/if}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(-1)
|
||||
selected = null
|
||||
isManuallySelected = false
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 dark:!text-red-400 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{`Reset debug mode`}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
</Dropdown>
|
||||
{/key}
|
||||
|
||||
@@ -124,27 +124,31 @@
|
||||
{/if}
|
||||
{#if quickStyleProperties?.length}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={richEditorOpen}
|
||||
on:selected={() => {
|
||||
selected={richEditorOpen ? 'true' : 'false'}
|
||||
on:selected={({ detail }) => {
|
||||
richEditorOpen = detail === 'true'
|
||||
if (richEditorOpen !== isQuickMenuOpen) {
|
||||
toggleQuickMenu()
|
||||
richEditorOpen = isQuickMenuOpen
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
value={false}
|
||||
value={'false'}
|
||||
icon={Code}
|
||||
tooltip="Edit the CSS directly"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
value={true}
|
||||
value={'true'}
|
||||
icon={Paintbrush2}
|
||||
tooltip="Open the rich editor to style the component with a visual interface"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
|
||||
@@ -4,13 +4,9 @@
|
||||
|
||||
import { Trash } from 'lucide-svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import NameEditor from './NameEditor.svelte'
|
||||
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { ResourceService } from '$lib/gen'
|
||||
|
||||
export let row: {
|
||||
@@ -52,6 +48,17 @@
|
||||
|
||||
sendUserToast('Component name updated:\n' + name)
|
||||
}
|
||||
|
||||
function getItems() {
|
||||
return [
|
||||
{
|
||||
action: toggleDelete,
|
||||
icon: Trash,
|
||||
displayName: 'Delete',
|
||||
type: 'delete' as const
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
@@ -69,23 +76,6 @@
|
||||
</Cell>
|
||||
|
||||
<Cell last>
|
||||
<div class={twMerge('flex flex-row gap-1 justify-end z-[10000]')}>
|
||||
<button on:pointerdown|stopPropagation>
|
||||
<ButtonDropdown target="#cc_portal" hasPadding={false}>
|
||||
<svelte:fragment slot="items">
|
||||
<MenuItem on:click={toggleDelete}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<Trash size={16} />
|
||||
Delete
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
</div>
|
||||
<Dropdown items={getItems()} />
|
||||
</Cell>
|
||||
</tr>
|
||||
|
||||
@@ -69,8 +69,6 @@
|
||||
getCustomComponents()
|
||||
</script>
|
||||
|
||||
<div id="cc_portal" />
|
||||
|
||||
<div class="p-2 flex flex-col items-start w-auto gap-2 relative">
|
||||
<div class="w-full flex flex-col gap-y-2 pb-8">
|
||||
<div>
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
import { deleteGroup, getGroup, updateGroup } from './groupUtils'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import Cell from '$lib/components/table/Cell.svelte'
|
||||
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { Trash } from 'lucide-svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import GroupNameEditor from './NameEditor.svelte'
|
||||
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
|
||||
export let row: {
|
||||
name: string
|
||||
path: string
|
||||
@@ -27,6 +22,17 @@
|
||||
dispatch('reloadGroups')
|
||||
sendUserToast('Group deleted:\n' + row.name)
|
||||
}
|
||||
|
||||
function getItems() {
|
||||
return [
|
||||
{
|
||||
action: toggleDelete,
|
||||
icon: Trash,
|
||||
displayName: 'Delete',
|
||||
type: 'delete' as const
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
@@ -54,23 +60,6 @@
|
||||
</Cell>
|
||||
|
||||
<Cell last>
|
||||
<div class={twMerge('flex flex-row gap-1 justify-end z-[10000]')}>
|
||||
<button on:pointerdown|stopPropagation>
|
||||
<ButtonDropdown target="#group_portal" hasPadding={false}>
|
||||
<svelte:fragment slot="items">
|
||||
<MenuItem on:click={toggleDelete}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<Trash size={16} />
|
||||
Delete
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
</div>
|
||||
<Dropdown items={getItems()} />
|
||||
</Cell>
|
||||
</tr>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
|
||||
import { Popup } from '$lib/components/common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
@@ -21,29 +20,30 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<Pen size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col w-80 gap-2">
|
||||
<div class="leading-6 font-semibold text-xs">Edit {kind} name</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<input on:keydown={onkeydown} bind:value={editedName} />
|
||||
<Button
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={() => dispatch('update', { path: row.path, name: editedName }) && close(null)}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="flex flex-col w-80 gap-2 p-4">
|
||||
<div class="leading-6 font-semibold text-xs">Edit {kind} name</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<input on:keydown={onkeydown} bind:value={editedName} />
|
||||
<Button
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
dispatch('update', { path: row.path, name: editedName })
|
||||
close()
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import { updateTheme } from './themeUtils'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
|
||||
import { Popup } from '$lib/components/common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
@@ -18,40 +17,38 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button color="light" size="xs2" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<Pen size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col w-80 gap-2">
|
||||
<div class="leading-6 font-semibold text-xs">Edit theme name</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<input bind:value={editedName} />
|
||||
<Button
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
if (!$workspaceStore) return
|
||||
await updateTheme($workspaceStore, row.path, {
|
||||
value: {
|
||||
...row,
|
||||
name: editedName
|
||||
}
|
||||
})
|
||||
dispatch('reloadThemes')
|
||||
close(null)
|
||||
sendUserToast('Theme name updated:\n' + editedName)
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="flex flex-col w-80 gap-2 p-4">
|
||||
<div class="leading-6 font-semibold text-xs">Edit theme name</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<input bind:value={editedName} />
|
||||
<Button
|
||||
color="dark"
|
||||
size="xs"
|
||||
on:click={async () => {
|
||||
if (!$workspaceStore) return
|
||||
await updateTheme($workspaceStore, row.path, {
|
||||
value: {
|
||||
...row,
|
||||
name: editedName
|
||||
}
|
||||
})
|
||||
dispatch('reloadThemes')
|
||||
close()
|
||||
sendUserToast('Theme name updated:\n' + editedName)
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
import ThemeNameEditor from './ThemeNameEditor.svelte'
|
||||
|
||||
import ThemeDrawer from './ThemeDrawer.svelte'
|
||||
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
|
||||
export let previewThemePath: string | undefined = undefined
|
||||
|
||||
@@ -134,6 +132,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function getDropdownItems() {
|
||||
return [
|
||||
{
|
||||
action: () => themeDrawer?.openDrawer(),
|
||||
icon: Code,
|
||||
displayName: 'View code',
|
||||
type: 'action' as const
|
||||
},
|
||||
{
|
||||
action: () => fork(row.path),
|
||||
icon: GitBranch,
|
||||
displayName: 'Fork',
|
||||
type: 'action' as const
|
||||
},
|
||||
{
|
||||
action: () => makeDefaultTheme(row.path),
|
||||
icon: Pin,
|
||||
displayName: 'Make default',
|
||||
type: 'action' as const,
|
||||
disabled: row.path === DEFAULT_THEME
|
||||
},
|
||||
{
|
||||
action: toggleDelete,
|
||||
icon: Trash,
|
||||
displayName: 'Delete',
|
||||
type: 'delete' as const,
|
||||
disabled: row.path === DEFAULT_THEME
|
||||
}
|
||||
]
|
||||
}
|
||||
let themeDrawer: ThemeDrawer
|
||||
</script>
|
||||
|
||||
@@ -172,55 +200,7 @@
|
||||
<Button color="dark" size="xs2" on:click={apply}>Apply</Button>
|
||||
{/if}
|
||||
|
||||
<button on:pointerdown|stopPropagation>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<svelte:fragment slot="items">
|
||||
<MenuItem on:click={() => themeDrawer?.openDrawer()}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<Code size={16} />
|
||||
View code
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem on:click={() => fork(row.path)}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<GitBranch size={16} />
|
||||
Fork
|
||||
</div>
|
||||
</MenuItem>
|
||||
{#if row.path !== DEFAULT_THEME}
|
||||
<MenuItem on:click={() => makeDefaultTheme(row.path)}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-primary flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<Pin size={16} />
|
||||
Make default
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem on:click={toggleDelete}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 flex flex-row items-center text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<Trash size={16} />
|
||||
Delete
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
<Dropdown items={getDropdownItems} />
|
||||
</div>
|
||||
</Cell>
|
||||
</tr>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Pencil } from 'lucide-svelte'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import IdEditorInput from '$lib/components/IdEditorInput.svelte'
|
||||
import { Popup } from '$lib/components/common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
|
||||
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
$: reservedIds = allItems($app.grid, $app.subgrids).map((item) => item.id)
|
||||
</script>
|
||||
|
||||
<Popup let:close floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<button
|
||||
on:click={() => {
|
||||
$selectedComponent = [id]
|
||||
@@ -28,13 +28,17 @@
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
<IdEditorInput
|
||||
initialId={id}
|
||||
on:close={() => close(null)}
|
||||
on:save={(e) => {
|
||||
dispatch('save', e.detail)
|
||||
close(null)
|
||||
}}
|
||||
{reservedIds}
|
||||
/>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="p-4">
|
||||
<IdEditorInput
|
||||
initialId={id}
|
||||
on:close={() => close()}
|
||||
on:save={(e) => {
|
||||
dispatch('save', e.detail)
|
||||
close()
|
||||
}}
|
||||
{reservedIds}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { Button, Popup, SecondsInput } from '$lib/components/common'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import { Button, SecondsInput } from '$lib/components/common'
|
||||
import { Database } from 'lucide-svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
|
||||
export let cache_ttl: number | undefined
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -16,7 +17,7 @@
|
||||
]
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
nonCaptureEvent={true}
|
||||
btnClasses={Boolean(cache_ttl)
|
||||
@@ -30,28 +31,30 @@
|
||||
title="Cache settings"
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary">
|
||||
<Toggle
|
||||
checked={Boolean(cache_ttl)}
|
||||
on:change={() => {
|
||||
if (cache_ttl != undefined) {
|
||||
cache_ttl = undefined
|
||||
} else {
|
||||
cache_ttl = 600
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Cache the results for each possible inputs'
|
||||
}}
|
||||
/>
|
||||
<div class="mb-4">
|
||||
<span class="text-xs font-bold">How long to keep cache valid</span>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="block text-primary p-4">
|
||||
<Toggle
|
||||
checked={Boolean(cache_ttl)}
|
||||
on:change={() => {
|
||||
if (cache_ttl != undefined) {
|
||||
cache_ttl = undefined
|
||||
} else {
|
||||
cache_ttl = 600
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'Cache the results for each possible inputs'
|
||||
}}
|
||||
/>
|
||||
<div class="mb-4">
|
||||
<span class="text-xs font-bold">How long to keep cache valid</span>
|
||||
|
||||
{#if cache_ttl}
|
||||
<SecondsInput bind:seconds={cache_ttl} />
|
||||
{:else}
|
||||
<SecondsInput disabled />
|
||||
{/if}
|
||||
{#if cache_ttl}
|
||||
<SecondsInput bind:seconds={cache_ttl} />
|
||||
{:else}
|
||||
<SecondsInput disabled />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Button, Drawer, DrawerContent, Popup } from '$lib/components/common'
|
||||
import { Button, Drawer, DrawerContent } from '$lib/components/common'
|
||||
import { base } from '$lib/base'
|
||||
import FlowModuleScript from '$lib/components/flows/content/FlowModuleScript.svelte'
|
||||
import FlowPathViewer from '$lib/components/flows/content/FlowPathViewer.svelte'
|
||||
@@ -23,6 +23,7 @@
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import { ExternalLink, Eye, GitFork, Pen, RefreshCw, Trash } from 'lucide-svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
|
||||
export let runnable: RunnableByPath
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
@@ -180,7 +181,7 @@
|
||||
Fork
|
||||
</Button>
|
||||
{/if}
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -196,7 +197,7 @@
|
||||
]
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
nonCaptureEvent={true}
|
||||
btnClasses={'bg-surface text-primay hover:bg-hover'}
|
||||
@@ -205,11 +206,14 @@
|
||||
size="xs">Cache</Button
|
||||
>
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary">
|
||||
Since this is a reference to a workspace {runnable.runType}, set the cache in the {runnable.runType}
|
||||
settings directly by editing it. The cache will be shared by any app or flow that uses this {runnable.runType}.
|
||||
</div>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="block text-primary p-4">
|
||||
Since this is a reference to a workspace {runnable.runType}, set the cache in the {runnable.runType}
|
||||
settings directly by editing it. The cache will be shared by any app or flow that uses this
|
||||
{runnable.runType}.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
<input
|
||||
on:keydown|stopPropagation
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
noWFull
|
||||
on:selected={() => ($app = $app)}
|
||||
bind:selected={component.horizontalAlignment}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="left" icon={AlignStartVertical} />
|
||||
<ToggleButton value="center" icon={AlignCenterVertical} />
|
||||
<ToggleButton value="right" icon={AlignEndVertical} />
|
||||
<ToggleButton value="left" icon={AlignStartVertical} {item} />
|
||||
<ToggleButton value="center" icon={AlignCenterVertical} {item} />
|
||||
<ToggleButton value="right" icon={AlignEndVertical} {item} />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if component.type !== 'formcomponent' && component.verticalAlignment}
|
||||
@@ -37,10 +38,11 @@
|
||||
noWFull
|
||||
on:selected={() => ($app = $app)}
|
||||
bind:selected={component.verticalAlignment}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="top" icon={AlignStartHorizontal} />
|
||||
<ToggleButton value="center" icon={AlignCenterHorizontal} />
|
||||
<ToggleButton value="bottom" icon={AlignEndHorizontal} />
|
||||
<ToggleButton value="top" icon={AlignStartHorizontal} {item} />
|
||||
<ToggleButton value="center" icon={AlignCenterHorizontal} {item} />
|
||||
<ToggleButton value="bottom" icon={AlignEndHorizontal} {item} />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
}}
|
||||
noWFull
|
||||
bind:selected={componentInput.type}
|
||||
let:item
|
||||
>
|
||||
{#if componentInput.fieldType === 'template'}
|
||||
{#if componentInput.type == 'template'}
|
||||
@@ -65,6 +66,7 @@
|
||||
disabled={disableStatic}
|
||||
icon={CurlyBraces}
|
||||
label="Template"
|
||||
{item}
|
||||
/>
|
||||
{:else}
|
||||
<ToggleButton
|
||||
@@ -73,6 +75,7 @@
|
||||
disabled={disableStatic}
|
||||
icon={CurlyBraces}
|
||||
label="Template"
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
{:else if componentInput.noStatic !== true}
|
||||
@@ -82,6 +85,7 @@
|
||||
disabled={disableStatic}
|
||||
iconOnly={clientWidth < iconOnlyThreshold}
|
||||
icon={Pen}
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
{#if componentInput.type == 'connected'}
|
||||
@@ -90,6 +94,7 @@
|
||||
icon={Plug2}
|
||||
iconOnly={clientWidth < iconOnlyThreshold}
|
||||
label="Connect"
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
<ToggleButton
|
||||
@@ -97,6 +102,7 @@
|
||||
icon={FunctionSquare}
|
||||
iconOnly={clientWidth < iconOnlyThreshold}
|
||||
label="Eval"
|
||||
{item}
|
||||
/>
|
||||
|
||||
<ToggleButton
|
||||
@@ -104,6 +110,7 @@
|
||||
icon={Code}
|
||||
iconOnly={clientWidth < iconOnlyThreshold}
|
||||
label="Compute"
|
||||
{item}
|
||||
id="data-source-compute"
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -397,16 +397,19 @@
|
||||
on:selected={() => {
|
||||
handleSelected(selected)
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
value="ui-editor"
|
||||
label="UI Editor"
|
||||
tooltip="Use the UI editor to quickly create a plotly chart."
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
value="json"
|
||||
label="JSON"
|
||||
tooltip="Switch to JSON mode for complete customization of Plotly settings."
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -137,21 +137,28 @@
|
||||
componentInput['expr'] = JSON.stringify(JSON.parse(componentInput['expr']), null, 4)
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="static" icon={Pen} iconOnly tooltip="Static" />
|
||||
<ToggleButton value="static" icon={Pen} iconOnly tooltip="Static" {item} />
|
||||
{#if userInputEnabled}
|
||||
<ToggleButton value="user" icon={User} iconOnly tooltip="User Input" />
|
||||
<ToggleButton value="user" icon={User} iconOnly tooltip="User Input" {item} />
|
||||
{/if}
|
||||
{#if fileUpload}
|
||||
<ToggleButton value="upload" icon={Upload} iconOnly tooltip="Upload" />
|
||||
<ToggleButton value="upload" icon={Upload} iconOnly tooltip="Upload" {item} />
|
||||
{/if}
|
||||
{#if componentInput?.type === 'connected'}
|
||||
<ToggleButton value="connected" icon={Plug2} iconOnly tooltip="Connect" />
|
||||
<ToggleButton value="connected" icon={Plug2} iconOnly tooltip="Connect" {item} />
|
||||
{/if}
|
||||
{#if componentInput?.type === 'eval'}
|
||||
<ToggleButton value="eval" icon={FunctionSquare} iconOnly tooltip="Eval Legacy" />
|
||||
<ToggleButton
|
||||
value="eval"
|
||||
icon={FunctionSquare}
|
||||
iconOnly
|
||||
tooltip="Eval Legacy"
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
<ToggleButton value="evalv2" icon={FunctionSquare} iconOnly tooltip="Eval" />
|
||||
<ToggleButton value="evalv2" icon={FunctionSquare} iconOnly tooltip="Eval" {item} />
|
||||
</ToggleButtonGroup>
|
||||
<ConnectionButton {closeConnection} {openConnection} isOpen={!!$connectingInput.opened} />
|
||||
{/if}
|
||||
|
||||
+55
-50
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { ClearableInput, Popup } from '../../../../common'
|
||||
import { ClearableInput } from '../../../../common'
|
||||
import { AllIcons } from './icons'
|
||||
import type { ComputeConfig } from 'svelte-floating-ui'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
|
||||
export let value: string | undefined = ''
|
||||
|
||||
@@ -43,8 +44,8 @@
|
||||
export let shouldUsePortal: boolean = true
|
||||
</script>
|
||||
|
||||
<Popup let:close {floatingConfig} {shouldUsePortal}>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover {floatingConfig} {shouldUsePortal}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<div class="relative">
|
||||
<ClearableInput
|
||||
readonly
|
||||
@@ -60,52 +61,56 @@
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
{#if !loading}
|
||||
{#if filteredItems}
|
||||
<div class="w-72">
|
||||
<input
|
||||
on:keydown={(event) => {
|
||||
if (!['ArrowDown', 'ArrowUp'].includes(event.key)) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}}
|
||||
bind:value={search}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
class="col-span-4 mb-2"
|
||||
/>
|
||||
<div class="grid gap-1 grid-cols-4 max-h-[300px] overflow-auto">
|
||||
{#each filteredItems as label}
|
||||
<button
|
||||
type="button"
|
||||
title={label}
|
||||
on:click={() => {
|
||||
select(label)
|
||||
close(null)
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="p-4">
|
||||
{#if !loading}
|
||||
{#if filteredItems}
|
||||
<div class="w-72">
|
||||
<input
|
||||
on:keydown={(event) => {
|
||||
if (!['ArrowDown', 'ArrowUp'].includes(event.key)) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}}
|
||||
class="w-full center-center flex-col font-normal p-1
|
||||
hover:bg-gray-100 focus:bg-gray-100 rounded duration-200 dark:hover:bg-frost-900 dark:focus:bg-frost-900
|
||||
{label === value ? 'text-blue-600 bg-blue-50 pointer-events-none' : ''}"
|
||||
>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<img
|
||||
class="dark:invert"
|
||||
loading="lazy"
|
||||
src="https://cdn.jsdelivr.net/npm/lucide-static@0.367.0/icons/{label}.svg"
|
||||
/>
|
||||
<span class="inline-block w-full text-[10px] ellipsize pt-0.5">
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
{:else}
|
||||
<div class="col-span-4 text-center text-secondary text-sm p-2">
|
||||
No icons match your search
|
||||
bind:value={search}
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
class="col-span-4 mb-2"
|
||||
/>
|
||||
<div class="grid gap-1 grid-cols-4 max-h-[300px] overflow-auto">
|
||||
{#each filteredItems as label}
|
||||
<button
|
||||
type="button"
|
||||
title={label}
|
||||
on:click={() => {
|
||||
select(label)
|
||||
close()
|
||||
}}
|
||||
class="w-full center-center flex-col font-normal p-1
|
||||
hover:bg-gray-100 focus:bg-gray-100 rounded duration-200 dark:hover:bg-frost-900 dark:focus:bg-frost-900
|
||||
{label === value ? 'text-blue-600 bg-blue-50 pointer-events-none' : ''}"
|
||||
>
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<img
|
||||
class="dark:invert"
|
||||
loading="lazy"
|
||||
src="https://cdn.jsdelivr.net/npm/lucide-static@0.367.0/icons/{label}.svg"
|
||||
/>
|
||||
<span class="inline-block w-full text-[10px] ellipsize pt-0.5">
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
{:else}
|
||||
<div class="col-span-4 text-center text-secondary text-sm p-2">
|
||||
No icons match your search
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center text-sm text-secondary p-2"> Couldn't load options </div>
|
||||
{/if}
|
||||
{/if}
|
||||
</Popup>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center text-sm text-secondary p-2"> Couldn't load options </div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { Filter } from 'lucide-svelte'
|
||||
import { Popup, Button } from '../common'
|
||||
import { Button } from '../common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
contentClasses="p-4 flex flex-col w-80"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true} startIcon={{ icon: Filter }}>
|
||||
Filters
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col w-80">
|
||||
<svelte:fragment slot="content">
|
||||
<slot name="filters" />
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { ButtonType } from './model'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import ButtonDropdown from './ButtonDropdown.svelte'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import { classNames, getModifierKey } from '$lib/utils'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import Dropdown from '$lib/components/DropdownV2.svelte'
|
||||
import { getModifierKey, type Item } from '$lib/utils'
|
||||
import { Loader2, ChevronDown } from 'lucide-svelte'
|
||||
|
||||
export let size: ButtonType.Size = 'md'
|
||||
export let spacingSize: ButtonType.Size = size
|
||||
@@ -30,7 +29,6 @@
|
||||
export let title: string | undefined = undefined
|
||||
export let style: string = ''
|
||||
export let download: string | undefined = undefined
|
||||
export let portalTarget: string | undefined = undefined
|
||||
export let startIcon: ButtonType.Icon | undefined = undefined
|
||||
export let endIcon: ButtonType.Icon | undefined = undefined
|
||||
export let shortCut:
|
||||
@@ -45,12 +43,14 @@
|
||||
}
|
||||
export let dropdownItems: MenuItem[] | (() => MenuItem[]) | undefined = undefined
|
||||
|
||||
function computeDropdowns(): MenuItem[] | undefined {
|
||||
if (typeof dropdownItems === 'function') {
|
||||
return dropdownItems()
|
||||
} else {
|
||||
return dropdownItems
|
||||
}
|
||||
function computeDropdowns(menuItems: MenuItem[] | (() => MenuItem[])): Item[] {
|
||||
const items = typeof menuItems === 'function' ? menuItems() : menuItems
|
||||
return items.map((item) => ({
|
||||
displayName: item.label,
|
||||
action: item.onClick ? (e) => item.onClick?.(e) : undefined,
|
||||
icon: item.icon,
|
||||
href: item.href
|
||||
}))
|
||||
}
|
||||
|
||||
export function focus() {
|
||||
@@ -230,32 +230,19 @@
|
||||
{/if}
|
||||
|
||||
{#if dropdownItems && dropdownItems.length > 0}
|
||||
<div
|
||||
class={twMerge(
|
||||
buttonClass,
|
||||
'rounded-md m-0 p-0 h-auto !w-10',
|
||||
variant === 'border' ? 'border-0 border-r border-y ' : 'border-0',
|
||||
'rounded-r-md !rounded-l-none'
|
||||
)}
|
||||
>
|
||||
<ButtonDropdown target={portalTarget}>
|
||||
<svelte:fragment slot="items">
|
||||
{#each computeDropdowns() ?? [] as item}
|
||||
<MenuItem on:click={item.onClick} href={item.href}>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-secondary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{#if item.icon}
|
||||
<svelte:component this={item.icon} class="w-4 h-4" size={lucideIconSize} />
|
||||
{/if}
|
||||
{item.label}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</div>
|
||||
<Dropdown items={computeDropdowns(dropdownItems)} class="h-full">
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div
|
||||
class={twMerge(
|
||||
buttonClass,
|
||||
'rounded-md m-0 p-0 !w-10 center-center h-full',
|
||||
variant === 'border' ? 'border-0 border-r border-y ' : 'border-0',
|
||||
'rounded-r-md !rounded-l-none'
|
||||
)}
|
||||
>
|
||||
<ChevronDown class="w-5 h-5" />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Dropdown>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Menu, Transition, MenuButton, MenuItems } from '@rgossiaux/svelte-headlessui'
|
||||
import { ChevronDown } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { createPopperActions, type PopperOptions } from 'svelte-popperjs'
|
||||
import Portal from '$lib/components/Portal.svelte'
|
||||
|
||||
export let hasPadding: boolean = true
|
||||
export let target: string | undefined = 'body'
|
||||
export let disabled: boolean = false
|
||||
|
||||
const [popperRef, popperContent] = createPopperActions({ placement: 'auto' })
|
||||
|
||||
const popperOptions: PopperOptions<{}> = {
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [
|
||||
{ name: 'offset', options: { offset: [8, 8] } },
|
||||
{
|
||||
name: 'arrow',
|
||||
options: {
|
||||
padding: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu let:open as="div" class="relative hover:z-50 flex w-full h-full">
|
||||
<span use:popperRef>
|
||||
<MenuButton
|
||||
class={twMerge('h-full w-full flex flex-row gap-2 items-center', hasPadding ? 'px-2' : '')}
|
||||
{disabled}
|
||||
>
|
||||
{#if $$slots.buttonReplacement}
|
||||
<slot name="buttonReplacement" />
|
||||
{:else}
|
||||
<slot name="label" />
|
||||
<ChevronDown class="w-5 h-5" />
|
||||
{/if}
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<Portal name="button-dropdown" {target}>
|
||||
<div use:popperContent={popperOptions} class="z-[6000]">
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition ease-out duration-[25ms]"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-[25ms]"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute border right-0 z-50 w-56 origin-top-right top-1 rounded-md bg-surface shadow-md focus:outline-none"
|
||||
>
|
||||
<div class="my-1">
|
||||
<slot name="items" />
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</div>
|
||||
</Portal>
|
||||
</Menu>
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Calendar } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Popup } from '..'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import type { Placement } from '@floating-ui/core'
|
||||
import DateTimeInput from '$lib/components/DateTimeInput.svelte'
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
export let placement: Placement = 'top-end'
|
||||
</script>
|
||||
|
||||
<Popup floatingConfig={{ placement: placement, strategy: 'absolute' }} {target}>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ placement: placement, strategy: 'absolute' }} portal={target}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<button
|
||||
title="Open calendar picker"
|
||||
class="absolute bottom-1 right-2 top-1 py-1 min-w-min !px-2 items-center text-primary bg-surface border rounded center-center hover:bg-surface-hover transition-all cursor-pointer"
|
||||
@@ -30,26 +30,27 @@
|
||||
<Calendar size={14} />
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<label class="block text-primary">
|
||||
<div class="pb-1 text-sm text-secondary">{label}</div>
|
||||
<svelte:fragment slot="content">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div on:click|stopPropagation class="flex w-full">
|
||||
<DateTimeInput
|
||||
{clearable}
|
||||
{useDropdown}
|
||||
value={date}
|
||||
on:change={(e) => {
|
||||
date = e.detail
|
||||
dispatch('change', date)
|
||||
}}
|
||||
on:clear={() => {
|
||||
dispatch('clear')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</Popup>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-1 text-sm text-secondary">{label}</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div on:click|stopPropagation class="flex w-full">
|
||||
<DateTimeInput
|
||||
{clearable}
|
||||
{useDropdown}
|
||||
value={date}
|
||||
on:change={(e) => {
|
||||
date = e.detail
|
||||
dispatch('change', date)
|
||||
}}
|
||||
on:clear={() => {
|
||||
dispatch('clear')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -9,7 +9,6 @@ export { default as DrawerContent } from './drawer/DrawerContent.svelte'
|
||||
export { default as Kbd } from './kbd/Kbd.svelte'
|
||||
export { default as Menu } from './menu/Menu.svelte'
|
||||
export { default as MenuItem } from './menu/MenuItem.svelte'
|
||||
export { default as Popup } from './popup/Popup.svelte'
|
||||
export { default as SecondsInput } from './seconds/SecondsInput.svelte'
|
||||
export { default as Skeleton } from './skeleton/Skeleton.svelte'
|
||||
export { default as Tab } from './tabs/Tab.svelte'
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Menu, MenuButton, MenuItems, Transition } from '@rgossiaux/svelte-headlessui'
|
||||
import Portal from '$lib/components/Portal.svelte'
|
||||
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import { createFloatingActions } from 'svelte-floating-ui'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import ResolveOpen from './ResolveOpen.svelte'
|
||||
|
||||
export let placement: any = 'bottom-start'
|
||||
export let justifyEnd: boolean = false
|
||||
export let lightMode: boolean = false
|
||||
export let maxHeight: number = 900
|
||||
export let disabled = false
|
||||
const [floatingRef, floatingContent] = createFloatingActions({
|
||||
strategy: 'fixed',
|
||||
middleware: [offset(), flip(), shift()],
|
||||
placement: placement
|
||||
})
|
||||
</script>
|
||||
|
||||
<Menu let:open as="div" class="relative hover:z-50 flex w-full h-8">
|
||||
<ResolveOpen {open} on:open on:close />
|
||||
<div use:floatingRef class="w-full">
|
||||
<MenuButton class={twMerge('w-full', justifyEnd ? 'flex justify-end' : '')} disabled={disabled}>
|
||||
<slot name="trigger" />
|
||||
</MenuButton>
|
||||
</div>
|
||||
<Portal name="menu-v2">
|
||||
<div use:floatingContent class="z-[6000]">
|
||||
<Transition
|
||||
{open}
|
||||
enter="transition ease-out duration-[25ms]"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-[25ms]"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems
|
||||
class={twMerge(
|
||||
'border w-56 origin-top-right rounded-md shadow-md focus:outline-none overflow-y-auto',
|
||||
lightMode ? 'bg-surface-inverse' : 'bg-surface'
|
||||
)}
|
||||
style="max-height: {maxHeight}px;"
|
||||
>
|
||||
<div class="my-1">
|
||||
<slot />
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</div>
|
||||
</Portal>
|
||||
</Menu>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Popover, PopoverButton, PopoverPanel, Transition } from '@rgossiaux/svelte-headlessui'
|
||||
import ConditionalPortal from '../drawer/ConditionalPortal.svelte'
|
||||
import { createFloatingActions, type ComputeConfig } from 'svelte-floating-ui'
|
||||
|
||||
export let floatingConfig: ComputeConfig = {
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom-start'
|
||||
}
|
||||
|
||||
export let containerClasses: string = 'rounded-lg shadow-md border p-4 bg-surface'
|
||||
export let floatingClasses: string = ''
|
||||
const [floatingRef, floatingContent] = createFloatingActions(floatingConfig)
|
||||
|
||||
export let blockOpen = false
|
||||
export let shouldUsePortal: boolean = true
|
||||
export let target: string | HTMLElement | undefined = undefined
|
||||
export let noTransition = false
|
||||
export let popupHover = false
|
||||
export let preventPopupClosingOnClickInside = false
|
||||
export let disabled = false
|
||||
</script>
|
||||
|
||||
<Popover on:close class="leading-none">
|
||||
<PopoverButton let:open {disabled}>
|
||||
<div use:floatingRef>
|
||||
<slot name="button" {open} />
|
||||
</div>
|
||||
</PopoverButton>
|
||||
<ConditionalPortal condition={shouldUsePortal} {target}>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
use:floatingContent
|
||||
class={`z5000 ${floatingClasses}`}
|
||||
on:mouseenter={() => {
|
||||
popupHover = true
|
||||
}}
|
||||
on:mouseleave={() => {
|
||||
popupHover = false
|
||||
}}
|
||||
on:click={(e) => preventPopupClosingOnClickInside && e.stopPropagation()}
|
||||
>
|
||||
{#if !noTransition}
|
||||
<Transition
|
||||
show={blockOpen || undefined}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<PopoverPanel let:close static={blockOpen}>
|
||||
<div class={containerClasses}>
|
||||
<slot {close} />
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
</Transition>
|
||||
{:else}
|
||||
<PopoverPanel focus={false} let:close static={blockOpen}>
|
||||
<div class={containerClasses}>
|
||||
<slot {close} />
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
{/if}
|
||||
</div>
|
||||
</ConditionalPortal>
|
||||
</Popover>
|
||||
@@ -1,13 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { Tab } from '@rgossiaux/svelte-headlessui'
|
||||
import type { ToggleButtonContext } from './ToggleButtonGroup.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
import Tooltip from '$lib/components/meltComponents/Tooltip.svelte'
|
||||
import { melt } from '@melt-ui/svelte'
|
||||
import { Info } from 'lucide-svelte'
|
||||
|
||||
export let value: any
|
||||
export let value: string | undefined = undefined
|
||||
export let label: string | undefined = undefined
|
||||
export let iconOnly: boolean = false
|
||||
export let tooltip: string | undefined = undefined
|
||||
@@ -20,37 +17,40 @@
|
||||
export let showTooltipIcon: boolean = false
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let id: string | undefined = undefined
|
||||
|
||||
const { select, selected } = getContext<ToggleButtonContext>('ToggleButtonGroup')
|
||||
export let item: any | undefined = undefined
|
||||
</script>
|
||||
|
||||
<Popover
|
||||
notClickable
|
||||
class={twMerge('flex', disabled ? 'cursor-not-allowed' : 'cursor-pointer')}
|
||||
disablePopup={tooltip === undefined}
|
||||
disappearTimeout={0}
|
||||
{documentationLink}
|
||||
>
|
||||
<div {id} class="flex">
|
||||
<Tab
|
||||
{#if item}
|
||||
<Tooltip
|
||||
class={twMerge('flex', disabled ? 'cursor-not-allowed' : 'cursor-pointer')}
|
||||
disablePopup={tooltip === undefined}
|
||||
disappearTimeout={0}
|
||||
{documentationLink}
|
||||
>
|
||||
<button
|
||||
{id}
|
||||
{disabled}
|
||||
class={twMerge(
|
||||
' rounded-md transition-all text-xs flex gap-1 flex-row items-center',
|
||||
'group rounded-md transition-all text-xs flex gap-1 flex-row items-center',
|
||||
small ? 'px-1.5 py-0.5 text-2xs' : 'px-2 py-1',
|
||||
light ? 'font-medium' : '',
|
||||
$selected === value
|
||||
? 'bg-surface shadow-md'
|
||||
: 'bg-surface-secondary hover:bg-surface-hover',
|
||||
'data-[state=on]:bg-surface data-[state=on]:shadow-md',
|
||||
'bg-surface-secondary hover:bg-surface-hover',
|
||||
$$props.class
|
||||
)}
|
||||
on:click={() => select(value)}
|
||||
use:melt={$item(value)}
|
||||
style={`--selected-color: ${selectedColor}`}
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
size={small ? 12 : 14}
|
||||
color={$selected === value ? selectedColor : '#9CA3AF'}
|
||||
{...iconProps}
|
||||
class={twMerge(
|
||||
'text-gray-400',
|
||||
'group-data-[state=on]:text-[var(--selected-color)]',
|
||||
iconProps.class
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{#if label && !iconOnly}
|
||||
@@ -59,9 +59,10 @@
|
||||
{#if showTooltipIcon}
|
||||
<Info size={14} class="text-gray-400" />
|
||||
{/if}
|
||||
</Tab>
|
||||
</div>
|
||||
<svelte:fragment slot="text">
|
||||
{tooltip}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</button>
|
||||
|
||||
<svelte:fragment slot="text">
|
||||
{tooltip}
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
<script context="module" lang="ts">
|
||||
export type ToggleButtonContext = {
|
||||
selected: Writable<any>
|
||||
select: (value: any) => void
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, setContext } from 'svelte'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { TabGroup, TabList } from '@rgossiaux/svelte-headlessui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { createSync } from '@melt-ui/svelte'
|
||||
|
||||
export let id: string | null | undefined = undefined
|
||||
export let selected: any
|
||||
export let selected: string | string[] | undefined = undefined
|
||||
export let noWFull: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let tabListClass: string = ''
|
||||
export let allowEmpty: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const selectedContent = writable(selected)
|
||||
|
||||
function setSelected(selected: any) {
|
||||
if ($selectedContent != selected) {
|
||||
selectedContent.set(selected)
|
||||
}
|
||||
}
|
||||
import { createToggleGroup, melt } from '@melt-ui/svelte'
|
||||
|
||||
$: setSelected(selected)
|
||||
|
||||
setContext<ToggleButtonContext>('ToggleButtonGroup', {
|
||||
selected: selectedContent,
|
||||
select: (value: any) => {
|
||||
selectedContent.set(value)
|
||||
selected = value
|
||||
dispatch('selected', value)
|
||||
const {
|
||||
elements: { root, item },
|
||||
options: { disabled: disabledOption },
|
||||
states
|
||||
} = createToggleGroup({
|
||||
type: 'single',
|
||||
onValueChange: ({ curr, next }) => {
|
||||
if (next === undefined && !allowEmpty) {
|
||||
return curr
|
||||
}
|
||||
dispatch('selected', next)
|
||||
return next
|
||||
}
|
||||
})
|
||||
|
||||
$: $disabledOption = disabled
|
||||
|
||||
const sync = createSync(states)
|
||||
$: sync.value(selected, (v) => (selected = v))
|
||||
</script>
|
||||
|
||||
<TabGroup
|
||||
{id}
|
||||
<div
|
||||
use:melt={$root}
|
||||
class={twMerge(
|
||||
`h-8 flex ${noWFull ? '' : 'w-full'} ${disabled ? 'disabled' : ''}`,
|
||||
$$props.class
|
||||
$$props.class,
|
||||
'flex items-center data-[orientation="vertical"]:flex-col'
|
||||
)}
|
||||
aria-label="Text alignment"
|
||||
{id}
|
||||
>
|
||||
<TabList
|
||||
class={twMerge('flex bg-surface-secondary rounded-md p-0.5 gap-1 h-full ', tabListClass)}
|
||||
>
|
||||
<slot />
|
||||
</TabList>
|
||||
</TabGroup>
|
||||
<div class={twMerge('flex bg-surface-secondary rounded-md p-0.5 gap-1 h-full ', tabListClass)}>
|
||||
<slot {item} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { Tab } from '@rgossiaux/svelte-headlessui'
|
||||
import type { ToggleButtonContext } from './ToggleButtonGroup.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import DropdownV2 from '$lib/components/DropdownV2.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
|
||||
export let disabled: boolean = false
|
||||
export let small = false
|
||||
export let light = false
|
||||
export let id: string | undefined = undefined
|
||||
|
||||
export let item: any | undefined = undefined
|
||||
export let selected: string | undefined = undefined
|
||||
type TogglableItem = {
|
||||
label: string
|
||||
value: string
|
||||
@@ -18,11 +17,14 @@
|
||||
|
||||
export let togglableItems: TogglableItem[]
|
||||
|
||||
const { select, selected } = getContext<ToggleButtonContext>('ToggleButtonGroup')
|
||||
function select(v: string) {
|
||||
console.log('dbg select', v)
|
||||
selected = v
|
||||
}
|
||||
|
||||
let items = togglableItems.map((i) => ({ displayName: i.label, action: () => select(i.value) }))
|
||||
|
||||
function isAnOptionSelected(selected: string) {
|
||||
function isAnOptionSelected(selected: string | undefined) {
|
||||
return togglableItems.some((i) => i.value === selected)
|
||||
}
|
||||
</script>
|
||||
@@ -34,21 +36,16 @@
|
||||
disappearTimeout={0}
|
||||
>
|
||||
<div {id} class="flex">
|
||||
{#if isAnOptionSelected($selected)}
|
||||
<Tab
|
||||
{#if isAnOptionSelected(selected)}
|
||||
<ToggleButton
|
||||
{disabled}
|
||||
class={twMerge(
|
||||
' rounded-md transition-all text-xs flex gap-1 flex-row items-center',
|
||||
small ? 'px-1.5 py-0.5 text-2xs' : 'px-2 py-1',
|
||||
light ? 'font-medium' : '',
|
||||
isAnOptionSelected($selected)
|
||||
? 'bg-surface shadow-md'
|
||||
: 'bg-surface-secondary hover:bg-surface-hover',
|
||||
$$props.class
|
||||
)}
|
||||
>
|
||||
{togglableItems.find((i) => i.value === $selected)?.label}
|
||||
</Tab>
|
||||
value={selected}
|
||||
{item}
|
||||
{small}
|
||||
{light}
|
||||
{id}
|
||||
label={togglableItems.find((i) => i.value === selected)?.label}
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex items-center">
|
||||
<DropdownV2 {items} />
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Tab } from '@rgossiaux/svelte-headlessui'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
|
||||
export let label: string | undefined = undefined
|
||||
export let tooltip: string | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let small = false
|
||||
export let light = false
|
||||
export let checked: boolean = false
|
||||
export let id: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div {id} class="flex">
|
||||
<Tab
|
||||
{disabled}
|
||||
class={twMerge(
|
||||
' rounded-md transition-all text-xs flex gap-1 flex-row items-center',
|
||||
small ? 'px-1.5 py-0.5 text-2xs' : 'px-2 py-1',
|
||||
light ? 'font-medium' : '',
|
||||
'bg-surface-secondary ',
|
||||
$$props.class
|
||||
)}
|
||||
>
|
||||
<Toggle
|
||||
size="xs"
|
||||
options={{
|
||||
right: label,
|
||||
rightTooltip: tooltip
|
||||
}}
|
||||
textClass="text-2xs whitespace-nowrap white !w-full"
|
||||
bind:checked
|
||||
/>
|
||||
</Tab>
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ExternalLink, Wand2 } from 'lucide-svelte'
|
||||
import Button from '../common/button/Button.svelte'
|
||||
import { getNonStreamingCompletion } from './lib'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { copilotInfo } from '$lib/stores'
|
||||
|
||||
@@ -74,12 +74,8 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
size="xs"
|
||||
@@ -93,47 +89,51 @@
|
||||
on:click={genLoading ? () => abortController?.abort() : undefined}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex w-96">
|
||||
<input
|
||||
bind:this={instructionsField}
|
||||
type="text"
|
||||
placeholder="CRON schedule description"
|
||||
bind:value={instructions}
|
||||
on:keypress={({ key }) => {
|
||||
if (key === 'Enter' && instructions.length > 0) {
|
||||
close(instructionsField || null)
|
||||
generateCron()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="contained"
|
||||
buttonType="button"
|
||||
btnClasses="!ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
title="Generate CRON schedule from prompt"
|
||||
aria-label="Generate"
|
||||
iconOnly
|
||||
on:click={() => {
|
||||
close(instructionsField || null)
|
||||
generateCron()
|
||||
}}
|
||||
disabled={instructions.length == 0}
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="border rounded-lg shadow-lg p-4 bg-surface">
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex w-96">
|
||||
<input
|
||||
bind:this={instructionsField}
|
||||
type="text"
|
||||
placeholder="CRON schedule description"
|
||||
bind:value={instructions}
|
||||
on:keypress={({ key }) => {
|
||||
if (key === 'Enter' && instructions.length > 0) {
|
||||
close()
|
||||
generateCron()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="contained"
|
||||
buttonType="button"
|
||||
btnClasses="!ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
title="Generate CRON schedule from prompt"
|
||||
aria-label="Generate"
|
||||
iconOnly
|
||||
on:click={() => {
|
||||
close()
|
||||
generateCron()
|
||||
}}
|
||||
disabled={instructions.length == 0}
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="block text-primary">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>workspace settings <ExternalLink size={16} /></a
|
||||
></p
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="block text-primary">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>workspace settings <ExternalLink size={16} /></a
|
||||
></p
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -40,9 +40,10 @@
|
||||
$currentStepStore = numberToChars(0)
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton value="sequence" label="Sequence" />
|
||||
<ToggleButton value="trigger" label="Trigger" />
|
||||
<ToggleButton value="sequence" label="Sequence" {item} />
|
||||
<ToggleButton value="trigger" label="Trigger" {item} />
|
||||
</ToggleButtonGroup>
|
||||
{#if flowCopilotMode === 'trigger'}
|
||||
<Alert title="Trigger flow builder">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import type { FlowCopilotContext } from './flow'
|
||||
import { charsToNumber } from '../flows/idUtils'
|
||||
import { copilotInfo } from '$lib/stores'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import type { FlowEditorContext } from '../flows/types'
|
||||
import { ExternalLink, Wand2 } from 'lucide-svelte'
|
||||
@@ -114,13 +114,8 @@
|
||||
</div>
|
||||
</ManualPopover>
|
||||
{:else}
|
||||
<Popup
|
||||
floatingConfig={{
|
||||
strategy: 'absolute',
|
||||
placement: 'bottom'
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Popover placement="bottom">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
size="xs"
|
||||
startIcon={{
|
||||
@@ -134,15 +129,17 @@
|
||||
AI builder
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>workspace settings <ExternalLink size={16} /></a
|
||||
></p
|
||||
>
|
||||
</div>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="block text-primary p-4">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>workspace settings <ExternalLink size={16} /></a
|
||||
></p
|
||||
>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { dfs } from '../flows/dfs'
|
||||
import { yamlStringifyExceptKeys } from './utils'
|
||||
import { copilotInfo, stepInputCompletionEnabled } from '$lib/stores'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import type { Flow } from '$lib/gen'
|
||||
|
||||
let loading = false
|
||||
@@ -84,12 +84,11 @@ Only return the expression without any wrapper. Do not explain or discuss.`
|
||||
</script>
|
||||
|
||||
{#if $copilotInfo.exists_ai_resource && $stepInputCompletionEnabled}
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
let:close
|
||||
contentClasses="p-4 flex w-96"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
color={loading ? 'red' : 'light'}
|
||||
size="xs"
|
||||
@@ -103,7 +102,7 @@ Only return the expression without any wrapper. Do not explain or discuss.`
|
||||
on:click={loading ? () => abortController?.abort() : undefined}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<div class="flex w-96">
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<input
|
||||
bind:this={instructionsField}
|
||||
type="text"
|
||||
@@ -111,7 +110,7 @@ Only return the expression without any wrapper. Do not explain or discuss.`
|
||||
bind:value={instructions}
|
||||
on:keypress={({ key }) => {
|
||||
if (key === 'Enter' && instructions.length > 0) {
|
||||
close(instructionsField || null)
|
||||
close()
|
||||
generatePredicate()
|
||||
}
|
||||
}}
|
||||
@@ -126,12 +125,12 @@ Only return the expression without any wrapper. Do not explain or discuss.`
|
||||
aria-label="Generate"
|
||||
iconOnly
|
||||
on:click={() => {
|
||||
close(instructionsField || null)
|
||||
close()
|
||||
generatePredicate()
|
||||
}}
|
||||
disabled={instructions.length == 0}
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
</Popup>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { getNonStreamingCompletion } from './lib'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { copilotInfo } from '$lib/stores'
|
||||
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
@@ -91,7 +91,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -99,9 +99,8 @@
|
||||
})
|
||||
]
|
||||
}}
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
title="Generate regexes from prompt"
|
||||
btnClasses="text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
@@ -115,67 +114,69 @@
|
||||
on:click={genLoading ? () => abortController?.abort() : () => {}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary z-[2000]">
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex w-96">
|
||||
<input
|
||||
type="text"
|
||||
bind:this={input}
|
||||
bind:value={funcDesc}
|
||||
on:keypress={({ key }) => {
|
||||
if (key === 'Enter' && funcDesc?.length > 0) {
|
||||
close(input || null)
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="block text-primary p-4">
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex w-96">
|
||||
<input
|
||||
type="text"
|
||||
bind:this={input}
|
||||
bind:value={funcDesc}
|
||||
on:keypress={({ key }) => {
|
||||
if (key === 'Enter' && funcDesc?.length > 0) {
|
||||
close()
|
||||
onGenerate()
|
||||
}
|
||||
}}
|
||||
placeholder={'Describe what the regex should doww'}
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
buttonType="button"
|
||||
btnClasses="!ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
aria-label="Generate"
|
||||
on:click={() => {
|
||||
close()
|
||||
onGenerate()
|
||||
}
|
||||
}}
|
||||
placeholder={'Describe what the regex should do'}
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
buttonType="button"
|
||||
btnClasses="!ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
aria-label="Generate"
|
||||
on:click={() => {
|
||||
close(input || null)
|
||||
onGenerate()
|
||||
}}
|
||||
disabled={funcDesc?.length <= 0}
|
||||
iconOnly
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
{#if promptHistory.length > 0}
|
||||
<div class="w-96 flex flex-col gap-1">
|
||||
{#each promptHistory as p}
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
btnClasses="justify-start overflow-x-scroll no-scrollbar"
|
||||
startIcon={{ icon: HistoryIcon, classes: 'shrink-0' }}
|
||||
on:click={() => {
|
||||
funcDesc = p
|
||||
}}>{p}</Button
|
||||
>
|
||||
{/each}
|
||||
<button
|
||||
class="underline text-xs text-start px-2 text-secondary font-normal"
|
||||
on:click={clearPromptHistory}>clear history</button
|
||||
>
|
||||
}}
|
||||
disabled={funcDesc?.length <= 0}
|
||||
iconOnly
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm">
|
||||
Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</Popup>
|
||||
{#if promptHistory.length > 0}
|
||||
<div class="w-96 flex flex-col gap-1">
|
||||
{#each promptHistory as p}
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
btnClasses="justify-start overflow-x-scroll no-scrollbar"
|
||||
startIcon={{ icon: HistoryIcon, classes: 'shrink-0' }}
|
||||
on:click={() => {
|
||||
funcDesc = p
|
||||
}}>{p}</Button
|
||||
>
|
||||
{/each}
|
||||
<button
|
||||
class="underline text-xs text-start px-2 text-secondary font-normal"
|
||||
on:click={clearPromptHistory}>clear history</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm">
|
||||
Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
import { dbSchemas, copilotInfo, type DBSchema, workspaceStore } from '$lib/stores'
|
||||
import type DiffEditor from '../DiffEditor.svelte'
|
||||
import { scriptLangToEditorLang } from '$lib/scripts'
|
||||
import Popover from '../Popover.svelte'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { sleep } from '$lib/utils'
|
||||
import { WindmillIcon } from '../icons'
|
||||
import HighlightCode from '../HighlightCode.svelte'
|
||||
import LoadingIcon from '../apps/svelte-select/lib/LoadingIcon.svelte'
|
||||
import { autoPlacement } from '@floating-ui/core'
|
||||
import { Check, Wand2, X } from 'lucide-svelte'
|
||||
import { Check, Wand2, X, RotateCw } from 'lucide-svelte'
|
||||
|
||||
// props
|
||||
export let lang: SupportedLanguage
|
||||
@@ -33,7 +31,7 @@
|
||||
let dbSchema: DBSchema | undefined = undefined
|
||||
let abortController: AbortController | undefined = undefined
|
||||
|
||||
async function onFix(closePopup: () => void) {
|
||||
async function onFix() {
|
||||
if (!error) {
|
||||
return
|
||||
}
|
||||
@@ -57,9 +55,6 @@
|
||||
)
|
||||
setupDiff()
|
||||
diffEditor?.setModified($generatedCode)
|
||||
await sleep(500)
|
||||
closePopup()
|
||||
await sleep(300)
|
||||
showDiff()
|
||||
} catch (err) {
|
||||
if (!abortController?.signal.aborted) {
|
||||
@@ -70,7 +65,6 @@
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
closePopup()
|
||||
} finally {
|
||||
genLoading = false
|
||||
}
|
||||
@@ -117,110 +111,134 @@
|
||||
}
|
||||
}
|
||||
$: updateSchema(lang, args)
|
||||
|
||||
let popover: Popover | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if SUPPORTED_LANGUAGES.has(lang)}
|
||||
<div>
|
||||
{#if !genLoading && $generatedCode.length > 0}
|
||||
<div class="flex gap-1">
|
||||
{#if !genLoading && $generatedCode.length > 0}
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
title="Discard generated code"
|
||||
size="xs"
|
||||
color="red"
|
||||
spacingSize="xs2"
|
||||
on:click={() => {
|
||||
popover?.close()
|
||||
rejectDiff()
|
||||
}}
|
||||
variant="contained"
|
||||
startIcon={{ icon: X }}
|
||||
propagateEvent={true}
|
||||
>
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
title="Accept generated code"
|
||||
size="xs"
|
||||
color="green"
|
||||
spacingSize="xs2"
|
||||
on:click={() => {
|
||||
popover?.close()
|
||||
acceptDiff()
|
||||
}}
|
||||
startIcon={{ icon: Check }}
|
||||
propagateEvent={true}
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
spacingSize="xs2"
|
||||
on:click={() => {
|
||||
$generatedExplanation = ''
|
||||
popover?.open()
|
||||
onFix()
|
||||
}}
|
||||
startIcon={{ icon: RotateCw }}
|
||||
title="Retry"
|
||||
btnClasses="text-violet-800 dark:text-violet-400"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<Popover
|
||||
bind:this={popover}
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
allowedPlacements: ['bottom-end', 'top-end']
|
||||
})
|
||||
]
|
||||
}}
|
||||
closeOnOutsideClick={!genLoading}
|
||||
closeButton={!genLoading}
|
||||
displayArrow={true}
|
||||
>
|
||||
<svelte:fragment slot="trigger" let:isOpen>
|
||||
<div class="flex flex-row">
|
||||
<Button
|
||||
title="Discard generated code"
|
||||
title="Fix code"
|
||||
size="xs"
|
||||
color="red"
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
spacingSize="xs2"
|
||||
on:click={rejectDiff}
|
||||
variant="contained"
|
||||
startIcon={{ icon: X }}
|
||||
startIcon={genLoading ? undefined : { icon: Wand2 }}
|
||||
propagateEvent={true}
|
||||
on:click={genLoading
|
||||
? () => abortController?.abort()
|
||||
: $generatedCode.length > 0
|
||||
? undefined
|
||||
: () => onFix()}
|
||||
btnClasses={genLoading
|
||||
? ''
|
||||
: 'text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700 min-w-[84px]'}
|
||||
>
|
||||
Discard
|
||||
</Button><Button
|
||||
title="Accept generated code"
|
||||
size="xs"
|
||||
color="green"
|
||||
spacingSize="xs2"
|
||||
on:click={acceptDiff}
|
||||
startIcon={{ icon: Check }}
|
||||
>
|
||||
Accept
|
||||
{#if genLoading}
|
||||
<WindmillIcon
|
||||
white
|
||||
class="mr-1 text-white"
|
||||
height="16px"
|
||||
width="20px"
|
||||
spin="veryfast"
|
||||
/>
|
||||
Stop
|
||||
{:else if $generatedCode.length > 0}
|
||||
<span class="text-xs">{isOpen ? 'Hide' : 'Show'}</span>
|
||||
{:else}
|
||||
AI Fix
|
||||
{/if}
|
||||
</Button>
|
||||
{#if $generatedExplanation.length > 0}
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">{$generatedExplanation}</svelte:fragment>
|
||||
<Button size="xs" color="light" variant="contained" spacingSize="xs2">Explain</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4">
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="w-[42rem] min-h-[3rem] max-h-[34rem] overflow-y-auto">
|
||||
{#if $generatedCode.length > 0 && genLoading}
|
||||
<div class="overflow-x-scroll">
|
||||
<HighlightCode language={lang} code={$generatedCode} />
|
||||
</div>
|
||||
{:else if genLoading}
|
||||
<LoadingIcon />
|
||||
{/if}
|
||||
{#if $generatedExplanation.length > 0}
|
||||
<p class="text-sm mt-2"
|
||||
><span class="font-bold">Explanation: test</span> {$generatedExplanation}</p
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-80">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank">workspace settings</a
|
||||
></p
|
||||
></div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Popup
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
allowedPlacements: ['bottom-end', 'top-end']
|
||||
})
|
||||
]
|
||||
}}
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Button
|
||||
title="Fix code"
|
||||
size="xs"
|
||||
color={genLoading ? 'red' : 'light'}
|
||||
spacingSize="xs2"
|
||||
startIcon={genLoading ? undefined : { icon: Wand2 }}
|
||||
nonCaptureEvent={!genLoading}
|
||||
on:click={genLoading ? () => abortController?.abort() : undefined}
|
||||
btnClasses={genLoading
|
||||
? ''
|
||||
: 'text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700'}
|
||||
>{#if genLoading}
|
||||
<WindmillIcon
|
||||
white
|
||||
class="mr-1 text-white"
|
||||
height="16px"
|
||||
width="20px"
|
||||
spin="veryfast"
|
||||
/>
|
||||
Stop
|
||||
{:else}
|
||||
AI Fix
|
||||
{/if}
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
{@const fixAction = (_) => {
|
||||
if ($copilotInfo.exists_ai_resource) {
|
||||
onFix(() => close(null))
|
||||
}
|
||||
}}
|
||||
<div use:fixAction>
|
||||
{#if $copilotInfo.exists_ai_resource}
|
||||
<div class="w-[42rem] min-h-[3rem] max-h-[34rem] overflow-y-scroll">
|
||||
{#if $generatedCode.length > 0}
|
||||
<div class="overflow-x-scroll">
|
||||
<HighlightCode language={lang} code={$generatedCode} />
|
||||
</div>
|
||||
{#if $generatedExplanation.length > 0}
|
||||
<p class="text-sm mt-2"
|
||||
><span class="font-bold">Explanation:</span> {$generatedExplanation}</p
|
||||
>
|
||||
{/if}
|
||||
{:else}
|
||||
<LoadingIcon />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-80">
|
||||
<p class="text-sm"
|
||||
>Enable Windmill AI in the <a
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank">workspace settings</a
|
||||
></p
|
||||
></div
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</Popup>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import type { SupportedLanguage } from '$lib/common'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import type Editor from '../Editor.svelte'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import TooltipV2 from '$lib/components/meltComponents/Tooltip.svelte'
|
||||
import {
|
||||
dbSchemas,
|
||||
copilotInfo,
|
||||
@@ -29,7 +30,6 @@
|
||||
import { fade } from 'svelte/transition'
|
||||
import { isInitialCode } from '$lib/script_helpers'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
// props
|
||||
@@ -249,6 +249,11 @@
|
||||
}
|
||||
$: $copilotInfo && checkForInvalidModel()
|
||||
|
||||
function handlePublicOnlySelected({ detail }: { detail: string }) {
|
||||
if (!dbSchema) return
|
||||
;(dbSchema as any).publicOnly = detail === 'true'
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
abortController?.abort()
|
||||
})
|
||||
@@ -311,7 +316,7 @@
|
||||
{/if}
|
||||
{/if}
|
||||
{#if ($generatedCode.length === 0 || genLoading) && SUPPORTED_LANGUAGES.has(lang ?? '')}
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
middleware: [
|
||||
autoPlacement({
|
||||
@@ -319,10 +324,9 @@
|
||||
})
|
||||
]
|
||||
}}
|
||||
let:close
|
||||
blockOpen={blockPopupOpen}
|
||||
disabled={blockPopupOpen}
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
{#if inlineScript}
|
||||
<Button
|
||||
size="xs"
|
||||
@@ -387,143 +391,159 @@
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<div class="block text-primary">
|
||||
{#if genLoading}
|
||||
<div class="w-[42rem] min-h-[3rem] max-h-[34rem] overflow-y-scroll" bind:this={codeDiv}>
|
||||
{#if $generatedCode.length > 0}
|
||||
<div class="overflow-x-scroll">
|
||||
<HighlightCode language={lang} code={$generatedCode} /></div
|
||||
>
|
||||
{:else}
|
||||
<LoadingIcon />
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row justify-between items-center w-96 gap-2">
|
||||
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={mode}>
|
||||
<ToggleButton value={'gen'} label="Generate from scratch" small light />
|
||||
<ToggleButton value={'edit'} label="Edit existing code" small light />
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<div class="min-w-0">
|
||||
{#if $copilotInfo.ai_models.length > 1}
|
||||
<select
|
||||
bind:value={$copilotSessionModel}
|
||||
class="!text-xs !pr-5 !bg-[right_center] overflow-ellipsis text-right !border-none !shadow-none"
|
||||
>
|
||||
{#each $copilotInfo.ai_models as model}
|
||||
<option value={model} class="pr-4">{model}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if $copilotInfo.ai_models.length === 1}
|
||||
<div class="text-xs whitespace-nowrap overflow-hidden overflow-ellipsis">
|
||||
{$copilotInfo.ai_models[0]}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-96 items-start">
|
||||
<textarea
|
||||
bind:this={input}
|
||||
bind:value={funcDesc}
|
||||
on:input={autoResize}
|
||||
on:keydown={({ key, shiftKey }) => {
|
||||
if (key === 'Enter' && !shiftKey && trimmedDesc.length > 0) {
|
||||
onGenerate(() => close(input || null))
|
||||
return false
|
||||
}
|
||||
}}
|
||||
placeholder={mode === 'edit'
|
||||
? 'Describe the changes you want'
|
||||
: 'Describe what the script should do'}
|
||||
rows="1"
|
||||
class="resize-none overflow-hidden"
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
buttonType="button"
|
||||
btnClasses="!h-[34px] qhd:!h-[38px] !ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
title="Generate code from prompt"
|
||||
aria-label="Generate"
|
||||
on:click={() => {
|
||||
onGenerate(() => close(input || null))
|
||||
}}
|
||||
disabled={trimmedDesc.length <= 0}
|
||||
iconOnly
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
{#if promptHistory.length > 0}
|
||||
<div class="w-96 flex flex-col gap-1">
|
||||
{#each promptHistory as p}
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
btnClasses="justify-start overflow-x-scroll no-scrollbar"
|
||||
startIcon={{ icon: HistoryIcon, classes: 'shrink-0' }}
|
||||
on:click={() => {
|
||||
funcDesc = p
|
||||
setTimeout(() => {
|
||||
autoResize()
|
||||
}, 0)
|
||||
}}>{p}</Button
|
||||
>
|
||||
{/each}
|
||||
<button
|
||||
class="underline text-xs text-start px-2 text-secondary font-normal"
|
||||
on:click={clearPromptHistory}>clear history</button
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="p-4">
|
||||
{#if genLoading}
|
||||
<div class="w-[42rem] min-h-[3rem] max-h-[34rem] overflow-y-scroll" bind:this={codeDiv}>
|
||||
{#if $generatedCode.length > 0}
|
||||
<div class="overflow-x-scroll">
|
||||
<HighlightCode language={lang} code={$generatedCode} /></div
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<LoadingIcon />
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $copilotInfo.exists_ai_resource}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row justify-between items-center w-96 gap-2">
|
||||
<ToggleButtonGroup class="w-auto shrink-0 h-auto" bind:selected={mode} let:item>
|
||||
<ToggleButton
|
||||
value={'gen'}
|
||||
label="Generate from scratch"
|
||||
light
|
||||
class="px-2"
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton value={'edit'} label="Edit existing code" light class="px-2" {item} />
|
||||
</ToggleButtonGroup>
|
||||
|
||||
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql, oracledb'].includes(lang ?? '') && dbSchema?.lang === lang}
|
||||
<div class="flex flex-row items-center justify-between gap-2 w-96">
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<p class="text-xs text-secondary">
|
||||
Context: {lang === 'graphql' ? 'GraphQL' : 'DB'} schema
|
||||
</p>
|
||||
<Tooltip placement="top">
|
||||
We pass the selected schema to GPT-4 Turbo for better script generation.
|
||||
</Tooltip>
|
||||
{#if dbSchema && dbSchema.stringified.length > MAX_SCHEMA_LENGTH}
|
||||
<Popover notClickable placement="top">
|
||||
<AlertTriangle size={16} class="text-yellow-500" />
|
||||
<svelte:fragment slot="text">
|
||||
The schema is about {addThousandsSeparator(dbSchema.stringified.length / 3.5)}
|
||||
tokens. To avoid exceeding the model's context length, it will be truncated to
|
||||
{addThousandsSeparator(MAX_SCHEMA_LENGTH / 3.5)}
|
||||
tokens.
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
<div class="min-w-0">
|
||||
{#if $copilotInfo.ai_models.length > 1}
|
||||
<select
|
||||
bind:value={$copilotSessionModel}
|
||||
class="!text-xs !pr-5 !bg-[right_center] overflow-ellipsis text-right !border-none !shadow-none"
|
||||
>
|
||||
{#each $copilotInfo.ai_models as model}
|
||||
<option value={model} class="pr-4">{model}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if $copilotInfo.ai_models.length === 1}
|
||||
<div class="text-xs whitespace-nowrap overflow-hidden overflow-ellipsis">
|
||||
{$copilotInfo.ai_models[0]}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if dbSchema && dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)}
|
||||
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={dbSchema.publicOnly}>
|
||||
<ToggleButton
|
||||
value={true}
|
||||
label={(dbSchema.schema?.dbo ? 'Dbo' : 'Public') + ' schema'}
|
||||
small
|
||||
light
|
||||
/>
|
||||
<ToggleButton value={false} label="All schemas" small light />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm">
|
||||
Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</Popup>
|
||||
<div class="flex w-96 items-start">
|
||||
<textarea
|
||||
bind:this={input}
|
||||
bind:value={funcDesc}
|
||||
on:input={autoResize}
|
||||
on:keydown={({ key, shiftKey }) => {
|
||||
if (key === 'Enter' && !shiftKey && trimmedDesc.length > 0) {
|
||||
onGenerate(() => close())
|
||||
return false
|
||||
}
|
||||
}}
|
||||
placeholder={mode === 'edit'
|
||||
? 'Describe the changes you want'
|
||||
: 'Describe what the script should do'}
|
||||
rows="1"
|
||||
class="resize-none overflow-hidden"
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
buttonType="button"
|
||||
btnClasses="!h-[34px] qhd:!h-[38px] !ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
|
||||
title="Generate code from prompt"
|
||||
aria-label="Generate"
|
||||
on:click={() => {
|
||||
onGenerate(() => close())
|
||||
}}
|
||||
disabled={trimmedDesc.length <= 0}
|
||||
iconOnly
|
||||
startIcon={{ icon: Wand2 }}
|
||||
/>
|
||||
</div>
|
||||
{#if promptHistory.length > 0}
|
||||
<div class="w-96 flex flex-col gap-1">
|
||||
{#each promptHistory as p}
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
btnClasses="justify-start overflow-x-scroll no-scrollbar"
|
||||
startIcon={{ icon: HistoryIcon, classes: 'shrink-0' }}
|
||||
on:click={() => {
|
||||
funcDesc = p
|
||||
setTimeout(() => {
|
||||
autoResize()
|
||||
}, 0)
|
||||
}}>{p}</Button
|
||||
>
|
||||
{/each}
|
||||
<button
|
||||
class="underline text-xs text-start px-2 text-secondary font-normal"
|
||||
on:click={clearPromptHistory}>clear history</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql, oracledb'].includes(lang ?? '') && dbSchema?.lang === lang}
|
||||
<div class="flex flex-row items-center justify-between gap-2 w-96">
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<p class="text-xs text-secondary">
|
||||
Context: {lang === 'graphql' ? 'GraphQL' : 'DB'} schema
|
||||
</p>
|
||||
<Tooltip placement="top">
|
||||
We pass the selected schema to GPT-4 Turbo for better script generation.
|
||||
</Tooltip>
|
||||
{#if dbSchema && dbSchema.stringified.length > MAX_SCHEMA_LENGTH}
|
||||
<TooltipV2 notClickable placement="top">
|
||||
<AlertTriangle size={16} class="text-yellow-500" />
|
||||
<svelte:fragment slot="text">
|
||||
The schema is about {addThousandsSeparator(
|
||||
dbSchema.stringified.length / 3.5
|
||||
)}
|
||||
tokens. To avoid exceeding the model's context length, it will be truncated to
|
||||
{addThousandsSeparator(MAX_SCHEMA_LENGTH / 3.5)}
|
||||
tokens.
|
||||
</svelte:fragment>
|
||||
</TooltipV2>
|
||||
{/if}
|
||||
</div>
|
||||
{#if dbSchema && dbSchema.lang !== 'graphql' && (dbSchema.schema?.public || dbSchema.schema?.PUBLIC || dbSchema.schema?.dbo)}
|
||||
<ToggleButtonGroup
|
||||
class="w-auto shrink-0"
|
||||
selected={dbSchema?.publicOnly ? 'true' : 'false'}
|
||||
on:selected={handlePublicOnlySelected}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
value={'true'}
|
||||
label={(dbSchema.schema?.dbo ? 'Dbo' : 'Public') + ' schema'}
|
||||
small
|
||||
light
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton value={'false'} label="All schemas" small light {item} />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-sm">
|
||||
Enable Windmill AI in the <a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import type { FlowCopilotContext } from './flow'
|
||||
import { Check, ExternalLink, Loader2, Wand2 } from 'lucide-svelte'
|
||||
import { copilotInfo, stepInputCompletionEnabled } from '$lib/stores'
|
||||
import { Popup } from '../common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import type { SchemaProperty, Schema } from '$lib/common'
|
||||
import FlowCopilotInputsModal from './FlowCopilotInputsModal.svelte'
|
||||
import type { Flow } from '$lib/gen'
|
||||
@@ -217,13 +217,12 @@ input_name2: expression2
|
||||
{/if}
|
||||
</Button>
|
||||
{:else}
|
||||
<Popup
|
||||
<Popover
|
||||
floatingConfig={{
|
||||
placement: 'top-end'
|
||||
}}
|
||||
let:close
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<svelte:fragment slot="trigger">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
@@ -236,29 +235,33 @@ input_name2: expression2
|
||||
Fill inputs
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<p class="text-sm">
|
||||
{#if !$copilotInfo.exists_ai_resource}
|
||||
Enable Windmill AI in the{' '}
|
||||
<a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
{:else}
|
||||
Enable step input completion in the{' '}
|
||||
<a
|
||||
href="#user-settings"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
on:click={() => {
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
user settings
|
||||
</a>
|
||||
{/if}
|
||||
</p>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="p-4">
|
||||
<p class="text-sm">
|
||||
{#if !$copilotInfo.exists_ai_resource}
|
||||
Enable Windmill AI in the{' '}
|
||||
<a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
>
|
||||
workspace settings <ExternalLink size={16} />
|
||||
</a>
|
||||
{:else}
|
||||
Enable step input completion in the{' '}
|
||||
<a
|
||||
href="#user-settings"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
on:click={() => {
|
||||
close()
|
||||
}}
|
||||
>
|
||||
user settings
|
||||
</a>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
|
||||
import Menu from '$lib/components/details/Menu.svelte'
|
||||
import MenuItem from '$lib/components/common/menu/MenuItem.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import DropdownV2 from '$lib/components/DropdownV2.svelte'
|
||||
import ErrorHandlerToggleButton from './ErrorHandlerToggleButton.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { userStore } from '$lib/stores'
|
||||
@@ -73,30 +71,17 @@
|
||||
</div>
|
||||
<div class="flex gap-1 md:gap-2 items-center">
|
||||
{#if menuItems.length > 0}
|
||||
<Menu>
|
||||
<svelte:fragment slot="items">
|
||||
{#each menuItems as { label, Icon, onclick, color } (label)}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
const div = document.querySelector('[id^="headlessui-menu-items"]')
|
||||
div?.parentElement?.remove()
|
||||
|
||||
onclick()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'text-xs flex items-center gap-2 flex-row-2 ',
|
||||
color === 'red' ? 'text-red-500' : ''
|
||||
)}
|
||||
>
|
||||
<Icon class="h-4" />
|
||||
{label}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
{#key menuItems}
|
||||
<DropdownV2
|
||||
items={menuItems.map((item) => ({
|
||||
displayName: item.label,
|
||||
icon: item.Icon,
|
||||
action: item.onclick,
|
||||
type: item.color === 'red' ? 'delete' : 'action'
|
||||
}))}
|
||||
placement="bottom-end"
|
||||
/>
|
||||
{/key}
|
||||
{/if}
|
||||
<ErrorHandlerToggleButton
|
||||
kind={errorHandlerKind}
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
Webhook,
|
||||
Unplug,
|
||||
PlugZap,
|
||||
|
||||
Database
|
||||
|
||||
} from 'lucide-svelte'
|
||||
|
||||
import HighlightTheme from '../HighlightTheme.svelte'
|
||||
@@ -111,9 +109,9 @@
|
||||
<slot name="postgres" />
|
||||
{:else if triggerSelected === 'kafka' || triggerSelected === 'nats'}
|
||||
<div class="m-1.5">
|
||||
<ToggleButtonGroup bind:selected={eventStreamType}>
|
||||
<ToggleButton value="kafka" label="Kafka" icon={KafkaIcon} />
|
||||
<ToggleButton value="nats" label="NATS" icon={NatsIcon} />
|
||||
<ToggleButtonGroup bind:selected={eventStreamType} let:item>
|
||||
<ToggleButton value="kafka" label="Kafka" icon={KafkaIcon} {item} />
|
||||
<ToggleButton value="nats" label="NATS" icon={NatsIcon} {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{#if eventStreamType === 'kafka'}
|
||||
|
||||
@@ -110,9 +110,9 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="text-xs font-semibold flex flex-row items-center">Call method</div>
|
||||
<ToggleButtonGroup class="h-[30px] w-auto" bind:selected={requestType}>
|
||||
<ToggleButton label="By path" value="path" />
|
||||
<ToggleButton label="By hash" value="hash" />
|
||||
<ToggleButtonGroup class="h-[30px] w-auto" bind:selected={requestType} let:item>
|
||||
<ToggleButton label="By path" value="path" {item} />
|
||||
<ToggleButton label="By hash" value="hash" {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Menu, Transition, MenuButton, MenuItems } from '@rgossiaux/svelte-headlessui'
|
||||
import { MoreVertical } from 'lucide-svelte'
|
||||
import { createPopperActions, type PopperOptions } from 'svelte-popperjs'
|
||||
import Portal from '$lib/components/Portal.svelte'
|
||||
|
||||
import Button from '../common/button/Button.svelte'
|
||||
|
||||
const [popperRef, popperContent] = createPopperActions({ placement: 'auto' })
|
||||
|
||||
const popperOptions: PopperOptions<{}> = {
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [
|
||||
{ name: 'offset', options: { offset: [8, 8] } },
|
||||
{
|
||||
name: 'arrow',
|
||||
options: {
|
||||
padding: 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu let:open as="div" class="relative hover:z-50 flex w-full h-full">
|
||||
<span use:popperRef>
|
||||
<MenuButton>
|
||||
<Button
|
||||
nonCaptureEvent
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{
|
||||
icon: MoreVertical
|
||||
}}
|
||||
iconOnly
|
||||
/>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<Portal name="menu">
|
||||
<div use:popperContent={popperOptions} class="z-[2000]">
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition ease-out duration-[25ms]"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-[25ms]"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute border right-0 z-50 w-56 origin-top-right top-1 rounded-md bg-surface shadow-md focus:outline-none cursor-pointer"
|
||||
>
|
||||
<div class="my-1">
|
||||
<slot name="items" />
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</div>
|
||||
</Portal>
|
||||
</Menu>
|
||||
@@ -68,12 +68,13 @@
|
||||
{:else}{#if !failureModule && !preprocessorModule}
|
||||
<div class="center-center">
|
||||
<div class="max-w-min">
|
||||
<ToggleButtonGroup bind:selected={kind}>
|
||||
<ToggleButtonGroup bind:selected={kind} let:item>
|
||||
<ToggleButton
|
||||
value="script"
|
||||
icon={Code}
|
||||
label="Action"
|
||||
tooltip="An action script is simply a script that is neither a trigger nor an approval script. Those are the majority of the scripts."
|
||||
{item}
|
||||
/>
|
||||
{#if !shouldDisableTriggerScripts}
|
||||
<ToggleButton
|
||||
@@ -81,6 +82,7 @@
|
||||
icon={Zap}
|
||||
label="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 and set the new state. The diffs are then treated one by one with a for-loop."
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
<ToggleButton
|
||||
@@ -88,6 +90,7 @@
|
||||
icon={Check}
|
||||
label="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."
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -629,13 +629,18 @@
|
||||
</div>
|
||||
<div class="flex gap-2 justify-between mb-4 items-center">
|
||||
<div class="flex gap-2">
|
||||
<ToggleButtonGroup bind:selected={s3Kind} class="w-auto">
|
||||
<ToggleButtonGroup bind:selected={s3Kind} class="w-auto" let:item>
|
||||
{#if flowModule.value['language'] === 'deno'}
|
||||
<ToggleButton value="s3_client" size="sm" label="S3 lite client" />
|
||||
<ToggleButton
|
||||
value="s3_client"
|
||||
size="sm"
|
||||
label="S3 lite client"
|
||||
{item}
|
||||
/>
|
||||
{:else}
|
||||
<ToggleButton value="s3_client" size="sm" label="Boto3" />
|
||||
<ToggleButton value="polars" size="sm" label="Polars" />
|
||||
<ToggleButton value="duckdb" size="sm" label="DuckDB" />
|
||||
<ToggleButton value="s3_client" size="sm" label="Boto3" {item} />
|
||||
<ToggleButton value="polars" size="sm" label="Polars" {item} />
|
||||
<ToggleButton value="duckdb" size="sm" label="DuckDB" {item} />
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -68,10 +68,11 @@
|
||||
setExponentialRetries()
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton light value="disabled" label="Disabled" />
|
||||
<ToggleButton light value="constant" label="Constant" />
|
||||
<ToggleButton light value="exponential" label="Exponential" />
|
||||
<ToggleButton light value="disabled" label="Disabled" {item} />
|
||||
<ToggleButton light value="constant" label="Constant" {item} />
|
||||
<ToggleButton light value="exponential" label="Exponential" {item} />
|
||||
</ToggleButtonGroup>
|
||||
<div class="flex h-[calc(100%-22px)]">
|
||||
<div class="w-1/2 h-full overflow-auto pr-2">
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Menu from '$lib/components/common/menu/Menu.svelte'
|
||||
import type { FlowCopilotContext } from '$lib/components/copilot/flow'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { copilotInfo } from '$lib/stores'
|
||||
import { ExternalLink, Wand2 } from 'lucide-svelte'
|
||||
import { base } from '$lib/base'
|
||||
|
||||
let openNoCopilot = false
|
||||
export let className = ''
|
||||
|
||||
const { drawerStore: copilotDrawerStore } =
|
||||
getContext<FlowCopilotContext | undefined>('FlowCopilotContext') || {}
|
||||
</script>
|
||||
|
||||
<div class={className}>
|
||||
<Popover>
|
||||
<div class={openNoCopilot ? 'z-10' : ''}>
|
||||
<Menu pointerDown noMinW placement="bottom-center" let:close bind:show={openNoCopilot}>
|
||||
<button
|
||||
title="AI Flow Builder"
|
||||
on:pointerdown={$copilotInfo.exists_ai_resource
|
||||
? (ev) => {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
$copilotDrawerStore?.openDrawer()
|
||||
}
|
||||
: undefined}
|
||||
slot="trigger"
|
||||
type="button"
|
||||
class=" bg-surface text-violet-800 dark:text-violet-400 border mx-0.5 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-gray-200 font-medium rounded-full text-sm w-7 h-7 flex items-center justify-center"
|
||||
>
|
||||
<Wand2 size={12} />
|
||||
</button>
|
||||
{#if !$copilotInfo.exists_ai_resource}
|
||||
<div class="text-primary p-4">
|
||||
<p class="text-sm w-80">
|
||||
Enable Windmill AI in the
|
||||
<a
|
||||
href="{base}/workspace_settings?tab=ai"
|
||||
target="_blank"
|
||||
class="inline-flex flex-row items-center gap-1"
|
||||
on:click={() => {
|
||||
close()
|
||||
}}
|
||||
>
|
||||
workspace settings
|
||||
<ExternalLink size={16} />
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="text">AI Flow builder</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import Menu from '$lib/components/common/menu/MenuV2.svelte'
|
||||
import { Menu, Menubar } from '$lib/components/meltComponents'
|
||||
import { melt } from '@melt-ui/svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { ListFilter, Lock, LockOpen } from 'lucide-svelte'
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -23,21 +24,31 @@
|
||||
filter > 0
|
||||
) {
|
||||
event.preventDefault()
|
||||
dispatch('selectedIteration', { index: filter - 1, id: flowJobs[filter - 1], manuallySet: true })
|
||||
dispatch('selectedIteration', {
|
||||
index: filter - 1,
|
||||
id: flowJobs[filter - 1],
|
||||
manuallySet: true
|
||||
})
|
||||
menu?.close()
|
||||
}
|
||||
}
|
||||
|
||||
let buttonHover = false
|
||||
let menu: Menu | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if selectedManually && flowJobsSuccess?.some((x) => x == undefined || x == null)}
|
||||
<Popover class="absolute top-1.5 right-12 cursor-pointer">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div on:mouseenter={() => (buttonHover = true)} on:mouseleave={() => (buttonHover = false)} on:click={(e) => {
|
||||
<div
|
||||
on:mouseenter={() => (buttonHover = true)}
|
||||
on:mouseleave={() => (buttonHover = false)}
|
||||
on:click={(e) => {
|
||||
buttonHover = false
|
||||
dispatch('selectedIteration', { manuallySet: false })}
|
||||
}>
|
||||
dispatch('selectedIteration', { manuallySet: false })
|
||||
}}
|
||||
>
|
||||
{#if buttonHover}
|
||||
<LockOpen class="text-primary" size={14} />
|
||||
{:else}
|
||||
@@ -50,45 +61,50 @@
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
<Menu maxHeight={300}>
|
||||
<div slot="trigger">
|
||||
<button
|
||||
title="Pick an iteration"
|
||||
id={`flow-editor-iteration picker-${id}`}
|
||||
type="button"
|
||||
class=" text-xs bg-surface border-[1px] border-gray-300 dark:border-gray-500 focus:outline-none
|
||||
hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-sm w-[40px] gap-1 h-[20px]
|
||||
flex items-center justify-center {flowJobsSuccess?.[selected] == false
|
||||
? 'text-red-400'
|
||||
: 'text-secondary'}"
|
||||
>
|
||||
#{selected == -1 ? '?' : selected + 1}
|
||||
<ListFilter size={15} />
|
||||
</button>
|
||||
</div>
|
||||
<Menubar let:createMenu>
|
||||
<Menu {createMenu} let:item placement="bottom" bind:this={menu} usePointerDownOutside>
|
||||
<svelte:fragment slot="trigger" let:trigger>
|
||||
<button
|
||||
title="Pick an iteration"
|
||||
id={`flow-editor-iteration picker-${id}`}
|
||||
type="button"
|
||||
class={twMerge(
|
||||
'text-xs bg-surface border-[1px] border-gray-300 dark:border-gray-500 focus:outline-none',
|
||||
'hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-sm w-[40px] gap-1 h-[20px]',
|
||||
'flex items-center justify-center',
|
||||
flowJobsSuccess?.[selected] == false ? 'text-red-400' : 'text-secondary'
|
||||
)}
|
||||
use:melt={trigger}
|
||||
>
|
||||
#{selected == -1 ? '?' : selected + 1}
|
||||
<ListFilter size={15} />
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
|
||||
<MenuItem disabled>
|
||||
<input type="number" bind:value={filter} on:keydown={onKeydown} />
|
||||
</MenuItem>
|
||||
{#each flowJobs ?? [] as id, idx (id)}
|
||||
{#if filter == undefined || (idx + 1).toString().includes(filter.toString())}
|
||||
<MenuItem>
|
||||
<button
|
||||
class="text-primary text-xs w-full text-left py-1 pl-2 hover:bg-surface-hover whitespace-nowrap flex flex-row gap-2 items-center {flowJobsSuccess?.[
|
||||
idx
|
||||
] == false
|
||||
? 'text-red-400'
|
||||
: ''}"
|
||||
on:pointerdown={() => {
|
||||
//close()
|
||||
dispatch('selectedIteration', { index: idx, id, manuallySet: true })
|
||||
}}
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
#{idx + 1}
|
||||
</button>
|
||||
</MenuItem>
|
||||
{/if}
|
||||
{/each}
|
||||
</Menu>
|
||||
<div class="flex flex-col px-1">
|
||||
<input type="number" bind:value={filter} on:keydown={onKeydown} />
|
||||
|
||||
<div class="overflow-y-auto max-h-[300px]">
|
||||
{#each flowJobs ?? [] as id, idx (id)}
|
||||
{#if filter == undefined || (idx + 1).toString().includes(filter.toString())}
|
||||
<button
|
||||
class={twMerge(
|
||||
'text-primary text-xs w-full text-left py-1 pl-2 hover:bg-surface-hover whitespace-nowrap flex flex-row gap-2 items-center',
|
||||
flowJobsSuccess?.[idx] == false ? 'text-red-400' : '',
|
||||
'data-[highlighted]:bg-surface-hover'
|
||||
)}
|
||||
on:click={() => {
|
||||
dispatch('selectedIteration', { index: idx, id, manuallySet: true })
|
||||
}}
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
use:melt={item}
|
||||
>
|
||||
#{idx + 1}
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Menu>
|
||||
</Menubar>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
|
||||
import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte'
|
||||
import { Popup } from '$lib/components/common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
import Tooltip from '$lib/components/meltComponents/Tooltip.svelte'
|
||||
import { Plug } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { PropPickerContext } from '$lib/components/prop_picker'
|
||||
import { getContext } from 'svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
import { tick } from 'svelte'
|
||||
|
||||
export let json = {}
|
||||
@@ -31,6 +32,7 @@
|
||||
}}
|
||||
on:keydown|preventDefault|stopPropagation
|
||||
data-prop-picker
|
||||
title=""
|
||||
>
|
||||
<AnimatedButton
|
||||
animate={isConnecting}
|
||||
@@ -38,9 +40,12 @@
|
||||
baseRadius="9999px"
|
||||
marginWidth="1px"
|
||||
>
|
||||
<Popup floatingConfig={{ strategy: 'fixed', placement: 'bottom-start' }}>
|
||||
<svelte:fragment slot="button" let:open>
|
||||
<Popover disablePopup={open}>
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'fixed', placement: 'bottom-start' }}
|
||||
closeOnOtherPopoverOpen={true}
|
||||
>
|
||||
<svelte:fragment slot="trigger" let:isOpen>
|
||||
<Tooltip disablePopup={isOpen}>
|
||||
<svelte:fragment slot="text">node outputs</svelte:fragment>
|
||||
<button
|
||||
class={twMerge(
|
||||
@@ -52,25 +57,24 @@
|
||||
>
|
||||
<Plug size={12} strokeWidth={2} />
|
||||
</button>
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
<div data-prop-picker>
|
||||
<ObjectViewer
|
||||
{json}
|
||||
topBrackets={false}
|
||||
pureViewer={false}
|
||||
{prefix}
|
||||
on:select={({ detail }) => {
|
||||
if ($flowPropPickerConfig?.onSelect(detail)) {
|
||||
$flowPropPickerConfig?.clearFocus()
|
||||
// console.log('BAR')
|
||||
} else {
|
||||
// console.log('FOO')
|
||||
}
|
||||
}}
|
||||
allowCopy={!$flowPropPickerConfig}
|
||||
/>
|
||||
</div>
|
||||
</Popup>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="p-4" data-prop-picker>
|
||||
<ObjectViewer
|
||||
{json}
|
||||
topBrackets={false}
|
||||
pureViewer={false}
|
||||
{prefix}
|
||||
on:select={({ detail }) => {
|
||||
if ($flowPropPickerConfig?.onSelect(detail)) {
|
||||
$flowPropPickerConfig?.clearFocus()
|
||||
}
|
||||
}}
|
||||
allowCopy={!$flowPropPickerConfig}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</AnimatedButton>
|
||||
</button>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user