diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts
index 54a174d2..4da9811a 100644
--- a/mateclaw-ui/src/api/index.ts
+++ b/mateclaw-ui/src/api/index.ts
@@ -82,6 +82,12 @@ export const agentApi = {
getState: (id: string | number) => http.get(`/agents/${id}/state`),
}
+// ==================== Templates ====================
+export const templateApi = {
+ list: () => http.get('/templates'),
+ apply: (id: string) => http.post(`/templates/${id}/apply`),
+}
+
// ==================== Chat ====================
export const chatApi = {
uploadFile: async (conversationId: string, file: File) => {
diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts
index 9469ca99..c96e5f63 100644
--- a/mateclaw-ui/src/i18n/locales/en-US.ts
+++ b/mateclaw-ui/src/i18n/locales/en-US.ts
@@ -33,6 +33,11 @@ export default {
disable: 'Disable',
},
chat: {
+ status: {
+ idle: 'Ready',
+ streaming: 'Generating...',
+ error: 'Disconnected',
+ },
thinking: 'Thinking',
stopped: 'Generation stopped',
failed: 'Generation failed',
@@ -579,6 +584,12 @@ export default {
title: 'Agent Management',
desc: 'Create, edit, and manage your AI agents',
newAgent: 'New Agent',
+ templates: {
+ title: 'Choose a Template',
+ desc: 'Start with a pre-configured agent, or create from scratch.',
+ skip: 'Start from Scratch',
+ applied: 'Agent created from template',
+ },
search: 'Search agents...',
tabs: {
all: 'All',
@@ -1507,11 +1518,18 @@ export default {
},
dashboard: {
title: 'Dashboard',
+ kicker: 'Operations Pulse',
desc: 'System usage overview and runtime status',
conversations: 'Conversations',
messages: 'Messages',
tokens: 'Token Usage',
toolCalls: 'Tool Calls',
+ periodDesc: 'A sharper view of how your system behaves across short, medium, and monthly horizons.',
+ runsDesc: 'Execution history with timing, cost, and outcome at a glance.',
+ trend: {
+ title: '7-Day Trend',
+ subtitle: 'Messages and token consumption over the past week.',
+ },
periodComparison: 'Period Comparison',
periods: {
today: 'Today',
diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts
index 3de7caa1..2093df1c 100644
--- a/mateclaw-ui/src/i18n/locales/zh-CN.ts
+++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts
@@ -33,6 +33,11 @@ export default {
disable: '停用',
},
chat: {
+ status: {
+ idle: '就绪',
+ streaming: '生成中...',
+ error: '连接断开',
+ },
thinking: '深度思考',
stopped: '已停止生成',
failed: '生成失败',
@@ -579,6 +584,12 @@ export default {
title: '智能体管理',
desc: '创建、编辑和管理你的 AI 智能体',
newAgent: '新建智能体',
+ templates: {
+ title: '选择模板',
+ desc: '选择预配置的 Agent 模板快速开始,或从空白创建。',
+ skip: '从空白开始',
+ applied: '已从模板创建 Agent',
+ },
search: '搜索智能体...',
tabs: {
all: '全部',
@@ -1517,11 +1528,18 @@ export default {
},
dashboard: {
title: '仪表盘',
+ kicker: '运营脉搏',
desc: '系统用量概览与运行状态',
conversations: '对话数',
messages: '消息数',
tokens: 'Token 消耗',
toolCalls: '工具调用',
+ periodDesc: '从日、周、月三个维度观察系统运行状况。',
+ runsDesc: '定时任务执行记录,包含耗时、消耗和结果。',
+ trend: {
+ title: '7 天趋势',
+ subtitle: '过去一周的消息量和 Token 消耗趋势。',
+ },
periodComparison: '周期对比',
periods: {
today: '今日',
diff --git a/mateclaw-ui/src/views/Agents.vue b/mateclaw-ui/src/views/Agents.vue
index 3cbde516..f5ff26ff 100644
--- a/mateclaw-ui/src/views/Agents.vue
+++ b/mateclaw-ui/src/views/Agents.vue
@@ -107,6 +107,49 @@
+
+
+
+
+
+
{{ t('agents.templates.desc') }}
+
+
+
{{ tpl.icon }}
+
+
{{ $i18n.locale === 'zh-CN' && tpl.nameZh ? tpl.nameZh : tpl.name }}
+
{{ $i18n.locale === 'zh-CN' && tpl.descriptionZh ? tpl.descriptionZh : tpl.description }}
+
+
+ {{ tag.trim() }}
+
+
+
+
+
+
+
+
@@ -235,7 +278,7 @@
import { ref, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { agentApi, agentBindingApi, skillApi, toolApi } from '@/api/index'
+import { agentApi, agentBindingApi, skillApi, toolApi, templateApi } from '@/api/index'
import type { Agent } from '@/types/index'
const { t } = useI18n()
@@ -252,6 +295,11 @@ const availableTools = ref
([])
const selectedSkillIds = ref([])
const selectedToolNames = ref([])
+// Template selector state
+const showTemplateSelector = ref(false)
+const templates = ref([])
+const applyingTemplate = ref(false)
+
const filterTabs = [
{ key: 'agents.tabs.all', value: 'all' },
{ key: 'agents.tabs.react', value: 'react' },
@@ -315,6 +363,13 @@ function formatTime(time?: string): string {
}
function openCreateModal() {
+ // Show template selector first
+ showTemplateSelector.value = true
+ loadTemplates()
+}
+
+function openBlankCreateModal() {
+ showTemplateSelector.value = false
editingAgent.value = null
form.value = defaultForm()
modalTab.value = 'basic'
@@ -323,6 +378,30 @@ function openCreateModal() {
showModal.value = true
}
+async function loadTemplates() {
+ try {
+ const res: any = await templateApi.list()
+ templates.value = res.data || []
+ } catch {
+ // Fallback: skip templates, open blank form
+ openBlankCreateModal()
+ }
+}
+
+async function applyTemplate(id: string) {
+ applyingTemplate.value = true
+ try {
+ await templateApi.apply(id)
+ ElMessage.success(t('agents.templates.applied'))
+ showTemplateSelector.value = false
+ await loadAgents()
+ } catch {
+ ElMessage.error(t('agents.messages.saveFailed'))
+ } finally {
+ applyingTemplate.value = false
+ }
+}
+
async function openEditModal(agent: Agent) {
editingAgent.value = agent
form.value = {
@@ -551,4 +630,26 @@ async function toggleAgent(agent: Agent) {
max-width: none;
}
}
+
+/* Template Selector */
+.template-modal { max-width: 640px; }
+.template-desc { font-size: 14px; color: var(--mc-text-secondary); margin: 0 0 18px; }
+
+.template-grid { display: flex; flex-direction: column; gap: 10px; }
+
+.template-card {
+ display: flex; align-items: flex-start; gap: 14px; padding: 16px; cursor: pointer;
+ border: 1px solid var(--mc-border); border-radius: 12px; transition: all 0.15s;
+}
+.template-card:hover { border-color: var(--mc-primary); background: var(--mc-primary-bg); }
+.template-card.applying { opacity: 0.5; pointer-events: none; }
+
+.template-icon { font-size: 28px; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: var(--mc-bg-muted); border-radius: 10px; flex-shrink: 0; }
+
+.template-info { flex: 1; min-width: 0; }
+.template-name { font-size: 15px; font-weight: 600; color: var(--mc-text-primary); margin: 0 0 4px; }
+.template-detail { font-size: 13px; color: var(--mc-text-secondary); margin: 0; line-height: 1.5; }
+
+.template-tags { display: flex; flex-wrap: wrap; gap: 4px; align-self: flex-start; margin-top: 2px; }
+.tag-chip { font-size: 11px; padding: 2px 8px; background: var(--mc-bg-sunken); color: var(--mc-text-tertiary); border-radius: 999px; white-space: nowrap; }
diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue
index 2be7a657..c91c0bd9 100644
--- a/mateclaw-ui/src/views/ChatConsole.vue
+++ b/mateclaw-ui/src/views/ChatConsole.vue
@@ -91,6 +91,7 @@
{{ currentAgent.icon || '🤖' }}
{{ currentAgent.name }}
{{ currentAgent.agentType === 'react' ? 'ReAct' : 'Plan-Execute' }}
+
{{ $t('chat.selectAgent') }}
@@ -394,6 +395,18 @@ const {
},
})
+// ============ 连接状态 ============
+const connectionStatusClass = computed(() => {
+ if (isGenerating.value) return 'status-streaming'
+ if (streamPhase.value === 'failed') return 'status-error'
+ return 'status-idle'
+})
+const connectionStatusLabel = computed(() => {
+ if (isGenerating.value) return t('chat.status.streaming', 'Generating...')
+ if (streamPhase.value === 'failed') return t('chat.status.error', 'Disconnected')
+ return t('chat.status.idle', 'Ready')
+})
+
// ============ 计算属性 ============
const currentAgent = computed(() => agents.value.find(a => String(a.id) === String(selectedAgentId.value)))
@@ -1451,6 +1464,15 @@ function handleCodeCopy(e: MouseEvent) {
border-radius: 10px;
}
+.status-dot {
+ width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; margin-left: 2px;
+ transition: background 0.3s;
+}
+.status-idle { background: #34d399; box-shadow: 0 0 4px rgba(52, 211, 153, 0.5); }
+.status-streaming { background: #fbbf24; box-shadow: 0 0 4px rgba(251, 191, 36, 0.5); animation: pulse-dot 1.2s infinite; }
+.status-error { background: #f87171; box-shadow: 0 0 4px rgba(248, 113, 113, 0.5); }
+@keyframes pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
+
.no-agent-hint {
font-size: 13px;
color: var(--mc-text-tertiary);
diff --git a/mateclaw-ui/src/views/Dashboard.vue b/mateclaw-ui/src/views/Dashboard.vue
index 12252719..338e1dfb 100644
--- a/mateclaw-ui/src/views/Dashboard.vue
+++ b/mateclaw-ui/src/views/Dashboard.vue
@@ -4,12 +4,12 @@
+
+
+
{{ t('dashboard.trend.title', '7-Day Trend') }}
+
{{ t('dashboard.trend.subtitle', 'Messages and token consumption over the past week.') }}
+
+
+
+
{{ t('dashboard.periodComparison') }}
-
A sharper view of how your system behaves across short, medium, and monthly horizons.
+
{{ t('dashboard.periodDesc') }}
@@ -86,7 +96,7 @@
{{ t('dashboard.recentRuns') }}
-
Execution should feel legible. If it runs, you should see its rhythm, cost, and outcome instantly.
+
{{ t('dashboard.runsDesc') }}