mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: move the terminal fully into the ssh-terminal plugin (B9)
This commit is contained in:
1 parent
535126fd1b
commit
79d77678c9
309 files changed
+41789
-14958
No files matched your search
@@ -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={() =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user