@@ -465,14 +465,14 @@ const wallPhotos = computed(() =>
allPhotos.value
.filter((p) => p.is_on_wall)
.sort((a, b) => (a.wall_position ?? 99) - (b.wall_position ?? 99))
- .slice(0, 9),
+ .slice(0, 10),
)
const allPhotoUrls = computed(() =>
allPhotos.value.map((p) => p.image_url),
)
-const emptySlots = computed(() => Math.max(0, 9 - wallPhotos.value.length))
+const emptySlots = computed(() => Math.max(0, 10 - wallPhotos.value.length))
// ── Photo management state ──
const photoInput = ref
(null)
@@ -637,8 +637,8 @@ async function onToggleWall(photo: Photo) {
const newIsOnWall = !photo.is_on_wall
const wallCount = allPhotos.value.filter((p) => p.is_on_wall).length
- if (newIsOnWall && wallCount >= 9) {
- photoError.value = '照片墙已满 (最多 9 张)'
+ if (newIsOnWall && wallCount >= 10) {
+ photoError.value = '照片墙已满 (最多 10 张)'
return
}
@@ -650,7 +650,7 @@ async function onToggleWall(photo: Photo) {
.filter((p) => p.is_on_wall && p.id !== photo.id)
.map((p) => p.wall_position),
)
- for (let i = 0; i <= 8; i++) {
+ for (let i = 0; i <= 9; i++) {
if (!usedPositions.has(i)) {
position = i
break
@@ -1020,7 +1020,7 @@ watch(visible, async (isVisible) => {
.car-layout {
display: flex;
height: 100%;
- padding: 5vh 5vw 5vh 9vw;
+ padding: 5vh 5vw 5vh 20vw;
gap: 4vw;
align-items: center;
}
@@ -1036,10 +1036,10 @@ watch(visible, async (isVisible) => {
.photo-grid {
display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 12px;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 10px;
width: 100%;
- max-width: 420px;
+ max-width: 880px;
}
.photo-frame {
@@ -1220,23 +1220,65 @@ watch(visible, async (isVisible) => {
padding: 48px 0;
}
+/* ── Suitcase Overlay (matches OverlayPanel) ── */
+.suitcase-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--overlay-backdrop);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+}
+
/* ── Suitcase Modal ── */
.suitcase-modal {
position: relative;
width: 75vw;
max-width: 900px;
max-height: 80vh;
- background: rgba(40, 34, 28, 0.95);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
- border-radius: 24px;
- border: 1px solid rgba(255, 255, 255, 0.1);
+ background: var(--glass-bg-heavy);
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border-radius: var(--glass-radius);
+ border: 1px solid var(--glass-border);
+ box-shadow: var(--glass-shadow), var(--glass-inner-glow);
padding: 32px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
}
+/* ── Suitcase Panel Transition ── */
+.suitcase-panel-enter-active {
+ transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1);
+}
+.suitcase-panel-enter-active .suitcase-modal {
+ transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.35s ease;
+}
+.suitcase-panel-leave-active {
+ transition: opacity 0.25s ease;
+}
+.suitcase-panel-leave-active .suitcase-modal {
+ transition: transform 0.25s ease, opacity 0.25s ease;
+}
+.suitcase-panel-enter-from {
+ opacity: 0;
+}
+.suitcase-panel-enter-from .suitcase-modal {
+ transform: scale(0.92) translateY(20px);
+ opacity: 0;
+}
+.suitcase-panel-leave-to {
+ opacity: 0;
+}
+.suitcase-panel-leave-to .suitcase-modal {
+ transform: scale(0.95) translateY(10px);
+ opacity: 0;
+}
+
.suitcase-title {
font-size: 20px;
font-weight: 600;
@@ -1469,11 +1511,58 @@ watch(visible, async (isVisible) => {
opacity: 0;
}
+/* ── Profile Overlay (matches OverlayPanel) ── */
+.profile-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--overlay-backdrop);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+}
+
/* ── Profile Modal ── */
.profile-modal {
width: 520px;
max-width: 90vw;
padding: 32px 28px;
+ background: var(--glass-bg-heavy);
+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
+ border: 1px solid var(--glass-border);
+ border-radius: var(--glass-radius);
+ box-shadow: var(--glass-shadow), var(--glass-inner-glow);
+}
+
+/* ── Profile Panel Transition ── */
+.profile-panel-enter-active {
+ transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1);
+}
+.profile-panel-enter-active .profile-modal {
+ transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.35s ease;
+}
+.profile-panel-leave-active {
+ transition: opacity 0.25s ease;
+}
+.profile-panel-leave-active .profile-modal {
+ transition: transform 0.25s ease, opacity 0.25s ease;
+}
+.profile-panel-enter-from {
+ opacity: 0;
+}
+.profile-panel-enter-from .profile-modal {
+ transform: scale(0.92) translateY(20px);
+ opacity: 0;
+}
+.profile-panel-leave-to {
+ opacity: 0;
+}
+.profile-panel-leave-to .profile-modal {
+ transform: scale(0.95) translateY(10px);
+ opacity: 0;
}
.profile-header {
diff --git a/frontend/src/components/overlay/ChatPanel.vue b/frontend/src/components/overlay/ChatPanel.vue
index 2a7c8f46..d8333e84 100644
--- a/frontend/src/components/overlay/ChatPanel.vue
+++ b/frontend/src/components/overlay/ChatPanel.vue
@@ -1,7 +1,21 @@
-
-
Echo 倾听
+
+
+
+
+
+
+
+
+
+
✨ 记住了
@@ -9,31 +23,32 @@
-
-
{{ preferredName }},你好,我是你的贝壳伙伴。
-
你好,我是你的贝壳伙伴。
-
有什么想聊的吗?
-
-
-
- {{ msg.text }}
+
+
+
{{ preferredName }},你好
+
你好
+
我是小石光,有什么想聊的吗?
+
+
+
{{ latestUserMsg.text }}
+
+
+
-
-
-
-
+
+
{{ latestUserMsg.text }}
+
+ {{ latestAssistantMsg.text }}
+
-
+
@@ -42,12 +57,12 @@
v-model="input"
type="text"
class="chat-input"
- placeholder="说点什么..."
+ placeholder="说说你的心情..."
:disabled="sending"
/>
@@ -93,6 +108,7 @@ const sending = ref(false)
const messagesEl = ref
(null)
const preferredName = ref(_preferredName)
const showMemoryToast = ref(false)
+const showRipple = ref(false)
const isOpen = computed(() => uiStore.activePanel === 'chat')
@@ -106,6 +122,47 @@ const colorToneMap: Record = {
sage: 'rgba(143,188,143,0.15)',
}
+const ambientColorMap: Record = {
+ soft_pink: { primary: 'rgba(255,182,193,0.25)', secondary: 'rgba(255,228,225,0.1)' },
+ warm_gold: { primary: 'rgba(255,215,0,0.2)', secondary: 'rgba(255,248,220,0.08)' },
+ gentle_blue: { primary: 'rgba(135,206,235,0.2)', secondary: 'rgba(176,224,230,0.08)' },
+ lavender: { primary: 'rgba(200,162,255,0.2)', secondary: 'rgba(230,230,250,0.08)' },
+ neutral_white: { primary: 'rgba(255,255,255,0.12)', secondary: 'rgba(255,255,255,0.04)' },
+ coral: { primary: 'rgba(255,127,80,0.2)', secondary: 'rgba(255,160,122,0.08)' },
+ sage: { primary: 'rgba(143,188,143,0.2)', secondary: 'rgba(193,225,193,0.08)' },
+}
+
+const latestColorTone = computed(() => {
+ for (let i = messages.value.length - 1; i >= 0; i--) {
+ const m = messages.value[i]
+ if (m.role === 'assistant' && m.visualMeta?.color_tone) return m.visualMeta.color_tone
+ }
+ return 'gentle_blue'
+})
+
+const ambientStyle = computed(() => ({
+ '--ambient-primary': ambientColorMap[latestColorTone.value]?.primary ?? ambientColorMap.gentle_blue.primary,
+ '--ambient-secondary': ambientColorMap[latestColorTone.value]?.secondary ?? ambientColorMap.gentle_blue.secondary,
+}))
+
+const ambientGradientStyle = computed(() => ({
+ background: `radial-gradient(ellipse at 30% 40%, var(--ambient-primary), var(--ambient-secondary), transparent)`,
+}))
+
+const latestUserMsg = computed(() => {
+ for (let i = messages.value.length - 1; i >= 0; i--) {
+ if (messages.value[i].role === 'user') return messages.value[i]
+ }
+ return null
+})
+
+const latestAssistantMsg = computed(() => {
+ for (let i = messages.value.length - 1; i >= 0; i--) {
+ if (messages.value[i].role === 'assistant') return messages.value[i]
+ }
+ return null
+})
+
function generateSessionId(): string {
// crypto.randomUUID() requires a secure context (HTTPS or localhost).
// Fall back to crypto.getRandomValues for non-secure HTTP environments.
@@ -141,7 +198,6 @@ function getBubbleStyle(msg: ChatMessage): Record {
return {
'--effect-color': color,
'--effect-intensity': String(0.5 + msg.visualMeta.intensity * 0.5),
- background: `radial-gradient(ellipse at 30% 50%, ${color}, rgba(255,255,255,0.1))`,
}
}
return {}
@@ -185,6 +241,8 @@ async function onSend() {
]
input.value = ''
sending.value = true
+ showRipple.value = true
+ setTimeout(() => { showRipple.value = false }, 800)
syncPersistent()
scrollToBottom()
@@ -248,15 +306,81 @@ async function onSend() {
position: relative;
}
-.chat-title {
- font-size: 20px;
- font-weight: 600;
- color: var(--text-primary);
- padding: 24px 24px 16px;
+/* ── Ambient background ── */
+.ambient-bg {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+ transition: background 2s ease-in-out;
+ animation: ambientBreath 4s ease-in-out infinite;
+}
+
+@keyframes ambientBreath {
+ 0%, 100% { opacity: 0.6; }
+ 50% { opacity: 1; }
+}
+
+/* ── Send ripple ── */
+.send-ripple {
+ position: absolute;
+ left: 50%;
+ bottom: 80px;
+ width: 0;
+ height: 0;
+ border-radius: 50%;
+ border: 2px solid var(--ambient-primary, rgba(135, 206, 235, 0.3));
+ transform: translate(-50%, 50%);
+ pointer-events: none;
+ z-index: 1;
+}
+
+.ripple-fx-enter-active {
+ animation: rippleExpand 0.8s ease-out forwards;
+}
+.ripple-fx-leave-active {
+ opacity: 0;
+ transition: opacity 0.1s;
+}
+
+@keyframes rippleExpand {
+ 0% { width: 0; height: 0; opacity: 0.8; }
+ 100% { width: 500px; height: 500px; opacity: 0; }
+}
+
+/* ── Header ── */
+.chat-header {
+ display: flex;
+ align-items: center;
+ padding: 24px 56px 16px 24px;
flex-shrink: 0;
+ gap: 12px;
+ position: relative;
+ z-index: 2;
}
-/* Memory toast */
+.memory-btn {
+ width: 36px;
+ height: 36px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ border-radius: 50%;
+ color: var(--text-primary);
+ cursor: pointer;
+ transition: background 0.2s, transform 0.15s;
+}
+
+.memory-btn:hover {
+ background: rgba(255, 255, 255, 0.2);
+ transform: scale(1.08);
+}
+
+/* ── Memory toast ── */
.memory-toast {
position: absolute;
top: 64px;
@@ -279,133 +403,142 @@ async function onSend() {
.toast-enter-active {
transition: opacity 0.3s ease, transform 0.3s ease;
}
-
.toast-leave-active {
transition: opacity 0.6s ease, transform 0.6s ease;
}
-
.toast-enter-from {
opacity: 0;
transform: translateX(-50%) translateY(-6px);
}
-
.toast-leave-to {
opacity: 0;
transform: translateX(-50%) translateY(-4px);
}
+/* ── Messages ── */
.messages {
flex: 1;
overflow-y: auto;
- padding: 0 24px 16px;
+ padding: 0 32px 16px;
display: flex;
- flex-direction: column;
- gap: 12px;
+ align-items: center;
+ justify-content: center;
overscroll-behavior: contain;
+ position: relative;
+ z-index: 2;
}
-.chat-empty {
+.msg-center {
text-align: center;
- padding: 60px 0 20px;
- color: var(--text-secondary);
- font-size: 15px;
- line-height: 2;
+ width: 100%;
+ max-width: 560px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20px;
}
-.message {
+/* ── Empty state ── */
+.chat-empty {
+ text-align: center;
+ color: var(--text-secondary);
display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
}
-.message.user {
- justify-content: flex-end;
+.empty-greeting {
+ font-size: 18px;
+ font-weight: 300;
+ color: var(--text-primary);
+ opacity: 0.8;
}
-.message.assistant {
- justify-content: flex-start;
+.empty-subtitle {
+ font-size: 14px;
+ color: var(--text-secondary);
+ opacity: 0.6;
}
-.msg-bubble {
- max-width: 80%;
- padding: 12px 16px;
- border-radius: 18px;
- font-size: 15px;
- line-height: 1.6;
- word-break: break-word;
+/* ── Replace transition ── */
+.msg-replace-enter-active {
+ transition: opacity 0.6s ease, transform 0.6s ease;
}
-
-.message.user .msg-bubble {
- background: var(--accent-warm);
- color: #fff;
- border-bottom-right-radius: 6px;
+.msg-replace-leave-active {
+ transition: opacity 0.3s ease, transform 0.3s ease;
}
-
-.message.assistant .msg-bubble {
- background: rgba(255, 255, 255, 0.1);
- color: var(--text-primary);
- border: 1px solid rgba(255, 255, 255, 0.08);
- border-bottom-left-radius: 6px;
- transition: background 0.6s ease, box-shadow 0.6s ease;
+.msg-replace-enter-from {
+ opacity: 0;
+ transform: translateY(12px);
}
-
-/* Visual effects */
-.effect-ripple {
- animation: effectRipple 3s ease-out forwards;
+.msg-replace-leave-to {
+ opacity: 0;
+ transform: translateY(-8px);
}
-.effect-sunlight {
- animation: effectSunlight 3s ease-out forwards;
+/* ── Text styles ── */
+.msg-text {
+ word-break: break-word;
}
-.effect-calm {
- animation: effectCalm 3s ease-in-out forwards;
+.user-text {
+ font-size: 14px;
+ color: var(--text-secondary);
+ opacity: 0.5;
+ font-weight: 400;
+ line-height: 1.6;
}
-.effect-warm_glow {
- animation: effectWarmGlow 3s ease-out forwards;
+.assistant-text {
+ font-size: 20px;
+ color: var(--text-primary);
+ font-weight: 300;
+ line-height: 1.8;
+ max-width: 480px;
+ transition: text-shadow 0.6s ease;
}
-.effect-gentle_wave {
- animation: effectGentleWave 3s ease-in-out forwards;
-}
+/* ── Visual effects ── */
+.effect-ripple { animation: effectRipple 3s ease-out forwards; }
+.effect-sunlight { animation: effectSunlight 3s ease-out forwards; }
+.effect-calm { animation: effectCalm 3s ease-in-out forwards; }
+.effect-warm_glow { animation: effectWarmGlow 3s ease-out forwards; }
+.effect-gentle_wave { animation: effectGentleWave 3s ease-in-out forwards; }
@keyframes effectRipple {
- 0% { box-shadow: 0 0 0 0 var(--effect-color, rgba(255, 255, 255, 0.1)); }
- 30% { box-shadow: 0 0 14px 6px var(--effect-color, rgba(255, 255, 255, 0.1)); }
- 100% { box-shadow: 0 0 0 0 transparent; }
+ 0% { text-shadow: 0 0 0 var(--effect-color, rgba(255, 255, 255, 0.1)); }
+ 30% { text-shadow: 0 0 20px var(--effect-color, rgba(255, 255, 255, 0.3)); }
+ 100% { text-shadow: 0 0 0 transparent; }
}
-
@keyframes effectSunlight {
- 0% { box-shadow: inset 10px 0 24px -10px var(--effect-color, rgba(255, 215, 0, 0.15)); }
- 100% { box-shadow: inset 0 0 0 0 transparent; }
+ 0% { text-shadow: 4px 0 24px var(--effect-color, rgba(255, 215, 0, 0.3)); }
+ 100% { text-shadow: 0 0 0 transparent; }
}
-
@keyframes effectCalm {
- 0% { transform: scale(1); box-shadow: 0 0 10px 0 var(--effect-color, rgba(255, 255, 255, 0.1)); }
- 50% { transform: scale(1.008); box-shadow: 0 0 14px 3px var(--effect-color, rgba(255, 255, 255, 0.1)); }
- 100% { transform: scale(1); box-shadow: 0 0 0 0 transparent; }
+ 0% { transform: scale(1); text-shadow: 0 0 10px var(--effect-color, rgba(255, 255, 255, 0.2)); }
+ 50% { transform: scale(1.01); text-shadow: 0 0 18px var(--effect-color, rgba(255, 255, 255, 0.3)); }
+ 100% { transform: scale(1); text-shadow: 0 0 0 transparent; }
}
-
@keyframes effectWarmGlow {
- 0% { box-shadow: 0 0 18px 4px var(--effect-color, rgba(255, 215, 0, 0.12)); }
- 100% { box-shadow: 0 0 0 0 transparent; }
+ 0% { text-shadow: 0 0 24px var(--effect-color, rgba(255, 215, 0, 0.3)); }
+ 100% { text-shadow: 0 0 0 transparent; }
}
-
@keyframes effectGentleWave {
- 0%, 100% { transform: translateX(0); }
- 20% { transform: translateX(1.5px); }
- 40% { transform: translateX(-1.5px); }
- 60% { transform: translateX(1px); }
- 80% { transform: translateX(-0.5px); }
+ 0%, 100% { transform: translateY(0); }
+ 25% { transform: translateY(-2px); }
+ 75% { transform: translateY(2px); }
}
-/* Typing indicator */
-.msg-bubble.typing {
+/* ── Typing indicator ── */
+.typing-dots {
display: flex;
- gap: 5px;
- padding: 14px 18px;
+ gap: 6px;
+ justify-content: center;
+ padding: 8px 0;
}
-.msg-bubble.typing span {
+.typing-dots span {
width: 7px;
height: 7px;
border-radius: 50%;
@@ -413,37 +546,41 @@ async function onSend() {
animation: typingDot 1.2s infinite ease-in-out;
}
-.msg-bubble.typing span:nth-child(2) { animation-delay: 0.15s; }
-.msg-bubble.typing span:nth-child(3) { animation-delay: 0.3s; }
+.typing-dots span:nth-child(2) { animation-delay: 0.15s; }
+.typing-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes typingDot {
0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
- 30% { opacity: 1; transform: translateY(-4px); }
+ 30% { opacity: 1; transform: translateY(-6px); }
}
-/* Input area */
+/* ── Input area ── */
.chat-input-area {
display: flex;
gap: 10px;
- padding: 16px 24px 24px;
+ padding: 0 24px 24px;
flex-shrink: 0;
- border-top: 1px solid rgba(255, 255, 255, 0.06);
+ position: relative;
+ z-index: 2;
}
.chat-input {
flex: 1;
- padding: 12px 16px;
+ padding: 14px 20px;
background: rgba(255, 255, 255, 0.08);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.12);
- border-radius: 20px;
+ border-radius: 24px;
color: var(--text-primary);
font-size: 15px;
outline: none;
- transition: border-color 0.2s;
+ transition: border-color 0.2s, box-shadow 0.3s;
}
.chat-input:focus {
border-color: rgba(255, 255, 255, 0.25);
+ box-shadow: 0 0 16px rgba(255, 255, 255, 0.06);
}
.chat-input::placeholder {
@@ -451,8 +588,8 @@ async function onSend() {
}
.send-btn {
- width: 44px;
- height: 44px;
+ width: 48px;
+ height: 48px;
display: flex;
align-items: center;
justify-content: center;
@@ -461,11 +598,15 @@ async function onSend() {
border-radius: 50%;
color: #fff;
cursor: pointer;
- transition: background 0.2s, transform 0.15s;
+ transition: background 0.2s, transform 0.15s, box-shadow 0.2s;
flex-shrink: 0;
+ box-shadow: 0 2px 12px rgba(255, 170, 85, 0.2);
}
-.send-btn:hover { background: var(--accent-warm-hover); }
-.send-btn:active { transform: scale(0.95); }
-.send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
+.send-btn:hover {
+ background: var(--accent-warm-hover);
+ box-shadow: 0 4px 20px rgba(255, 170, 85, 0.3);
+}
+.send-btn:active { transform: scale(0.93); }
+.send-btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }
diff --git a/frontend/src/lib/api/chat.ts b/frontend/src/lib/api/chat.ts
index e7328a9e..e1154f74 100644
--- a/frontend/src/lib/api/chat.ts
+++ b/frontend/src/lib/api/chat.ts
@@ -28,6 +28,29 @@ export interface ChatProfile {
community_interactions: string[]
}
+export interface MemoryFact {
+ id: string
+ content: string
+ category: string
+ created_at: string
+ last_referenced_at: string | null
+}
+
+export interface MemoryFactsResponse {
+ facts: MemoryFact[]
+ total: number
+}
+
+export interface ConversationTurn {
+ user_input: string
+ assistant_response: string
+}
+
+export interface ConversationHistoryResponse {
+ turns: ConversationTurn[]
+ summary: string
+}
+
export function sendChatMessage(data: ChatRequest): Promise {
return apiClient.post('/api/v1/companion/chat', data).then((r) => r.data)
}
@@ -35,3 +58,19 @@ export function sendChatMessage(data: ChatRequest): Promise {
export function getChatProfile(): Promise {
return apiClient.get('/api/v1/companion/profile').then((r) => r.data)
}
+
+export function getMemories(): Promise {
+ return apiClient.get('/api/v1/companion/memories').then((r) => r.data)
+}
+
+export function deleteMemory(id: string): Promise {
+ return apiClient.delete(`/api/v1/companion/memories/${id}`).then(() => undefined)
+}
+
+export function getConversationHistory(): Promise {
+ return apiClient.get('/api/v1/companion/history').then((r) => r.data)
+}
+
+export function clearConversationHistory(): Promise {
+ return apiClient.delete('/api/v1/companion/history').then(() => undefined)
+}
diff --git a/frontend/src/stores/ui.ts b/frontend/src/stores/ui.ts
index cd836995..d7a86bc2 100644
--- a/frontend/src/stores/ui.ts
+++ b/frontend/src/stores/ui.ts
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
import { useAuthStore } from './auth'
-export type PanelName = 'auth' | 'role' | 'memory' | 'community' | 'chat' | 'car' | 'bar' | 'whisper' | 'task' | null
+export type PanelName = 'auth' | 'role' | 'memory' | 'community' | 'chat' | 'car' | 'bar' | 'whisper' | 'task' | 'ai-memory' | null
export type AuthMode = 'login' | 'register' | 'guest'
export const useUiStore = defineStore('ui', () => {