Phase 006: migrate the chat page from naive-ui to Arco Design Vue
Swaps the rendering layer only. Chat store logic, keyboard handling, and the /chat-run Socket.IO contract are untouched, so a mixed UI cannot hide a chat regression behind a library swap. Arco's dropdowns take their items from a #content slot of a-doption children rather than a droplist prop, and Arco Triggers cannot be nested, so the two toolbar menus lost their a-tooltip in favour of a native title. Both rules are recorded in docs/arco-mapping.md, and tests/e2e/chat-input-dropdown.spec.ts plus tests/e2e/chat-rendering-smoke.spec.ts keep the menus, the streaming markdown rendering, and the no-Arco-inside-the-bubble red line from regressing silently.
This commit is contained in:
@@ -5,7 +5,6 @@ import { FitAddon } from "@xterm/addon-fit";
|
||||
import { WebLinksAddon } from "@xterm/addon-web-links";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import { getApiKey, getBaseUrlValue } from "@/api/client";
|
||||
import { NButton, NPopconfirm, NTooltip, NSelect } from "naive-ui";
|
||||
import { useMessage } from "@/composables/useMessage";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { ITheme } from "@xterm/xterm";
|
||||
@@ -417,7 +416,8 @@ function handleTerminalTouchEnd() {
|
||||
|
||||
// ─── Theme ───────────────────────────────────────────────────────
|
||||
|
||||
function applyTheme(themeName: string) {
|
||||
function applyTheme(value: unknown) {
|
||||
const themeName = String(value);
|
||||
selectedTheme.value = themeName;
|
||||
localStorage.setItem(STORAGE_KEY_THEME, themeName);
|
||||
const themeObj = TERMINAL_THEMES[themeName]?.theme;
|
||||
@@ -473,19 +473,17 @@ onUnmounted(() => {
|
||||
>
|
||||
<div class="sidebar-header">
|
||||
<span class="sidebar-title">{{ t("terminal.sessions") }}</span>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NButton quaternary size="tiny" @click="createSession" circle>
|
||||
<template #icon>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<a-tooltip trigger="hover">
|
||||
<a-button type="secondary" size="mini" @click="createSession" shape="circle">
|
||||
<template #icon>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
</template>
|
||||
</a-button>
|
||||
{{ t("terminal.newTab") }}
|
||||
</NTooltip>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="session-list">
|
||||
<div v-if="connectionError" class="session-error">
|
||||
@@ -495,7 +493,7 @@ onUnmounted(() => {
|
||||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||||
</svg>
|
||||
<span>{{ connectionError }}</span>
|
||||
<NButton size="tiny" @click="connect">{{ t("common.retry") }}</NButton>
|
||||
<a-button size="mini" @click="connect">{{ t("common.retry") }}</a-button>
|
||||
</div>
|
||||
<div v-else-if="sessions.length === 0" class="session-empty">
|
||||
<template v-if="isConnecting">
|
||||
@@ -524,17 +522,18 @@ onUnmounted(() => {
|
||||
}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<NPopconfirm v-if="sessions.length > 1" @positive-click="closeSession(s.id)">
|
||||
<template #trigger>
|
||||
<button class="session-item-delete" @click.stop>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
{{ t("terminal.closeSession") }}
|
||||
</NPopconfirm>
|
||||
<a-popconfirm
|
||||
v-if="sessions.length > 1"
|
||||
:content="t('terminal.closeSession')"
|
||||
@ok="closeSession(s.id)"
|
||||
>
|
||||
<button class="session-item-delete" @click.stop>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
</a-popconfirm>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -545,7 +544,7 @@ onUnmounted(() => {
|
||||
activeSession.title
|
||||
}}</span>
|
||||
<div class="header-actions">
|
||||
<NButton
|
||||
<a-button
|
||||
size="small"
|
||||
@click="showSidebar = !showSidebar"
|
||||
class="sidebar-toggle"
|
||||
@@ -557,16 +556,15 @@ onUnmounted(() => {
|
||||
</svg>
|
||||
</template>
|
||||
{{ t("terminal.sessions") }}
|
||||
</NButton>
|
||||
<NSelect
|
||||
:value="selectedTheme"
|
||||
</a-button>
|
||||
<a-select
|
||||
:model-value="selectedTheme"
|
||||
:options="themeOptions"
|
||||
size="small"
|
||||
:consistent-menu-width="false"
|
||||
class="theme-select"
|
||||
@update:value="applyTheme"
|
||||
@change="applyTheme"
|
||||
/>
|
||||
<NButton size="small" @click="createSession">
|
||||
<a-button size="small" @click="createSession">
|
||||
<template #icon>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
@@ -574,7 +572,7 @@ onUnmounted(() => {
|
||||
</svg>
|
||||
</template>
|
||||
{{ t("terminal.newTab") }}
|
||||
</NButton>
|
||||
</a-button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="terminal-container">
|
||||
|
||||
Reference in New Issue
Block a user