mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-04 16:03:06 +00:00
make more things available
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
(x) =>
|
||||
passwords.includes(x) ||
|
||||
['token', 'secret', 'key', 'pass', 'private'].some((y) => x.toLowerCase().includes(y))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let linkedSecret: string | undefined = undefined
|
||||
@@ -487,6 +487,8 @@
|
||||
{#if !nativeLanguagesCategory.includes(key)}
|
||||
<!-- Exclude specific items -->
|
||||
<Button
|
||||
aiId={`app-connect-inner-${key}`}
|
||||
aiDescription={`Connect to ${key}`}
|
||||
size="sm"
|
||||
variant="border"
|
||||
color={key === resourceType ? 'blue' : 'light'}
|
||||
@@ -552,7 +554,8 @@
|
||||
{#if renderDescription}
|
||||
<div>
|
||||
<div class="flex flex-row-reverse text-2xs text-tertiary -mt-1">GH Markdown</div>
|
||||
<textarea use:autosize bind:value={description} placeholder={'Resource description'}></textarea>
|
||||
<textarea use:autosize bind:value={description} placeholder={'Resource description'}
|
||||
></textarea>
|
||||
</div>
|
||||
{:else if description == undefined || description == ''}
|
||||
<div class="text-sm text-tertiary">No description provided</div>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
href={item?.href}
|
||||
disabled={item?.disabled}
|
||||
class={twMerge(
|
||||
'px-4 py-2 text-primary font-semibold hover:bg-surface-hover cursor-pointer text-xs transition-all',
|
||||
'px-4 py-2 text-primary font-semibold hover:bg-surface-hover cursor-pointer text-xs transition-all w-full',
|
||||
'data-[highlighted]:bg-surface-hover',
|
||||
'flex flex-row gap-2 items-center',
|
||||
item?.disabled && 'text-gray-400 cursor-not-allowed',
|
||||
|
||||
@@ -594,6 +594,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
<div class="flex items-center gap-0.5">
|
||||
{#if showContextVarPicker && customUi?.contextVar != false}
|
||||
<Button
|
||||
aiId="editor-bar-add-context-variable"
|
||||
aiDescription="Add context variable"
|
||||
title="Add context variable"
|
||||
color="light"
|
||||
on:click={contextualVariablePicker.openDrawer}
|
||||
@@ -607,6 +609,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
{/if}
|
||||
{#if showVarPicker && customUi?.variable != false}
|
||||
<Button
|
||||
aiId="editor-bar-add-variable"
|
||||
aiDescription="Add variable"
|
||||
title="Add variable"
|
||||
color="light"
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
@@ -622,6 +626,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
|
||||
{#if showResourcePicker && customUi?.resource != false}
|
||||
<Button
|
||||
aiId="editor-bar-add-resource"
|
||||
aiDescription="Add resource"
|
||||
title="Add resource"
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
size="xs"
|
||||
@@ -637,6 +643,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
|
||||
{#if showResourceTypePicker && customUi?.type != false}
|
||||
<Button
|
||||
aiId="editor-bar-add-resource-type"
|
||||
aiDescription="Add resource type"
|
||||
title="Add resource type"
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
size="xs"
|
||||
@@ -652,6 +660,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
|
||||
{#if customUi?.reset != false}
|
||||
<Button
|
||||
aiId="editor-bar-reset-content"
|
||||
aiDescription="Reset content"
|
||||
title="Reset Content"
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
size="xs"
|
||||
@@ -668,6 +678,8 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
|
||||
{#if customUi?.assistants != false}
|
||||
{#if lang == 'deno' || lang == 'python3' || lang == 'go' || lang == 'bash' || lang == 'nu'}
|
||||
<Button
|
||||
aiId="editor-bar-reload-assistants"
|
||||
aiDescription="Reload assistants"
|
||||
btnClasses="!font-medium text-tertiary"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
|
||||
@@ -945,13 +945,17 @@
|
||||
<div class="flex flex-col h-full">
|
||||
<Tabs bind:selected={selectedTab} wrapperClass="flex-none w-full">
|
||||
{#if customUi?.settingsPanel?.disableMetadata !== true}
|
||||
<Tab value="metadata">Metadata</Tab>
|
||||
<Tab value="metadata" aiId="script-builder-metadata" aiDescription="Metadata settings">
|
||||
Metadata
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if customUi?.settingsPanel?.disableRuntime !== true}
|
||||
<Tab value="runtime">Runtime</Tab>
|
||||
<Tab value="runtime" aiId="script-builder-runtime" aiDescription="Runtime settings">
|
||||
Runtime
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if customUi?.settingsPanel?.disableGeneratedUi !== true}
|
||||
<Tab value="ui">
|
||||
<Tab value="ui" aiId="script-builder-ui" aiDescription="Generated UI settings">
|
||||
Generated UI
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/json_schema_and_parsing"
|
||||
@@ -962,7 +966,7 @@
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if customUi?.settingsPanel?.disableTriggers !== true}
|
||||
<Tab value="triggers">
|
||||
<Tab value="triggers" aiId="script-builder-triggers" aiDescription="Triggers settings">
|
||||
Triggers
|
||||
<Tooltip documentationLink="https://www.windmill.dev/docs/getting_started/triggers">
|
||||
Configure how this script will be triggered.
|
||||
@@ -1634,6 +1638,8 @@
|
||||
<div class="flex flex-row gap-x-1 lg:gap-x-2">
|
||||
{#if customUi?.topBar?.settings != false}
|
||||
<Button
|
||||
aiId="script-builder-settings"
|
||||
aiDescription="Script builder settings to configure metadata, runtime, triggers, and generated UI."
|
||||
color="light"
|
||||
variant="border"
|
||||
size="xs"
|
||||
|
||||
@@ -11,24 +11,41 @@ import OpenAI from 'openai'
|
||||
|
||||
// System prompt for the LLM
|
||||
export const CHAT_SYSTEM_PROMPT = `
|
||||
You are an assistant that helps the user understand what he can do on the application, and guide him through the application by interacting with it.
|
||||
You have access to tools that let you:
|
||||
1. View the current triggerable components on the page
|
||||
2. Execute the trigger function of a triggerable component
|
||||
3. Get documentation for the user request
|
||||
You are Windmill's intelligent assistant, designed to help users navigate the application and answer questions about its functionality.
|
||||
|
||||
When asked to explain something about the application:
|
||||
- Use the get_documentation tool to get the documentation for the user request
|
||||
- Ask the user if he wants to be guided through the application to do what he wants.
|
||||
You have access to these tools:
|
||||
1. View current triggerable components on the page (get_triggerable_components)
|
||||
2. Execute component trigger functions (trigger_component)
|
||||
3. Get documentation for user requests (get_documentation)
|
||||
|
||||
When asked to do something on the application:
|
||||
- First examine the page structure to understand what's available
|
||||
- Explain what you're doing before taking action
|
||||
- Take action only if you're sure it's what the user wants
|
||||
- After executing a command, wait for 1 second before rechecking the page and continuing fulfulling the user request. At each step, explain what you're doing before taking action.
|
||||
- After fulfilling the user request, if there is a close button associated with a drawer or settings panel, use it to close the drawer or settings panel.
|
||||
RESPONDING TO QUESTIONS:
|
||||
- When users ask about application features or concepts, use get_documentation to retrieve accurate information
|
||||
- Present information concisely and clearly, highlighting key points
|
||||
- For complex topics, offer to guide users through relevant sections of the application
|
||||
|
||||
Use the provided tools only when necessary and appropriate.
|
||||
NAVIGATION ASSISTANCE:
|
||||
- When users want to perform an action, first use get_triggerable_components to understand available options
|
||||
- Always explain what you'll do before taking any action
|
||||
- Take action only when you're confident it matches the user's intent
|
||||
- For multi-step processes:
|
||||
* Guide users step-by-step, explaining each action
|
||||
* After each action, wait briefly then recheck available components before continuing
|
||||
* Maintain context throughout the interaction
|
||||
|
||||
USER EXPERIENCE GUIDELINES:
|
||||
- Be proactive in suggesting helpful next steps
|
||||
- If a request is ambiguous, ask clarifying questions before taking action
|
||||
- After completing a task involving panels or drawers, look for and use close/dismiss buttons
|
||||
- If you encounter an error or can't complete a request, explain why and suggest alternatives
|
||||
- Adapt your level of guidance based on user expertise (more detailed for beginners)
|
||||
|
||||
GENERAL PRINCIPLES:
|
||||
- Be concise but thorough
|
||||
- Focus on being helpful rather than just informative
|
||||
- Maintain a friendly, professional tone
|
||||
- Remember user preferences between interactions when possible
|
||||
|
||||
Always use the provided tools purposefully and appropriately to achieve the user's goals.
|
||||
`
|
||||
|
||||
const GET_DOCUMENTATION_TOOL: ChatCompletionTool = {
|
||||
|
||||
@@ -140,6 +140,8 @@
|
||||
{:else if script.canWrite && !script.archived}
|
||||
<div>
|
||||
<Button
|
||||
aiId={`edit-script-button-${script.summary ?? script.path}`}
|
||||
aiDescription={`Edits the script ${script.summary ?? script.path}`}
|
||||
color="light"
|
||||
size="xs"
|
||||
variant="border"
|
||||
@@ -152,6 +154,8 @@
|
||||
{:else if !script.draft_only}
|
||||
<div>
|
||||
<Button
|
||||
aiId={`fork-script-button-${script.summary ?? script.path}`}
|
||||
aiDescription={`Fork the script ${script.summary ?? script.path}`}
|
||||
color="light"
|
||||
size="xs"
|
||||
variant="border"
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { TabsContext } from './Tabs.svelte'
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let value: string
|
||||
export let size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'sm'
|
||||
let c = ''
|
||||
@@ -41,30 +44,42 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
class={twMerge(
|
||||
'border-b-2 py-1 px-2 cursor-pointer transition-all z-10 ease-linear font-normal text-tertiary',
|
||||
isSelected
|
||||
? 'wm-tab-active font-main'
|
||||
: 'border-gray-300 dark:border-gray-600 border-opacity-0 hover:border-opacity-100 ',
|
||||
fontSizeClasses[size],
|
||||
c,
|
||||
isSelected ? selectedClass : '',
|
||||
disabled ? 'cursor-not-allowed text-tertiary' : ''
|
||||
)}
|
||||
style={`${style} ${isSelected ? selectedStyle : ''}`}
|
||||
on:click={() => {
|
||||
<TriggerableByAI
|
||||
id={aiId}
|
||||
description={aiDescription}
|
||||
onTrigger={() => {
|
||||
if (hashNavigation) {
|
||||
window.location.hash = value
|
||||
} else {
|
||||
update(value)
|
||||
}
|
||||
}}
|
||||
on:pointerdown|stopPropagation
|
||||
{disabled}
|
||||
{id}
|
||||
>
|
||||
<div class={twMerge(active ? 'bg-blue-50 text-blue-800 rounded-md ' : '', 'px-2 ')}>
|
||||
<slot />
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class={twMerge(
|
||||
'border-b-2 py-1 px-2 cursor-pointer transition-all z-10 ease-linear font-normal text-tertiary',
|
||||
isSelected
|
||||
? 'wm-tab-active font-main'
|
||||
: 'border-gray-300 dark:border-gray-600 border-opacity-0 hover:border-opacity-100 ',
|
||||
fontSizeClasses[size],
|
||||
c,
|
||||
isSelected ? selectedClass : '',
|
||||
disabled ? 'cursor-not-allowed text-tertiary' : ''
|
||||
)}
|
||||
style={`${style} ${isSelected ? selectedStyle : ''}`}
|
||||
on:click={() => {
|
||||
if (hashNavigation) {
|
||||
window.location.hash = value
|
||||
} else {
|
||||
update(value)
|
||||
}
|
||||
}}
|
||||
on:pointerdown|stopPropagation
|
||||
{disabled}
|
||||
{id}
|
||||
>
|
||||
<div class={twMerge(active ? 'bg-blue-50 text-blue-800 rounded-md ' : '', 'px-2 ')}>
|
||||
<slot />
|
||||
</div>
|
||||
</button>
|
||||
</TriggerableByAI>
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
if (allTriggerTypes) {
|
||||
numberOfTriggers = allTriggerTypes?.length
|
||||
}
|
||||
console.log(allTriggerTypes)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -237,6 +238,8 @@
|
||||
{@const { icon: SvelteComponent, countKey } = triggerTypeConfig[type]}
|
||||
|
||||
<MeltButton
|
||||
aiId={`trigger-button-${type}`}
|
||||
aiDescription={`Trigger button for ${type}`}
|
||||
class={twMerge(
|
||||
'hover:bg-surface-hover rounded-md shadow-sm text-xs relative center-center cursor-pointer bg-slate-100 dark:bg-slate-700',
|
||||
'dark:outline dark:outline-1 outline-tertiary/20 group',
|
||||
|
||||
@@ -1,21 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { type AnyMeltElement } from '@melt-ui/svelte'
|
||||
import { conditionalMelt } from '$lib/utils'
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let meltElement: AnyMeltElement | undefined = undefined
|
||||
export let type: 'button' | 'submit' | 'reset' | null | undefined = undefined
|
||||
export let title: string = ''
|
||||
export let id: string | undefined = undefined
|
||||
|
||||
$: console.log(aiId, aiDescription)
|
||||
|
||||
let buttonRef: HTMLButtonElement | undefined = undefined
|
||||
</script>
|
||||
|
||||
<button
|
||||
use:conditionalMelt={meltElement}
|
||||
class={$$props.class}
|
||||
{type}
|
||||
{title}
|
||||
{id}
|
||||
{...$meltElement}
|
||||
on:click
|
||||
<TriggerableByAI
|
||||
id={aiId}
|
||||
description={aiDescription}
|
||||
onTrigger={() => {
|
||||
buttonRef?.click()
|
||||
}}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
<button
|
||||
bind:this={buttonRef}
|
||||
use:conditionalMelt={meltElement}
|
||||
class={$$props.class}
|
||||
{type}
|
||||
{title}
|
||||
{id}
|
||||
{...$meltElement}
|
||||
on:click
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</TriggerableByAI>
|
||||
|
||||
@@ -1,37 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { melt } from '@melt-ui/svelte'
|
||||
import type { MenubarMenuElements } from '@melt-ui/svelte'
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let href: string | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let target: string | undefined = undefined
|
||||
export let item: MenubarMenuElements['item']
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a
|
||||
use:melt={$item}
|
||||
{href}
|
||||
class={$$props.class}
|
||||
role="menuitem"
|
||||
aria-disabled={disabled}
|
||||
tabindex={disabled ? -1 : undefined}
|
||||
{target}
|
||||
on:m-focusin
|
||||
on:m-focusout
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
on:click
|
||||
use:melt={$item}
|
||||
{disabled}
|
||||
class={$$props.class}
|
||||
role="menuitem"
|
||||
on:m-focusin
|
||||
on:m-focusout
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
{/if}
|
||||
<TriggerableByAI
|
||||
id={aiId}
|
||||
description={aiDescription}
|
||||
onTrigger={() => {
|
||||
if (href) {
|
||||
goto(href)
|
||||
} else {
|
||||
dispatch('click')
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if href}
|
||||
<a
|
||||
use:melt={$item}
|
||||
{href}
|
||||
class={$$props.class}
|
||||
role="menuitem"
|
||||
aria-disabled={disabled}
|
||||
tabindex={disabled ? -1 : undefined}
|
||||
{target}
|
||||
on:m-focusin
|
||||
on:m-focusout
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
on:click
|
||||
use:melt={$item}
|
||||
{disabled}
|
||||
class={$$props.class}
|
||||
role="menuitem"
|
||||
on:m-focusin
|
||||
on:m-focusout
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
{/if}
|
||||
</TriggerableByAI>
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { conditionalMelt } from '$lib/utils'
|
||||
import type { MenubarMenuElements } from '@melt-ui/svelte'
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let label: string | undefined = undefined
|
||||
export let icon: any | undefined = undefined
|
||||
export let isCollapsed: boolean
|
||||
@@ -19,6 +22,8 @@
|
||||
export let trigger: MenubarMenuElements['trigger'] | undefined = undefined
|
||||
export let href: string | undefined = undefined
|
||||
|
||||
let buttonRef: HTMLButtonElement | undefined = undefined
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
@@ -30,68 +35,82 @@
|
||||
disablePopup={!isCollapsed}
|
||||
placement="right"
|
||||
>
|
||||
<button
|
||||
on:click={(e) => {
|
||||
if (stopPropagationOnClick) e.preventDefault()
|
||||
if (href) {
|
||||
goto(href)
|
||||
<TriggerableByAI
|
||||
id={aiId}
|
||||
description={aiDescription}
|
||||
onTrigger={() => {
|
||||
if (buttonRef) {
|
||||
buttonRef.click()
|
||||
}
|
||||
dispatch('click')
|
||||
}}
|
||||
class={twMerge(
|
||||
'group flex items-center px-2 py-2 font-light rounded-md h-8 gap-3 w-full',
|
||||
lightMode
|
||||
? 'text-primary data-[highlighted]:bg-surface-hover hover:bg-surface-hover'
|
||||
: 'data-[highlighted]:bg-[#2A3648] hover:bg-[#2A3648] text-primary-inverse dark:text-primary',
|
||||
color ? 'border-4' : '',
|
||||
'transition-all relative',
|
||||
$$props.class
|
||||
)}
|
||||
style={color ? `border-color: ${color}; padding: 0 calc(0.5rem - 4px);` : ''}
|
||||
use:conditionalMelt={trigger}
|
||||
title={isCollapsed ? undefined : label}
|
||||
{...$trigger}
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
size={16}
|
||||
class={twMerge(
|
||||
'flex-shrink-0',
|
||||
lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-primary-inverse group-hover:text-secondary-inverse dark:group-hover:text-secondary dark:text-primary',
|
||||
'transition-all'
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
<button
|
||||
bind:this={buttonRef}
|
||||
on:click={(e) => {
|
||||
if (stopPropagationOnClick) e.preventDefault()
|
||||
if (href) {
|
||||
goto(href)
|
||||
}
|
||||
dispatch('click')
|
||||
}}
|
||||
class={twMerge(
|
||||
'group flex items-center px-2 py-2 font-light rounded-md h-8 gap-3 w-full',
|
||||
lightMode
|
||||
? 'text-primary data-[highlighted]:bg-surface-hover hover:bg-surface-hover'
|
||||
: 'data-[highlighted]:bg-[#2A3648] hover:bg-[#2A3648] text-primary-inverse dark:text-primary',
|
||||
color ? 'border-4' : '',
|
||||
'transition-all relative',
|
||||
$$props.class
|
||||
)}
|
||||
style={color ? `border-color: ${color}; padding: 0 calc(0.5rem - 4px);` : ''}
|
||||
use:conditionalMelt={trigger}
|
||||
title={isCollapsed ? undefined : label}
|
||||
{...$trigger}
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
size={16}
|
||||
class={twMerge(
|
||||
'flex-shrink-0',
|
||||
lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-primary-inverse group-hover:text-secondary-inverse dark:group-hover:text-secondary dark:text-primary',
|
||||
'transition-all'
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !isCollapsed && label}
|
||||
<span
|
||||
class={twMerge(
|
||||
'whitespace-pre truncate',
|
||||
lightMode ? 'text-primary' : 'text-primary-inverse dark:text-primary',
|
||||
'transition-all',
|
||||
$$props.class
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
<span class="pl-2 text-xs dark:text-secondary light:text-secondary-inverse font-semibold">
|
||||
{shortcut}
|
||||
{#if !isCollapsed && label}
|
||||
<span
|
||||
class={twMerge(
|
||||
'whitespace-pre truncate',
|
||||
lightMode ? 'text-primary' : 'text-primary-inverse dark:text-primary',
|
||||
'transition-all',
|
||||
$$props.class
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
<span
|
||||
class="pl-2 text-xs dark:text-secondary light:text-secondary-inverse font-semibold"
|
||||
>
|
||||
{shortcut}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if isCollapsed && notificationsCount > 0}
|
||||
<div class="absolute translate-x-1/2 translate-y-1/2 -top-2 right-1 flex h-fit w-fit">
|
||||
<SideBarNotification notificationCount={notificationsCount} small={true} />
|
||||
</div>
|
||||
{:else if notificationsCount > 0}
|
||||
<div class="ml-auto">
|
||||
<SideBarNotification notificationCount={notificationsCount} small={false} />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</TriggerableByAI>
|
||||
|
||||
{#if isCollapsed && notificationsCount > 0}
|
||||
<div class="absolute translate-x-1/2 translate-y-1/2 -top-2 right-1 flex h-fit w-fit">
|
||||
<SideBarNotification notificationCount={notificationsCount} small={true} />
|
||||
</div>
|
||||
{:else if notificationsCount > 0}
|
||||
<div class="ml-auto">
|
||||
<SideBarNotification notificationCount={notificationsCount} small={false} />
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
<svelte:fragment slot="text">
|
||||
{#if label}
|
||||
{label}
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
import { navigating, page } from '$app/stores'
|
||||
import Popover from '../Popover.svelte'
|
||||
import { base } from '$app/paths'
|
||||
import TriggerableByAI from '$lib/components/TriggerableByAI.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
export let aiId: string | undefined = undefined
|
||||
export let aiDescription: string | undefined = undefined
|
||||
export let label: string
|
||||
export let href: string
|
||||
export let icon: any | undefined = undefined
|
||||
@@ -25,65 +29,73 @@
|
||||
</script>
|
||||
|
||||
{#if !disabled}
|
||||
<Popover appearTimeout={0} disappearTimeout={0} class="w-full" disablePopup={!isCollapsed}>
|
||||
<a
|
||||
{href}
|
||||
class={classNames(
|
||||
'group flex items-center px-2 py-2 text-sm font-light rounded-md h-8 gap-3',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'bg-surface-selected hover:bg-surface-hover rounded-none data-[highlighted]:bg-surface-hover'
|
||||
: 'bg-frost-700 hover:bg-[#30404e] data-[highlighted]:bg-[#30404e]'
|
||||
: lightMode
|
||||
? 'hover:bg-surface-hover rounded-none data-[highlighted]:bg-surface-hover'
|
||||
: 'hover:bg-[#2A3648] data-[highlighted]:bg-[#2A3648]',
|
||||
<TriggerableByAI
|
||||
id={aiId}
|
||||
description={aiDescription}
|
||||
onTrigger={() => {
|
||||
goto(href)
|
||||
}}
|
||||
>
|
||||
<Popover appearTimeout={0} disappearTimeout={0} class="w-full" disablePopup={!isCollapsed}>
|
||||
<a
|
||||
{href}
|
||||
class={classNames(
|
||||
'group flex items-center px-2 py-2 text-sm font-light rounded-md h-8 gap-3',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'bg-surface-selected hover:bg-surface-hover rounded-none data-[highlighted]:bg-surface-hover'
|
||||
: 'bg-frost-700 hover:bg-[#30404e] data-[highlighted]:bg-[#30404e]'
|
||||
: lightMode
|
||||
? 'hover:bg-surface-hover rounded-none data-[highlighted]:bg-surface-hover'
|
||||
: 'hover:bg-[#2A3648] data-[highlighted]:bg-[#2A3648]',
|
||||
|
||||
'hover:transition-all',
|
||||
$$props.class
|
||||
)}
|
||||
target={href.includes('http') ? '_blank' : null}
|
||||
title={isCollapsed ? undefined : label}
|
||||
use:conditionalMelt={item}
|
||||
{...$item}
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
size={16}
|
||||
class={classNames(
|
||||
'flex-shrink-0',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
'transition-all'
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
'hover:transition-all',
|
||||
$$props.class
|
||||
)}
|
||||
target={href.includes('http') ? '_blank' : null}
|
||||
title={isCollapsed ? undefined : label}
|
||||
use:conditionalMelt={item}
|
||||
{...$item}
|
||||
>
|
||||
{#if icon}
|
||||
<svelte:component
|
||||
this={icon}
|
||||
size={16}
|
||||
class={classNames(
|
||||
'flex-shrink-0',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
'transition-all'
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !isCollapsed}
|
||||
<span
|
||||
class={classNames(
|
||||
'whitespace-pre truncate',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
'transition-all duration-75'
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
<svelte:fragment slot="text">
|
||||
{label}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{#if !isCollapsed}
|
||||
<span
|
||||
class={classNames(
|
||||
'whitespace-pre truncate',
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
'transition-all duration-75'
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
<svelte:fragment slot="text">
|
||||
{label}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</TriggerableByAI>
|
||||
{/if}
|
||||
|
||||
@@ -65,15 +65,36 @@
|
||||
export let numUnacknowledgedCriticalAlerts = 0
|
||||
|
||||
$: mainMenuLinks = [
|
||||
{ label: 'Home', href: `${base}/`, icon: Home },
|
||||
{ label: 'Runs', href: `${base}/runs`, icon: Play },
|
||||
{
|
||||
label: 'Home',
|
||||
href: `${base}/`,
|
||||
icon: Home,
|
||||
aiId: 'sidebar-menu-link-home',
|
||||
aiDescription: 'Button to navigate to home'
|
||||
},
|
||||
{
|
||||
label: 'Runs',
|
||||
href: `${base}/runs`,
|
||||
icon: Play,
|
||||
aiId: 'sidebar-menu-link-runs',
|
||||
aiDescription: 'Button to navigate to runs'
|
||||
},
|
||||
{
|
||||
label: 'Variables',
|
||||
href: `${base}/variables`,
|
||||
icon: DollarSign,
|
||||
disabled: $userStore?.operator
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-variables',
|
||||
aiDescription: 'Button to navigate to variables'
|
||||
},
|
||||
{ label: 'Resources', href: `${base}/resources`, icon: Boxes, disabled: $userStore?.operator }
|
||||
{
|
||||
label: 'Resources',
|
||||
href: `${base}/resources`,
|
||||
icon: Boxes,
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-resources',
|
||||
aiDescription: 'Button to navigate to resources'
|
||||
}
|
||||
]
|
||||
|
||||
$: triggerMenuLinks = [
|
||||
@@ -81,7 +102,9 @@
|
||||
label: 'Schedules',
|
||||
href: `${base}/schedules`,
|
||||
icon: Calendar,
|
||||
disabled: !SIDEBAR_SHOW_SCHEDULES || $userStore?.operator
|
||||
disabled: !SIDEBAR_SHOW_SCHEDULES || $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-schedules',
|
||||
aiDescription: 'Button to navigate to schedules'
|
||||
},
|
||||
...defaultExtraTriggerLinks.filter(
|
||||
(link) => $usedTriggerKinds.includes(link.kind) || $page.url.pathname.includes(link.href)
|
||||
@@ -100,56 +123,72 @@
|
||||
href: '/routes',
|
||||
icon: Route,
|
||||
disabled: $userStore?.operator,
|
||||
kind: 'http'
|
||||
kind: 'http',
|
||||
aiId: 'sidebar-menu-link-http',
|
||||
aiDescription: 'Button to navigate to http routes'
|
||||
},
|
||||
{
|
||||
label: 'WebSockets',
|
||||
href: '/websocket_triggers',
|
||||
icon: Unplug,
|
||||
disabled: $userStore?.operator,
|
||||
kind: 'ws'
|
||||
kind: 'ws',
|
||||
aiId: 'sidebar-menu-link-ws',
|
||||
aiDescription: 'Button to navigate to websocket triggers'
|
||||
},
|
||||
{
|
||||
label: 'Postgres',
|
||||
href: '/postgres_triggers',
|
||||
icon: Database,
|
||||
disabled: $userStore?.operator,
|
||||
kind: 'postgres'
|
||||
kind: 'postgres',
|
||||
aiId: 'sidebar-menu-link-postgres',
|
||||
aiDescription: 'Button to navigate to postgres triggers'
|
||||
},
|
||||
{
|
||||
label: 'Kafka' + ($enterpriseLicense ? '' : ' (EE)'),
|
||||
href: '/kafka_triggers',
|
||||
icon: KafkaIcon,
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'kafka'
|
||||
kind: 'kafka',
|
||||
aiId: 'sidebar-menu-link-kafka',
|
||||
aiDescription: 'Button to navigate to kafka triggers'
|
||||
},
|
||||
{
|
||||
label: 'NATS' + ($enterpriseLicense ? '' : ' (EE)'),
|
||||
href: '/nats_triggers',
|
||||
icon: NatsIcon,
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'nats'
|
||||
kind: 'nats',
|
||||
aiId: 'sidebar-menu-link-nats',
|
||||
aiDescription: 'Button to navigate to nats triggers'
|
||||
},
|
||||
{
|
||||
label: 'SQS' + ($enterpriseLicense ? '' : ' (EE)'),
|
||||
href: '/sqs_triggers',
|
||||
icon: AwsIcon,
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'sqs'
|
||||
kind: 'sqs',
|
||||
aiId: 'sidebar-menu-link-sqs',
|
||||
aiDescription: 'Button to navigate to sqs triggers'
|
||||
},
|
||||
{
|
||||
label: 'GCP Pub/Sub' + ($enterpriseLicense ? '' : ' (EE)'),
|
||||
href: '/gcp_triggers',
|
||||
icon: GoogleCloudIcon,
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'gcp'
|
||||
kind: 'gcp',
|
||||
aiId: 'sidebar-menu-link-gcp',
|
||||
aiDescription: 'Button to navigate to gcp triggers'
|
||||
},
|
||||
{
|
||||
label: 'MQTT',
|
||||
href: '/mqtt_triggers',
|
||||
icon: MqttIcon,
|
||||
disabled: $userStore?.operator,
|
||||
kind: 'mqtt'
|
||||
kind: 'mqtt',
|
||||
aiId: 'sidebar-menu-link-mqtt',
|
||||
aiDescription: 'Button to navigate to mqtt triggers'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -166,11 +205,16 @@
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: Settings,
|
||||
aiId: 'sidebar-menu-link-settings',
|
||||
aiDescription:
|
||||
'Button to navigate to settings, including account, workspace, and instance settings',
|
||||
subItems: [
|
||||
{
|
||||
label: 'Account',
|
||||
href: '#user-settings',
|
||||
icon: UserCog,
|
||||
aiId: 'sidebar-menu-link-account',
|
||||
aiDescription: 'Button to navigate to account settings',
|
||||
faIcon: undefined
|
||||
},
|
||||
...($userStore?.is_admin || $superadmin
|
||||
@@ -179,6 +223,8 @@
|
||||
label: 'Workspace',
|
||||
href: `${base}/workspace_settings`,
|
||||
icon: FolderCog,
|
||||
aiId: 'sidebar-menu-link-workspace',
|
||||
aiDescription: 'Button to navigate to workspace settings',
|
||||
faIcon: undefined
|
||||
}
|
||||
]
|
||||
@@ -189,6 +235,8 @@
|
||||
label: 'Instance',
|
||||
href: '#superadmin-settings',
|
||||
icon: ServerCog,
|
||||
aiId: 'sidebar-menu-link-instance',
|
||||
aiDescription: 'Button to navigate to instance settings',
|
||||
faIcon: undefined
|
||||
}
|
||||
]
|
||||
@@ -209,16 +257,27 @@
|
||||
],
|
||||
disabled: $userStore?.operator
|
||||
},
|
||||
{ label: 'Workers', href: `${base}/workers`, icon: Bot, disabled: $userStore?.operator },
|
||||
{
|
||||
label: 'Workers',
|
||||
href: `${base}/workers`,
|
||||
icon: Bot,
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-workers',
|
||||
aiDescription: 'Button to navigate to workers'
|
||||
},
|
||||
{
|
||||
label: 'Folders & Groups',
|
||||
icon: FolderOpen,
|
||||
aiId: 'sidebar-menu-link-folders-groups',
|
||||
aiDescription: 'Button to navigate to folders and groups',
|
||||
subItems: [
|
||||
{
|
||||
label: 'Folders',
|
||||
href: `${base}/folders`,
|
||||
icon: FolderOpen,
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-folders',
|
||||
aiDescription: 'Button to navigate to folders',
|
||||
faIcon: undefined
|
||||
},
|
||||
{
|
||||
@@ -226,6 +285,8 @@
|
||||
href: `${base}/groups`,
|
||||
icon: UserCog,
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-groups',
|
||||
aiDescription: 'Button to navigate to groups',
|
||||
faIcon: undefined
|
||||
}
|
||||
],
|
||||
@@ -235,18 +296,24 @@
|
||||
? {
|
||||
label: 'Logs',
|
||||
icon: Logs,
|
||||
aiId: 'sidebar-menu-link-logs',
|
||||
aiDescription: 'Button to navigate to logs',
|
||||
subItems: [
|
||||
{
|
||||
label: 'Audit logs',
|
||||
href: `${base}/audit_logs`,
|
||||
icon: Eye
|
||||
icon: Eye,
|
||||
aiId: 'sidebar-menu-link-audit-logs',
|
||||
aiDescription: 'Button to navigate to audit logs'
|
||||
},
|
||||
...($devopsRole
|
||||
? [
|
||||
{
|
||||
label: 'Service logs',
|
||||
href: `${base}/service_logs`,
|
||||
icon: Logs
|
||||
icon: Logs,
|
||||
aiId: 'sidebar-menu-link-service-logs',
|
||||
aiDescription: 'Button to navigate to service logs'
|
||||
}
|
||||
]
|
||||
: []),
|
||||
@@ -258,7 +325,9 @@
|
||||
isCriticalAlertsUIOpen.set(true)
|
||||
},
|
||||
icon: AlertCircle,
|
||||
notificationCount: numUnacknowledgedCriticalAlerts
|
||||
notificationCount: numUnacknowledgedCriticalAlerts,
|
||||
aiId: 'sidebar-menu-link-critical-alerts',
|
||||
aiDescription: 'Button to navigate to critical alerts'
|
||||
}
|
||||
]
|
||||
: [])
|
||||
@@ -268,7 +337,9 @@
|
||||
label: 'Audit logs',
|
||||
href: `${base}/audit_logs`,
|
||||
icon: Eye,
|
||||
disabled: $userStore?.operator
|
||||
disabled: $userStore?.operator,
|
||||
aiId: 'sidebar-menu-link-audit-logs',
|
||||
aiDescription: 'Button to navigate to audit logs'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -298,21 +369,33 @@
|
||||
label: 'Help',
|
||||
icon: HelpCircle,
|
||||
subItems: [
|
||||
{ label: 'Docs', href: 'https://www.windmill.dev/docs/intro/', icon: BookOpen },
|
||||
{
|
||||
label: 'Docs',
|
||||
href: 'https://www.windmill.dev/docs/intro/',
|
||||
icon: BookOpen,
|
||||
aiId: 'sidebar-menu-link-docs',
|
||||
aiDescription: 'Button to navigate to docs'
|
||||
},
|
||||
{
|
||||
label: 'Feedbacks',
|
||||
href: 'https://discord.gg/V7PM2YHsPB',
|
||||
icon: DiscordIcon
|
||||
icon: DiscordIcon,
|
||||
aiId: 'sidebar-menu-link-feedbacks',
|
||||
aiDescription: 'Button to navigate to feedbacks'
|
||||
},
|
||||
{
|
||||
label: 'Issues',
|
||||
href: 'https://github.com/windmill-labs/windmill/issues/new',
|
||||
icon: Github
|
||||
icon: Github,
|
||||
aiId: 'sidebar-menu-link-issues',
|
||||
aiDescription: 'Button to navigate to issues'
|
||||
},
|
||||
{
|
||||
label: 'Changelog',
|
||||
href: 'https://www.windmill.dev/changelog/',
|
||||
icon: Newspaper
|
||||
icon: Newspaper,
|
||||
aiId: 'sidebar-menu-link-changelog',
|
||||
aiDescription: 'Button to navigate to changelog'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -370,6 +453,8 @@
|
||||
</svelte:fragment>
|
||||
{#each extraTriggerLinks as subItem (subItem.href ?? subItem.label)}
|
||||
<MenuItem
|
||||
aiId={subItem.aiId}
|
||||
aiDescription={subItem.aiDescription}
|
||||
href={subItem.disabled ? '' : subItem.href}
|
||||
class={twMerge(itemClass, subItem.disabled ? 'pointer-events-none opacity-50' : '')}
|
||||
{item}
|
||||
@@ -414,6 +499,8 @@
|
||||
on:click={() => {
|
||||
subItem?.['action']?.()
|
||||
}}
|
||||
aiId={subItem.aiId}
|
||||
aiDescription={subItem.aiDescription}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{#if subItem.icon}
|
||||
|
||||
@@ -616,10 +616,23 @@
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/resources_and_types"
|
||||
>
|
||||
<div class="flex flex-row justify-end gap-4">
|
||||
<Button variant="border" size="md" startIcon={{ icon: Plus }} on:click={startNewType}>
|
||||
<Button
|
||||
variant="border"
|
||||
size="md"
|
||||
startIcon={{ icon: Plus }}
|
||||
on:click={startNewType}
|
||||
aiId="resources-add-resource-type"
|
||||
aiDescription="Add resource type"
|
||||
>
|
||||
Add resource type
|
||||
</Button>
|
||||
<Button size="md" startIcon={{ icon: Link }} on:click={() => appConnect.open?.()}>
|
||||
<Button
|
||||
size="md"
|
||||
startIcon={{ icon: Link }}
|
||||
on:click={() => appConnect.open?.()}
|
||||
aiId="resources-add-resource"
|
||||
aiDescription="Add resource"
|
||||
>
|
||||
Add resource
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -256,8 +256,14 @@
|
||||
tooltip="Trigger Scripts and Flows according to a cron schedule"
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/scheduling"
|
||||
>
|
||||
<Button size="md" startIcon={{ icon: Plus }} on:click={() => scheduleEditor.openNew(false)}>
|
||||
New schedule
|
||||
<Button
|
||||
size="md"
|
||||
startIcon={{ icon: Plus }}
|
||||
on:click={() => scheduleEditor.openNew(false)}
|
||||
aiId="schedules-add-schedule"
|
||||
aiDescription="Add schedule"
|
||||
>
|
||||
New schedule
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<div class="w-full h-full flex flex-col">
|
||||
|
||||
@@ -644,46 +644,106 @@
|
||||
goto(`?${$page.url.searchParams.toString()}`)
|
||||
}}
|
||||
>
|
||||
<Tab size="xs" value="users">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="users"
|
||||
aiId="workspace-settings-users"
|
||||
aiDescription="Users workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Users</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="git_sync">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="git_sync"
|
||||
aiId="workspace-settings-git-sync"
|
||||
aiDescription="Git sync workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1">Git Sync</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="deploy_to">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="deploy_to"
|
||||
aiId="workspace-settings-deploy-to"
|
||||
aiDescription="Deployment UI workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1">Deployment UI</div>
|
||||
</Tab>
|
||||
{#if WORKSPACE_SHOW_SLACK_CMD}
|
||||
<Tab size="xs" value="slack">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="slack"
|
||||
aiId="workspace-settings-slack"
|
||||
aiDescription="Slack / Teams workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Slack / Teams</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if isCloudHosted()}
|
||||
<Tab size="xs" value="premium">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="premium"
|
||||
aiId="workspace-settings-premium"
|
||||
aiDescription="Premium plans workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Premium Plans </div>
|
||||
</Tab>
|
||||
{/if}
|
||||
{#if WORKSPACE_SHOW_WEBHOOK_CLI_SYNC}
|
||||
<Tab size="xs" value="webhook">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="webhook"
|
||||
aiId="workspace-settings-webhook"
|
||||
aiDescription="Webhook workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1">Webhook</div>
|
||||
</Tab>
|
||||
{/if}
|
||||
<Tab size="xs" value="error_handler">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="error_handler"
|
||||
aiId="workspace-settings-error-handler"
|
||||
aiDescription="Error handler workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1">Error Handler</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="ai">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="ai"
|
||||
aiId="workspace-settings-ai"
|
||||
aiDescription="Windmill AI workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1">Windmill AI</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="windmill_lfs">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="windmill_lfs"
|
||||
aiId="workspace-settings-windmill-lfs"
|
||||
aiDescription="Object Storage (S3) workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Object Storage (S3)</div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="default_app">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="default_app"
|
||||
aiId="workspace-settings-default-app"
|
||||
aiDescription="Default app workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Default App </div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="encryption">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="encryption"
|
||||
aiId="workspace-settings-encryption"
|
||||
aiDescription="Encryption workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> Encryption </div>
|
||||
</Tab>
|
||||
<Tab size="xs" value="general">
|
||||
<Tab
|
||||
size="xs"
|
||||
value="general"
|
||||
aiId="workspace-settings-general"
|
||||
aiDescription="General workspace settings"
|
||||
>
|
||||
<div class="flex gap-2 items-center my-1"> General </div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
Reference in New Issue
Block a user