Files
hermes-web/packages/client/src/components/hermes/profiles/ProfileRenameModal.vue
T
toor 4baccd5d88 Phase 005: vendor ArkScope, land the Arco style baseline, and fix bridge interpreter resolution
- Vendor ArkScope decorative layers (ScrambleTitle / IsometricWave) with provenance notes; decorative-only per AGENTS.md red lines
- Migrate theme tokens to Arco, register the Arco theme, and add hook adapter composables (useMessage / useDialog / useNotification) with matching signatures
- Sample pages: LoginView and AppSidebar on the new baseline; docs/arco-mapping.md and docs/ui-baseline.md land
- agent-bridge: prefer the PM-selected venv from facts.json over legacy in-tree venvs (legacy lacks the mcp extra, so every MCP server showed 0 tools / not connected)
- agent-bridge python (vendored, deliberate deviation): MCP discovery import fallback and pyyaml -> hermes_yaml so plugins and MCP work under the PM env; install the mcp extra via `hermes pm install --extra mcp`
- Chat-chain fragment: docs/chat-chain-changes/2026-10-17-phase005-arkscope-baseline.md

Verified: harness:check, test (2020 passed), test:e2e (30 passed), build all green; manual: plugins 100/0 warnings, MCP 6/6 servers connected with 104 tools.
2026-10-17 09:45:30 +08:00

105 lines
2.6 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import { NModal, NForm, NFormItem, NInput, NButton, NText } from 'naive-ui'
import { useMessage } from '@/composables/useMessage'
import { useProfilesStore } from '@/stores/hermes/profiles'
import { useI18n } from 'vue-i18n'
const props = defineProps<{ profileName: string }>()
const emit = defineEmits<{
close: []
saved: []
}>()
const { t } = useI18n()
const profilesStore = useProfilesStore()
const message = useMessage()
const showModal = ref(true)
const loading = ref(false)
const newName = ref('')
const nameValidationMessage = ref('')
function handleNameInput(value: string) {
// 过滤掉不符合规则的字符,只保留小写字母、数字、下划线和连字符
const filtered = value.toLowerCase().replace(/[^a-z0-9_-]/g, '')
if (filtered !== value) {
nameValidationMessage.value = t('profiles.nameValidation')
} else {
nameValidationMessage.value = ''
}
newName.value = filtered
}
async function handleSave() {
if (!newName.value) {
message.warning(t('profiles.newNamePlaceholder'))
return
}
if (!/^[a-z0-9_-]+$/.test(newName.value)) {
message.error(t('profiles.nameValidation'))
return
}
loading.value = true
try {
const ok = await profilesStore.renameProfile(props.profileName, newName.value.trim())
if (ok) {
message.success(t('profiles.renameSuccess'))
emit('saved')
} else {
message.error(t('profiles.renameFailed'))
}
} finally {
loading.value = false
}
}
function handleClose() {
showModal.value = false
setTimeout(() => emit('close'), 200)
}
</script>
<template>
<NModal
v-model:show="showModal"
preset="card"
:title="t('profiles.rename')"
:style="{ width: 'min(420px, calc(100vw - 32px))' }"
:mask-closable="!loading"
@after-leave="emit('close')"
>
<NForm label-placement="top">
<NFormItem :label="t('profiles.newName')" required>
<NInput
v-model:value="newName"
:placeholder="t('profiles.newNamePlaceholder')"
@input="handleNameInput"
/>
</NFormItem>
<NText v-if="nameValidationMessage" depth="3" type="warning" style="font-size: 12px;">
{{ nameValidationMessage }}
</NText>
</NForm>
<template #footer>
<div class="modal-footer">
<NButton @click="handleClose">{{ t('common.cancel') }}</NButton>
<NButton type="primary" :loading="loading" @click="handleSave">
{{ t('common.confirm') }}
</NButton>
</div>
</template>
</NModal>
</template>
<style scoped lang="scss">
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>