Compare commits

...
Author SHA1 Message Date
Guilhem 424cb4e7db fix migrate toggle butttons to melt 2025-02-19 08:57:27 +00:00
Guilhem fe5e8d8bdb add class to flow icon 2025-02-19 08:17:06 +00:00
Guilhem d2c868156b Merge branch 'glm/replace_menuV2' into glm/use_melt_menu
# Conflicts:
#	frontend/src/lib/components/meltComponents/Menu.svelte
2025-02-19 08:00:05 +00:00
Guilhem 8ed283f812 fix component jumping due to z-index 2025-02-18 19:23:23 +00:00
Guilhem f7b7ff06ff Add debounce effect in operator menu 2025-02-18 18:32:08 +00:00
Guilhem af186df512 Merge remote-tracking branch 'origin' into glm/replace_menuV2 2025-02-18 18:17:57 +00:00
Guilhem 673e02315f remove unused Portal 2025-02-18 18:17:22 +00:00
Guilhem cc7b819012 fix glitch when toggle in Popover 2025-02-18 17:40:03 +00:00
Guilhem 6bf388d7d7 fix addProperty display 2025-02-18 16:34:32 +00:00
Guilhem ac6a337a00 clean code 2025-02-18 16:16:33 +00:00
Guilhem b292f54db7 Merge branch 'glm/replace_menuV2' into glm/use_melt_menu 2025-02-18 13:22:20 +00:00
Guilhem 70a016eb6b Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-18 13:21:35 +00:00
Ruben Fiszel e16344dd19 fix: improve v2 migration finalizer to avoid deadlocks 2025-02-18 13:18:43 +00:00
Ruben Fiszel 375228d14d fix: pin chrono tz version to 0.10.1 2025-02-18 13:18:43 +00:00
Ruben Fiszel 1b18794a40 fix: make teams cleanup non critical 2025-02-18 13:18:43 +00:00
Ruben Fiszel e1a414d3ef chore(main): release 1.463.5 (#5318)
* chore(main): release 1.463.4

* update

* Update CHANGELOG.md
2025-02-18 13:18:43 +00:00
Ruben Fiszel b2f2e55425 fix: fix teams cleanup preventing start 2025-02-18 13:18:43 +00:00
Ruben Fiszel 5cad0bfe70 improve benchmarks 2025-02-18 13:18:43 +00:00
Ruben Fiszel 851e2f29f7 make benchmarks even more stable 2025-02-18 13:18:43 +00:00
Ruben Fiszel effcccf30f nit benchmarks 2025-02-18 13:18:43 +00:00
Ruben Fiszelandrubenfiszel 0568474ba4 chore(main): release 1.463.4 (#5309)
* chore(main): release 1.463.4

* Update CHANGELOG.md

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-02-18 13:18:43 +00:00
Ruben Fiszel e8cf9457a1 ensure index creation of root_job_by_path 2025-02-18 13:18:43 +00:00
Alexander Petric 170d8f7cda fix: improve teams settings in workspace settings (#5316) 2025-02-18 13:18:43 +00:00
Ruben Fiszel ee0ef7d8b0 output incorrect jobs in benchmarks 2025-02-18 13:18:43 +00:00
Ruben Fiszel 9e999106f0 nit benchmarks improvement 2025-02-18 13:18:43 +00:00
Ruben Fiszel be1fcc016e fix: improve que job indices for faster performances 2025-02-18 13:18:43 +00:00
Ruben Fiszel 731802a186 nit on bench scripts 2025-02-18 13:18:43 +00:00
Ruben Fiszel 8641a7e95f fix: improve que job indices for faster performances 2025-02-18 13:18:43 +00:00
Ruben Fiszel 3eb1af6c81 fix: improve que job indices for faster performances 2025-02-18 13:18:42 +00:00
Ruben Fiszelandrubenfiszel a2f0be9d6f chore(main): release 1.463.3 (#5308)
* chore(main): release 1.463.3

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-02-18 13:18:42 +00:00
Ruben Fiszel c79227079e fix: windmill_admin has implicit bypass rls on v2_job even if role not set 2025-02-18 13:18:42 +00:00
Ruben Fiszel 60f8924a4c nits 2025-02-18 13:18:42 +00:00
Ruben Fiszel d6271a18b8 improve version detection from source 2025-02-18 13:18:42 +00:00
Guilhem 5f696f12f9 migrate ToggleButtonGroup to melt 2025-02-18 13:18:42 +00:00
Guilhem 4c0177c255 fix typo 2025-02-18 13:18:42 +00:00
Guilhem 5e73d41e85 remove unused TotorialItem 2025-02-18 13:18:42 +00:00
Guilhem d483940297 remove unused Popup component 2025-02-18 13:18:42 +00:00
Guilhem 4960f3ce6b remove unused ButtonDropdown 2025-02-18 13:18:42 +00:00
Guilhem 5684035098 nit 2025-02-18 13:18:42 +00:00
Guilhem a6bad016bc replace popup with melt popover in settings pages 2025-02-18 13:18:42 +00:00
Guilhem f793080828 fix melt tooltip display 2025-02-18 13:18:42 +00:00
Guilhem 4c48665314 dropdown v2 default stop propagation 2025-02-18 13:18:42 +00:00
Guilhem-lm 762e58b5cd Apply automatic changes 2025-02-17 14:28:41 +00:00
Guilhem 76f826a222 fix nit 2025-02-17 14:26:46 +00:00
Guilhem 6b8ee38caa replace popup with popover for app components 2025-02-17 14:25:09 +00:00
Guilhem 7fd59292eb replace popup with melt popover 2025-02-17 10:12:42 +00:00
Guilhem db4c02785f Merge remote-tracking branch 'origin/main' into glm/use_melt_menu 2025-02-17 08:23:42 +00:00
Guilhem 215c48aa53 wip migrate popup to popover 2025-02-13 21:23:21 +00:00
Guilhem d93e3422cf Migrating popup to popover 2025-02-13 21:13:52 +00:00
Guilhem dec070458f replace popup with melt popover 2025-02-12 22:55:01 +00:00
Guilhem 7438e94df0 Fix toggleButton 2025-02-12 21:13:45 +00:00
Guilhem 590c325607 Merge branch 'glm/replace_menuV2' into glm/use_melt_menu 2025-02-12 21:02:54 +00:00
Guilhem 0c431b0b2c replace popup with melt popover in ApiConnectForm 2025-02-12 21:00:04 +00:00
Guilhem 6c5cde4532 Merge remote-tracking branch 'origin/main' into glm/use_melt_menu 2025-02-12 20:52:01 +00:00
Guilhem b0c4783b66 Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-12 20:32:44 +00:00
Guilhem 3b40f40a8b replace popup with popover in StepinputGen 2025-02-12 20:31:46 +00:00
Guilhem d37d090b07 replace popup wwith melt popover in CronGen 2025-02-12 19:52:48 +00:00
Guilhem fa197262cc replace popup with melt popover in RegexGen 2025-02-12 19:46:57 +00:00
Guilhem b1f4a8e40a replace popup with popover in ScriptFix 2025-02-12 19:29:05 +00:00
Guilhem f37659207a replace Popup with melt Popover in ScriptGen 2025-02-12 16:20:59 +00:00
Guilhem b97af4dbf1 clean FlowPropertyEditor workaround flicker 2025-02-12 16:11:23 +00:00
Guilhem a038513bfa fix flicker due to z index change 2025-02-12 16:10:44 +00:00
Guilhem 69ebe10851 Merge remote-tracking branch 'origin/glm/replace_menuV2' into glm/use_melt_menu 2025-02-12 14:29:52 +00:00
Guilhem eff81bcae0 Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-12 14:27:07 +00:00
Guilhem 3e277fa5ec Merge remote-tracking branch 'origin/main' into glm/use_melt_menu 2025-02-12 14:23:24 +00:00
Guilhem 51549db78b replace Popup with melt Popover in FlowPropertyEditor 2025-02-12 14:21:23 +00:00
Guilhem 0aae35c74a replace Popup with melt Popover in FlowCopilotStatus 2025-02-12 12:07:27 +00:00
Guilhem bdce1b75cb replace popup with melt popover in RunForm 2025-02-12 11:52:22 +00:00
Guilhem 526e659dc7 add floating config and usePointerdownOutside to popover props 2025-02-12 11:49:10 +00:00
Guilhem 4135784719 use melt popover in EditableSchemaForm 2025-02-12 11:15:53 +00:00
Guilhem cc5c50beed polish popover 2025-02-12 11:15:33 +00:00
Guilhem 10a5eabf5c use tooltip in ToggleButton 2025-02-12 11:02:16 +00:00
Guilhem dc32bcb2d6 reolace addUser popup with popover 2025-02-12 11:01:52 +00:00
Guilhem 7df118e7d6 Add new tooltip component 2025-02-12 10:59:05 +00:00
Guilhem 335ae41c39 remove unused ButtonDropDown 2025-02-12 10:47:57 +00:00
Guilhem 6add112a54 Merge remote-tracking branch 'origin/main' into glm/use_melt_menu 2025-02-12 08:22:39 +00:00
Guilhem 02567839a3 replace ButtonDropdown with DropdownV2 2025-02-11 21:36:12 +00:00
Guilhem 8ef175e77b fix action arg type 2025-02-11 14:59:52 +00:00
Guilhem e2de8f43ee Merge branch 'glm/replace_menuV2' into glm/use_melt_menu
# Conflicts:
#	frontend/src/lib/components/apps/components/display/AppMenu.svelte
#	frontend/src/lib/components/flows/map/FlowJobsMenu.svelte
#	frontend/src/lib/components/meltComponents/Menu.svelte
#	frontend/src/lib/components/meltComponents/MenuItem.svelte
#	frontend/src/lib/components/meltComponents/index.ts
2025-02-11 13:47:47 +00:00
Guilhem e19c24224c put conditionalMelt as utility function 2025-02-11 13:37:28 +00:00
Guilhem 17279bf83e clean code 2025-02-11 11:44:22 +00:00
Guilhem 1f159dd212 nit 2025-02-11 09:45:52 +00:00
Guilhem 8df6b6fdfe nit 2025-02-11 09:27:50 +00:00
Guilhem 7a64644b9f Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-11 08:48:25 +00:00
Guilhem 0c395ed4d1 clean 2025-02-11 08:48:00 +00:00
Guilhem 0594838c36 add a MenuItem component 2025-02-10 20:09:26 +00:00
Guilhem 805478790c keep more open in operator menu 2025-02-10 17:17:56 +00:00
Guilhem 47fdcc126f Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-10 16:43:23 +00:00
Guilhem 27ba2f72a0 add max-h to app dropdown menu 2025-02-10 16:37:39 +00:00
Guilhem 987990ee41 use pointerdown in menus 2025-02-10 15:32:29 +00:00
Guilhem b728b0ec01 use pointerdown_outside
# Conflicts:
#	frontend/src/lib/components/meltComponents/Menu.svelte
2025-02-10 15:32:10 +00:00
Guilhem cae8f5bbd4 use pointerdown_outside 2025-02-10 14:57:30 +00:00
Guilhem e10e5e74d1 Merge remote-tracking branch 'origin/main' into glm/replace_menuV2 2025-02-10 09:21:24 +00:00
Guilhem 70b44528bb add use clickOutside option to Menu 2025-02-09 23:35:16 +00:00
Guilhem 7a178c7fc7 clean code 2025-02-05 11:17:57 +00:00
Guilhem c770fab977 Merge remote-tracking branch 'origin/main' into glm/replace_menuV2
# Conflicts:
#	frontend/src/lib/components/sidebar/WorkspaceMenu.svelte
2025-02-05 10:39:58 +00:00
Guilhem d886f59cd3 clean code 2025-02-05 10:29:07 +00:00
Guilhem 5c3ea422e9 nit 2025-02-05 10:18:50 +00:00
Guilhem c584efd2f6 nit 2025-02-04 20:13:44 +00:00
Guilhem c868c27d9e replace detail page dropdown with DropdownV2 2025-02-04 20:08:59 +00:00
Guilhem 767b6fe41b clean code 2025-02-04 19:04:28 +00:00
Guilhem 0e58d96a4a remove unused FlowCopilotButton 2025-02-04 18:54:33 +00:00
Guilhem 582f2cd8f9 delete MenuV2 2025-02-04 18:42:09 +00:00
Guilhem b891a40ac1 use melt menu for DropDownV2 2025-02-04 18:41:28 +00:00
Guilhem 68d9a2bb40 use melt menu for AppMenu 2025-02-04 17:40:01 +00:00
Guilhem eb885752f4 Use melt menu in FlowJobsMenu 2025-02-04 16:16:05 +00:00
Guilhem 87a5a6fdbd fix operator menu 2025-02-04 15:19:57 +00:00
Guilhem 2029f6afbc fix notification 2025-02-04 13:54:31 +00:00
Guilhem a612320087 use melt menu for operator menu 2025-02-04 11:50:31 +00:00
Guilhem 1fb8a24f69 fix popover placement for menuButton 2025-02-04 11:10:33 +00:00
Guilhem f5fc11a51d use melt menu for FavoriteMenu and WorkspaceMenu 2025-02-04 10:52:34 +00:00
Guilhem 67417de142 stop keyboard navigation for disabled items 2025-02-04 09:35:41 +00:00
Guilhem 41e46238d3 use melt menu in sidebar 2025-02-04 09:16:26 +00:00
164 changed files with 5181 additions and 4995 deletions
+5 -5
View File
@@ -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
}
}
+57 -54
View File
@@ -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
+2 -1
View File
@@ -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&nbsp;<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>
+12 -13
View File
@@ -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>
+1 -1
View File
@@ -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>
+10 -8
View File
@@ -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&nbsp;<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>
+33 -25
View File
@@ -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}
+9 -29
View File
@@ -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>
+48 -43
View File
@@ -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">
+3
View File
@@ -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">
+14 -14
View File
@@ -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>
+10 -5
View File
@@ -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
+12 -3
View File
@@ -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}
@@ -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>
@@ -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}
@@ -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