fix: infinite remount loop — use stable key for session components
When replaceSession changed the session ID from pending-XXX to a real UUID, Vue's :key="session.id" treated it as a new element, destroyed and recreated TerminalInstance, which called connectToHost again, got another UUID, replaced again — infinite loop. Added a stable `key` field to sessions that never changes after creation, used as the Vue :key instead of the mutable `id`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4ccf138744
commit
aa457b54d4
@ -41,7 +41,7 @@ function handleRdpClipboard(sessionId: string, text: string) {
|
||||
<div class="flex-1 overflow-hidden relative">
|
||||
<div
|
||||
v-for="session in sessions.sessions"
|
||||
:key="session.id"
|
||||
:key="session.key"
|
||||
v-show="session.id === sessions.activeSessionId"
|
||||
class="absolute inset-0 flex"
|
||||
>
|
||||
|
||||
@ -8,7 +8,7 @@ const sessions = useSessionStore()
|
||||
<div class="flex h-8 bg-gray-950 border-b border-gray-800 overflow-x-auto shrink-0">
|
||||
<SessionTab
|
||||
v-for="session in sessions.sessions"
|
||||
:key="session.id"
|
||||
:key="session.key"
|
||||
:session="session"
|
||||
:is-active="session.id === sessions.activeSessionId"
|
||||
@activate="sessions.setActive(session.id)"
|
||||
|
||||
@ -182,6 +182,7 @@ export function useRdp() {
|
||||
// Wire tunnel callbacks
|
||||
tunnel.onConnected = (_resolvedHostId: number, resolvedHostName: string) => {
|
||||
sessions.addSession({
|
||||
key: sessionId,
|
||||
id: sessionId,
|
||||
hostId,
|
||||
hostName: resolvedHostName || hostName,
|
||||
|
||||
@ -68,7 +68,7 @@ export function useTerminal() {
|
||||
switch (msg.type) {
|
||||
case 'connected':
|
||||
// Replace the pending placeholder with the real backend session
|
||||
sessions.replaceSession(pendingSessionId, { id: msg.sessionId, hostId, hostName, protocol, color, active: true })
|
||||
sessions.replaceSession(pendingSessionId, { key: pendingSessionId, id: msg.sessionId, hostId, hostName, protocol, color, active: true })
|
||||
// Send initial terminal size
|
||||
ws!.send(JSON.stringify({ type: 'resize', sessionId: msg.sessionId, cols: term.cols, rows: term.rows }))
|
||||
break
|
||||
|
||||
@ -90,6 +90,7 @@ function connectHost(host: any) {
|
||||
}
|
||||
const pendingId = `pending-${Date.now()}`
|
||||
sessions.addSession({
|
||||
key: pendingId,
|
||||
id: pendingId,
|
||||
hostId: host.id,
|
||||
hostName: host.name,
|
||||
@ -107,6 +108,7 @@ function handleQuickConnect(params: { hostname: string; port: number; username:
|
||||
: params.hostname
|
||||
|
||||
sessions.addSession({
|
||||
key: sessionId,
|
||||
id: sessionId,
|
||||
hostId: null,
|
||||
hostName: displayName,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
interface Session {
|
||||
id: string // uuid from backend
|
||||
key: string // stable Vue key — never changes after creation
|
||||
id: string // uuid from backend (starts as pending-XXX, replaced with real UUID)
|
||||
hostId: number
|
||||
hostName: string
|
||||
protocol: 'ssh' | 'rdp'
|
||||
@ -32,6 +33,8 @@ export const useSessionStore = defineStore('sessions', {
|
||||
replaceSession(oldId: string, newSession: Session) {
|
||||
const idx = this.sessions.findIndex(s => s.id === oldId)
|
||||
if (idx !== -1) {
|
||||
// Preserve the stable key so Vue doesn't remount the component
|
||||
newSession.key = this.sessions[idx].key
|
||||
this.sessions[idx] = newSession
|
||||
} else {
|
||||
this.sessions.push(newSession)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user