Rust SSH service: russh async client, DashMap session registry, TOFU host key verification, CWD tracking via separate exec channel (never touches terminal stream), base64 event emission for terminal I/O. 52/52 tests passing. Vue 3 frontend: ported from Wails v3 to Tauri v2 — useTerminal composable with streaming TextDecoder + rAF batching, session store with multi-connection support, connection store/tree, sidebar, tab bar, status bar, keyboard shortcuts. All Wails imports replaced with Tauri API equivalents. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.2 KiB
Vue
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>
|