make more things available

This commit is contained in:
centdix
2025-05-28 10:52:29 +02:00
parent 4da5255569
commit f13b8f3dcd
16 changed files with 524 additions and 232 deletions
@@ -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"
+32 -15
View File
@@ -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&nbsp;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>