Multi-session tabs + home navigation: - Tab bar with Home button persists above sessions - Clicking Home shows the underlying page (hosts, vault, etc.) - Clicking a session tab switches back to that session - Header nav links also trigger home view - Sessions stay alive in background when viewing home Monaco editor in popup window: - Opening a file in SFTP launches a detached popup with Monaco - Full syntax highlighting, minimap, Ctrl+S save - File tree stays visible while editing - Toolbar with save/close buttons and dirty indicator Drag-and-drop upload: - Drop files anywhere on the SFTP sidebar to upload - Visual overlay with dashed border on drag-over - Supports multiple files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
2.9 KiB
Vue
88 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useSessionStore } from '~/stores/session.store'
|
|
|
|
const sessions = useSessionStore()
|
|
|
|
// Sessions with pending-XXX IDs are mounting TerminalInstance which will get a real UUID.
|
|
// SFTP sidebar only connects once we have a real (non-pending) backend session ID.
|
|
function isRealSession(id: string) {
|
|
return !id.startsWith('pending-')
|
|
}
|
|
|
|
// Per-session refs to RdpCanvas instances (keyed by session.id)
|
|
// Used to forward toolbar actions (clipboard, disconnect) to the canvas
|
|
const rdpCanvasRefs = ref<Record<string, any>>({})
|
|
|
|
function setRdpRef(sessionId: string, el: any) {
|
|
if (el) {
|
|
rdpCanvasRefs.value[sessionId] = el
|
|
} else {
|
|
delete rdpCanvasRefs.value[sessionId]
|
|
}
|
|
}
|
|
|
|
function handleRdpDisconnect(sessionId: string) {
|
|
rdpCanvasRefs.value[sessionId]?.disconnect()
|
|
}
|
|
|
|
function handleRdpClipboard(sessionId: string, text: string) {
|
|
rdpCanvasRefs.value[sessionId]?.sendClipboard(text)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Session container — always in DOM when sessions exist -->
|
|
<div v-if="sessions.hasSessions" class="absolute inset-0 flex flex-col z-10" :class="sessions.showHome ? '' : 'bg-gray-950'">
|
|
<!-- Tab bar — always visible when sessions exist -->
|
|
<TerminalTabs />
|
|
|
|
<!-- Session panels — hidden when Home tab is active, underlying page shows through -->
|
|
<div v-show="!sessions.showHome" class="flex-1 overflow-hidden relative bg-gray-950">
|
|
<div
|
|
v-for="session in sessions.sessions"
|
|
:key="session.key"
|
|
v-show="session.id === sessions.activeSessionId"
|
|
class="absolute inset-0 flex"
|
|
>
|
|
<!-- SSH session: SFTP sidebar + terminal -->
|
|
<template v-if="session.protocol === 'ssh'">
|
|
<SftpSidebar
|
|
v-if="isRealSession(session.id)"
|
|
:session-id="session.id"
|
|
/>
|
|
<div class="flex-1 overflow-hidden">
|
|
<TerminalInstance
|
|
:session-id="session.id"
|
|
:host-id="session.hostId"
|
|
:host-name="session.hostName"
|
|
:color="session.color"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- RDP session: Guacamole canvas + floating toolbar -->
|
|
<template v-else-if="session.protocol === 'rdp'">
|
|
<div class="flex-1 overflow-hidden relative">
|
|
<RdpCanvas
|
|
:ref="(el) => setRdpRef(session.id, el)"
|
|
:host-id="session.hostId"
|
|
:host-name="session.hostName"
|
|
:session-id="session.id"
|
|
:color="session.color"
|
|
/>
|
|
<RdpToolbar
|
|
:host-name="session.hostName"
|
|
@disconnect="handleRdpDisconnect(session.id)"
|
|
@send-clipboard="(text) => handleRdpClipboard(session.id, text)"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Transfer status bar -->
|
|
<TransferStatus v-show="!sessions.showHome" :transfers="[]" />
|
|
</div>
|
|
</template>
|