feat: 重构 Skill 管理与编辑工作台

- 统一列表、分类、新建与详情页的产品交互

- 提供 Markdown 实时编辑、源码与脚本资源工作台

- 实现能力自动保存、导入导出及完整状态反馈
This commit is contained in:
2026-07-27 18:54:57 +08:00
parent 2a9e882ac6
commit 0dc5c3ca55
78 changed files with 16413 additions and 799 deletions

View File

@@ -0,0 +1,52 @@
import type { SkillInfo } from './types';
import { syncSkillMarkdownFrontmatter } from './skill-markdown';
export type SkillCreateIntent = 'DRAFT' | 'PUBLISH';
/** Build a hidden, standards-compliant canonical name for a new Skill. */
export function createSkillCanonicalName(
displayName: string,
suffix = createRandomSuffix(),
): string {
const slug = displayName
.normalize('NFKD')
.toLowerCase()
.replaceAll(/[^a-z0-9]+/g, '-')
.replaceAll(/^-+|-+$/g, '')
.slice(0, 48)
.replaceAll(/-+$/g, '');
const prefix = slug || 'skill';
return `${prefix}-${suffix}`.slice(0, 64).replaceAll(/-+$/g, '');
}
/** Create the minimal valid package accepted by the Skill draft endpoint. */
export function buildInitialSkillDraft(input: {
categoryId?: number | string;
displayName: string;
visibilityScope: string;
}): SkillInfo {
const displayName = input.displayName.trim();
const name = createSkillCanonicalName(displayName);
const content = syncSkillMarkdownFrontmatter(
'# Instructions\n\n',
name,
displayName,
);
return {
categoryId: input.categoryId || undefined,
description: displayName,
displayName,
enabled: true,
name,
publishStatus: 'DRAFT',
skillContent: content,
visibilityScope: input.visibilityScope,
};
}
function createRandomSuffix() {
const uuid = globalThis.crypto?.randomUUID?.().replaceAll('-', '');
if (uuid) return uuid.slice(0, 10);
return Math.random().toString(36).slice(2, 12).padEnd(10, '0');
}