feat: enrich Codex skill metadata display
This commit is contained in:
116
public/app.js
116
public/app.js
@@ -3587,6 +3587,120 @@
|
||||
}, ttl);
|
||||
}
|
||||
|
||||
function normalizeMentionList(value) {
|
||||
return Array.isArray(value) ? value.filter((item) => item && typeof item === 'object') : [];
|
||||
}
|
||||
|
||||
function escapeHtmlAttr(value) {
|
||||
return String(value || '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function shortPreviewText(text, maxLength = 140) {
|
||||
const normalized = String(text || '').trim().replace(/\s+/g, ' ');
|
||||
if (!normalized) return '';
|
||||
return normalized.length > maxLength ? `${normalized.slice(0, Math.max(0, maxLength - 3))}...` : normalized;
|
||||
}
|
||||
|
||||
function mentionDependencyStateLabel(state) {
|
||||
return state === 'configured' ? '已配置' : state === 'declared' ? '未配置' : '';
|
||||
}
|
||||
|
||||
function mentionDependencyLabel(dep) {
|
||||
const name = dep?.value || dep?.name || dep?.server || '';
|
||||
const state = mentionDependencyStateLabel(dep?.state);
|
||||
return state ? `${name} · ${state}` : name;
|
||||
}
|
||||
|
||||
function buildMentionTooltip(mention) {
|
||||
const lines = [];
|
||||
const title = mention.title || mention.name || mention.label || '';
|
||||
const description = shortPreviewText(mention.description || '');
|
||||
if (title) lines.push(title);
|
||||
if (description) lines.push(description);
|
||||
if (mention.defaultPromptPreview) lines.push(`默认提示: ${shortPreviewText(mention.defaultPromptPreview, 180)}`);
|
||||
const dependencies = Array.isArray(mention.dependencies) ? mention.dependencies : [];
|
||||
for (const dep of dependencies.slice(0, 4)) {
|
||||
const label = mentionDependencyLabel(dep);
|
||||
if (label) lines.push(`MCP: ${label}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function createMentionChip(mention) {
|
||||
const chip = document.createElement('div');
|
||||
const kind = String(mention.kind || '').trim() || 'mention';
|
||||
chip.className = `msg-mention-chip kind-${kind}`;
|
||||
if (mention.brandColor) chip.style.setProperty('--mention-accent', mention.brandColor);
|
||||
const tooltip = buildMentionTooltip(mention);
|
||||
if (tooltip) chip.title = tooltip;
|
||||
|
||||
if (kind === 'skill') {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'msg-mention-badge';
|
||||
if (mention.iconSmall && /^https?:\/\//i.test(String(mention.iconSmall))) {
|
||||
const img = document.createElement('img');
|
||||
img.src = mention.iconSmall;
|
||||
img.alt = mention.title || mention.name || 'skill';
|
||||
img.loading = 'lazy';
|
||||
badge.appendChild(img);
|
||||
} else {
|
||||
badge.textContent = 'Skill';
|
||||
}
|
||||
chip.appendChild(badge);
|
||||
}
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'msg-mention-body';
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.className = 'msg-mention-title';
|
||||
if (kind === 'skill') {
|
||||
title.textContent = mention.title || mention.name || mention.label || '';
|
||||
} else if (kind === 'prompt') {
|
||||
title.textContent = mention.title || mention.label || mention.name || '';
|
||||
} else {
|
||||
title.textContent = mention.label || mention.name || '';
|
||||
}
|
||||
body.appendChild(title);
|
||||
|
||||
const descriptionText = mention.description || (kind === 'prompt' ? 'Prompt 模板' : '');
|
||||
if (descriptionText) {
|
||||
const desc = document.createElement('div');
|
||||
desc.className = 'msg-mention-desc';
|
||||
desc.textContent = shortPreviewText(descriptionText, kind === 'skill' ? 92 : 72);
|
||||
body.appendChild(desc);
|
||||
}
|
||||
|
||||
const dependencies = Array.isArray(mention.dependencies) ? mention.dependencies : [];
|
||||
if (dependencies.length > 0) {
|
||||
const meta = document.createElement('div');
|
||||
meta.className = 'msg-mention-meta';
|
||||
dependencies.slice(0, 2).forEach((dep) => {
|
||||
const pill = document.createElement('span');
|
||||
pill.className = `msg-mention-pill state-${dep.state || 'declared'}`;
|
||||
pill.textContent = mentionDependencyLabel(dep);
|
||||
meta.appendChild(pill);
|
||||
});
|
||||
body.appendChild(meta);
|
||||
}
|
||||
|
||||
chip.appendChild(body);
|
||||
return chip;
|
||||
}
|
||||
|
||||
function renderComposerMentionsStrip(meta) {
|
||||
const mentions = normalizeMentionList(meta?.composerMentions);
|
||||
if (mentions.length === 0) return null;
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'msg-mentions';
|
||||
mentions.forEach((mention) => wrap.appendChild(createMentionChip(mention)));
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function createMsgElement(role, content, attachments = [], meta = {}) {
|
||||
const div = document.createElement('div');
|
||||
const isCrossConversation = !!meta.crossConversation;
|
||||
@@ -3705,6 +3819,8 @@
|
||||
if (attachments.length > 0) {
|
||||
bubble.insertAdjacentHTML('beforeend', renderAttachmentPreviews(attachments));
|
||||
}
|
||||
const mentionsStrip = renderComposerMentionsStrip(meta);
|
||||
if (mentionsStrip) bubble.appendChild(mentionsStrip);
|
||||
} else {
|
||||
renderAssistantContent(bubble, content);
|
||||
if (attachments.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user