feat: move the terminal fully into the ssh-terminal plugin (B9)

This commit is contained in:
LukeGus committed 2026-09-23 21:41:22 -05:00
1 parent 535126fd1b
commit 79d77678c9
309 files changed
+41789 -14958

No files matched your search

+6 -33
View File
@@ -13,7 +13,6 @@ import {
import {
Server,
Settings,
Terminal,
Globe,
Plus,
MessagesSquare,
@@ -66,12 +65,11 @@ function getSshActions(host: Host): {
}[] {
return [
// --- tmux-monitor --- opt-in per host, off by default
host.enableTerminal !== false &&
host.enableTmuxMonitor && {
type: "tmux_monitor",
icon: Layers,
label: "Tmux Monitor",
},
host.enableTmuxMonitor && {
type: "tmux_monitor",
icon: Layers,
label: "Tmux Monitor",
},
].filter(Boolean) as {
type: TabType;
icon: React.ElementType;
@@ -173,11 +171,7 @@ export function CommandPalette({
setSelectedValue(`host-${firstHost.id}`);
return;
}
setSelectedValue(
window.electronAPI?.isElectron
? "quick-action-local-terminal"
: "quick-action-add-host",
);
setSelectedValue("quick-action-add-host");
}, [filteredHosts, isOpen, search]);
const activeTargetTab =
@@ -235,27 +229,6 @@ export function CommandPalette({
heading={t("commandPalette.quickActions")}
className="px-2"
>
{window.electronAPI?.isElectron && (
<CommandItem
value="quick-action-local-terminal"
onSelect={() =>
handleAction(() => onOpenTab("local-terminal"))
}
className="group flex items-center gap-3 px-3 py-2.5 rounded-none hover:bg-accent-brand/10 cursor-pointer"
>
<div className="size-8 rounded-none bg-muted flex items-center justify-center group-hover:bg-accent-brand/20 transition-colors">
<Terminal className="size-4 text-accent-brand" />
</div>
<div className="flex flex-col flex-1">
<span className="text-sm font-semibold">
{t("commandPalette.localTerminal")}
</span>
<span className="text-xs text-muted-foreground">
{t("commandPalette.localTerminalDesc")}
</span>
</div>
</CommandItem>
)}
<CommandItem
value="quick-action-add-host"
onSelect={() =>
+5 -6
View File
@@ -36,13 +36,11 @@ import type { Tab, TabType, SplitMode } from "@/types/ui-types";
import { SPLIT_MODES, PANE_COUNTS } from "@/lib/theme";
/**
* Tabs holding a live connection that can be refreshed and shared: local
* terminals and registered session tabs. Serial sessions are not shareable.
* Tabs holding a live connection that can be refreshed and shared: the
* registered session tabs. Serial sessions are not shareable.
*/
function isConnectionTab(type: TabType): boolean {
return (
type === "local-terminal" || (type !== "serial" && isSessionTabType(type))
);
return type !== "serial" && isSessionTabType(type);
}
export function TabBar({
@@ -689,7 +687,8 @@ export function TabBar({
{t("nav.refreshTab")}
</button>
)}
{ctxTab.type === "terminal" &&
{typeof ctxTab.terminalRef?.current?.openFileManager ===
"function" &&
ctxTab.host &&
onOpenFileManager && (
<button
+6 -5
View File
@@ -1,4 +1,5 @@
/* eslint-disable react-refresh/only-export-components */
import { getTabType } from "./tab-registry";
import React, {
createContext,
useContext,
@@ -175,8 +176,9 @@ export function TabProvider({ children }: TabProviderProps) {
const id = nextTabId.current++;
const instanceId = `tab_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const isCommandTarget = !!getTabType(tabData.type)?.commandTarget;
const needsUniqueTitle =
tabData.type === "terminal" || tabData.type === "file_manager";
isCommandTarget || tabData.type === "file_manager";
const effectiveTitle = needsUniqueTitle
? computeUniqueTitle(tabData.type, tabData.title)
: tabData.type === "tmux_monitor" // --- tmux-monitor ---
@@ -187,10 +189,9 @@ export function TabProvider({ children }: TabProviderProps) {
id,
instanceId,
title: effectiveTitle,
terminalRef:
tabData.type === "terminal"
? React.createRef<TerminalRefHandle>()
: undefined,
terminalRef: isCommandTarget
? React.createRef<TerminalRefHandle>()
: undefined,
hostConfig: tabData.hostConfig
? {
...tabData.hostConfig,
+2
View File
@@ -3,6 +3,8 @@ import { createRegistry } from "@/lib/registry";
import type { TabShellCallbacks } from "./tab-registry";
export interface PanelRenderProps {
/** The command-target tab the user is working in, if any. */
targetTab?: import("@/types/ui-types").Tab;
active: boolean;
shell: TabShellCallbacks;
setEditing: (editing: boolean) => void;
-3
View File
@@ -25,7 +25,6 @@ const CORE_CAPTURABLE = new Set([
"termix-id",
"session-logs",
"macros",
"history",
"ssh-tools",
]);
@@ -36,14 +35,12 @@ const CORE_SINGLETON = new Set([
"termix-id",
"session-logs",
"macros",
"history",
"ssh-tools",
]);
/** Core tab types that are never part of a saved arrangement. */
const CORE_UNSAVED = new Set([
"dashboard",
"local-terminal",
"host-manager",
"user-profile",
"admin-settings",
+7 -1
View File
@@ -23,7 +23,7 @@ export interface TabShellCallbacks {
) => void;
closeTab: (tabId: string) => void;
renameTab: (tabId: string, label: string) => void;
openTerminalTab: (host: Host, path?: string) => void;
openRailView: (id: string) => void;
/** Closes a rail view wherever it is shown, e.g. when its feature goes away. */
closeRailView: (id: string) => void;
@@ -69,6 +69,12 @@ export interface TabTypeDef {
panelFrame?: boolean;
/** False keeps this tab out of saved layouts and workspaces. */
inLayouts?: boolean;
/** History, macros and SSH tools act on the last one of these focused. */
commandTarget?: boolean;
/** Paints its own background; its frame stays transparent. */
ownBackground?: boolean;
/** Every open is a new tab. */
multiInstance?: boolean;
preload?: () => Promise<unknown>;
}
+3 -29
View File
@@ -8,9 +8,7 @@ import {
Settings,
Usb,
User,
TerminalSquare,
Layers, // --- tmux-monitor ---
Clock,
Fingerprint,
Hammer,
ScrollText,
@@ -38,11 +36,7 @@ const CollabRoomTab = lazy(() =>
default: m.CollabRoomTab,
})),
);
const LocalTerminal = lazy(() =>
import("@/features/local-terminal/LocalTerminal").then((m) => ({
default: m.LocalTerminal,
})),
);
const loadTmuxMonitor = () =>
import("@/features/tmux-monitor/TmuxMonitor").then((m) => ({
default: m.TmuxMonitor,
@@ -75,9 +69,7 @@ const SessionLogsPanel = lazy(() =>
const MacrosPanel = lazy(() =>
import("@/sidebar/MacrosPanel").then((m) => ({ default: m.MacrosPanel })),
);
const HistoryPanel = lazy(() =>
import("@/sidebar/HistoryPanel").then((m) => ({ default: m.HistoryPanel })),
);
const SshToolsPanel = lazy(() =>
import("@/sidebar/SshToolsPanel").then((m) => ({ default: m.SshToolsPanel })),
);
@@ -146,8 +138,6 @@ export function tabIcon(type: TabType) {
switch (type) {
case "dashboard":
return <LayoutDashboard className="size-3.5" />;
case "local-terminal":
return <TerminalSquare className="size-3.5" />;
case "host-manager":
return <Server className="size-3.5" />;
case "user-profile":
@@ -169,8 +159,7 @@ export function tabIcon(type: TabType) {
return <ScrollText className="size-3.5" />;
case "macros":
return <Braces className="size-3.5" />;
case "history":
return <Clock className="size-3.5" />;
case "ssh-tools":
return <Hammer className="size-3.5" />;
case "split-screen":
@@ -262,11 +251,6 @@ export function renderTabContent(tab: Tab, context: TabRenderContext) {
/>,
);
case "local-terminal":
return withTabSuspense(
<LocalTerminal instanceId={tab.instanceId} isVisible={isVisible} />,
);
// --- tmux-monitor ---
case "tmux_monitor":
return withTabSuspense(
@@ -324,16 +308,6 @@ export function renderTabContent(tab: Tab, context: TabRenderContext) {
</PanelTabFrame>,
);
case "history":
return withTabSuspense(
<PanelTabFrame>
<HistoryPanel
terminalTabs={panelProps?.terminalTabs ?? []}
activeTabId={panelProps?.targetTerminalTabId ?? ""}
/>
</PanelTabFrame>,
);
case "ssh-tools":
return withTabSuspense(
<PanelTabFrame>