Files
Termix/src/types/ui-types.ts
T

330 lines
8.8 KiB
TypeScript

import type { SSHAuthType, TerminalConfig } from "./index.js";
import type { HostAuthOverrides } from "./auth-protocols.js";
export type Host = {
id: string;
name: string;
username: string;
ip: string;
port: number;
folder: string;
/** Sub-host nesting: the id of the host this one is organized under, if any. */
parentHostId?: string | null;
/**
* Sub-hosts nested under this host, populated client-side by buildHostTree.
* A host with children still renders and behaves as a normal, connectable
* HostItem row -- this only adds an expand/collapse chevron for its nested
* children, it never wraps the host in a synthetic folder node.
*/
childHosts?: Host[];
online: boolean;
status?: "online" | "reachable" | "offline" | "unknown";
cpu: number | null;
ram: number | null;
lastAccess: string;
tags?: string[];
authType: SSHAuthType;
shareSshAuth?: boolean;
credentialId?: string;
overrideCredentialUsername?: boolean;
password?: string;
hasPassword?: boolean;
sudoPassword?: string;
hasSudoPassword?: boolean;
hasKey?: boolean;
hasKeyPassword?: boolean;
key?: string;
keyPassword?: string;
keyType?: string;
notes?: string;
pin?: boolean;
/** Quick connect only: core can save this host as-is. */
quickConnectSavable?: boolean;
sortOrder?: number | null;
/** Stable identity across a desktop/server sync pair. */
syncId?: string | null;
terminalConfig?: Partial<TerminalConfig>;
useSocks5?: boolean;
socks5Host?: string;
socks5Port?: number;
connectionOrigin?: "local" | "remote" | null;
socks5Username?: string;
socks5Password?: string;
socks5ProxyChain?: {
host: string;
port: number;
type: 4 | 5 | "http" | "socks4" | "socks5";
username?: string;
password?: string;
}[];
/** hostid is a legacy lowercase spelling still present in stored rows. */
jumpHosts?: { hostId: string; hostid?: string }[];
portKnockSequence?: {
port: number;
protocol: "tcp" | "udp";
delay: number;
}[];
statusCheckEnabled?: boolean;
/** Seconds between status checks; null follows the global setting. */
statusCheckInterval?: number | null;
quickActions: { name: string; snippetId: string }[];
enableSsh: boolean;
sshPort: number;
rdpAuthType?: "direct" | "credential" | "none";
rdpCredentialId?: string;
rdpUser?: string;
rdpPassword?: string;
hasRdpPassword?: boolean;
domain?: string;
vncAuthType?: "direct" | "credential";
vncCredentialId?: string;
vncPassword?: string;
hasVncPassword?: boolean;
vncUser?: string;
telnetAuthType?: "direct" | "credential";
telnetCredentialId?: string;
telnetUser?: string;
telnetPassword?: string;
hasTelnetPassword?: boolean;
/** Host-scope plugin settings, keyed by plugin id. Secrets are redacted. */
pluginSettings?: Record<string, Record<string, unknown>>;
forceKeyboardInteractive?: boolean;
isShared?: boolean;
authOverrides?: HostAuthOverrides<string>;
permissionLevel?: SharePermissionLevel;
sharedExpiresAt?: string;
ownerUsername?: string;
};
export type SharePermissionLevel = "connect" | "view" | "edit" | "manage";
export type Credential = {
id: string;
name: string;
username: string;
type: "password" | "key";
value?: string;
password?: string;
publicKey?: string;
passphrase?: string;
description?: string;
folder?: string;
tags?: string[];
pin?: boolean;
sortOrder?: number | null;
certPublicKey?: string;
/** Set when someone else owns this credential and shared it with you. */
isShared?: boolean;
ownerUsername?: string | null;
permissionLevel?: "use" | "manage";
};
export type HostFolder = {
name: string;
children: (Host | HostFolder)[];
path?: string;
color?: string;
icon?: string;
credentialId?: number | null;
sortOrder?: number | null;
};
/** Core's own tab types. Plugins register theirs at runtime. */
type KnownTabType =
| "dashboard"
| "host-manager"
| "user-profile"
| "admin-settings"
// Rail panels that can also open full-width in the main area.
| "macros"
| "ssh-tools"
| "split-screen";
/**
* TabType covers every built-in tab plus any plugin-contributed tab id.
* `string & {}` (rather than plain `string`) keeps IDE autocomplete
* suggesting the known members while still accepting an arbitrary id, since
* a bare `string` would widen every literal and kill autocomplete entirely.
* Plugin tab ids are resolved at render time via the tab-component registry
* in tabUtils.tsx, not through this type.
*/
export type TabType = KnownTabType | (string & {});
export type Tab = {
id: string;
instanceId: string;
type: TabType;
label: string;
customLabel?: string;
host?: Host;
openedAt: number;
restoredSessionId?: string | null;
/** Set when this tab joins someone else's live shared session instead of connecting/attaching its own. */
joinSharedSessionId?: string | null;
joinShareId?: string | null;
initialFilePath?: string;
/** Directory to open a Files tab into, distinct from initialFilePath (a specific file to open in an editor window). */
initialPath?: string;
/** Payload owned by the tab's plugin, e.g. which fleet or endpoint it shows. */
data?: Record<string, unknown>;
/** Present only on a split-screen container tab. Pane ids reference live child tabs. */
splitConfig?: SplitTabConfig;
/** Hides this session from the top-level tab bar while it belongs to a split tab. */
parentSplitTabId?: string;
terminalRef?: import("react").RefObject<{
disconnect?: () => void;
isConnected?: () => boolean;
sendInput?: (data: string) => void;
subscribeOutput?: (listener: (data: string) => void) => () => void;
paste?: (text: string) => void;
reconnect?: () => void;
fit?: () => void;
notifyResize?: () => void;
refresh?: () => void;
getApplicationCursorKeysMode?: () => boolean;
openFileManager?: () => void;
focus?: () => void;
} | null>;
};
/** Core cards are named here; plugin cards add their own ids. */
export type DashboardCardId =
| "stats_bar"
| "counters_bar"
| "quick_actions"
| "host_status"
| "recent_activity"
| (string & {});
export type DashboardCardConfig = {
id: DashboardCardId;
label: string;
description: string;
defaultEnabled: boolean;
};
export type AdminSection =
| "general"
| "users"
| "sessions"
| "roles"
| "host-defaults"
| "branding"
| "database"
| "api-keys"
| "audit-log"
| "ssl";
export type ThemeId =
| "dark"
| "light"
| "system"
| "dracula"
| "catppuccin"
| "nord"
| "solarized"
| "tokyo-night"
| "one-dark"
| "gruvbox";
export type FontSizeId = "xs" | "sm" | "md" | "lg" | "xl";
export type UiFontId =
| "jetbrains-mono"
| "system-sans"
| "fira-code"
| "source-code-pro"
| "caskaydia-cove";
/** A tools panel view: core's own, or a rail panel a plugin registered. */
export type ToolsTab = "ssh-tools" | "macros" | "split-screen" | (string & {});
export type SplitMode =
| "none"
| "2-way"
| "2-way-horizontal"
| "3-way"
| "3-way-horizontal"
| "4-way"
| "5-way"
| "6-way";
export type SplitTabConfig = {
mode: Exclude<SplitMode, "none">;
paneTabIds: (string | null)[];
rowSizes: number[];
rowColSizes: number[][];
};
export type WorkspaceTabSnapshot = {
/** Stable key within the saved tab list, not the live Tab.id (which is regenerated on every open). */
slotId: string;
type: TabType;
/** Set for host-bound tab types, resolved by Host.syncId on apply. */
hostSyncId?: string | null;
/** Denormalized snapshot for display and graceful-skip messaging if the host is later deleted. */
hostNameSnapshot?: string | null;
label: string;
customLabel?: string;
initialFilePath?: string;
initialPath?: string;
/** Read from payloads saved before tabs carried `data`. */
fleetId?: number;
/** The tab's plugin payload. */
data?: Record<string, unknown>;
};
/** One dock's arrangement. `view` is a RailView, or null when the dock is closed. */
type WorkspaceDockState = {
view: string | null;
open: boolean;
width: number;
};
export type WorkspacePayload = {
version: 1;
tabs: WorkspaceTabSnapshot[];
activeSlotId: string | null;
splitMode: SplitMode;
/** Indexed identically to AppShell's paneTabIds (length 6), holding slotId instead of a live Tab.id. */
paneTabIds: (string | null)[];
rowSizes: number[];
rowColSizes: number[][];
/** Sidebar arrangement, so a workspace restores the whole layout and not just tabs. */
sidebar?: {
left: WorkspaceDockState;
right: WorkspaceDockState;
};
};
export type Snippet = {
id: number;
name: string;
description?: string;
content: string;
folder: string | null;
order: number;
hostIds?: number[];
isNote?: boolean;
};
const FOLDER_ICONS = [
"folder",
"server",
"cloud",
"database",
"box",
"network",
"copy",
"settings",
"cpu",
"globe",
] as const;
type FolderIconId = (typeof FOLDER_ICONS)[number];