fix: 6 UX regressions — popups, themes, cursor, selection, status bar
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m51s
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m51s
Popup windows (tools/editor/help):
- CSP script-src 'self' blocked Tauri's inline IPC bridge scripts in
child WebviewWindows. Added 'unsafe-inline' to script-src. Still
restrictive (was null before SEC-4).
Theme application:
- Watcher on sessionStore.activeTheme needed { deep: true } — Pinia
reactive proxy identity doesn't change on object replacement
- LocalTerminalView.vue had ZERO theme support — added full applyTheme()
with watcher and mount-time application
- Container background now syncs with theme (was stuck on CSS variable)
Cursor blink:
- terminal.focus() after mount in useTerminal.ts — terminal opened
without focus, xterm.js rendered static outline instead of blinking block
Selection highlighting:
- applyTheme() was overwriting theme without selectionBackground/
selectionForeground/selectionInactiveBackground — selection invisible
after any theme change
- Removed !important from terminal.css that overrode canvas selection
- Bumped default selection opacity 0.3 → 0.4
Status bar:
- h-6 text-[10px] → h-8 text-xs (24px/10px → 32px/12px)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6acd674905
commit
aa2ef88ed7
@ -23,7 +23,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; connect-src 'self' ipc: http://ipc.localhost"
|
"csp": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; connect-src 'self' ipc: http://ipc.localhost"
|
||||||
},
|
},
|
||||||
"withGlobalTauri": false
|
"withGlobalTauri": false
|
||||||
},
|
},
|
||||||
|
|||||||
@ -20,9 +20,11 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Selection styling */
|
/* Selection styling — let xterm.js theme handle selection colors.
|
||||||
|
The !important override was removed because it conflicts with
|
||||||
|
theme-driven selectionBackground set via terminal.options.theme. */
|
||||||
.terminal-container .xterm-selection div {
|
.terminal-container .xterm-selection div {
|
||||||
background-color: rgba(88, 166, 255, 0.3) !important;
|
background-color: rgba(88, 166, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cursor styling */
|
/* Cursor styling */
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-6 flex items-center justify-between px-4 bg-[var(--wraith-bg-secondary)] border-t border-[var(--wraith-border)] text-[10px] text-[var(--wraith-text-muted)] shrink-0">
|
<div class="h-8 flex items-center justify-between px-4 bg-[var(--wraith-bg-secondary)] border-t border-[var(--wraith-border)] text-xs text-[var(--wraith-text-muted)] shrink-0">
|
||||||
<!-- Left: connection info -->
|
<!-- Left: connection info -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<template v-if="sessionStore.activeSession">
|
<template v-if="sessionStore.activeSession">
|
||||||
|
|||||||
@ -112,6 +112,8 @@ export interface ThemeDefinition {
|
|||||||
brightMagenta: string;
|
brightMagenta: string;
|
||||||
brightCyan: string;
|
brightCyan: string;
|
||||||
brightWhite: string;
|
brightWhite: string;
|
||||||
|
selectionBackground?: string;
|
||||||
|
selectionForeground?: string;
|
||||||
isBuiltin?: boolean;
|
isBuiltin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
|
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { useTerminal } from "@/composables/useTerminal";
|
import { useTerminal } from "@/composables/useTerminal";
|
||||||
|
import { useSessionStore } from "@/stores/session.store";
|
||||||
import "@/assets/css/terminal.css";
|
import "@/assets/css/terminal.css";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -19,13 +20,55 @@ const props = defineProps<{
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
const containerRef = ref<HTMLElement | null>(null);
|
const containerRef = ref<HTMLElement | null>(null);
|
||||||
const { terminal, mount, fit, destroy } = useTerminal(props.sessionId, "pty");
|
const { terminal, mount, fit, destroy } = useTerminal(props.sessionId, "pty");
|
||||||
|
|
||||||
|
/** Apply the session store's active theme to this local terminal instance. */
|
||||||
|
function applyTheme(): void {
|
||||||
|
const theme = sessionStore.activeTheme;
|
||||||
|
if (!theme) return;
|
||||||
|
terminal.options.theme = {
|
||||||
|
background: theme.background,
|
||||||
|
foreground: theme.foreground,
|
||||||
|
cursor: theme.cursor,
|
||||||
|
cursorAccent: theme.background,
|
||||||
|
selectionBackground: theme.selectionBackground ?? "rgba(88, 166, 255, 0.4)",
|
||||||
|
selectionForeground: theme.selectionForeground ?? "#ffffff",
|
||||||
|
selectionInactiveBackground: theme.selectionBackground ?? "rgba(88, 166, 255, 0.2)",
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (containerRef.value) {
|
||||||
|
containerRef.value.style.backgroundColor = theme.background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (containerRef.value) {
|
if (containerRef.value) {
|
||||||
mount(containerRef.value);
|
mount(containerRef.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply current theme immediately if one is already active
|
||||||
|
if (sessionStore.activeTheme) {
|
||||||
|
applyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fit();
|
fit();
|
||||||
terminal.focus();
|
terminal.focus();
|
||||||
@ -56,6 +99,11 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Watch for theme changes and apply to this local terminal
|
||||||
|
watch(() => sessionStore.activeTheme, (newTheme) => {
|
||||||
|
if (newTheme) applyTheme();
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
destroy();
|
destroy();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -185,6 +185,10 @@ function applyTheme(): void {
|
|||||||
background: theme.background,
|
background: theme.background,
|
||||||
foreground: theme.foreground,
|
foreground: theme.foreground,
|
||||||
cursor: theme.cursor,
|
cursor: theme.cursor,
|
||||||
|
cursorAccent: theme.background,
|
||||||
|
selectionBackground: theme.selectionBackground ?? "rgba(88, 166, 255, 0.4)",
|
||||||
|
selectionForeground: theme.selectionForeground ?? "#ffffff",
|
||||||
|
selectionInactiveBackground: theme.selectionBackground ?? "rgba(88, 166, 255, 0.2)",
|
||||||
black: theme.black,
|
black: theme.black,
|
||||||
red: theme.red,
|
red: theme.red,
|
||||||
green: theme.green,
|
green: theme.green,
|
||||||
@ -202,12 +206,19 @@ function applyTheme(): void {
|
|||||||
brightCyan: theme.brightCyan,
|
brightCyan: theme.brightCyan,
|
||||||
brightWhite: theme.brightWhite,
|
brightWhite: theme.brightWhite,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sync the container background so areas outside the canvas match the theme
|
||||||
|
if (containerRef.value) {
|
||||||
|
containerRef.value.style.backgroundColor = theme.background;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for theme changes in the session store and apply to this terminal
|
// Watch for theme changes in the session store and apply to this terminal.
|
||||||
|
// Uses deep comparison because the theme is an object — a shallow watch may miss
|
||||||
|
// updates if Pinia returns the same reactive proxy wrapper after reassignment.
|
||||||
watch(() => sessionStore.activeTheme, (newTheme) => {
|
watch(() => sessionStore.activeTheme, (newTheme) => {
|
||||||
if (newTheme) applyTheme();
|
if (newTheme) applyTheme();
|
||||||
});
|
}, { deep: true });
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (resizeDisposable) {
|
if (resizeDisposable) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const defaultTheme = {
|
|||||||
foreground: "#e0e0e0",
|
foreground: "#e0e0e0",
|
||||||
cursor: "#58a6ff",
|
cursor: "#58a6ff",
|
||||||
cursorAccent: "#0d1117",
|
cursorAccent: "#0d1117",
|
||||||
selectionBackground: "rgba(88, 166, 255, 0.3)",
|
selectionBackground: "rgba(88, 166, 255, 0.4)",
|
||||||
selectionForeground: "#ffffff",
|
selectionForeground: "#ffffff",
|
||||||
black: "#0d1117",
|
black: "#0d1117",
|
||||||
red: "#f85149",
|
red: "#f85149",
|
||||||
@ -155,6 +155,7 @@ export function useTerminal(sessionId: string, backend: 'ssh' | 'pty' = 'ssh'):
|
|||||||
// cell widths — producing tiny dashes and 200+ column terminals.
|
// cell widths — producing tiny dashes and 200+ column terminals.
|
||||||
document.fonts.ready.then(() => {
|
document.fonts.ready.then(() => {
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
|
terminal.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Right-click paste on the terminal's DOM element
|
// Right-click paste on the terminal's DOM element
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user