diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts
index a3aee32d..ef708d7e 100644
--- a/mateclaw-ui/src/i18n/locales/en-US.ts
+++ b/mateclaw-ui/src/i18n/locales/en-US.ts
@@ -3732,6 +3732,7 @@ export default {
manage: 'Manage auto-approve rules...',
},
chipLabel: 'Auto-approve active ({count})',
+ chipShort: 'Auto-approve {count}',
menu: 'Auto-approve',
title: 'Auto-approve rules',
desc: 'Rules let specific tool calls skip manual approval. Safety-floor patterns (rm -rf /, pipe-to-shell, etc.) always apply, and CRITICAL severity always falls back to human approval.',
diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts
index 6adc5177..0f798271 100644
--- a/mateclaw-ui/src/i18n/locales/zh-CN.ts
+++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts
@@ -3824,6 +3824,7 @@ export default {
manage: '管理自动批准策略...',
},
chipLabel: '自动批准已启用 ({count})',
+ chipShort: '自动批准 {count}',
menu: '自动批准',
title: '自动批准策略',
desc: '策略让特定工具调用跳过人审。地板规则(如 rm -rf /、pipe-to-shell)始终生效,CRITICAL 严重度永远人审。',
diff --git a/mateclaw-ui/src/views/Security/AutoApproveGrants/index.vue b/mateclaw-ui/src/views/Security/AutoApproveGrants/index.vue
index 28cdcd47..f0f507b0 100644
--- a/mateclaw-ui/src/views/Security/AutoApproveGrants/index.vue
+++ b/mateclaw-ui/src/views/Security/AutoApproveGrants/index.vue
@@ -16,17 +16,12 @@
-
-
-
-
-
- {{ t(`approval.grant.scope.${scopeI18nKey(row.scopeType)}`) }}
-
- {{ row.scopeId }}
-
-
+
+
+
+
+
+ | {{ t('approval.grant.columns.scope') }} |
+ {{ t('approval.grant.columns.tool') }} |
+ {{ t('approval.grant.columns.rule') }} |
+ {{ t('approval.grant.columns.severity') }} |
+ {{ t('approval.grant.columns.kind') }} |
+ {{ t('approval.grant.columns.expire') }} |
+ {{ t('approval.grant.columns.grantedBy') }} |
+ {{ t('approval.grant.columns.note') }} |
+ {{ t('approval.grant.columns.actions') }} |
+
+
+
+
+ |
+
+ {{ t(`approval.grant.scope.${scopeI18nKey(row.scopeType)}`) }}
+
+ {{ row.scopeId }}
+ |
+
+ {{ row.toolName }}
+ ∗ any
+ |
+
+ {{ row.ruleId }}
+ ∗
+ |
+
+
+ {{ row.maxSeverity }}
+
+ |
+ {{ t(`approval.grant.kind.${kindI18nKey(row.grantKind)}`) }} |
+ {{ formatDate(row.expireAt) }} |
+ {{ row.grantedBy }} |
+ {{ row.note }} |
+
+
+ {{ t('common.revoked') }}
+ |
+
+
+
+
{{ t('common.loading') }}
+
{{ t('approval.grant.empty') }}
+
-
-
- {{ row.toolName }}
- ∗ any
-
-
-
-
-
- {{ row.ruleId }}
- ∗
-
-
-
-
-
-
- {{ row.maxSeverity }}
-
-
-
-
-
-
- {{ t(`approval.grant.kind.${kindI18nKey(row.grantKind)}`) }}
-
-
-
-
-
- {{ formatDate(row.expireAt) }}
-
-
-
-
-
-
-
- {{ row.note }}
-
-
-
-
-
-
- {{ t('approval.grant.revokeBtn') }}
-
-
- {{ t('common.revoked') }}
-
-
-
-
-
-
@@ -312,6 +287,7 @@ import {
WarningFilled,
} from '@element-plus/icons-vue'
import { approvalApi } from '@/api'
+import McPagination from '@/components/common/McPagination.vue'
import type {
ApprovalGrant,
CreateGrantPayload,
@@ -365,6 +341,16 @@ const requiresPassword = computed(() => {
return noTool && (form.scopeType === 'WORKSPACE' || form.scopeType === 'AGENT')
})
+function onPageChange(p: number) {
+ currentPage.value = p
+ loadGrants()
+}
+function onSizeChange(s: number) {
+ pageSize.value = s
+ currentPage.value = 1
+ loadGrants()
+}
+
async function loadGrants() {
loading.value = true
try {
@@ -471,23 +457,6 @@ function scopeI18nKey(scope: GrantScope): string {
}
}
-function scopeTagType(scope: GrantScope): 'primary' | 'success' | 'warning' | 'danger' | 'info' {
- switch (scope) {
- case 'CONVERSATION': return 'primary'
- case 'AGENT': return 'warning'
- case 'USER': return 'success'
- case 'WORKSPACE': return 'danger'
- }
-}
-
-function severityTagType(sev: GrantSeverity): 'success' | 'warning' | 'danger' {
- switch (sev) {
- case 'LOW': return 'success'
- case 'MEDIUM': return 'warning'
- case 'HIGH': return 'danger'
- }
-}
-
function kindI18nKey(kind: GrantKind): string {
switch (kind) {
case 'ALWAYS': return 'always'
@@ -523,8 +492,19 @@ onMounted(loadGrants)
flex-basis: 100%;
margin: 4px 0 0;
}
-.active-summary {
+/* Summary pill — reuses the same danger-tinted tokens as the sidebar chip
+ so the two surfaces feel like one design language; no el-tag here. */
+.summary-pill {
+ display: inline-flex;
+ align-items: center;
+ padding: 2px 10px;
+ border-radius: 999px;
+ background: var(--mc-danger-bg, rgba(192, 57, 43, 0.12));
+ color: var(--mc-danger, #C0392B);
+ border: 1px solid var(--mc-danger-border, rgba(192, 57, 43, 0.4));
+ font-size: 12px;
font-weight: 600;
+ line-height: 1.5;
}
/* Header actions: matches McpServers / ToolGuard layout (.section-header__actions
@@ -567,17 +547,36 @@ onMounted(loadGrants)
color: #991b1b;
}
+/* Scope badge — same family as .severity-badge in shared.css, 4 token-driven
+ color variants for CONVERSATION / AGENT / USER / WORKSPACE. */
+.scope-badge {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 600;
+ line-height: 1.5;
+}
+.scope-conversation { background: rgba(59, 130, 246, 0.12); color: #3b82f6; }
+.scope-agent { background: rgba(245, 158, 11, 0.12); color: #f59e0b; }
+.scope-user { background: rgba(16, 185, 129, 0.12); color: #10b981; }
+.scope-workspace { background: var(--mc-danger-bg, rgba(192, 57, 43, 0.12));
+ color: var(--mc-danger, #C0392B); }
.scope-id {
- margin-left: 8px;
+ margin-top: 2px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- font-size: 12px;
+ font-size: 11px;
color: var(--mc-text-tertiary, #94a3b8);
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.mono {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
- background: var(--mc-surface-tertiary, #f1f5f9);
+ background: var(--mc-surface-tertiary, var(--mc-bg-muted, rgba(0, 0, 0, 0.04)));
padding: 1px 6px;
border-radius: 3px;
}
@@ -596,8 +595,29 @@ onMounted(loadGrants)
font-size: 12px;
}
-.grants-pagination {
- margin-top: 16px;
+/* Revoked rows: dim out so live rows stay visually dominant. */
+.row-revoked { opacity: 0.55; }
+.row-revoked .scope-badge { filter: grayscale(0.5); }
+
+/* Actions column header sits right-aligned, matches ToolGuard. */
+.col-actions { text-align: center; width: 80px; }
+
+/* This page has more columns than ToolGuard / AuditLogs, so the wrapper needs
+ to allow horizontal scroll on narrow viewports — shared.css uses
+ overflow:hidden which would chop the rightmost columns off. Scoped override
+ only affects this view; the shared style stays untouched. */
+.rules-table-wrapper {
+ overflow-x: auto;
+}
+.rules-table {
+ min-width: 900px;
+}
+
+/* Pagination row — right-aligned beneath the table. McPagination provides
+ its own pill background, so just lay it out. */
+.grants-pagination-row {
+ margin-top: 14px;
+ display: flex;
justify-content: flex-end;
}
diff --git a/mateclaw-ui/src/views/layout/MainLayout.vue b/mateclaw-ui/src/views/layout/MainLayout.vue
index 7820be46..aa0e38b0 100644
--- a/mateclaw-ui/src/views/layout/MainLayout.vue
+++ b/mateclaw-ui/src/views/layout/MainLayout.vue
@@ -80,11 +80,34 @@