mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-22 08:03:38 +00:00
* feat(frontend): simplify custom skills workspace settings UI Collapse the "Custom skills" AI settings section into a single block. When no skills exist, show two side-by-side zones: a drag-and-drop folder dropzone (reusing FileInput) and a paste textarea whose add button appears only once content is entered. When skills exist, an "+ Add skills" dropdown offers "Import a folder of skills" (native picker) and "Paste a skill" (modal), above the skills list. Folder ingestion is shared by both the picker and the dropzone via processFolderFiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(frontend): custom skills detail modal + shared zod validation Rework the Custom skills settings: header Add-skills dropdown, per-row ellipsis menu (edit/delete), a Show more detail modal with a view/edit toggle (rendered markdown in read mode), accent Save gated by dirty detection and inline validation, and a folder-import conflict modal with per-skill overwrite toggles. Extract skill parsing/validation into a shared Zod-backed aiSkills module used by both the modal and the importer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): use Button for Show more; surface empty-body validation Address review: swap the raw <button> Show-more affordance for the design-system Button (per frontend component standards), and render the Save/inline-error block whenever editing an existing skill so clearing the body surfaces "body is required" instead of hiding both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(frontend): unit-test aiSkills; use themed border token Add aiSkills.test.ts covering parseSkillMd (BOM, CRLF, malformed YAML), validateSkill (code-point vs byte limits, name pattern), parseAndValidateSkill (nameOverride precedence) and buildSkillMd round-trip. Replace the hardcoded gray borders with the themed border-border-light token. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(frontend): cap custom skills list height and scroll Constrain the skills list to max-h-96 with overflow-y-auto so a large number of skills scrolls within the section instead of pushing the page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): manage-mode batch delete for custom skills Add a "Manage skills" button (shown only with more than one skill) that enters a multi-select mode: a checkbox per row plus a sticky select-all (tri-state) header, and a batch Delete gated on the selection with a confirmation. Manage mode auto-exits when the list drops to one skill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): destructive delete, subtle manage button, Esc exits manage mode Batch Delete uses the destructive accent variant, Manage skills uses the subtle variant, and Escape leaves manage mode (mirroring Done) unless a modal or menu is open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(frontend): add neutral 'info' type to ConfirmationModal; use for skill import ConfirmationModal only had 'danger' and 'reload' semantics, so a constructive confirmation like importing skills defaulted to danger (red warning + destructive button). Add a neutral 'info' type (blue Info icon, non-destructive accent confirm) and use it for the Import skills modal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
1.8 KiB
Svelte
81 lines
1.8 KiB
Svelte
<script lang="ts">
|
|
import { twMerge } from 'tailwind-merge'
|
|
import { enterpriseLicense } from '$lib/stores'
|
|
import EEOnly from '../EEOnly.svelte'
|
|
import Tooltip from '../Tooltip.svelte'
|
|
import { Button } from '../common'
|
|
import type { ButtonType } from '../common/button/model'
|
|
|
|
interface Props {
|
|
label?: string
|
|
description?: string
|
|
ee_only?: string
|
|
tooltip?: string
|
|
settingKey?: string
|
|
actionButton?: {
|
|
label: string
|
|
onclick: (values: Record<string, any>) => Promise<void>
|
|
variant?: ButtonType.Variant
|
|
}
|
|
values?: Record<string, any>
|
|
children: import('svelte').Snippet
|
|
headerAction?: import('svelte').Snippet
|
|
class?: string
|
|
}
|
|
|
|
let {
|
|
label,
|
|
description,
|
|
ee_only,
|
|
tooltip,
|
|
settingKey,
|
|
actionButton,
|
|
values,
|
|
children,
|
|
headerAction,
|
|
class: clazz
|
|
}: Props = $props()
|
|
</script>
|
|
|
|
<div
|
|
data-setting-key={settingKey}
|
|
class={twMerge('p-4 rounded-md bg-surface-tertiary shadow-sm flex flex-col gap-1', clazz)}
|
|
>
|
|
{#if label}
|
|
<div class="flex items-center justify-between gap-2 w-full">
|
|
<div class="flex gap-1 items-baseline">
|
|
<span class="text-emphasis font-semibold text-xs">{label}</span>
|
|
{#if ee_only != undefined && !$enterpriseLicense}
|
|
{#if ee_only != ''}
|
|
<EEOnly>{ee_only}</EEOnly>
|
|
{:else}
|
|
<EEOnly />
|
|
{/if}
|
|
{/if}
|
|
{#if tooltip}
|
|
<Tooltip>{tooltip}</Tooltip>
|
|
{/if}
|
|
</div>
|
|
{#if headerAction}
|
|
{@render headerAction()}
|
|
{:else if actionButton}
|
|
<Button
|
|
disabled={ee_only != undefined && !$enterpriseLicense}
|
|
variant={actionButton.variant ?? 'default'}
|
|
unifiedSize="sm"
|
|
onclick={async () => await actionButton?.onclick(values ?? {})}
|
|
>
|
|
{actionButton.label}
|
|
</Button>
|
|
{/if}
|
|
</div>
|
|
{#if description}
|
|
<span class="text-secondary font-normal text-xs">
|
|
{@html description}
|
|
</span>
|
|
{/if}
|
|
{/if}
|
|
|
|
{@render children()}
|
|
</div>
|