- 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.
105 lines
2.6 KiB
Vue
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>
|