wraith/frontend/src/components/terminal/TerminalView.vue
Vantz Stockwell a6db3ddfa4 feat: fix 6 frontend issues (F-1, F-5, F-6, F-7, F-10, F-11)
F-1 (Theme Application): Theme selection now applies to all active xterm.js
terminals at runtime via session store reactive propagation. TerminalView
watches sessionStore.activeTheme and calls terminal.options.theme = {...}.

F-5 (Tab Badges): isRootUser() now checks session.username, connection
options JSON, and connection tags — no longer hardcoded to false.

F-6 (Keyboard Shortcuts): Added Ctrl+W (close tab), Ctrl+Tab / Ctrl+Shift+Tab
(next/prev tab), Ctrl+1–9 (tab by index), Ctrl+B (toggle sidebar). Input
field guard prevents shortcuts from firing while typing.

F-7 (Status Bar Dimensions): StatusBar now reads live cols×rows from
sessionStore.activeDimensions. TerminalView hooks onResize to call
sessionStore.setTerminalDimensions(). Falls back to "120×40" until first resize.

F-10 (Multiple Sessions): Removed the "already connected" early-return guard.
Multiple SSH/RDP sessions to the same host are now allowed. Disambiguated tab
names auto-generated: "Asgard", "Asgard (2)", "Asgard (3)", etc.

F-11 (First-Run MobaConf): onMounted checks connectionStore.connections.length
after loadAll(). If empty, shows a dialog offering to import from MobaXTerm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 13:41:28 -04:00

90 lines
2.2 KiB
Vue

<template>
<div
ref="containerRef"
class="terminal-container"
@focus="handleFocus"
/>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import { useTerminal } from "@/composables/useTerminal";
import { useSessionStore } from "@/stores/session.store";
import "@/assets/css/terminal.css";
const props = defineProps<{
sessionId: string;
isActive: boolean;
}>();
const sessionStore = useSessionStore();
const containerRef = ref<HTMLElement | null>(null);
const { terminal, mount, fit } = useTerminal(props.sessionId);
onMounted(() => {
if (containerRef.value) {
mount(containerRef.value);
}
// Apply the current theme immediately if one is already active
if (sessionStore.activeTheme) {
applyTheme();
}
// Track terminal dimensions in the session store
terminal.onResize(({ cols, rows }) => {
sessionStore.setTerminalDimensions(props.sessionId, cols, rows);
});
});
// Re-fit and focus terminal when this tab becomes active
watch(
() => props.isActive,
(active) => {
if (active) {
// nextTick is not needed — fit and focus happen after the DOM update
setTimeout(() => {
fit();
terminal.focus();
}, 0);
}
},
);
/** Apply the session store's active theme to this terminal instance. */
function applyTheme(): void {
const theme = sessionStore.activeTheme;
if (!theme) return;
terminal.options.theme = {
background: theme.background,
foreground: theme.foreground,
cursor: theme.cursor,
black: theme.black,
red: theme.red,
green: theme.green,
yellow: theme.yellow,
blue: theme.blue,
magenta: theme.magenta,
cyan: theme.cyan,
white: theme.white,
brightBlack: theme.brightBlack,
brightRed: theme.brightRed,
brightGreen: theme.brightGreen,
brightYellow: theme.brightYellow,
brightBlue: theme.brightBlue,
brightMagenta: theme.brightMagenta,
brightCyan: theme.brightCyan,
brightWhite: theme.brightWhite,
};
}
// Watch for theme changes in the session store and apply to this terminal
watch(() => sessionStore.activeTheme, (newTheme) => {
if (newTheme) applyTheme();
});
function handleFocus(): void {
terminal.focus();
}
</script>