diff --git a/packages/client/src/api/hermes/skills.ts b/packages/client/src/api/hermes/skills.ts index b31fd104..2543d8d1 100644 --- a/packages/client/src/api/hermes/skills.ts +++ b/packages/client/src/api/hermes/skills.ts @@ -1,9 +1,17 @@ import { request } from '../client' +export type SkillSource = 'builtin' | 'hub' | 'local' + export interface SkillInfo { name: string description: string enabled?: boolean + source?: SkillSource + modified?: boolean + patchCount?: number + useCount?: number + viewCount?: number + pinned?: boolean } export interface SkillCategory { @@ -14,6 +22,7 @@ export interface SkillCategory { export interface SkillListResponse { categories: SkillCategory[] + archived: SkillInfo[] } export interface SkillFileEntry { @@ -31,9 +40,14 @@ export interface MemoryData { soul_mtime: number | null } -export async function fetchSkills(): Promise { +export interface SkillsData { + categories: SkillCategory[] + archived: SkillInfo[] +} + +export async function fetchSkills(): Promise { const res = await request('/api/hermes/skills') - return res.categories + return { categories: res.categories, archived: res.archived ?? [] } } export async function fetchSkillContent(skillPath: string): Promise { @@ -63,3 +77,10 @@ export async function toggleSkill(name: string, enabled: boolean): Promise body: JSON.stringify({ name, enabled }), }) } + +export async function pinSkillApi(name: string, pinned: boolean): Promise { + await request('/api/hermes/skills/pin', { + method: 'PUT', + body: JSON.stringify({ name, pinned }), + }) +} diff --git a/packages/client/src/components/hermes/skills/SkillDetail.vue b/packages/client/src/components/hermes/skills/SkillDetail.vue index 54b1262d..4d6588dd 100644 --- a/packages/client/src/components/hermes/skills/SkillDetail.vue +++ b/packages/client/src/components/hermes/skills/SkillDetail.vue @@ -1,14 +1,25 @@ @@ -77,6 +104,23 @@ watch(() => `${props.category}/${props.skill}`, loadSkill, { immediate: true }) {{ category }} / {{ skill }} +
+ + + + {{ viewCount }} + + + + {{ useCount }} + + + + {{ patchCount }} + +
{{ t('common.loading') }}
@@ -136,6 +180,8 @@ watch(() => `${props.category}/${props.skill}`, loadSkill, { immediate: true }) border-bottom: 1px solid $border-color; margin-bottom: 12px; font-size: 15px; + display: flex; + align-items: center; } .detail-category { @@ -153,6 +199,59 @@ watch(() => `${props.category}/${props.skill}`, loadSkill, { immediate: true }) font-weight: 600; } +.usage-stats { + display: flex; + align-items: center; + gap: 12px; + margin-left: auto; + padding-left: 12px; +} + +.usage-stat { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 13px; + font-weight: 500; + color: $text-secondary; + white-space: nowrap; + + svg { + opacity: 0.7; + } +} + +.pin-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid transparent; + background: none; + color: $text-muted; + cursor: pointer; + padding: 4px; + border-radius: 6px; + opacity: 0.5; + transition: all $transition-fast; + + &:hover { + opacity: 1; + color: $accent-primary; + background: rgba(var(--accent-primary-rgb), 0.08); + border-color: rgba(var(--accent-primary-rgb), 0.15); + } + + &.active { + opacity: 1; + color: $accent-primary; + } + + &:disabled { + cursor: wait; + opacity: 0.3; + } +} + .detail-loading { flex: 1; display: flex; diff --git a/packages/client/src/components/hermes/skills/SkillList.vue b/packages/client/src/components/hermes/skills/SkillList.vue index 18127764..82251ab1 100644 --- a/packages/client/src/components/hermes/skills/SkillList.vue +++ b/packages/client/src/components/hermes/skills/SkillList.vue @@ -1,235 +1,335 @@ diff --git a/packages/client/src/i18n/locales/de.ts b/packages/client/src/i18n/locales/de.ts index e34029dd..4187a3a7 100644 --- a/packages/client/src/i18n/locales/de.ts +++ b/packages/client/src/i18n/locales/de.ts @@ -220,7 +220,18 @@ jobTriggered: 'Job ausgelost', attachedFiles: 'Angehange Dateien', loadFailed: 'Laden der Fahigkeit fehlgeschlagen', fileLoadFailed: 'Laden der Datei fehlgeschlagen', + modified: 'Benutzerbearbeitet', + archived: 'Archiviert', + pinned: 'Angeheftet', + pin: 'Fahigkeit anheften', + unpin: 'Anheften aufheben', + pinFailed: 'Anheft-Status konnte nicht geandert werden', toggleFailed: 'Aktivieren/Deaktivieren der Fahigkeit fehlgeschlagen', + source: { + builtin: 'Integriert', + hub: 'Hub', + local: 'Lokal', + }, }, // Memory diff --git a/packages/client/src/i18n/locales/en.ts b/packages/client/src/i18n/locales/en.ts index 690a7076..981dee53 100644 --- a/packages/client/src/i18n/locales/en.ts +++ b/packages/client/src/i18n/locales/en.ts @@ -249,7 +249,18 @@ export default { attachedFiles: 'Attached Files', loadFailed: 'Failed to load skill', fileLoadFailed: 'Failed to load file', + modified: 'Modified', + archived: 'Archived', + pinned: 'Pinned', + pin: 'Pin skill', + unpin: 'Unpin skill', + pinFailed: 'Failed to change pin status', toggleFailed: 'Failed to toggle skill', + source: { + builtin: 'Builtin', + hub: 'Hub', + local: 'Local', + }, }, // Memory diff --git a/packages/client/src/i18n/locales/es.ts b/packages/client/src/i18n/locales/es.ts index eb0f10ba..713700ad 100644 --- a/packages/client/src/i18n/locales/es.ts +++ b/packages/client/src/i18n/locales/es.ts @@ -220,7 +220,18 @@ jobTriggered: 'Job ejecutado', attachedFiles: 'Archivos adjuntos', loadFailed: 'Error al cargar la habilidad', fileLoadFailed: 'Error al cargar el archivo', + modified: 'Modificado por el usuario', + archived: 'Archivado', + pinned: 'Fijado', + pin: 'Fijar habilidad', + unpin: 'Desfijar habilidad', + pinFailed: 'Error al cambiar estado de fijacion', toggleFailed: 'Error al activar/desactivar la habilidad', + source: { + builtin: 'Integrado', + hub: 'Hub', + local: 'Local', + }, }, // Memory diff --git a/packages/client/src/i18n/locales/fr.ts b/packages/client/src/i18n/locales/fr.ts index 61de30ac..6883c4bc 100644 --- a/packages/client/src/i18n/locales/fr.ts +++ b/packages/client/src/i18n/locales/fr.ts @@ -220,7 +220,18 @@ jobTriggered: 'Job declenche', attachedFiles: 'Fichiers joints', loadFailed: 'Echec du chargement de la competence', fileLoadFailed: 'Echec du chargement du fichier', + modified: "Modifi\u00e9 par l'utilisateur", + archived: 'Archivé', + pinned: 'Épinglé', + pin: 'Épingler la compétence', + unpin: 'Désépingler la compétence', + pinFailed: "Impossible de changer le statut d'épinglage", toggleFailed: 'Echec de l\'activation/desactivation de la competence', + source: { + builtin: 'Intégré', + hub: 'Hub', + local: 'Local', + }, }, // Memory diff --git a/packages/client/src/i18n/locales/ja.ts b/packages/client/src/i18n/locales/ja.ts index 6e4a93d8..0b3dce19 100644 --- a/packages/client/src/i18n/locales/ja.ts +++ b/packages/client/src/i18n/locales/ja.ts @@ -220,7 +220,18 @@ export default { attachedFiles: '添付ファイル', loadFailed: 'スキルの読み込みに失敗しました', fileLoadFailed: 'ファイルの読み込みに失敗しました', + modified: 'ユーザー変更あり', + archived: 'アーカイブ済み', + pinned: 'ピン留め', + pin: 'スキルをピン留め', + unpin: 'ピン留めを解除', + pinFailed: 'ピン留め状態の変更に失敗しました', toggleFailed: 'スキルの切り替えに失敗しました', + source: { + builtin: '組み込み', + hub: 'Hub', + local: 'ローカル', + }, }, // メモリ diff --git a/packages/client/src/i18n/locales/ko.ts b/packages/client/src/i18n/locales/ko.ts index 54c25ae4..c3ed72cf 100644 --- a/packages/client/src/i18n/locales/ko.ts +++ b/packages/client/src/i18n/locales/ko.ts @@ -220,7 +220,18 @@ export default { attachedFiles: '첨부 파일', loadFailed: '스킬을 불러오지 못했습니다', fileLoadFailed: '파일을 불러오지 못했습니다', + modified: '사용자 수정됨', + archived: '보관됨', + pinned: '고정됨', + pin: '스킬 고정', + unpin: '고정 해제', + pinFailed: '고정 상태 변경 실패', toggleFailed: '스킬 상태를 전환하지 못했습니다', + source: { + builtin: '내장', + hub: 'Hub', + local: '로컬', + }, }, // 메모리 diff --git a/packages/client/src/i18n/locales/pt.ts b/packages/client/src/i18n/locales/pt.ts index 99c21080..1d826579 100644 --- a/packages/client/src/i18n/locales/pt.ts +++ b/packages/client/src/i18n/locales/pt.ts @@ -220,7 +220,18 @@ jobTriggered: 'Job acionado', attachedFiles: 'Arquivos anexados', loadFailed: 'Falha ao carregar a habilidade', fileLoadFailed: 'Falha ao carregar o arquivo', + modified: 'Modificado pelo usuário', + archived: 'Arquivado', + pinned: 'Fixado', + pin: 'Fixar habilidade', + unpin: 'Desfixar habilidade', + pinFailed: 'Falha ao alterar estado de fixacao', toggleFailed: 'Falha ao ativar/desativar a habilidade', + source: { + builtin: 'Integrado', + hub: 'Hub', + local: 'Local', + }, }, // Memory diff --git a/packages/client/src/i18n/locales/zh.ts b/packages/client/src/i18n/locales/zh.ts index 6c26cf39..8778fca5 100644 --- a/packages/client/src/i18n/locales/zh.ts +++ b/packages/client/src/i18n/locales/zh.ts @@ -249,7 +249,18 @@ export default { attachedFiles: '附件文件', loadFailed: '加载技能失败', fileLoadFailed: '加载文件失败', + modified: '用户已修改', + archived: '已归档', + pinned: '已置顶', + pin: '置顶技能', + unpin: '取消置顶', + pinFailed: '更改置顶状态失败', toggleFailed: '切换技能状态失败', + source: { + builtin: '内置', + hub: 'Hub 安装', + local: '本地安装', + }, }, // 记忆 diff --git a/packages/client/src/views/hermes/SkillsView.vue b/packages/client/src/views/hermes/SkillsView.vue index 544122d5..a1d2e260 100644 --- a/packages/client/src/views/hermes/SkillsView.vue +++ b/packages/client/src/views/hermes/SkillsView.vue @@ -1,20 +1,33 @@