diff --git a/public/app.js b/public/app.js index 98aca11..8bff990 100644 --- a/public/app.js +++ b/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, '>'); + } + + 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) { diff --git a/public/index.html b/public/index.html index 8c72ae9..fc64209 100644 --- a/public/index.html +++ b/public/index.html @@ -14,7 +14,7 @@ document.documentElement.dataset.dividerTime = dividerTime; })(); - +
@@ -150,6 +150,6 @@ - +