feat: add ability to edit id in flows (#4364)

* all

* all

* nit mailto

* fix

* fix
This commit is contained in:
Ruben Fiszel
2024-09-10 20:16:43 +02:00
committed by GitHub
parent 5dda5df77d
commit a19db9a8d3
24 changed files with 298 additions and 145 deletions

View File

@@ -4737,26 +4737,6 @@ paths:
schema:
type: string
/w/{workspace}/flows/input_history/p/{path}:
get:
summary: list inputs for previous completed flow jobs
operationId: getFlowInputHistoryByPath
tags:
- flow
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
responses:
"200":
description: input history for completed jobs with this flow path
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Input"
/w/{workspace}/raw_apps/list:
get:

View File

@@ -51,10 +51,10 @@
{#if job.is_flow_step}
<div class="flex flex-row gap-2 items-center text-sm">
<BarsStaggered size={SMALL_ICON_SIZE} class="text-secondary min-w-3.5" />
<span class="whitespace-nowrap">
<span class="whitespace-nowrap text-sm">
Step of flow
<a href={`${base}/run/${job.parent_job}?workspace=${$workspaceStore}`}>
{job.parent_job}
{truncateRev(job.parent_job, 18)}
</a>
</span>
</div>

View File

@@ -0,0 +1,85 @@
<script lang="ts">
import { ArrowRight } from 'lucide-svelte'
import { Button } from './common'
import { createEventDispatcher } from 'svelte'
import { forbiddenIds } from './flows/idUtils'
import { slide } from 'svelte/transition'
export let initialId: string
export let reservedIds: string[] = []
export let label: string = 'Component ID'
export let value = initialId
export let buttonText = ''
export let btnClasses = '!p-1 !w-[34px] !ml-1'
let error = ''
const dispatch = createEventDispatcher()
const regex = /^[a-zA-Z][a-zA-Z0-9]*$/
$: validateId(value, reservedIds)
function validateId(id: string, reservedIds: string[]) {
if (id == initialId) {
error = ''
return
}
if (!regex.test(value)) {
error = 'The ID must include only letters and numbers and start with a letter'
} else if (forbiddenIds.includes(value)) {
error = 'This ID is reserved'
} else if (reservedIds.some((rid) => rid === value)) {
error = 'This ID is already in use'
} else {
error = ''
}
}
let inputDiv: HTMLInputElement | undefined = undefined
$: inputDiv?.focus()
</script>
<label class="block text-primary">
{#if label != ''}
<div class="pb-1 text-sm text-secondary">{label}</div>
{/if}
<div class="flex w-full">
<input
bind:this={inputDiv}
autofocus
type="text"
bind:value
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation={({ key }) => {
if (key === 'Enter' && error === '' && value !== initialId) {
dispatch('save', value)
} else if (key == 'Escape') {
dispatch('close')
}
}}
on:keypress|stopPropagation
/>
<Button
size="xs"
color="blue"
buttonType="button"
{btnClasses}
aria-label="Save ID"
disabled={error != '' || value === initialId}
on:click={() => {
dispatch('save', value)
}}
>
{buttonText}<ArrowRight size={18} />
</Button>
</div>
{#if error != ''}
<div
transition:slide|local={{ duration: 100 }}
class="w-full text-sm text-red-600 whitespace-pre-wrap pt-1"
>
{error}
</div>
{/if}
</label>

View File

@@ -1,40 +1,18 @@
<script lang="ts">
import type { AppViewerContext } from '$lib/components/apps/types'
import { allItems } from '$lib/components/apps/utils'
import { forbiddenIds } from '$lib/components/flows/idUtils'
import { ArrowRight, Pencil } from 'lucide-svelte'
import { Pencil } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { slide } from 'svelte/transition'
import { Button, Popup } from '../../../../common'
import IdEditorInput from '$lib/components/IdEditorInput.svelte'
import { Popup } from '$lib/components/common'
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
const dispatch = createEventDispatcher()
const regex = /^[a-zA-Z][a-zA-Z0-9]*$/
let value = id
let error = ''
$: if (!regex.test(value)) {
error = 'The ID must include only letters and numbers and start with a letter'
} else if (forbiddenIds.includes(value)) {
error = 'This ID is reserved'
} else if (
allItems($app.grid, $app.subgrids).some((item) => item.id === value && item.id !== id)
) {
error = 'This ID is already in use'
} else {
error = ''
}
function save() {
if (error != '') {
return
}
if (value != id) {
dispatch('change', value)
}
}
$: reservedIds = allItems($app.grid, $app.subgrids).map((item) => item.id)
</script>
<Popup let:close floatingConfig={{ strategy: 'absolute', placement: 'bottom-start' }}>
@@ -50,44 +28,13 @@
<Pencil size={14} />
</button>
</svelte:fragment>
<label class="block text-primary">
<div class="pb-1 text-sm text-secondary">Component ID</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') {
save()
close(null)
}
}}
/>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Save ID"
disabled={error != ''}
on:click={() => {
save()
close(null)
}}
>
<ArrowRight size={18} />
</Button>
</div>
{#if error != ''}
<div
transition:slide|local={{ duration: 100 }}
class="w-full text-sm text-red-600 whitespace-pre-wrap pt-1"
>
{error}
</div>
{/if}
</label>
<IdEditorInput
initialId={id}
on:close={() => close(null)}
on:save={(e) => {
dispatch('save', e.detail)
close(null)
}}
{reservedIds}
/>
</Popup>

View File

@@ -47,7 +47,7 @@
style="width: 275px; height: 34px; background-color: {getStateColor(
undefined,
darkMode,
'#fff'
true
)};"
on:click={() => {
selected = true

View File

@@ -17,7 +17,7 @@
dispatch('node')
}}
type="button"
class="text-primary bg-surface border-[1px] mx-[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-full text-sm w-[25px] h-[25px] flex items-center justify-center"
class="text-primary bg-surface outline-[1px] outline dark:outline-gray-500 outline-gray-300 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center"
>
<Cross class="mx-[5px]" size={15} />
</button>
@@ -28,7 +28,7 @@
type="button"
on:click={() => dispatch('addBranch')}
class={twMerge(
'text-primary bg-surface border-[1px] mx-[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-full text-sm w-[25px] h-[25px] flex items-center justify-center',
'text-secondary bg-surface outline-[1px] outline dark:outline-gray-500 outline-gray-300 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center',
!canAddNode && 'ml-16 mb-2'
)}
>

View File

@@ -44,7 +44,7 @@
<div
class={classNames(
'fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity',
open ? 'ease-out duration-300 opacity-80' : 'ease-in duration-200 opacity-0'
open ? 'ease-out duration-300 opacity-100' : 'ease-in duration-200 opacity-0'
)}
/>

View File

@@ -29,7 +29,10 @@
>
Single JavaScript expression. The following functions and objects are available:
<ul class="ml-4">
<li><b>{'results.<id>'}</b>: the result of step at id 'id'</li>
<li
><b>{'results.<id>'}</b>: the result of step at id 'id' (use <b>{'results?.<id>'}</b> if id may
not exist because branch was not chosen)</li
>
<li><b>flow_input</b>: the object containing the flow input arguments</li>
<li><b>params</b>: the object containing the current step static values</li>
<li>

View File

@@ -7,7 +7,7 @@ import { charsToNumber, numberToChars } from './idUtils'
export function nextId(flowState: FlowState, fullFlow: OpenFlow): string {
const allIds = dfs(fullFlow.value.modules, (fm) => fm.id)
const max = allIds.concat(Object.keys(flowState)).reduce((acc, key) => {
if (key === 'failure' || key.includes('branch') || key.includes('loop')) {
if (key.length >= 4) {
return acc
} else {
const num = charsToNumber(key)

View File

@@ -34,3 +34,9 @@ export async function copyFirstStepSchema(flowState: FlowState, flowStore: Writa
return flow
})
}
export function replaceId(expr: string, id: string, newId: string): string {
return expr
.replaceAll(`results.${id}`, `results.${newId}`)
.replaceAll(`results?.${id}`, `results?.${newId}`)
}

View File

@@ -9,6 +9,7 @@
Database,
Gauge,
Move,
Pencil,
PhoneIncoming,
Repeat,
Square,
@@ -17,9 +18,15 @@
} from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { fade } from 'svelte/transition'
import type { FlowInput } from '../types'
import type { Writable } from 'svelte/store'
import type { FlowEditorContext, FlowInput } from '../types'
import { get, type Writable } from 'svelte/store'
import { twMerge } from 'tailwind-merge'
import IdEditorInput from '$lib/components/IdEditorInput.svelte'
import { dfs } from '../dfs'
import { Drawer } from '$lib/components/common'
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
import { getDependeeAndDependentComponents } from '../flowExplorer'
import { replaceId } from '../flowStore'
export let selected: boolean = false
export let deletable: boolean = false
@@ -41,22 +48,85 @@
const { flowInputsStore } = getContext<{ flowInputsStore: Writable<FlowInput | undefined> }>(
'FlowGraphContext'
)
const flowEditorContext = getContext<FlowEditorContext>('FlowEditorContext')
const dispatch = createEventDispatcher()
const { currentStepStore: copilotCurrentStepStore } =
getContext<FlowCopilotContext | undefined>('FlowCopilotContext') || {}
let editId = false
let newId: string = id ?? ''
let hover = false
</script>
{#if deletable && id && editId}
{@const flowStore = flowEditorContext?.flowStore ? get(flowEditorContext?.flowStore) : undefined}
{@const getDeps = getDependeeAndDependentComponents(
id,
flowStore?.value.modules ?? [],
flowStore?.value.failure_module
)}
<Drawer bind:open={editId}>
<DrawerContent title="Edit Step Id {id}" on:close={() => (editId = false)}>
<div>
<IdEditorInput
buttonText="Edit Id "
btnClasses="!ml-1"
label=""
initialId={id}
reservedIds={dfs(flowStore?.value.modules ?? [], (x) => x.id)}
bind:value={newId}
on:save={(e) => {
dispatch('changeId', { id, newId: e.detail, deps: getDeps?.dependents ?? {} })
editId = false
}}
on:close={() => {
editId = false
}}
/>
<div class="mt-8">
<h3>Step Inputs Replacements</h3>
<div class="text-2xs text-tertiary pt-0.5">
Replace all occurrences of `results.<span class="font-bold">{id}</span>` with{' '}
results.<span class="font-bold">{newId}</span> in the step inputs of all steps that depend
on it.
</div>
<div class="pt-8 flex flex-col gap-y-4">
{#if Object.keys(getDeps?.dependents ?? {})?.length > 0}
{#each Object.entries(getDeps?.dependents ?? {}) as dependents}
<div>
<h4>{dependents[0]}</h4>
{#each dependents?.[1] as d}
<span class="font-mono text-sm">{d}</span> &rightarrow;
<span class="font-mono text-sm">{replaceId(d, id, newId)}</span>
{/each}
</div>
{/each}
{:else}
<div class="text-2xs text-tertiary"> No dependents </div>
{/if}
</div>
</div>
</div>
</DrawerContent>
</Drawer>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class={classNames(
'w-full module flex rounded-sm cursor-pointer',
selected ? 'outline outline-offset-1 outline-2 outline-gray-600 dark:outline-gray-400' : '',
selected ? 'outline outline-offset-0 outline-2 outline-slate-500 dark:outline-gray-400' : '',
'flex relative',
$copilotCurrentStepStore === id ? 'z-[901]' : ''
)}
style="width: 275px; height: 34px; background-color: {bgColor};"
on:mouseenter={() => (hover = true)}
on:mouseleave={() => (hover = false)}
on:click
>
<div class="absolute text-sm right-12 -bottom-3 flex flex-row gap-1 z-10">
@@ -67,7 +137,7 @@
class="center-center rounded border bg-surface border-gray-400 text-secondary px-1 py-0.5"
>
{#if retries}<span class="text-red-400 mr-2">{retries}</span>{/if}
<Repeat size={14} />
<Repeat size={12} />
</div>
<svelte:fragment slot="text">Retries</svelte:fragment>
</Popover>
@@ -79,7 +149,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center rounded border bg-surface border-gray-400 text-secondary px-1 py-0.5"
>
<Gauge size={14} />
<Gauge size={12} />
</div>
<svelte:fragment slot="text">Concurrency Limits</svelte:fragment>
</Popover>
@@ -90,7 +160,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center rounded border bg-surface border-gray-400 text-secondary px-1 py-0.5"
>
<Database size={14} />
<Database size={12} />
</div>
<svelte:fragment slot="text">Cached</svelte:fragment>
</Popover>
@@ -101,7 +171,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center bg-surface rounded border border-gray-400 text-secondary px-1 py-0.5"
>
<Square size={14} />
<Square size={12} />
</div>
<svelte:fragment slot="text">Early stop/break</svelte:fragment>
</Popover>
@@ -112,7 +182,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center bg-surface rounded border border-gray-400 text-secondary px-1 py-0.5"
>
<PhoneIncoming size={14} />
<PhoneIncoming size={12} />
</div>
<svelte:fragment slot="text">Suspend</svelte:fragment>
</Popover>
@@ -123,7 +193,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center bg-surface rounded border border-gray-400 text-secondary px-1 py-0.5"
>
<Bed size={14} />
<Bed size={12} />
</div>
<svelte:fragment slot="text">Sleep</svelte:fragment>
</Popover>
@@ -134,7 +204,7 @@
transition:fade|local={{ duration: 200 }}
class="center-center bg-surface rounded border border-gray-400 text-secondary px-1 py-0.5"
>
<Voicemail size={14} />
<Voicemail size={12} />
</div>
<svelte:fragment slot="text">Mocked</svelte:fragment>
</Popover>
@@ -143,38 +213,49 @@
<div
class="flex gap-1 justify-between items-center w-full overflow-hidden rounded-sm
border border-gray-400 dark:border-gray-600 p-2 text-2xs module text-primary"
p-2 text-2xs module text-primary"
>
{#if $$slots.icon}
<slot name="icon" />
{/if}
<div class="truncate" class:font-bold={bold}>{label}</div>
<div class="flex items-center space-x-2">
<div class="flex items-center relative">
{#if id}
<Badge color="indigo">{id}</Badge>
{#if deletable}
<button
class="absolute -left-[20px] z-10 h-[20px] rounded-l rounded-t rounded-s w-[20px] trash center-center text-secondary bg-surface duration-150 hover:bg-blue-400 {editId
? '!bg-blue-400'
: ''} hover:text-white
hover:border-blue-700 hover:!visible {hover ? '' : '!hidden'}"
on:click|preventDefault|stopPropagation={(event) => (editId = !editId)}
title="Edit Id"><Pencil size={14} /></button
>
{/if}
{/if}
</div>
</div>
{#if deletable}
<button
class="absolute -top-[10px] -right-[10px] rounded-full h-[20px] w-[20px] trash center-center text-primary
border-[1.5px] border-gray-700 bg-surface duration-150 hover:bg-red-400 hover:text-white
hover:border-red-700 {selected ? '' : '!hidden'}"
class="absolute -top-[10px] -right-[10px] rounded-full h-[20px] w-[20px] trash center-center text-secondary
outline-[1px] outline dark:outline-gray-500 outline-gray-300 bg-surface duration-150 hover:bg-red-400 hover:text-white
{hover || selected ? '' : '!hidden'}"
title="Delete"
on:click|preventDefault|stopPropagation={(event) =>
dispatch('delete', { event, id, type: modType })}
>
<X class="mx-[3px]" size={14} strokeWidth={2} />
<X class="mx-[3px]" size={12} strokeWidth={2} />
</button>
<button
class="absolute -top-[10px] right-[35px] rounded-full h-[20px] w-[20px] trash center-center text-primary
border-[1.5px] border-gray-700 bg-surface duration-150 hover:bg-blue-400 hover:text-white
hover:border-blue-700 {selected ? '' : '!hidden'}"
class="absolute -top-[10px] right-[60px] rounded-full h-[20px] w-[20px] trash center-center text-secondary
outline-[1px] outline dark:outline-gray-500 outline-gray-300 bg-surface duration-150 hover:bg-blue-400 hover:text-white
{hover ? '' : '!hidden'}"
on:click|preventDefault|stopPropagation={(event) => dispatch('move')}
title="Move"
>
<Move class="mx-[3px]" size={14} strokeWidth={2} />
<Move class="mx-[3px]" size={12} strokeWidth={2} />
</button>
{#if (id && Object.values($flowInputsStore?.[id]?.flowStepWarnings || {}).length > 0) || Boolean(warningMessage)}

View File

@@ -30,6 +30,7 @@
import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials'
import { tutorialInProgress } from '$lib/tutorialUtils'
import FlowGraphV2 from '$lib/components/graph/FlowGraphV2.svelte'
import { replaceId } from '../flowStore'
export let modules: FlowModule[] | undefined
export let sidebarSize: number | undefined = undefined
@@ -328,6 +329,35 @@
$flowStore = $flowStore
}
}}
on:changeId={({ detail }) => {
let { id, newId, deps } = detail
dfs($flowStore.value.modules, (mod) => {
if (deps[mod.id]) {
deps[mod.id].forEach((dep) => {
if (
mod.value.type == 'rawscript' ||
mod.value.type == 'script' ||
mod.value.type == 'flow'
) {
mod.value.input_transforms = Object.fromEntries(
Object.entries(mod.value.input_transforms).map(([k, v]) => {
if (v.type == 'javascript') {
return [k, { ...v, expr: replaceId(v.expr, id, newId) }]
} else {
return [k, v]
}
})
)
}
})
}
if (mod.id == id) {
mod.id = newId
}
})
$flowStore = $flowStore
$selectedId = newId
}}
on:deleteBranch={async ({ detail }) => {
if (detail.module) {
await removeBranch(detail.module, detail.index)

View File

@@ -35,13 +35,13 @@
id={`flow-editor-add-step-${index}`}
type="button"
class={twMerge(
'w-6 h-6 flex items-center justify-center',
'border border-gray-300 dark:border-gray-500',
'text-primary text-sm',
'bg-surface focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-surface-selected rounded-full '
'w-5 h-5 flex items-center justify-center',
'outline-[1px] outline dark:outline-gray-500 outline-gray-300',
'text-secondary',
'bg-surface focus:outline-none hover:bg-surface-hover rounded '
)}
>
<Cross size={14} />
<Cross size={12} />
</button>
</svelte:fragment>
<div id="flow-editor-insert-module">

View File

@@ -27,9 +27,9 @@
title="Add a Trigger"
slot="trigger"
type="button"
class="text-primary bg-surface border-[1px] mx-[1px] border-gray-300 dark:border-gray-500 rotate-180 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-gray-200 font-medium rounded-full text-sm w-[25px] h-[25px] flex items-center justify-center"
class="text-secondary bg-surface outline-[1px] outline dark:outline-gray-500 outline-gray-300 rotate-180 focus:outline-none hover:bg-surface-hover focus:ring-4 focus:ring-gray-200 font-medium rounded text-sm w-[20px] h-[20px] flex items-center justify-center"
>
<Zap size={14} />
<Zap size={12} />
</button>
{#if !disableAi}
<StepGen {index} bind:funcDesc bind:open {close} {modules} trigger on:insert />

View File

@@ -102,6 +102,7 @@
mod.value.skip_failures ? '(skip failures)' : ''
}`}
id={mod.id}
on:changeId
on:move={() => dispatch('move')}
on:delete={onDelete}
on:click={() => dispatch('select', mod.id)}
@@ -120,6 +121,7 @@
{:else if mod.value.type === 'branchone'}
<FlowModuleSchemaItem
deletable={insertable}
on:changeId
on:delete={onDelete}
on:move={() => dispatch('move')}
on:click={() => dispatch('select', mod.id)}
@@ -135,6 +137,7 @@
{:else if mod.value.type === 'branchall'}
<FlowModuleSchemaItem
deletable={insertable}
on:changeId
on:delete={onDelete}
on:move={() => dispatch('move')}
on:click={() => dispatch('select', mod.id)}
@@ -150,6 +153,7 @@
{:else}
<FlowModuleSchemaItem
{retries}
on:changeId
on:click={() => dispatch('select', mod.id)}
on:delete={onDelete}
on:move={() => dispatch('move')}

View File

@@ -5,7 +5,7 @@
import { createEventDispatcher, getContext } from 'svelte'
import type { FlowCopilotContext } from '$lib/components/copilot/flow'
export let label: string
export let label: string | undefined = undefined
export let bgColor: string = ''
export let selected: boolean
export let selectable: boolean
@@ -13,6 +13,7 @@
export let center = true
export let borderColor: string | undefined = undefined
export let hideId: boolean = false
export let preLabel: string | undefined = undefined
const dispatch = createEventDispatcher<{
insert: {
@@ -35,8 +36,7 @@
'w-full flex relative overflow-hidden rounded-sm',
selectable ? 'cursor-pointer' : '',
selected ? 'outline outline-offset-1 outline-2 outline-gray-600' : '',
label === 'Input' && $copilotCurrentStepStore === 'Input' ? 'z-[901]' : '',
'bg-surface'
label === 'Input' && $copilotCurrentStepStore === 'Input' ? 'z-[901]' : ''
)}
style="width: 275px; max-height: 34px; background-color: {bgColor} !important;"
on:click={() => {
@@ -44,14 +44,15 @@
if (id) {
dispatch('select', id)
} else {
dispatch('select', label)
dispatch('select', label || label || '')
}
}
}}
id={`flow-editor-virtual-${encodeURIComponent(label)}`}
title={(label ? label + ' ' : '') + (label ?? '')}
id={`flow-editor-virtual-${encodeURIComponent(label || label || '')}`}
>
<div
style={borderColor ? `border-color: ${borderColor};` : ''}
style={borderColor ? `border-color: ${borderColor};` : 'border: 0'}
class="flex gap-1 justify-between {center
? 'items-center'
: 'items-baseline'} w-full overflow-hidden rounded-sm border p-2 text-2xs module text-primary border-gray-400 dark:border-gray-600"
@@ -61,7 +62,14 @@
<span class="mr-2" />
{/if}
<div />
<div class="truncate"><pre>{label}</pre></div>
<div class="flex flex-col w-full">
{#if label}
<div class="truncate text-center">{label}</div>
{/if}
{#if preLabel}
<div class="truncate text-2xs text-center"><pre>{preLabel}</pre></div>
{/if}
</div>
<div class="flex items-center space-x-2">
{#if id && !hideId}
<Badge color="indigo">{id}</Badge>

View File

@@ -146,6 +146,9 @@
dispatch('select', modId)
}
},
changeId: (detail) => {
dispatch('changeId', detail)
},
delete: (detail, label) => {
$selectedId = label
@@ -274,6 +277,7 @@
showLock={false}
showZoom={false}
showFitView={false}
class="!shadow-none"
>
{#if showDataflow}
<Toggle

View File

@@ -12,6 +12,7 @@ export type GraphEventHandlers = {
newBranch: (module: FlowModule) => void
move: (module: FlowModule, modules: FlowModule[]) => void
selectedIteration: (detail, moduleId: string) => void
changeId: (newId: string) => void
}
export default function graphBuilder(
@@ -354,11 +355,8 @@ export default function graphBuilder(
id: `${module.id}-branch-${branchIndex}`,
data: {
offset: currentOffset,
label:
defaultIfEmptyString(branch.summary, 'Branch ' + (branchIndex + 1)) +
'\n`' +
branch.expr +
'`',
label: defaultIfEmptyString(branch.summary, 'Branch ' + (branchIndex + 1)),
preLabel: branch.summary ? '' : branch.expr,
id: module.id,
branchIndex: branchIndex,
modules: modules,

View File

@@ -47,7 +47,7 @@
<button
title="Delete branch"
class="z-50 absolute -top-[10px] -right-[10px] rounded-full h-[20px] w-[20px] center-center text-primary
border-[1.5px] border-gray-700 bg-surface duration-150 hover:bg-red-400 hover:text-white
outline-[1px] outline outline-gray-700 bg-surface duration-150 hover:bg-red-400 hover:text-white
hover:border-red-700"
on:click|preventDefault|stopPropagation={() => {
data.eventHandlers.deleteBranch(

View File

@@ -10,6 +10,7 @@
export let data: {
label: string
preLabel: string | undefined
insertable: boolean
flowModuleStates: Record<string, GraphModuleState> | undefined
id: string
@@ -30,12 +31,11 @@
<NodeWrapper let:darkMode offset={data.offset}>
<VirtualItem
label={data.label}
preLabel={data.preLabel}
selectable
selected={data.selected}
bgColor={getStateColor(undefined, darkMode)}
borderColor={borderStatus
? getStateColor(borderStatus, darkMode) + (!darkMode ? '; border-width: 3px' : '')
: undefined}
borderColor={borderStatus ? getStateColor(borderStatus, darkMode) : undefined}
on:select={() => {
data.eventHandlers.select(data.id)
}}

View File

@@ -46,9 +46,9 @@
: 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-8 h-8 flex items-center justify-center"
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={16} />
<Wand2 size={12} />
</button>
{#if !$copilotInfo.exists_openai_resource_path}
<div class="text-primary p-4">

View File

@@ -56,7 +56,7 @@
'/' +
(state?.iteration_total ?? '?')
: ''}
bgColor={getStateColor(type, darkMode, '#fff')}
bgColor={getStateColor(type, darkMode, true)}
modules={data.modules ?? []}
moving={data.moving}
duration_ms={state?.duration_ms}
@@ -68,6 +68,9 @@
on:insert={(e) => {
data.eventHandlers.insert(e.detail)
}}
on:changeId={(e) => {
data.eventHandlers.changeId(e.detail)
}}
on:move={(e) => {
data.eventHandlers.move(data.module, data.modules)
}}
@@ -87,7 +90,7 @@
{#if (data.value.type === 'branchall' || data.value.type === 'branchone') && data.insertable}
<button
title="Add branch"
class="rounded-full border hover:bg-surface-hover bg-surface p-1"
class="rounded text-secondary border hover:bg-surface-hover bg-surface p-1"
on:click={() => {
data?.eventHandlers?.newBranch(data.module)
}}

View File

@@ -12,7 +12,7 @@ export const NODE = {
export function getStateColor(
state: FlowStatusModule['type'] | undefined,
isDark: boolean,
lightModeBackground: string = '#dfe6ee'
nonVirtualItem?: boolean
): string {
switch (state) {
case 'Success':
@@ -26,6 +26,10 @@ export function getStateColor(
case 'WaitingForExecutor':
return isDark ? '#ea580c' : 'rgb(255, 208, 193)'
default:
return isDark ? '#2e3440' : lightModeBackground
if (nonVirtualItem) {
return isDark ? '#2E3440' : 'white'
} else {
return isDark ? '#313742' : '#dfe6ee'
}
}
}

View File

@@ -312,7 +312,7 @@
{#if filteredUsers}
{#each filteredUsers.slice(0, nbDisplayed) as { email, username, is_admin, operator, disabled } (email)}
<tr class="!hover:bg-surface-hover">
<Cell first>{truncate(email, 20)}</Cell>
<Cell first><a href="mailto:{email}">{truncate(email, 20)}</a></Cell>
<Cell>{truncate(username, 30)}</Cell>
<Cell
>{#if usage?.[email] != undefined}{usage?.[email]}{:else}<Loader2