forked from bai/curriculum-project-hub
feat(hub): org-scoped agent role/skill folder tree (ADR-0028)
Add a shared transparent OrganizationAgentConfigFolder tree for grouping agent roles and skills in the admin UI without affecting identity, bindings, run loading, or slash commands.
This commit is contained in:
@@ -3,18 +3,24 @@
|
||||
import { api } from '$lib/api';
|
||||
import { fmtDate } from '$lib/format';
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
import SelectField from '$lib/components/SelectField.svelte';
|
||||
import { toastError, toastSuccess } from '$lib/toast';
|
||||
|
||||
let {
|
||||
slug,
|
||||
skill,
|
||||
folderItems,
|
||||
oninstalled,
|
||||
ondisabled,
|
||||
onfolderchanged,
|
||||
}: {
|
||||
slug: string;
|
||||
skill: AgentSkillRow;
|
||||
/** ADR-0028 folder choices ('' = 未分类); transparent grouping only */
|
||||
folderItems: { value: string; label: string }[];
|
||||
oninstalled: (result: { id: string; name: string; contentDigest: string }) => void;
|
||||
ondisabled: (name: string) => void;
|
||||
onfolderchanged: (name: string, folderId: string | null) => void;
|
||||
} = $props();
|
||||
|
||||
type FileNode = { path: string; content: string };
|
||||
@@ -28,6 +34,8 @@
|
||||
let dirty = $state(false);
|
||||
let newFilePath = $state('');
|
||||
let showNewFile = $state(false);
|
||||
let folderValue = $state(skill.folderId ?? '');
|
||||
let savingFolder = $state(false);
|
||||
|
||||
const selectedFile = $derived(files.find((f) => f.path === selectedPath) ?? null);
|
||||
const hasManifest = $derived(files.some((f) => f.path === 'SKILL.md'));
|
||||
@@ -152,6 +160,24 @@
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// ADR-0028: folder assignment is a label-class change — instant-apply, no
|
||||
// session archival, independent of the content save button.
|
||||
async function saveFolder(next: string) {
|
||||
const folderId = next === '' ? null : next;
|
||||
if (folderId === skill.folderId) return;
|
||||
savingFolder = true;
|
||||
try {
|
||||
await api.setAgentSkillFolder(slug, skill.name, folderId);
|
||||
onfolderchanged(skill.name, folderId);
|
||||
toastSuccess('已更新所属文件夹');
|
||||
} catch (err) {
|
||||
folderValue = skill.folderId ?? '';
|
||||
toastError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
savingFolder = false;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFrontmatter(content: string, key: string, value: string): string {
|
||||
const regex = new RegExp(`^(${key}:\\s*)(.*?)(\\s*)$`, 'm');
|
||||
if (regex.test(content)) {
|
||||
@@ -178,6 +204,12 @@
|
||||
{#if skill.disabledAt}
|
||||
<span class="saas-badge-error">已禁用</span>
|
||||
{/if}
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<span class="text-xs text-surface-600">文件夹</span>
|
||||
<div class="w-44">
|
||||
<SelectField items={folderItems} bind:value={folderValue} disabled={savingFolder} onchange={saveFolder} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
|
||||
Reference in New Issue
Block a user