mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
feat(ui): agent integration of SkillIcon + SkillIconPicker
This commit is contained in:
parent
efb91796fb
commit
8ce7e7d26d
@ -42,7 +42,7 @@
|
||||
<select v-model="form.agentId" class="form-input">
|
||||
<option :value="null">{{ t('channels.placeholders.selectAgent') }}</option>
|
||||
<option v-for="agent in agents" :key="agent.id" :value="agent.id">
|
||||
{{ agent.icon || '🤖' }} {{ agent.name }}
|
||||
{{ plainTextIcon(agent.icon) }} {{ agent.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -440,6 +440,7 @@ import { useWeixinQrcodePoll } from '@/composables/channels/useWeixinQrcodePoll'
|
||||
import { useWecomBotAuth } from '@/composables/channels/useWecomBotAuth'
|
||||
import { useFeishuAppRegister } from '@/composables/channels/useFeishuAppRegister'
|
||||
import { useDingTalkAppRegister } from '@/composables/channels/useDingTalkAppRegister'
|
||||
import { plainTextIcon } from '@/composables/usePixelarticons'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
|
||||
@ -54,6 +54,16 @@ export type ParsedIcon =
|
||||
| { kind: 'emoji'; value: string }
|
||||
| { kind: 'empty' }
|
||||
|
||||
/**
|
||||
* Plain-text icon glyph for hosts that can't render components — chiefly
|
||||
* `<select><option>`. Returns the emoji as-is, falls back for `pi:` /
|
||||
* URL icons (which would otherwise render as literal `pi:robot` text).
|
||||
*/
|
||||
export function plainTextIcon(value: string | null | undefined, fallback = '🤖'): string {
|
||||
const parsed = parseIconValue(value)
|
||||
return parsed.kind === 'emoji' ? parsed.value : fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a stored icon string into a renderable shape. Returns
|
||||
* {@code kind: 'empty'} for null/blank so callers can branch on a
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<select v-model="selectedAgentId" class="agent-select" @change="onAgentChange">
|
||||
<option value="" disabled>{{ t('agentContext.selectAgent') }}</option>
|
||||
<option v-for="agent in agents" :key="agent.id" :value="agent.id">
|
||||
{{ agent.icon || '🤖' }} {{ agent.name }}
|
||||
{{ plainTextIcon(agent.icon) }} {{ agent.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -209,6 +209,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { agentApi, agentContextApi } from '@/api/index'
|
||||
import type { Agent, WorkspaceFile } from '@/types/index'
|
||||
import { useMarkdownRenderer } from '@/composables/useMarkdownRenderer'
|
||||
import { plainTextIcon } from '@/composables/usePixelarticons'
|
||||
|
||||
const { renderMarkdown } = useMarkdownRenderer()
|
||||
|
||||
|
||||
@ -42,7 +42,9 @@
|
||||
:class="{ 'agent-card--disabled': !agent.enabled }"
|
||||
>
|
||||
<div class="agent-card__header">
|
||||
<span class="agent-card__icon">{{ agent.icon || '🤖' }}</span>
|
||||
<span class="agent-card__icon">
|
||||
<SkillIcon :value="agent.icon" :size="36" :fallback="'🤖'" />
|
||||
</span>
|
||||
<label class="toggle-switch toggle-switch--sm">
|
||||
<input type="checkbox" :checked="agent.enabled" @change="toggleAgent(agent)" />
|
||||
<span class="toggle-slider"></span>
|
||||
@ -137,6 +139,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared icon picker — opened from the Basic tab's [Pick icon] button. -->
|
||||
<SkillIconPicker
|
||||
v-model:visible="iconPickerVisible"
|
||||
:model-value="form.icon"
|
||||
@apply="(v: string) => (form.icon = v)"
|
||||
/>
|
||||
|
||||
<!-- Create/Edit Modal -->
|
||||
<div v-if="showModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
@ -176,7 +185,13 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ t('agents.fields.icon') }}</label>
|
||||
<input v-model="form.icon" class="form-input" :placeholder="t('agents.placeholders.icon')" />
|
||||
<button type="button" class="icon-picker-trigger" @click="iconPickerVisible = true">
|
||||
<SkillIcon :value="form.icon" :size="24" :fallback="'🤖'" />
|
||||
<span class="icon-picker-trigger__label">
|
||||
{{ form.icon || t('common.iconPicker.none') }}
|
||||
</span>
|
||||
<span class="icon-picker-trigger__action">{{ t('common.iconPicker.pickerOpen') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ t('agents.fields.type') }}</label>
|
||||
@ -233,7 +248,7 @@
|
||||
:class="{ selected: selectedSkillIds.includes(skill.id) }"
|
||||
>
|
||||
<input type="checkbox" :value="skill.id" v-model="selectedSkillIds" class="binding-checkbox" />
|
||||
<span class="binding-icon">{{ skill.icon || '🧩' }}</span>
|
||||
<span class="binding-icon"><SkillIcon :value="skill.icon" :size="20" :fallback="'🧩'" /></span>
|
||||
<div class="binding-info">
|
||||
<span class="binding-name">{{ skill.name }}</span>
|
||||
<span v-if="skill.description" class="binding-desc">{{ skill.description?.slice(0, 80) }}</span>
|
||||
@ -267,7 +282,7 @@
|
||||
:class="{ selected: selectedToolNames.includes(tool.name) }"
|
||||
>
|
||||
<input type="checkbox" :value="tool.name" v-model="selectedToolNames" class="binding-checkbox" />
|
||||
<span class="binding-icon">{{ tool.icon || '🔧' }}</span>
|
||||
<span class="binding-icon"><SkillIcon :value="tool.icon" :size="20" :fallback="'🔧'" /></span>
|
||||
<div class="binding-info">
|
||||
<span class="binding-name">{{ tool.displayName || tool.name }}</span>
|
||||
<span v-if="tool.description" class="binding-desc">{{ tool.description?.slice(0, 80) }}</span>
|
||||
@ -328,6 +343,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { agentApi, agentBindingApi, modelApi, skillApi, toolApi, templateApi } from '@/api/index'
|
||||
import type { Agent } from '@/types/index'
|
||||
import SkillIcon from '@/components/common/SkillIcon.vue'
|
||||
import SkillIconPicker from '@/components/common/SkillIconPicker.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
@ -370,13 +387,17 @@ const defaultForm = (): Partial<Agent> & { name: string; defaultThinkingLevel: s
|
||||
agentType: 'react',
|
||||
systemPrompt: '',
|
||||
maxIterations: 10,
|
||||
icon: '🤖',
|
||||
// Empty so SkillIcon's fallback (🤖) shows instead of pinning a literal
|
||||
// emoji into form.icon — otherwise the picker thinks the user picked
|
||||
// the robot emoji explicitly and reopens on the Emoji tab.
|
||||
icon: '',
|
||||
tags: '',
|
||||
enabled: true,
|
||||
defaultThinkingLevel: null,
|
||||
})
|
||||
|
||||
const form = ref(defaultForm())
|
||||
const iconPickerVisible = ref(false)
|
||||
|
||||
const filteredAgents = computed(() => {
|
||||
let list = agents.value
|
||||
@ -494,7 +515,7 @@ async function openEditModal(agent: Agent) {
|
||||
agentType: agent.agentType,
|
||||
systemPrompt: agent.systemPrompt || '',
|
||||
maxIterations: agent.maxIterations,
|
||||
icon: agent.icon || '🤖',
|
||||
icon: agent.icon || '',
|
||||
tags: agent.tags || '',
|
||||
enabled: agent.enabled,
|
||||
defaultThinkingLevel: (agent as any).defaultThinkingLevel || null,
|
||||
@ -896,4 +917,40 @@ async function toggleAgent(agent: Agent) {
|
||||
.advanced-tools-count { padding: 1px 8px; background: var(--mc-primary-bg); color: var(--mc-primary); border-radius: 999px; font-size: 11px; font-weight: 700; }
|
||||
.advanced-tools-chevron { color: var(--mc-text-tertiary); font-size: 12px; }
|
||||
.advanced-tools-note { font-style: italic; color: var(--mc-text-tertiary); margin-top: 4px; }
|
||||
|
||||
/* Icon picker trigger — replaces the old free-text icon input. Tile shape
|
||||
* mirrors SkillMarket's identity-icon-row so the create/edit affordance
|
||||
* is consistent across the app. */
|
||||
.icon-picker-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--mc-border);
|
||||
border-radius: 8px;
|
||||
background: var(--mc-bg-sunken);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
width: 100%;
|
||||
}
|
||||
.icon-picker-trigger:hover { border-color: var(--mc-primary); background: var(--mc-bg-elevated); }
|
||||
.icon-picker-trigger:focus-visible { outline: none; border-color: var(--mc-primary); box-shadow: 0 0 0 2px rgba(217,119,87,0.15); }
|
||||
.icon-picker-trigger__label {
|
||||
flex: 1; min-width: 0;
|
||||
font-size: 13px;
|
||||
color: var(--mc-text-primary);
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.icon-picker-trigger__action {
|
||||
flex-shrink: 0;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--mc-primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
:class="{ active: String(agent.id) === String(selectedAgentId) }"
|
||||
@click="selectAgent(agent)"
|
||||
>
|
||||
<span class="agent-dropdown-item__icon">{{ agent.icon || '🤖' }}</span>
|
||||
<span class="agent-dropdown-item__icon"><SkillIcon :value="agent.icon" :size="18" :fallback="'🤖'" /></span>
|
||||
<div class="agent-dropdown-item__info">
|
||||
<span class="agent-dropdown-item__name">{{ agent.name }}</span>
|
||||
<span class="agent-dropdown-item__desc">{{ agent.description || agent.agentType }}</span>
|
||||
@ -312,6 +312,7 @@ import type { Conversation, Agent, ModelConfig, ProviderInfo, ActiveModelsInfo,
|
||||
|
||||
// 导入组件化组件
|
||||
import MessageList from '@/components/chat/MessageList.vue'
|
||||
import SkillIcon from '@/components/common/SkillIcon.vue'
|
||||
import ChatInput from '@/components/chat/ChatInput.vue'
|
||||
import StreamLoadingBar from '@/components/chat/StreamLoadingBar.vue'
|
||||
import TalkMode from '@/components/chat/TalkMode.vue'
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
:class="{ active: agent.id === selectedAgentId }"
|
||||
@click="selectAgent(agent)"
|
||||
>
|
||||
<span class="agent-dropdown-item__icon">{{ agent.icon || '🤖' }}</span>
|
||||
<span class="agent-dropdown-item__icon"><SkillIcon :value="agent.icon" :size="18" :fallback="'🤖'" /></span>
|
||||
<div class="agent-dropdown-item__info">
|
||||
<span class="agent-dropdown-item__name">{{ agent.name }}</span>
|
||||
<span class="agent-dropdown-item__desc">{{ agent.description || agent.agentType }}</span>
|
||||
@ -184,6 +184,7 @@ import { ElMessage } from 'element-plus'
|
||||
import { http } from '@/api'
|
||||
import { useAgentStore } from '@/stores/useAgentStore'
|
||||
import { useMemoryStore, type DreamReportItem } from '@/stores/useMemoryStore'
|
||||
import SkillIcon from '@/components/common/SkillIcon.vue'
|
||||
import MorningCard from './components/MorningCard.vue'
|
||||
import FactList from './components/FactList.vue'
|
||||
import MemoryBrowser from './components/MemoryBrowser.vue'
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="agent-cell">
|
||||
<span class="agent-icon-sm">{{ session.agentIcon || '🤖' }}</span>
|
||||
<span class="agent-icon-sm"><SkillIcon :value="session.agentIcon" :size="16" :fallback="'🤖'" /></span>
|
||||
<span>{{ session.agentName || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
@ -97,6 +97,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { conversationApi } from '@/api/index'
|
||||
import { channelIconUrl, sourceLabel } from '@/utils/channelSource'
|
||||
import type { Conversation } from '@/types/index'
|
||||
import SkillIcon from '@/components/common/SkillIcon.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -525,7 +525,7 @@
|
||||
<p v-else-if="detailEmployees.length === 0" class="detail-empty">{{ t('skills.detail.noEmployees') }}</p>
|
||||
<ul v-else class="memory-agent-list">
|
||||
<li v-for="agent in detailEmployees" :key="agent.id" class="memory-agent-item">
|
||||
<span class="memory-agent-icon">{{ agent.icon || '🤖' }}</span>
|
||||
<span class="memory-agent-icon"><SkillIcon :value="agent.icon" :size="20" :fallback="'🤖'" /></span>
|
||||
<div class="memory-agent-info">
|
||||
<span class="memory-agent-name">{{ agent.name }}</span>
|
||||
<span class="memory-agent-binding" :class="`binding-${agent.binding || 'explicit'}`">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user