refac
This commit is contained in:
@@ -97,133 +97,114 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
|
||||
<div class="mx-auto w-full md:px-0 h-full">
|
||||
<form class=" flex flex-col max-h-[100dvh] h-full" on:submit|preventDefault={submitHandler}>
|
||||
<div class="flex flex-col flex-1 overflow-auto h-0 rounded-lg">
|
||||
<div class="w-full mb-2 flex flex-col gap-0.5">
|
||||
<div class="flex w-full items-center">
|
||||
<div class=" shrink-0 mr-2">
|
||||
<Tooltip content={$i18n.t('Back')}>
|
||||
<button
|
||||
class="w-full text-left text-sm py-1.5 px-1 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
|
||||
aria-label={$i18n.t('Back')}
|
||||
on:click={() => {
|
||||
goto('/workspace/skills');
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<ChevronLeft strokeWidth="2.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<div class="flex h-full w-full min-w-0 flex-col overflow-hidden">
|
||||
<form class="flex h-full min-h-0 min-w-0 flex-col" on:submit|preventDefault={submitHandler}>
|
||||
<button
|
||||
class="mb-1 flex h-6 w-fit items-center gap-1 rounded-md px-0.5 text-xs text-gray-400 transition-colors duration-75 hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
goto('/workspace/skills');
|
||||
}}
|
||||
>
|
||||
<ChevronLeft className="size-3" strokeWidth="2" />
|
||||
<span>{$i18n.t('Back')}</span>
|
||||
</button>
|
||||
|
||||
<div class="flex shrink-0 items-start gap-2 pb-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Tooltip content={$i18n.t('e.g. Code Review Guidelines')} placement="top-start">
|
||||
<input
|
||||
class="w-full bg-transparent text-sm outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Skill Name')}
|
||||
aria-label={$i18n.t('Skill Name')}
|
||||
bind:value={name}
|
||||
required
|
||||
{disabled}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<div class="mt-0.5 flex min-w-0 items-center gap-2 text-xs text-gray-500">
|
||||
{#if edit}
|
||||
<div class="shrink-0 truncate font-mono" title={id}>
|
||||
{id}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<Tooltip content={$i18n.t('e.g. Code Review Guidelines')} placement="top-start">
|
||||
<input
|
||||
class="w-full text-2xl bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Skill Name')}
|
||||
aria-label={$i18n.t('Skill Name')}
|
||||
bind:value={name}
|
||||
required
|
||||
{disabled}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="self-center shrink-0">
|
||||
{#if !disabled}
|
||||
<AccessButton on:click={() => (showAccessControlModal = true)} />
|
||||
{:else}
|
||||
<span
|
||||
class="text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded-full"
|
||||
>{$i18n.t('Read Only')}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex gap-2 px-1 items-center">
|
||||
{#if edit}
|
||||
<div class="text-sm text-gray-500 shrink-0">
|
||||
{id}
|
||||
</div>
|
||||
{:else}
|
||||
<Tooltip
|
||||
className="w-full"
|
||||
content={$i18n.t('e.g. code-review-guidelines')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class="w-full text-sm disabled:text-gray-500 bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Skill ID')}
|
||||
aria-label={$i18n.t('Skill ID')}
|
||||
bind:value={id}
|
||||
required
|
||||
disabled={edit}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{:else}
|
||||
<Tooltip
|
||||
className="w-full self-center items-center flex"
|
||||
content={$i18n.t('e.g. Step-by-step instructions for code reviews')}
|
||||
className="min-w-[8rem] flex-1"
|
||||
content={$i18n.t('e.g. code-review-guidelines')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class="w-full text-sm bg-transparent outline-hidden"
|
||||
class="w-full bg-transparent font-mono outline-hidden disabled:text-gray-500"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Skill Description')}
|
||||
aria-label={$i18n.t('Skill Description')}
|
||||
bind:value={description}
|
||||
{disabled}
|
||||
placeholder={$i18n.t('Skill ID')}
|
||||
aria-label={$i18n.t('Skill ID')}
|
||||
bind:value={id}
|
||||
required
|
||||
disabled={edit}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
|
||||
<div class="h-full flex flex-col">
|
||||
<div
|
||||
class="bg-gray-50 dark:bg-gray-900 rounded-xl border border-gray-100/50 dark:border-gray-850/50 flex-1 min-h-0 overflow-hidden flex flex-col"
|
||||
>
|
||||
{#if disabled}
|
||||
<div class="px-4 py-3 overflow-y-auto flex-1">
|
||||
<pre class="text-xs whitespace-pre-wrap font-mono">{content}</pre>
|
||||
</div>
|
||||
{:else}
|
||||
<textarea
|
||||
class="w-full flex-1 text-xs bg-transparent outline-hidden resize-none font-mono px-4 py-3"
|
||||
bind:value={content}
|
||||
on:input={handleContentInput}
|
||||
placeholder={$i18n.t('Enter skill instructions in markdown...')}
|
||||
aria-label={$i18n.t('Skill Instructions')}
|
||||
required
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pb-3 flex justify-end">
|
||||
{#if !disabled}
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex items-center gap-2 whitespace-nowrap"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{$i18n.t(edit ? 'Save' : 'Save & Create')}
|
||||
{#if loading}
|
||||
<span class="shrink-0">
|
||||
<Spinner />
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<Tooltip
|
||||
className="flex min-w-0 flex-1 items-center"
|
||||
content={$i18n.t('e.g. Step-by-step instructions for code reviews')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class="w-full bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Skill Description')}
|
||||
aria-label={$i18n.t('Skill Description')}
|
||||
bind:value={description}
|
||||
{disabled}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-1 pr-0.5">
|
||||
{#if !disabled}
|
||||
<AccessButton on:click={() => (showAccessControlModal = true)} />
|
||||
{:else}
|
||||
<span class="rounded-lg bg-gray-100 px-2 py-1 text-xs text-gray-500 dark:bg-gray-850">
|
||||
{$i18n.t('Read Only')}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 flex-1 overflow-hidden rounded-lg bg-gray-50/60 dark:bg-white/[0.03]">
|
||||
{#if disabled}
|
||||
<div class="h-full overflow-y-auto px-3 py-2">
|
||||
<pre class="whitespace-pre-wrap font-mono text-[11px] leading-relaxed">{content}</pre>
|
||||
</div>
|
||||
{:else}
|
||||
<textarea
|
||||
class="h-full w-full resize-none bg-transparent px-3 py-2 font-mono text-[11px] leading-relaxed outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-600"
|
||||
bind:value={content}
|
||||
on:input={handleContentInput}
|
||||
placeholder={$i18n.t('Enter skill instructions in markdown...')}
|
||||
aria-label={$i18n.t('Skill Instructions')}
|
||||
required
|
||||
></textarea>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if !disabled}
|
||||
<div class="flex shrink-0 justify-end py-2">
|
||||
<button
|
||||
class="flex h-7 items-center gap-1.5 rounded-lg bg-gray-900 px-2.5 text-xs text-white transition hover:bg-black disabled:opacity-60 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-white"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{$i18n.t(edit ? 'Save' : 'Save & Create')}
|
||||
{#if loading}
|
||||
<Spinner className="size-3" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import AccessButton from '$lib/components/common/AccessButton.svelte';
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
import AccessControlModal from '../common/AccessControlModal.svelte';
|
||||
|
||||
let formElement = null;
|
||||
@@ -25,7 +26,7 @@
|
||||
export let edit = false;
|
||||
export let clone = false;
|
||||
|
||||
export let onSave = () => {};
|
||||
export let onSave = /** @param {any} _value */ async (_value) => {};
|
||||
|
||||
export let id = '';
|
||||
export let name = '';
|
||||
@@ -159,13 +160,17 @@ class Tools:
|
||||
|
||||
const saveHandler = async () => {
|
||||
loading = true;
|
||||
onSave({
|
||||
id,
|
||||
name,
|
||||
meta,
|
||||
content,
|
||||
access_grants: accessGrants
|
||||
});
|
||||
try {
|
||||
await onSave({
|
||||
id,
|
||||
name,
|
||||
meta,
|
||||
content,
|
||||
access_grants: accessGrants
|
||||
});
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
@@ -207,143 +212,142 @@ class Tools:
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" flex flex-col justify-between w-full min-w-0 overflow-y-auto overflow-x-hidden h-full">
|
||||
<div class="mx-auto w-full min-w-0 md:px-0 h-full">
|
||||
<form
|
||||
bind:this={formElement}
|
||||
class=" flex flex-col min-w-0 max-h-[100dvh] h-full"
|
||||
on:submit|preventDefault={() => {
|
||||
if (edit) {
|
||||
submitHandler();
|
||||
} else {
|
||||
showConfirm = true;
|
||||
}
|
||||
<div class="flex h-full w-full min-w-0 flex-col overflow-hidden">
|
||||
<form
|
||||
bind:this={formElement}
|
||||
class="flex h-full min-h-0 min-w-0 flex-col"
|
||||
on:submit|preventDefault={() => {
|
||||
if (edit) {
|
||||
submitHandler();
|
||||
} else {
|
||||
showConfirm = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class="mb-1 flex h-6 w-fit items-center gap-1 rounded-md px-0.5 text-xs text-gray-400 transition-colors duration-75 hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
goto('/workspace/tools');
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col flex-1 min-w-0 overflow-auto h-0 rounded-lg">
|
||||
<div class="w-full mb-2 flex flex-col gap-0.5">
|
||||
<div class="flex w-full min-w-0 items-center">
|
||||
<div class=" shrink-0 mr-2">
|
||||
<Tooltip content={$i18n.t('Back')}>
|
||||
<button
|
||||
class="w-full text-left text-sm py-1.5 px-1 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
|
||||
aria-label={$i18n.t('Back')}
|
||||
on:click={() => {
|
||||
goto('/workspace/tools');
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<ChevronLeft strokeWidth="2.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<ChevronLeft className="size-3" strokeWidth="2" />
|
||||
<span>{$i18n.t('Back')}</span>
|
||||
</button>
|
||||
|
||||
<div class="flex shrink-0 items-start gap-2 pb-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Tooltip content={$i18n.t('e.g. My Tools')} placement="top-start">
|
||||
<input
|
||||
class="w-full bg-transparent text-sm outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Tool Name')}
|
||||
aria-label={$i18n.t('Tool Name')}
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<div class="mt-0.5 flex min-w-0 items-center gap-2 text-xs text-gray-500">
|
||||
{#if edit}
|
||||
<div class="shrink-0 truncate font-mono" title={id}>
|
||||
{id}
|
||||
</div>
|
||||
|
||||
<div class="flex-1 min-w-0">
|
||||
<Tooltip content={$i18n.t('e.g. My Tools')} placement="top-start">
|
||||
<input
|
||||
class="w-full text-2xl bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Tool Name')}
|
||||
aria-label={$i18n.t('Tool Name')}
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class="self-center shrink-0">
|
||||
<AccessButton
|
||||
on:click={() => {
|
||||
showAccessControlModal = true;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex min-w-0 gap-2 px-1 items-center">
|
||||
{#if edit}
|
||||
<div class="text-sm text-gray-500 shrink-0">
|
||||
{id}
|
||||
</div>
|
||||
{:else}
|
||||
<Tooltip className="w-full" content={$i18n.t('e.g. my_tools')} placement="top-start">
|
||||
<input
|
||||
class="w-full text-sm disabled:text-gray-500 bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Tool ID')}
|
||||
aria-label={$i18n.t('Tool ID')}
|
||||
bind:value={id}
|
||||
required
|
||||
disabled={edit}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{:else}
|
||||
<Tooltip
|
||||
className="w-full self-center items-center flex"
|
||||
content={$i18n.t('e.g. Tools for performing various operations')}
|
||||
className="min-w-[8rem] flex-1"
|
||||
content={$i18n.t('e.g. my_tools')}
|
||||
placement="top-start"
|
||||
>
|
||||
<input
|
||||
class="w-full text-sm bg-transparent outline-hidden"
|
||||
class="w-full bg-transparent font-mono outline-hidden disabled:text-gray-500"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Tool Description')}
|
||||
aria-label={$i18n.t('Tool Description')}
|
||||
bind:value={meta.description}
|
||||
placeholder={$i18n.t('Tool ID')}
|
||||
aria-label={$i18n.t('Tool ID')}
|
||||
bind:value={id}
|
||||
required
|
||||
disabled={edit}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mb-2 flex-1 min-w-0 overflow-auto h-0 rounded-lg">
|
||||
<CodeEditor
|
||||
bind:this={codeEditor}
|
||||
value={content}
|
||||
lang="python"
|
||||
{boilerplate}
|
||||
onChange={(e) => {
|
||||
_content = e;
|
||||
if (!edit) {
|
||||
const fm = extractFrontmatter(e);
|
||||
if (fm.title && !name) {
|
||||
name = formatSkillName(fm.title);
|
||||
id = nameToId(fm.title);
|
||||
}
|
||||
if (fm.description && !meta.description) {
|
||||
meta = { ...meta, description: fm.description };
|
||||
}
|
||||
}
|
||||
}}
|
||||
onSave={async () => {
|
||||
if (formElement) {
|
||||
formElement.requestSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pb-3 flex justify-between">
|
||||
<div class="flex-1 pr-3">
|
||||
<div class="text-xs text-gray-500 line-clamp-2">
|
||||
<span class=" font-normal dark:text-gray-200">{$i18n.t('Warning:')}</span>
|
||||
{$i18n.t('Tools are a function calling system with arbitrary code execution')} <br />—
|
||||
<span class=" font-normal dark:text-gray-400"
|
||||
>{$i18n.t(`don't install random tools from sources you don't trust.`)}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
|
||||
type="submit"
|
||||
<Tooltip
|
||||
className="flex min-w-0 flex-1 items-center"
|
||||
content={$i18n.t('e.g. Tools for performing various operations')}
|
||||
placement="top-start"
|
||||
>
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
<input
|
||||
class="w-full bg-transparent outline-hidden"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Tool Description')}
|
||||
aria-label={$i18n.t('Tool Description')}
|
||||
bind:value={meta.description}
|
||||
required
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-1 pr-0.5">
|
||||
<AccessButton
|
||||
on:click={() => {
|
||||
showAccessControlModal = true;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="min-h-0 flex-1 overflow-hidden rounded-lg">
|
||||
<CodeEditor
|
||||
bind:this={codeEditor}
|
||||
value={content}
|
||||
lang="python"
|
||||
{boilerplate}
|
||||
className="text-[11px]"
|
||||
onChange={(e) => {
|
||||
_content = e;
|
||||
if (!edit) {
|
||||
const fm = extractFrontmatter(e);
|
||||
if (fm.title && !name) {
|
||||
name = formatSkillName(fm.title);
|
||||
id = nameToId(fm.title);
|
||||
}
|
||||
if (fm.description && !meta.description) {
|
||||
meta = { ...meta, description: fm.description };
|
||||
}
|
||||
}
|
||||
}}
|
||||
onSave={async () => {
|
||||
if (formElement) {
|
||||
formElement.requestSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 py-2 text-xs text-gray-500">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<span class="font-normal dark:text-gray-200">{$i18n.t('Warning:')}</span>
|
||||
{$i18n.t('Tools can execute arbitrary code.')}
|
||||
<span class="font-normal dark:text-gray-400">
|
||||
{$i18n.t('Only install tools from sources you trust.')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="flex h-7 shrink-0 items-center gap-1.5 rounded-lg bg-gray-900 px-2.5 text-xs text-white transition hover:bg-black disabled:opacity-60 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-white"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{$i18n.t(edit ? 'Save' : 'Save & Create')}
|
||||
{#if loading}
|
||||
<Spinner className="size-3" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
|
||||
Reference in New Issue
Block a user