feat: add lockable version to scripts inside flows (#972)

* add it to openflow

* push

* push

* final
This commit is contained in:
Ruben Fiszel
2022-12-06 13:11:11 +01:00
committed by GitHub
parent 383170bf1b
commit fa7e8d107b
23 changed files with 197 additions and 150 deletions
+2
View File
@@ -426,6 +426,7 @@ mod tests {
InputTransform::Static { value: serde_json::json!("test2") },
)]
.into(),
hash: None,
},
stop_after_if: None,
summary: None,
@@ -477,6 +478,7 @@ mod tests {
value: FlowModuleValue::Script {
path: "test".to_string(),
input_transforms: HashMap::new(),
hash: None,
},
stop_after_if: Some(StopAfterIf {
expr: "previous.isEmpty()".to_string(),
+2 -1
View File
@@ -12,7 +12,7 @@ use serde::{self, Deserialize, Serialize};
use crate::{
more_serde::{default_false, default_id, default_true, is_default},
scripts::{Schema, ScriptLang},
scripts::{Schema, ScriptHash, ScriptLang},
};
#[derive(Serialize)]
@@ -200,6 +200,7 @@ pub enum FlowModuleValue {
#[serde(alias = "input_transform")]
input_transforms: HashMap<String, InputTransform>,
path: String,
hash: Option<ScriptHash>,
},
ForloopFlow {
iterator: InputTransform,
+30 -16
View File
@@ -816,19 +816,27 @@ async fn push_next_flow_job(
if let Some(it) = sleep_input_transform {
let json_value = match it {
InputTransform::Static { value } => value,
InputTransform::Javascript { expr } => eval_timeout(
expr.to_string(),
[("result".to_string(), last_result.clone())].into(),
None,
None,
"".to_string(),
)
.await
.map_err(|e| {
Error::ExecutionErr(format!(
"Error during isolated evaluation of expression `{expr}`:\n{e}"
))
})?,
InputTransform::Javascript { expr } => {
let flow_input = flow_job.args.clone().unwrap_or_else(|| json!({}));
eval_timeout(
expr.to_string(),
[
("result".to_string(), last_result.clone()),
("flow_input".to_string(), flow_input),
]
.into(),
None,
None,
"".to_string(),
)
.await
.map_err(|e| {
Error::ExecutionErr(format!(
"Error during isolated evaluation of expression `{expr}`:\n{e}"
))
})?
}
};
match json_value {
serde_json::Value::Number(n) => {
@@ -1445,9 +1453,15 @@ async fn compute_next_flow_transform<'c>(
tx,
NextFlowTransform::Continue(vec![JobPayload::Identity], NextStatus::NextStep),
)),
FlowModuleValue::Script { path: script_path, .. } => {
let payload =
script_path_to_payload(script_path, &mut tx, &flow_job.workspace_id).await?;
FlowModuleValue::Script { path: script_path, hash: script_hash, .. } => {
let payload = if script_hash.is_none() {
script_path_to_payload(script_path, &mut tx, &flow_job.workspace_id).await?
} else {
JobPayload::ScriptHash {
hash: script_hash.clone().unwrap(),
path: script_path.to_owned(),
}
};
Ok((
tx,
NextFlowTransform::Continue(vec![payload], NextStatus::NextStep),
+37 -37
View File
@@ -270,21 +270,6 @@
+Resource
</Button>
</div>
<div>
<Button
btnClasses="mx-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
on:click={scriptPicker.openDrawer}
{iconOnly}
startIcon={{ icon: faEye }}
>
Script
</Button>
</div>
<div>
<Button
btnClasses="mx-1 !font-medium"
@@ -298,30 +283,45 @@
Reset
</Button>
</div>
<div>
<Button
btnClasses="!font-medium"
size="xs"
spacingSize="md"
color="light"
on:click={editor.reloadWebsocket}
startIcon={{ icon: faRotate }}
>
{#if !iconOnly}
Assistant
{/if}
<span class="ml-1 -my-1">
{#if lang == 'deno'}
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
{:else if lang == 'go'}
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
{:else if lang == 'python3'}
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
<span class={websocketAlive.black ? 'green' : 'text-red-700'}>Black</span>)
{/if}
</span>
</Button></div
>
</div>
<div class="py-1">
<Button
btnClasses="!font-medium"
size="xs"
spacingSize="md"
color="light"
on:click={editor.reloadWebsocket}
startIcon={{ icon: faRotate }}
>
{#if !iconOnly}
Assistant
{/if}
<span class="ml-1 -my-1">
{#if lang == 'deno'}
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
{:else if lang == 'go'}
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
{:else if lang == 'python3'}
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
<span class={websocketAlive.black ? 'green' : 'text-red-700'}>Black</span>)
{/if}
</span>
</Button>
<div>
<Button
btnClasses="mx-1 !font-medium"
size="xs"
spacingSize="md"
color="light"
on:click={scriptPicker.openDrawer}
{iconOnly}
startIcon={{ icon: faEye }}
>
Script
</Button>
</div>
</div>
</div>
+13 -5
View File
@@ -1,18 +1,26 @@
<script lang="ts">
import Badge from './common/badge/Badge.svelte'
import { truncateHash } from '$lib/utils'
import { Building } from 'svelte-lucide'
import { Badge } from './common'
import IconedResourceType from './IconedResourceType.svelte'
export let path: string
export let hash: string | undefined = undefined
</script>
<div class="flex space-x-2 items-center">
<div class="flex space-x-2 items-center w-full shrink min-w-0 {$$props.class}">
{#if path.startsWith('hub/')}
<div>
<IconedResourceType width="20px" height="20px" name={path.split('/')[2]} silent={true} />
</div>
<span class="text-sm">{path}</span>
<span class="text-sm truncate">{path}</span>
{:else}
<Badge color="blue">Workspace</Badge>
<span class="text-sm">{path}</span>
<div class="center-center">
<Building size="16px" />
</div>
<span class="text-sm truncate">{path}</span>
{#if hash}
<Badge>{truncateHash(hash)}</Badge>
{/if}
{/if}
</div>
+1 -1
View File
@@ -21,7 +21,7 @@
</script>
<Drawer bind:this={logViewer} size="900px">
<DrawerContent title={'Expanded Logs'} on:close={logViewer.closeDrawer}>
<DrawerContent title="Expanded Logs" on:close={logViewer.closeDrawer}>
<div>
<pre class="bg-gray-50 text-xs w-full p-2"
>{#if content}{content}{:else if isLoading}Waiting for job to start...{:else}No logs are available yet{/if}</pre
@@ -4,7 +4,7 @@
import Icon from 'svelte-awesome'
import { ButtonType } from './model'
import { goto } from '$app/navigation'
import { faArrowsRotate, faSpinner } from '@fortawesome/free-solid-svg-icons'
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
export let size: ButtonType.Size = 'md'
export let spacingSize: ButtonType.Size = size
@@ -3,13 +3,11 @@
import Badge from '$lib/components/common/badge/Badge.svelte'
import IconedPath from '$lib/components/IconedPath.svelte'
import { RawScript, type FlowModule } from '$lib/gen'
import { isEmptyFlowModule } from '../utils'
import { truncateHash } from '$lib/utils'
export let flowModule: FlowModule | undefined = undefined
export let title: string | undefined = undefined
$: shouldPick = flowModule && isEmptyFlowModule(flowModule)
const languageColors: Record<RawScript.language, BadgeColor> = {
[RawScript.language.GO]: 'dark-indigo',
[RawScript.language.DENO]: 'dark-blue',
@@ -22,18 +20,18 @@
class="flex items-center justify-between py-2 px-4 border-b border-gray-300 space-x-2 h-full max-h-12 flex-nowrap"
>
{#if flowModule}
<span class="text-sm w-full">
<span class="text-sm w-full mr-4">
<div class="flex items-center space-x-2">
{#if shouldPick}
<span class="font-bold text-xs">Select a step kind</span>
{#if flowModule.value.type === 'identity'}
<span class="font-bold text-xs">Identity (input copied to output)</span>
{:else if flowModule?.value.type === 'rawscript'}
<Badge color={languageColors[flowModule?.value.language] ?? 'gray'} capitalize>
{flowModule?.value.language}
</Badge>
<input bind:value={flowModule.summary} placeholder={'Summary'} />
<input bind:value={flowModule.summary} placeholder={'Summary'} class="w-full grow" />
{:else if flowModule?.value.type === 'script' && 'path' in flowModule.value && flowModule.value.path}
<IconedPath path={flowModule.value.path} />
<input bind:value={flowModule.summary} placeholder="Summary" class="ml-2" />
<IconedPath path={flowModule.value.path} hash={flowModule.value.hash} class="grow" />
<input bind:value={flowModule.summary} placeholder="Summary" class="w-full grow" />
{/if}
</div>
</span>
@@ -5,7 +5,6 @@
import SchemaForm from '$lib/components/SchemaForm.svelte'
import FlowCard from '../common/FlowCard.svelte'
import { copyFirstStepSchema, flowStore } from '../flowStore'
import { isEmptyFlowModule } from '../utils'
import CapturePayload from './CapturePayload.svelte'
let capturePayload: CapturePayload
@@ -29,7 +28,7 @@
color="dark"
size="sm"
disabled={$flowStore.value.modules.length === 0 ||
isEmptyFlowModule($flowStore.value.modules[0])}
$flowStore.value.modules[0].value.type == 'identity'}
on:click={copyFirstStepSchema}
>
First step's inputs
@@ -1,11 +1,10 @@
<script lang="ts">
import { ToggleButton, ToggleButtonGroup } from '$lib/components/common'
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
import ToggleHubWorkspace from '$lib/components/ToggleHubWorkspace.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
import { RawScript, Script } from '$lib/gen'
import { faBolt, faBuilding, faCheck, faCode } from '@fortawesome/free-solid-svg-icons'
import { faBolt, faCheck, faCode } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import FlowScriptPicker from '../pickers/FlowScriptPicker.svelte'
import PickHubScript from '../pickers/PickHubScript.svelte'
@@ -51,7 +50,7 @@
</div>
</div>
{/if}
<h3 class="pb-2">
<h3 class="pb-2 pt-4">
Inline new <span class="text-blue-500">{kind == 'script' ? 'common' : kind}</span> script
<Tooltip>
Embed a script directly inside a flow instead of saving the script into your workspace for
@@ -138,7 +137,7 @@
<ToggleHubWorkspace bind:selected={pick_existing} />
</PickHubScript>
{:else}
<WorkspaceScriptPicker bind:filter {kind} on:pick>
<WorkspaceScriptPicker displayLock bind:filter {kind} on:pick>
<ToggleHubWorkspace bind:selected={pick_existing} />
</WorkspaceScriptPicker>
{/if}
@@ -127,6 +127,7 @@
<FlowModuleHeader
bind:module={flowModule}
on:toggleSuspend={() => (selected = 'advanced-suspend')}
on:toggleSleep={() => (selected = 'advanced-sleep')}
on:toggleRetry={() => (selected = 'advanced-retries')}
on:toggleStopAfterIf={() => (selected = 'advanced-early-stop')}
on:fork={async () => {
@@ -1,18 +1,11 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { EDITOR_BAR_WIDTH_THRESHOLD } from '$lib/components/EditorBar.svelte'
import type { FlowModule } from '$lib/gen'
import { classNames } from '$lib/utils'
import {
faArrowRotateForward,
faBed,
faCodeBranch,
faSave,
faStop
} from '@fortawesome/free-solid-svg-icons'
import { faBed, faCodeBranch, faSave, faStop } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import { isEmptyFlowModule } from '../utils'
import { PhoneIncoming, Repeat } from 'svelte-lucide'
export let module: FlowModule
@@ -20,36 +13,38 @@
let width = 0
$: shouldPick = isEmptyFlowModule(module)
$: iconOnly = width < EDITOR_BAR_WIDTH_THRESHOLD
$: moduleRetry = module.retry?.constant || module.retry?.exponential
</script>
<div class="flex flex-row space-x-2" bind:clientWidth={width}>
{#if !shouldPick}
<span
{#if module.value.type === 'script' || module.value.type === 'rawscript'}
<button
class={classNames('badge', module.stop_after_if ? 'badge-on' : 'badge-off')}
on:click={() => dispatch('toggleStopAfterIf')}
>
<Icon data={faStop} scale={0.8} />
</span>
<span
class={classNames('badge', moduleRetry ? 'badge-on' : 'badge-off')}
</button>
<button
class={classNames('badge', moduleRetry ? 'badge-on' : 'badge-off', 'center-center')}
on:click={() => dispatch('toggleRetry')}
>
<Icon data={faArrowRotateForward} scale={0.8} />
</span>
<span
class={classNames(
'badge',
Boolean(module.suspend) || Boolean(module.sleep) ? 'badge-on' : 'badge-off'
)}
on:click={() => dispatch('toggleSuspend')}
<Repeat size="14px" />
</button>
<button
class={classNames('badge', Boolean(module.sleep) ? 'badge-on' : 'badge-off')}
on:click={() => dispatch('toggleSleep')}
>
<Icon data={faBed} scale={0.8} />
</span>
</button>
<button
class={classNames('badge', Boolean(module.suspend) ? 'badge-on' : 'badge-off')}
on:click={() => dispatch('toggleSuspend')}
>
<PhoneIncoming size="14px" />
</button>
{/if}
{#if module.value.type === 'script' && !shouldPick}
{#if module.value.type === 'script'}
<div class="w-2" />
<Button
size="xs"
color="light"
@@ -62,7 +57,7 @@
</Button>
{/if}
{#if module.value.type === 'rawscript' && !shouldPick}
{#if module.value.type === 'rawscript'}
<Button
size="xs"
color="light"
@@ -46,8 +46,8 @@
previousModule !== undefined ||
$selectedId == 'failure'}
on:pick={async ({ detail }) => {
const { path, summary, kind } = detail
const [module, state] = await pickScript(path, summary, flowModule.id)
const { path, summary, kind, hash } = detail
const [module, state] = await pickScript(path, summary, flowModule.id, hash)
if (kind == Script.kind.APPROVAL) {
module.suspend = { required_events: 1, timeout: 1800 }
@@ -2,7 +2,7 @@ import type { Schema } from '$lib/common'
import type { Flow, FlowModule, FlowStatusModule } from '$lib/gen'
import { writable } from 'svelte/store'
import { loadFlowModuleState } from './flowStateUtils'
import { emptyFlowModuleState, isEmptyFlowModule } from './utils'
import { emptyFlowModuleState } from './utils'
export type FlowModuleState = {
schema: Schema
@@ -57,7 +57,7 @@ async function mapFlowModule(flowModule: FlowModule, modulesState: FlowState) {
)
}
if (isEmptyFlowModule(flowModule)) {
if (value.type === 'identity') {
modulesState[flowModule.id] = emptyFlowModuleState()
} else {
const flowModuleState = await loadFlowModuleState(flowModule)
@@ -62,11 +62,12 @@ export function nextId(): string {
export async function pickScript(
path: string,
summary: string,
id: string
id: string,
hash?: string
): Promise<[FlowModule & { value: PathScript }, FlowModuleState]> {
const flowModule: FlowModule & { value: PathScript } = {
id,
value: { type: 'script', path, input_transforms: {} },
value: { type: 'script', path, hash, input_transforms: {} },
summary
}
@@ -95,7 +96,7 @@ export async function createLoop(id: string): Promise<[FlowModule, FlowModuleSta
value: {
type: 'forloopflow',
modules: [],
iterator: { type: 'javascript', expr: '["iterator require expression that returns an array"]' },
iterator: { type: 'javascript', expr: '["dynamic or static array"]' },
skip_failures: true
}
}
@@ -200,7 +201,7 @@ export async function createScriptFromInlineScript(
const availablePath = await findNextAvailablePath(path)
await ScriptService.createScript({
const hash = await ScriptService.createScript({
workspace: get(workspaceStore)!,
requestBody: {
path: availablePath,
@@ -214,7 +215,7 @@ export async function createScriptFromInlineScript(
}
})
return pickScript(availablePath, flowModule.summary ?? '', flowModule.id)
return pickScript(availablePath, flowModule.summary ?? '', flowModule.id, hash)
}
export function deleteFlowStateById(id: string) {
@@ -1,9 +1,11 @@
<script lang="ts">
import Badge from '$lib/components/common/badge/Badge.svelte'
import Popover from '$lib/components/Popover.svelte'
import { classNames } from '$lib/utils'
import { faBed, faRepeat, faStop, faTimesCircle } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import { PhoneIncoming, Repeat } from 'svelte-lucide'
export let isFirst: boolean = false
export let isLast: boolean = false
@@ -38,21 +40,38 @@
margin
)}
>
<div class="absolute text-sm right-14 -bottom-3 flex flex-row gap-1">
<div class="absolute text-sm right-12 -bottom-3 flex flex-row gap-1 z-20">
{#if retry}
<div class="bg-white rounded border text-gray-600 px-1">
<Icon scale={0.8} data={faRepeat} />
</div>
<Popover notClickable>
<div class="bg-white rounded border text-gray-700 px-1 center-center">
<Repeat size="14px" />
</div>
<span slot="text">Retries</span>
</Popover>
{/if}
{#if earlyStop}
<div class="bg-white rounded border text-gray-600 px-1">
<Icon scale={0.8} data={faStop} />
</div>
<Popover notClickable>
<div class="bg-white rounded border text-gray-700 px-1">
<Icon scale={0.8} data={faStop} />
</div>
<span slot="text">Early Stop</span>
</Popover>
{/if}
{#if suspend || sleep}
<div class="bg-white rounded border text-gray-600 px-1">
<Icon scale={0.8} data={faBed} />
</div>
{#if sleep}
<Popover notClickable>
<div class="bg-white rounded border text-gray-700 px-1">
<Icon scale={0.8} data={faBed} />
</div>
<span slot="text">Sleep</span>
</Popover>
{/if}
{#if suspend}
<Popover notClickable>
<div class="bg-white rounded border text-gray-700 px-1 center-center">
<PhoneIncoming size="12px" />
</div>
<span slot="text">Suspend</span>
</Popover>
{/if}
</div>
<div
@@ -13,7 +13,7 @@
import type { FlowModule } from '$lib/gen'
import FlowErrorHandlerItem from './FlowErrorHandlerItem.svelte'
import RemoveStepConfirmationModal from '../content/RemoveStepConfirmationModal.svelte'
import { emptyFlowModuleState, isEmptyFlowModule } from '../utils'
import { emptyFlowModuleState } from '../utils'
import MapItem from './MapItem.svelte'
import FlowSettingsItem from './FlowSettingsItem.svelte'
import FlowInputsItem from './FlowInputsItem.svelte'
@@ -112,7 +112,7 @@
<MapItem
bind:mod
on:delete={(event) => {
if (event.detail.detail.shiftKey || isEmptyFlowModule(mod)) {
if (event.detail.detail.shiftKey || mod.value.type === 'identity') {
removeAtIndex(index)
} else {
indexToRemove = index
@@ -1,8 +1,9 @@
<script lang="ts">
import { Menu } from '$lib/components/common'
import { faCode, faCodeBranch, faPlus, faRepeat } from '@fortawesome/free-solid-svg-icons'
import { faCode, faCodeBranch, faPlus } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
import { Repeat } from 'svelte-lucide'
const dispatch = createEventDispatcher()
</script>
@@ -26,17 +27,20 @@
tabindex="-1"
>
<Icon data={faCode} scale={0.8} class="mr-1" />
Script or Module
Script
</button>
<button
class="w-full text-left p-2 hover:bg-gray-100"
class="w-full inline-flex gap-1 text-left p-2 hover:bg-gray-100"
on:click={() => {
close()
dispatch('new', 'forloop')
}}
role="menuitem"
>
<Icon data={faRepeat} scale={0.8} class="mr-1" />
<span>
<Repeat size="14px" />
</span>
For Loop
</button>
@@ -7,17 +7,11 @@
import InsertModuleButton from './InsertModuleButton.svelte'
import FlowBranchOneMap from './FlowBranchOneMap.svelte'
import FlowBranchAllMap from './FlowBranchAllMap.svelte'
import {
faArrowRotateForward,
faBuilding,
faCode,
faCodeBranch,
faLongArrowDown,
faRepeat
} from '@fortawesome/free-solid-svg-icons'
import { faCodeBranch, faLongArrowDown } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import IconedResourceType from '$lib/components/IconedResourceType.svelte'
import LanguageIcon from '$lib/components/common/languageIcons/LanguageIcon.svelte'
import { Building, Repeat } from 'svelte-lucide'
export let mod: FlowModule
@@ -53,7 +47,7 @@
{...itemProps}
>
<div slot="icon">
<Icon data={faArrowRotateForward} scale={1.1} />
<Repeat size="16px" />
</div>
</FlowModuleSchemaItem>
<div class="flex flex-row w-full">
@@ -125,7 +119,7 @@
/>
</div>
{:else}
<Icon data={faBuilding} scale={1.1} />
<Building size="14px" />
{/if}
{/if}
</div>
@@ -6,15 +6,18 @@
import { Badge, Skeleton } from '$lib/components/common'
import { fade } from 'svelte/transition'
import { flip } from 'svelte/animate'
import { emptyString } from '$lib/utils'
import { emptyString, truncateHash } from '$lib/utils'
import Toggle from '$lib/components/Toggle.svelte'
export let kind: 'script' | 'trigger' | 'approval' | 'failure' = 'script'
export let isTemplate: boolean | undefined = undefined
export let displayLock = false
type Item = {
path: string
summary?: string
description?: string
hash?: string
}
let items: Item[] | undefined = undefined
@@ -36,6 +39,7 @@
).sort()
const dispatch = createEventDispatcher()
let lockHash = displayLock
</script>
<SearchItems
@@ -47,6 +51,7 @@
<div class="flex flex-col min-h-0">
<div class="w-full flex mt-1 items-center gap-2 mb-3">
<slot />
<input
type="text"
placeholder="Search Workspace Scripts"
@@ -75,16 +80,24 @@
{/each}
</div>
{/if}
{#if displayLock}
<div class="flex flex-row-reverse">
<Toggle
bind:checked={lockHash}
options={{ left: 'Latest version', right: 'Lock current hash permanently' }}
/>
</div>
{/if}
{#if filter.length > 0 && filteredItems.length == 0}
<p class="py-5">No items found</p>
{/if}
<ul class="divide-y divide-gray-200 overflow-auto">
{#each filteredItems as { path, summary, description, marked }}
{#each filteredItems as { path, hash, summary, description, marked }}
<li class="flex flex-row w-full">
<button
class="py-4 px-1 gap-1 flex flex-row grow hover:bg-blue-50 bg-white transition-all text-black"
on:click={() => {
dispatch('pick', { path })
dispatch('pick', { path, hash: lockHash ? hash : undefined })
}}
>
<div class="flex flex-col">
@@ -102,6 +115,7 @@
</div>
<div class="text-xs font-light italic text-left">{description ?? ''}</div>
</div>
{#if lockHash}<Badge large baseClass="ml-4">{truncateHash(hash ?? '')}</Badge>{/if}
</button>
</li>
{/each}
@@ -212,9 +212,6 @@ export function charsToNumber(n: string): number {
return res - 1
}
export function isEmptyFlowModule(flowModule: FlowModule): boolean {
return flowModule.value.type === 'identity'
}
export async function findNextAvailablePath(path: string): Promise<string> {
try {
@@ -1,8 +1,8 @@
<script lang="ts">
import { workspaceStore, usersWorkspaceStore } from '$lib/stores'
import { classNames } from '$lib/utils'
import { faBuilding } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import { Building } from 'svelte-lucide'
import Menu from '../common/menu/Menu.svelte'
@@ -17,10 +17,9 @@
'group w-full flex items-center text-white hover:bg-gray-50 hover:text-gray-900 focus:ring-4 focus:outline-none focus:ring-gray-300 px-2 py-2 text-sm font-medium rounded-md h-8 '
)}
>
<Icon
data={faBuilding}
class={classNames('flex-shrink-0 h-4 w-4', isCollapsed ? '-mr-1' : 'mr-2')}
/>
<div class="center-center mr-2">
<Building size="16px" />
</div>
{#if !isCollapsed}
<span class={classNames('whitespace-pre truncate')}> {$workspaceStore} </span>
+2
View File
@@ -202,6 +202,8 @@ components:
$ref: "#/components/schemas/InputTransform"
path:
type: string
hash:
type: string
type:
type: string
enum: