mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
fix: finish 2.9.0 plugin moves and get every check green
This commit is contained in:
1 parent
8c1e02af89
commit
6489b62ece
335 files changed
+29878
-17749
No files matched your search
@@ -22,6 +22,7 @@ import {
|
||||
import { ADMIN_KEYS } from "./settings.js";
|
||||
import { commandHistory } from "./tables.js";
|
||||
import { createTerminalSocket } from "./terminal-socket.js";
|
||||
import { validateAdminSettings } from "./settings-validation.js";
|
||||
|
||||
function toInfo(session: TerminalSession): LiveSessionInfo {
|
||||
return {
|
||||
@@ -55,6 +56,7 @@ function optionalService<T extends object>(
|
||||
|
||||
export async function activate(ctx: PluginContext) {
|
||||
const log = createTerminalLogger(ctx.log);
|
||||
ctx.settings.onValidate("admin", validateAdminSettings);
|
||||
|
||||
const table = await ctx.db.define(commandHistory);
|
||||
const history = createHistoryRepository(ctx.db, table);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
parseImageHostPath,
|
||||
parseImageLocalDir,
|
||||
} from "./images/image-storage-settings.js";
|
||||
import { ADMIN_KEYS } from "./settings.js";
|
||||
|
||||
/**
|
||||
* Save-time checks for the admin settings: the image storage paths must be
|
||||
* absolute, since a relative one silently depends on the server's cwd.
|
||||
*/
|
||||
export function validateAdminSettings(
|
||||
values: Record<string, unknown>,
|
||||
): Record<string, string> {
|
||||
const errors: Record<string, string> = {};
|
||||
const localDir = values[ADMIN_KEYS.imageLocalDir];
|
||||
if (
|
||||
typeof localDir === "string" &&
|
||||
localDir.trim() !== "" &&
|
||||
parseImageLocalDir(localDir) === null
|
||||
) {
|
||||
errors[ADMIN_KEYS.imageLocalDir] = "Use an absolute path";
|
||||
}
|
||||
const hostPath = values[ADMIN_KEYS.imageHostPath];
|
||||
if (
|
||||
typeof hostPath === "string" &&
|
||||
hostPath.trim() !== "" &&
|
||||
parseImageHostPath(hostPath) === null
|
||||
) {
|
||||
errors[ADMIN_KEYS.imageHostPath] = "Use an absolute path";
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
@@ -81,7 +81,6 @@ interface ConnectToHostData {
|
||||
keepaliveCountMax?: number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
enableSessionLogging?: boolean;
|
||||
/** When true, ignore key material and force password auth (fallback path). */
|
||||
passwordFallbackOnly?: boolean;
|
||||
};
|
||||
@@ -1432,7 +1431,6 @@ export function createTerminalSocket(deps: TerminalSocketDeps) {
|
||||
socks5ProxyChain?: unknown;
|
||||
portKnockSequence?: ConnectToHostData["hostConfig"]["portKnockSequence"];
|
||||
terminalConfig?: ConnectToHostData["hostConfig"]["terminalConfig"];
|
||||
enableSessionLogging?: boolean;
|
||||
})
|
||||
| null = null;
|
||||
|
||||
@@ -1776,10 +1774,8 @@ export function createTerminalSocket(deps: TerminalSocketDeps) {
|
||||
|
||||
const hostDisplayName = `${username}@${ip}:${port}`;
|
||||
const tabInstanceId = hostConfig.instanceId;
|
||||
const sessionLoggingEnabled =
|
||||
resolvedHostData?.enableSessionLogging ??
|
||||
hostConfig.enableSessionLogging ??
|
||||
true;
|
||||
// The recordings.writer provider decides per host and user.
|
||||
const sessionLoggingEnabled = true;
|
||||
currentSessionId = sessionManager.createSession(
|
||||
userId,
|
||||
id,
|
||||
|
||||
@@ -107,24 +107,23 @@ import {
|
||||
getNextTerminalFontSize,
|
||||
getTerminalFontZoomDirection,
|
||||
hydrateLocalSharedHostAuth,
|
||||
getUserPreferences,
|
||||
parseCustomKeybindings,
|
||||
findMatchingKeybinding,
|
||||
SnippetVariablesDialog,
|
||||
type CustomKeybinding,
|
||||
useConnectionDefaults,
|
||||
getCookie,
|
||||
isElectron,
|
||||
logActivity,
|
||||
getHostPassword,
|
||||
patchOpenTab,
|
||||
setHostAutoTmux,
|
||||
} from "@termix/plugin-sdk/ui";
|
||||
import {
|
||||
useTranslation,
|
||||
invokeAction,
|
||||
usePluginApi,
|
||||
useSlotContributions,
|
||||
getCustomKeybindings,
|
||||
getClientPreference,
|
||||
logActivity,
|
||||
getHostPassword,
|
||||
patchOpenTab,
|
||||
setHostAutoTmux,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
|
||||
type HostKeyVerificationData = Omit<
|
||||
@@ -412,8 +411,11 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
const totpTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const connectionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const activityLoggedRef = useRef(false);
|
||||
const commandHistoryTrackingEnabled =
|
||||
hostConfig.enableCommandHistory !== false;
|
||||
const commandHistoryTrackingEnabled = hostSetting(
|
||||
hostConfig,
|
||||
"enableCommandHistory",
|
||||
true,
|
||||
);
|
||||
|
||||
const { trackInput, getCurrentCommand, updateCurrentCommand } =
|
||||
useCommandTracker({
|
||||
@@ -758,6 +760,7 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
[clearAutosuggestion, terminal, trackInput],
|
||||
);
|
||||
|
||||
const slotApiRef = useRef<TerminalSlotApi | null>(null);
|
||||
const slotApi = useMemo<TerminalSlotApi>(
|
||||
() => ({
|
||||
host,
|
||||
@@ -791,6 +794,7 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
hostConfig.instanceId,
|
||||
],
|
||||
);
|
||||
slotApiRef.current = slotApi;
|
||||
|
||||
const overlayProps = useMemo<TerminalOverlayProps>(
|
||||
() => ({
|
||||
@@ -1426,6 +1430,8 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
refresh: () => hardRefresh(),
|
||||
getApplicationCursorKeysMode: () =>
|
||||
terminal?.modes?.applicationCursorKeysMode ?? false,
|
||||
// What the tab menu's share entry needs, read at call time.
|
||||
getShareTarget: () => slotApiRef.current?.getShareTarget() ?? null,
|
||||
openFileManager: () => {
|
||||
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
||||
webSocketRef.current.send(JSON.stringify({ type: "get_cwd" }));
|
||||
@@ -1438,7 +1444,7 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
);
|
||||
|
||||
function getCopyOnSelect() {
|
||||
return getCookie("copyOnSelect") === "true";
|
||||
return getClientPreference("copyOnSelect") === "true";
|
||||
}
|
||||
|
||||
function attemptReconnection() {
|
||||
@@ -2945,12 +2951,12 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const loadKeybindings = () => {
|
||||
getUserPreferences()
|
||||
.then((prefs) => {
|
||||
getCustomKeybindings()
|
||||
.then((bindings) => {
|
||||
if (!cancelled) {
|
||||
customKeybindingsRef.current = parseCustomKeybindings(
|
||||
prefs.customKeybindings,
|
||||
).filter((kb) => kb.enabled);
|
||||
customKeybindingsRef.current = bindings.filter(
|
||||
(kb) => kb.enabled,
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -43,8 +43,12 @@ import {
|
||||
useIsMobile,
|
||||
} from "@termix/plugin-sdk/ui";
|
||||
import {
|
||||
useHostActions,
|
||||
useTabs,
|
||||
useTranslation,
|
||||
useSlotContributions,
|
||||
type PluginHostRecord,
|
||||
type ShellApi,
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
type SelectedToolbarDensity = ToolbarDensity;
|
||||
|
||||
@@ -120,6 +124,25 @@ export const TerminalToolbar: React.FC<TerminalToolbarProps> = ({
|
||||
),
|
||||
[allSlotContributions],
|
||||
);
|
||||
// Quick links to the other tools a plugin offers for this host (tmux
|
||||
// monitor, docker, tunnels, host metrics), from their host actions.
|
||||
const hostActions = useHostActions();
|
||||
const tabs = useTabs();
|
||||
const hostLinks = useMemo(() => {
|
||||
const record = host as unknown as PluginHostRecord;
|
||||
return hostActions.filter(
|
||||
(action) =>
|
||||
action.kind === "open" &&
|
||||
!action.items &&
|
||||
(action.run || action.tabType) &&
|
||||
action.when(record),
|
||||
);
|
||||
}, [hostActions, host]);
|
||||
const openHostLink = (action: (typeof hostLinks)[number]) => {
|
||||
const record = host as unknown as PluginHostRecord;
|
||||
if (action.run) action.run(record, tabs as unknown as ShellApi);
|
||||
else if (action.tabType) tabs.openTab(record, action.tabType);
|
||||
};
|
||||
const [density, setDensity] =
|
||||
useState<SelectedToolbarDensity>(readStoredDensity);
|
||||
const [responsiveDensity, setResponsiveDensity] =
|
||||
@@ -233,6 +256,7 @@ export const TerminalToolbar: React.FC<TerminalToolbarProps> = ({
|
||||
isTmuxAttached,
|
||||
actionsEnabled,
|
||||
slotContributions,
|
||||
hostLinks,
|
||||
t,
|
||||
]);
|
||||
|
||||
@@ -603,6 +627,13 @@ export const TerminalToolbar: React.FC<TerminalToolbarProps> = ({
|
||||
{density !== "icon" && t(contribution.titleKey)}
|
||||
</span>
|
||||
))}
|
||||
{actionsEnabled &&
|
||||
hostLinks.map((action) => (
|
||||
<span key={action.id} className={CONTROL}>
|
||||
<action.icon className="size-4" />
|
||||
{density !== "icon" && t(action.titleKey)}
|
||||
</span>
|
||||
))}
|
||||
<span className={cn(CONTROL, "h-8 px-2")}>
|
||||
<LayoutGrid className="size-4" />
|
||||
<ChevronDown className="size-4" />
|
||||
@@ -683,6 +714,24 @@ export const TerminalToolbar: React.FC<TerminalToolbarProps> = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{actionsEnabled && hostLinks.length > 0 && (
|
||||
<>
|
||||
<div className={SEPARATOR} />
|
||||
{hostLinks.map((action) => (
|
||||
<button
|
||||
key={action.id}
|
||||
type="button"
|
||||
className={CONTROL}
|
||||
aria-label={t(action.titleKey)}
|
||||
title={t(action.titleKey)}
|
||||
onClick={() => openHostLink(action)}
|
||||
>
|
||||
<action.icon className="size-4" />
|
||||
{effectiveDensity !== "icon" && t(action.titleKey)}
|
||||
</button>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
<div className={SEPARATOR} />
|
||||
{densitySelect}
|
||||
<button
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getClientPreference } from "@termix/plugin-sdk/frontend";
|
||||
import type { Terminal } from "@xterm/xterm";
|
||||
import { getCookie } from "@termix/plugin-sdk/ui";
|
||||
import { isPhysicalShortcutKey } from "./terminal-key-event";
|
||||
|
||||
export function getUseRightClickCopyPaste(): boolean {
|
||||
return getCookie("rightClickCopyPaste") !== "false";
|
||||
return getClientPreference("rightClickCopyPaste") !== "false";
|
||||
}
|
||||
|
||||
export interface TerminalClipboardActions {
|
||||
|
||||
Reference in new issue
Block a user