diff --git a/packages/plugin-sdk/schema/manifest.schema.json b/packages/plugin-sdk/schema/manifest.schema.json
index d98deed63..bc206c474 100644
--- a/packages/plugin-sdk/schema/manifest.schema.json
+++ b/packages/plugin-sdk/schema/manifest.schema.json
@@ -279,6 +279,51 @@
"guest": {
"type": "boolean"
},
+ "keybindingActions": {
+ "type": "array",
+ "description": "Keybinding actions the plugin adds, with the parameters a saved binding carries.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["id"],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^[a-zA-Z][a-zA-Z0-9.-]{0,63}$",
+ "not": {
+ "enum": [
+ "nextTab",
+ "previousTab",
+ "openCommandPalette",
+ "reconnectSession"
+ ]
+ }
+ },
+ "params": {
+ "type": "object",
+ "propertyNames": {
+ "pattern": "^[a-zA-Z][a-zA-Z0-9]{0,31}$",
+ "not": { "const": "type" }
+ },
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["type"],
+ "properties": {
+ "type": { "enum": ["string", "boolean"] },
+ "required": { "type": "boolean" },
+ "pattern": { "type": "string" },
+ "maxLength": { "type": "integer", "minimum": 1 },
+ "syncEntity": {
+ "type": "string",
+ "pattern": "^[a-zA-Z][a-zA-Z0-9]*$"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"syncEntities": {
"type": "array",
"minItems": 1,
@@ -840,6 +885,10 @@
"additionalProperties": false,
"description": "HTTP options for the plugin's /plugin-api// router.",
"properties": {
+ "adminImpersonation": {
+ "type": "boolean",
+ "description": "An admin may call the plugin's routes on behalf of another user with the X-Admin-Target-User header."
+ },
"legacyPaths": {
"type": "array",
"description": "Old URLs under // served by the plugin's router with that prefix removed.",
diff --git a/packages/plugin-sdk/src/backend.ts b/packages/plugin-sdk/src/backend.ts
index 8fe28c664..ddbd6dc70 100644
--- a/packages/plugin-sdk/src/backend.ts
+++ b/packages/plugin-sdk/src/backend.ts
@@ -191,7 +191,7 @@ export interface SyncEntityRegistration {
singleton?: boolean;
/**
* Names core uses when its own rows point at this entity without knowing
- * which plugin provides it, e.g. "commandSnippet" for a host quick action.
+ * which plugin provides it. Nothing in core uses one today.
*/
answersTo?: readonly string[];
/**
diff --git a/packages/plugin-sdk/src/frontend.ts b/packages/plugin-sdk/src/frontend.ts
index db62f4874..acb4c9a88 100644
--- a/packages/plugin-sdk/src/frontend.ts
+++ b/packages/plugin-sdk/src/frontend.ts
@@ -301,7 +301,8 @@ export interface HostEditorSectionProps {
) => void;
host?: PluginHostRecord;
credentials?: unknown[];
- snippets?: unknown[];
+ /** Set while an admin edits another user's host from the admin panel. */
+ adminTargetUserId?: string;
/** Which connection protocols are switched on for this host. */
protocols: Record;
}
@@ -408,6 +409,97 @@ export interface PaletteEntryContribution {
run: (shell: ShellApi, host?: PluginHostRecord) => void;
}
+/** What a palette item's run gets. */
+export interface PaletteRunContext {
+ /** The command-target tab the user is working in, if any. */
+ targetTab?: PluginTabRecord;
+ shell: ShellApi;
+}
+
+/** One searchable row in a palette group. */
+export interface PaletteItem {
+ id: string;
+ /** Already translated; items are data, not keys. */
+ title: string;
+ description?: string;
+ icon?: IconComponent;
+ /** Extra words the search matches. */
+ keywords?: string[];
+ /** Acts on the command-target tab, so it is greyed out without one. */
+ needsTarget?: boolean;
+ /** Short text at the row's end, e.g. "Run in terminal". */
+ hint?: string;
+ run: (context: PaletteRunContext) => void;
+}
+
+/**
+ * A group of items in the command palette, e.g. the user's snippets. `load`
+ * runs each time the palette opens; the palette filters the items by what
+ * the user types.
+ */
+export interface PaletteGroupContribution {
+ id: string;
+ titleKey: string;
+ /** Lower sorts first. */
+ order?: number;
+ load: () => PaletteItem[] | Promise;
+ /** Show the items before anything is typed. Default false. */
+ showWhenEmpty?: boolean;
+}
+
+/** A keybinding action's parameter editor in Appearance > Keybindings. */
+export interface KeybindingActionEditorProps {
+ action: KeybindingAction;
+ onChange: (action: KeybindingAction) => void;
+}
+
+/** Where a keybinding action runs when the terminal hands it on. */
+export interface KeybindingRunContext {
+ /** The session the key was pressed in. */
+ sessionId?: string;
+ host?: {
+ ip?: string;
+ username?: string;
+ port?: number | string;
+ name?: string;
+ } | null;
+ /** Writes raw input to that session. */
+ send?: (data: string) => void;
+}
+
+/**
+ * A keybinding action. `id` is stored as the binding's action.type and must
+ * be declared in contributes.keybindingActions, which is what the server
+ * validates saved bindings against.
+ */
+export interface KeybindingActionContribution {
+ id: string;
+ titleKey: string;
+ /**
+ * "session" runs while a session tab such as a terminal has focus (the
+ * default). "global" runs anywhere in the app.
+ */
+ scope?: "session" | "global";
+ /** Draws the action's parameters in the binding form. */
+ editor?: ComponentType;
+ /** Drawn after the action's name in the binding list, e.g. a warning. */
+ summary?: ComponentType;
+ /** A plugin translation key when the action cannot be saved yet. */
+ validate?: (action: KeybindingAction) => string | null;
+ /**
+ * Runs it. A terminal hands on every bound action it does not handle
+ * itself; a global action runs from anywhere.
+ */
+ run?: (action: KeybindingAction, context: KeybindingRunContext) => void;
+}
+
+/** A built-in key the user can rebind in Appearance > Keybindings. */
+export interface KeybindingDefaultContribution {
+ id: string;
+ combo: KeyCombo;
+ descriptionKey: string;
+}
+
export interface DashboardCardProps {
isVisible: boolean;
shell: ShellApi;
@@ -627,6 +719,11 @@ export interface TermixApp extends TermixAppInfo {
item: HostContextMenuItemContribution,
) => Disposer;
registerPaletteEntry: (entry: PaletteEntryContribution) => Disposer;
+ registerPaletteGroup: (group: PaletteGroupContribution) => Disposer;
+ registerKeybindingAction: (action: KeybindingActionContribution) => Disposer;
+ registerKeybindingDefault: (
+ binding: KeybindingDefaultContribution,
+ ) => Disposer;
registerDashboardCard: (card: DashboardCardContribution) => Disposer;
registerHomepageWidget: (widget: HomepageWidgetContribution) => Disposer;
registerSettingsComponent: (
@@ -815,23 +912,16 @@ export interface KeyCombo {
meta: boolean;
}
-export type KeybindingActionType =
- | "copy"
- | "paste"
- | "sendControlCode"
- | "sendText"
- | "runSnippet"
- | "nextTab"
- | "previousTab"
- | "openCommandPalette"
- | "reconnectSession";
+/**
+ * The shell's own actions (nextTab, previousTab, openCommandPalette,
+ * reconnectSession) or one a plugin declared in contributes.keybindingActions.
+ */
+export type KeybindingActionType = string;
+/** A bound action: its type plus the parameters its declaration lists. */
export interface KeybindingAction {
type: KeybindingActionType;
- text?: string;
- controlCode?: string;
- snippetId?: string;
- appendEnter?: boolean;
+ [param: string]: unknown;
}
export interface CustomKeybinding {
@@ -869,6 +959,15 @@ export interface PluginCoreApi {
) => Promise;
/** The user's custom keybindings, enabled or not. */
getCustomKeybindings: () => Promise;
+ /**
+ * Runs a bound action through whoever registered it (a plugin's
+ * registerKeybindingAction, or the shell for its own). False when nothing
+ * running handles that type.
+ */
+ runKeybindingAction: (
+ action: KeybindingAction,
+ context?: KeybindingRunContext,
+ ) => boolean;
/** A browser-side UI preference (a cookie, or the desktop app's store). */
getClientPreference: (name: string) => string | undefined;
/** Saves a browser-side UI preference where getClientPreference reads it. */
@@ -1021,6 +1120,13 @@ export function getCustomKeybindings(): Promise {
return requireHost().core.getCustomKeybindings();
}
+export function runKeybindingAction(
+ action: KeybindingAction,
+ context?: KeybindingRunContext,
+): boolean {
+ return requireHost().core.runKeybindingAction(action, context);
+}
+
export function getClientPreference(name: string): string | undefined {
return requireHost().core.getClientPreference(name);
}
diff --git a/packages/plugin-sdk/src/manifest.ts b/packages/plugin-sdk/src/manifest.ts
index 5ef2f31de..38f256146 100644
--- a/packages/plugin-sdk/src/manifest.ts
+++ b/packages/plugin-sdk/src/manifest.ts
@@ -344,8 +344,46 @@ export interface PluginContributions {
* off and a desktop can list them in its sync settings.
*/
syncEntities?: string[];
+ /**
+ * Keybinding actions this plugin adds to Appearance > Keybindings, with
+ * the parameters a saved binding carries. Core validates saved bindings
+ * against these without running plugin code, and translates a parameter
+ * naming a sync entity over sync.
+ */
+ keybindingActions?: PluginKeybindingActionContribution[];
}
+/** One parameter a saved keybinding action carries next to its type. */
+export interface PluginKeybindingParam {
+ type: "string" | "boolean";
+ required?: boolean;
+ /** A regular expression a string value must match. */
+ pattern?: string;
+ maxLength?: number;
+ /**
+ * The value is a local row id of this sync entity, stored as a string.
+ * Sync sends the row's syncId instead.
+ */
+ syncEntity?: string;
+}
+
+export interface PluginKeybindingActionContribution {
+ /**
+ * Stored as the binding's action.type. New actions use
+ * "."; the bundled ones keep their 2.8 names.
+ */
+ id: string;
+ params?: Record;
+}
+
+/** Keybinding action types the shell runs itself; no plugin may declare them. */
+export const CORE_KEYBINDING_ACTIONS: readonly string[] = [
+ "nextTab",
+ "previousTab",
+ "openCommandPalette",
+ "reconnectSession",
+];
+
/**
* How a desktop linked to a server treats this plugin.
* - "mirror": follows the server, installed and switched on or off with it.
@@ -376,6 +414,12 @@ export interface PluginHttpContribution {
* Core routes win a clash.
*/
legacyRedirects?: PluginLegacyRedirect[];
+ /**
+ * An admin may call this plugin's routes on behalf of another user with
+ * the X-Admin-Target-User header (the admin "manage user" panel). Without
+ * it core refuses the header on every route under /plugin-api//.
+ */
+ adminImpersonation?: boolean;
}
export interface PluginLegacyRedirect {
@@ -478,6 +522,7 @@ const ALLOWED_CONTRIBUTES = new Set([
"uiPresets",
"auth",
"syncEntities",
+ "keybindingActions",
]);
const ALLOWED_SETTINGS_FIELD = [
@@ -947,6 +992,132 @@ function validateContributes(
validateAuthContribution(contributes.auth, errors);
validateHttpContribution(contributes.http, pluginId, errors);
validateUiPresets(contributes.uiPresets, errors);
+ validateKeybindingActions(contributes.keybindingActions, errors);
+}
+
+const KEYBINDING_ACTION_PATTERN = /^[a-zA-Z][a-zA-Z0-9.-]{0,63}$/;
+const KEYBINDING_PARAM_PATTERN = /^[a-zA-Z][a-zA-Z0-9]{0,31}$/;
+
+function validateKeybindingParam(
+ param: unknown,
+ at: string,
+ errors: string[],
+): void {
+ if (!isPlainObject(param)) {
+ errors.push(`${at} must be an object`);
+ return;
+ }
+ rejectUnknown(
+ param,
+ ["type", "required", "pattern", "maxLength", "syncEntity"],
+ at,
+ errors,
+ );
+ if (param.type !== "string" && param.type !== "boolean") {
+ errors.push(`${at}.type must be "string" or "boolean"`);
+ }
+ if (param.required !== undefined && typeof param.required !== "boolean") {
+ errors.push(`${at}.required must be a boolean`);
+ }
+ if (param.pattern !== undefined) {
+ let valid = typeof param.pattern === "string";
+ if (valid) {
+ try {
+ new RegExp(param.pattern as string);
+ } catch {
+ valid = false;
+ }
+ }
+ if (!valid) errors.push(`${at}.pattern must be a regular expression`);
+ }
+ if (
+ param.maxLength !== undefined &&
+ (typeof param.maxLength !== "number" ||
+ !Number.isInteger(param.maxLength) ||
+ param.maxLength < 1)
+ ) {
+ errors.push(`${at}.maxLength must be a positive integer`);
+ }
+ if (param.syncEntity !== undefined) {
+ if (
+ typeof param.syncEntity !== "string" ||
+ !/^[a-zA-Z][a-zA-Z0-9]*$/.test(param.syncEntity)
+ ) {
+ errors.push(`${at}.syncEntity must be a sync entity wire name`);
+ } else if (param.type !== "string") {
+ errors.push(`${at}.syncEntity needs a string parameter`);
+ }
+ }
+}
+
+function validateKeybindingActions(value: unknown, errors: string[]): void {
+ if (value === undefined) return;
+ const where = "contributes.keybindingActions";
+ if (!Array.isArray(value)) {
+ errors.push(`Field "${where}" must be an array`);
+ return;
+ }
+ const seen = new Set();
+ value.forEach((raw, index) => {
+ const at = `${where}[${index}]`;
+ if (!isPlainObject(raw)) {
+ errors.push(`${at} must be an object`);
+ return;
+ }
+ rejectUnknown(raw, ["id", "params"], at, errors);
+ if (typeof raw.id !== "string" || !KEYBINDING_ACTION_PATTERN.test(raw.id)) {
+ errors.push(`${at}.id must be letters, digits, dots and dashes`);
+ } else if (CORE_KEYBINDING_ACTIONS.includes(raw.id)) {
+ errors.push(`${at}.id "${raw.id}" is one of the shell's own actions`);
+ } else {
+ if (seen.has(raw.id)) errors.push(`${at}.id duplicates "${raw.id}"`);
+ seen.add(raw.id);
+ }
+ if (raw.params === undefined) return;
+ if (!isPlainObject(raw.params)) {
+ errors.push(`${at}.params must be an object`);
+ return;
+ }
+ for (const [name, param] of Object.entries(raw.params)) {
+ if (!KEYBINDING_PARAM_PATTERN.test(name) || name === "type") {
+ errors.push(`${at}.params.${name} is not a valid parameter name`);
+ }
+ validateKeybindingParam(param, `${at}.params.${name}`, errors);
+ }
+ });
+}
+
+/**
+ * Checks one saved keybinding action against the parameters its declaration
+ * lists. A parameter the declaration does not list is refused, so a binding
+ * cannot carry arbitrary data under a declared type.
+ */
+export function validateKeybindingActionParams(
+ action: Record,
+ declaration: PluginKeybindingActionContribution,
+): boolean {
+ const params = declaration.params ?? {};
+ for (const key of Object.keys(action)) {
+ if (key !== "type" && !(key in params) && action[key] != null) {
+ return false;
+ }
+ }
+ for (const [name, param] of Object.entries(params)) {
+ const value = action[name];
+ if (value === undefined || value === null) {
+ if (param.required) return false;
+ continue;
+ }
+ if (param.type === "boolean") {
+ if (typeof value !== "boolean") return false;
+ continue;
+ }
+ if (typeof value !== "string") return false;
+ if (param.required && value.length === 0) return false;
+ if (value.length > (param.maxLength ?? 65_536)) return false;
+ if (param.pattern && !new RegExp(param.pattern).test(value)) return false;
+ }
+ return true;
}
function isPresetValue(value: unknown): boolean {
@@ -1041,10 +1212,18 @@ function validateHttpContribution(
}
rejectUnknown(
value,
- ["legacyPaths", "legacyRedirects"],
+ ["legacyPaths", "legacyRedirects", "adminImpersonation"],
"contributes.http",
errors,
);
+ if (
+ value.adminImpersonation !== undefined &&
+ typeof value.adminImpersonation !== "boolean"
+ ) {
+ errors.push(
+ 'Field "contributes.http.adminImpersonation" must be a boolean',
+ );
+ }
validateLegacyRedirects(value.legacyRedirects, errors);
const paths = value.legacyPaths;
if (paths === undefined) return;
diff --git a/packages/plugin-sdk/src/testing.ts b/packages/plugin-sdk/src/testing.ts
index f1d811755..ed3ead2a6 100644
--- a/packages/plugin-sdk/src/testing.ts
+++ b/packages/plugin-sdk/src/testing.ts
@@ -2000,7 +2000,14 @@ export interface RenderedPluginApp {
actions: () => string[];
loginMethods: () => string[];
secondFactors: () => string[];
+ paletteGroups: () => string[];
+ keybindingActions: () => string[];
+ keybindingDefaults: () => string[];
};
+ /** A registered palette group's items, as the palette loads them on open. */
+ loadPaletteGroup: (
+ id: string,
+ ) => Promise;
/** Shell calls made by the plugin's code, applyLayout included. */
shellCalls: ShellCall[];
/**
diff --git a/packages/plugin-sdk/src/testing/setup.ts b/packages/plugin-sdk/src/testing/setup.ts
index cfe261376..bc898a3c5 100644
--- a/packages/plugin-sdk/src/testing/setup.ts
+++ b/packages/plugin-sdk/src/testing/setup.ts
@@ -137,6 +137,7 @@ const offline = () =>
getHostPassword: async () => null,
patchOpenTab: async () => {},
getCustomKeybindings: async () => [],
+ runKeybindingAction: () => false,
getClientPreference: () => undefined,
setClientPreference: () => {},
listHosts: async () => [],
diff --git a/plugins/fleets/manifest.json b/plugins/fleets/manifest.json
index 2e1533728..328a1891b 100644
--- a/plugins/fleets/manifest.json
+++ b/plugins/fleets/manifest.json
@@ -23,6 +23,9 @@
"network:serve",
"ui:surface"
],
+ "optionalDependencies": {
+ "snippets": "^1.0.0"
+ },
"provides": [
{
"service": "fleets.access",
diff --git a/plugins/fleets/src/backend/command-variables.ts b/plugins/fleets/src/backend/command-variables.ts
deleted file mode 100644
index 1285218a5..000000000
--- a/plugins/fleets/src/backend/command-variables.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-export interface CommandHostVars {
- ip?: string;
- username?: string;
- port?: number | string;
- name?: string;
-}
-
-const INPUT_PATTERN =
- /\$\{INPUT_(\d+)(?::([^}$]+))?\}|\$INPUT_(\d+)(?![a-zA-Z0-9_])/g;
-
-function replaceVar(content: string, name: string, value?: string): string {
- if (value === undefined) return content;
- const pattern = new RegExp(`\\$\\{?${name}\\}?`, "g");
- return content.replace(pattern, value);
-}
-
-/**
- * $HOST/$USER/$PORT/$NAME/$INPUT_n substitution for an ad hoc fleet command,
- * typed directly into the "run on fleet" box rather than loaded from a saved
- * snippet. Mirrors the snippets plugin's own resolveSnippetCommand and the
- * frontend's src/ui/lib/snippet-variables.ts - kept as a small standalone
- * copy rather than a cross-plugin dependency for one pure function with no
- * snippet data of its own to fetch.
- */
-export function resolveCommandVariables(
- content: string,
- host: CommandHostVars | null,
- inputValues: Record = {},
-): string {
- let resolved = content;
-
- resolved = replaceVar(resolved, "HOST", host?.ip);
- resolved = replaceVar(resolved, "USER", host?.username);
- resolved = replaceVar(
- resolved,
- "PORT",
- host?.port !== undefined ? String(host.port) : undefined,
- );
- resolved = replaceVar(resolved, "NAME", host?.name);
-
- resolved = resolved.replace(
- INPUT_PATTERN,
- (
- fullMatch,
- braceDigits: string | undefined,
- _label,
- plainDigits: string | undefined,
- ) => {
- const key = `INPUT_${braceDigits ?? plainDigits}`;
- return key in inputValues ? inputValues[key] : fullMatch;
- },
- );
-
- return resolved;
-}
diff --git a/plugins/fleets/src/backend/routes.ts b/plugins/fleets/src/backend/routes.ts
index 3611cd530..99d91f77c 100644
--- a/plugins/fleets/src/backend/routes.ts
+++ b/plugins/fleets/src/backend/routes.ts
@@ -16,7 +16,6 @@ import {
ElevationError,
} from "@termix/plugin-sdk/host-commands";
import type { FleetRepository } from "./repository.js";
-import { resolveCommandVariables } from "./command-variables.js";
const FLEET_TRANSFER_MAX_BYTES = 200 * 1024 * 1024;
const upload = multer({
@@ -74,6 +73,35 @@ function parseShareTargets(
return targets;
}
+interface CommandVariables {
+ resolve: (
+ content: string,
+ host: {
+ ip?: string;
+ username?: string;
+ port?: number | string;
+ name?: string;
+ } | null,
+ inputValues?: Record,
+ ) => string;
+}
+
+/**
+ * $HOST, $USER, $PORT, $NAME and $INPUT_n in an ad hoc fleet command, through
+ * the snippets plugin, which owns the syntax. Without it the command runs as
+ * typed.
+ */
+export function resolveCommandVariables(
+ ctx: Pick,
+ command: string,
+ host: Parameters[1],
+ inputValues: Record = {},
+): string {
+ const variables =
+ ctx.registry.consume("snippets.variables");
+ return variables ? variables.resolve(command, host, inputValues) : command;
+}
+
function actor(ctx: PluginContext): string {
return ctx.currentActor() as string;
}
@@ -884,6 +912,7 @@ export function registerFleetRoutes(
}
const resolvedCommand = resolveCommandVariables(
+ ctx,
command,
{
ip: fullHost.ip,
diff --git a/plugins/fleets/src/frontend/FleetsPanel.tsx b/plugins/fleets/src/frontend/FleetsPanel.tsx
index 7a43819d3..c707e00b3 100644
--- a/plugins/fleets/src/frontend/FleetsPanel.tsx
+++ b/plugins/fleets/src/frontend/FleetsPanel.tsx
@@ -1,10 +1,7 @@
-import {
- getErrorMessage,
- extractSnippetInputs,
- type SnippetInput,
-} from "./helpers.js";
+import { getErrorMessage, type SnippetInput } from "./helpers.js";
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import {
+ invokeAction,
useTranslation,
useHosts,
type PluginHostRecord,
@@ -792,8 +789,16 @@ function RunCommandTab({ api, fleetId }: { api: FleetsApi; fleetId: number }) {
const [running, setRunning] = useState(false);
const [results, setResults] = useState(null);
+ // The snippets plugin owns $INPUT_n parsing; without it the command
+ // runs as typed.
useEffect(() => {
- setInputs(extractSnippetInputs(command));
+ let cancelled = false;
+ void invokeAction("snippets.extractInputs", command).then((found) => {
+ if (!cancelled) setInputs(Array.isArray(found) ? found : []);
+ });
+ return () => {
+ cancelled = true;
+ };
}, [command]);
async function handleRun() {
diff --git a/plugins/fleets/src/frontend/helpers.ts b/plugins/fleets/src/frontend/helpers.ts
index 564215094..1695f8017 100644
--- a/plugins/fleets/src/frontend/helpers.ts
+++ b/plugins/fleets/src/frontend/helpers.ts
@@ -21,24 +21,3 @@ export interface SnippetInput {
key: string;
label: string;
}
-
-const INPUT_PATTERN =
- /\$\{INPUT_(\d+)(?::([^}$]+))?\}|\$INPUT_(\d+)(?![a-zA-Z0-9_])/g;
-
-/**
- * Finds the $INPUT_n placeholders in a command, for the fleet run box's
- * input fields. Mirrors src/ui/lib/snippet-variables.ts extractSnippetInputs.
- */
-export function extractSnippetInputs(content: string): SnippetInput[] {
- const seen = new Map();
- let match: RegExpExecArray | null;
- INPUT_PATTERN.lastIndex = 0;
- while ((match = INPUT_PATTERN.exec(content)) !== null) {
- const digits = match[1] ?? match[3];
- const key = `INPUT_${digits}`;
- if (!seen.has(key)) {
- seen.set(key, { key, label: match[2]?.trim() || `Input ${digits}` });
- }
- }
- return Array.from(seen.values());
-}
diff --git a/plugins/fleets/tests/backend/command-variables.test.ts b/plugins/fleets/tests/backend/command-variables.test.ts
new file mode 100644
index 000000000..eadd38b49
--- /dev/null
+++ b/plugins/fleets/tests/backend/command-variables.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, it } from "vitest";
+import { resolveCommandVariables } from "../../src/backend/routes.js";
+
+function registry(value?: unknown) {
+ return {
+ registry: {
+ provide: () => {},
+ revoke: () => false,
+ consume: () => value as T | undefined,
+ },
+ } as never;
+}
+
+describe("resolveCommandVariables", () => {
+ it("fills variables through the snippets plugin's resolver", () => {
+ const ctx = registry({
+ resolve: (content: string, host: { ip?: string } | null) =>
+ content.replace("$HOST", host?.ip ?? ""),
+ });
+ expect(resolveCommandVariables(ctx, "ping $HOST", { ip: "10.0.0.1" })).toBe(
+ "ping 10.0.0.1",
+ );
+ });
+
+ it("runs the command as typed while snippets is off", () => {
+ expect(
+ resolveCommandVariables(registry(), "ping $HOST", { ip: "10.0.0.1" }),
+ ).toBe("ping $HOST");
+ });
+});
diff --git a/plugins/host-metrics/locales/en.json b/plugins/host-metrics/locales/en.json
index b76e474a4..b355fd3df 100644
--- a/plugins/host-metrics/locales/en.json
+++ b/plugins/host-metrics/locales/en.json
@@ -54,10 +54,6 @@
"processesRunning": "running",
"loginStats": "SSH Login Statistics",
"noRecentLoginData": "No recent login data",
- "executingQuickAction": "Executing {{name}}...",
- "quickActionSuccess": "{{name}} completed successfully",
- "quickActionFailed": "{{name}} failed",
- "quickActionError": "Failed to execute {{name}}",
"ports": {
"title": "Listening Ports",
"protocol": "Protocol",
@@ -182,8 +178,7 @@
"wgLastHandshake": "Last handshake",
"wgHandshakeNever": "Never",
"wgTransfer": "Transfer"
- },
- "snippetsUnavailable": "Snippets are turned off, so quick actions cannot run."
+ }
},
"metricsHistory": {
"title": "Metrics History",
@@ -219,12 +214,6 @@
"addExcludedMount": "Add",
"visibleWidgets": "Visible Widgets",
"widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.",
- "quickActionsLabel": "Quick Actions",
- "quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.",
- "noQuickActions": "No quick actions yet.",
- "buttonLabel": "Button label",
- "selectSnippetPlaceholder": "Select snippet...",
- "addActionBtn": "Add Action",
"hostNotFound": "Host not found",
"loadingHost": "Loading host..."
},
@@ -269,15 +258,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Cancel",
- "confirmRunButton": "Run",
- "confirmRunMessage": "Run \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Connected"
},
diff --git a/plugins/host-metrics/locales/translated/af_ZA.json b/plugins/host-metrics/locales/translated/af_ZA.json
index 48a45e1b7..f83c47314 100644
--- a/plugins/host-metrics/locales/translated/af_ZA.json
+++ b/plugins/host-metrics/locales/translated/af_ZA.json
@@ -51,10 +51,6 @@
"processesRunning": "hardloop",
"loginStats": "SSH-aanmeldingstatistieke",
"noRecentLoginData": "Geen onlangse aanmelddata nie",
- "executingQuickAction": "Voer tans {{name}} uit ...",
- "quickActionSuccess": "{{name}} suksesvol voltooi",
- "quickActionFailed": "{{name}} het misluk",
- "quickActionError": "Kon nie {{name}} uitvoer nie",
"ports": {
"title": "Luisterpoorte",
"protocol": "Protokol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Voeg by",
"visibleWidgets": "Sigbare Widgets",
"widgetsMovedToHostMetrics": "Kaarte word nou direk in die Gasheermetrieke-oortjie bygevoeg, gerangskik en herskaal. Maak Gasheermetrieke vir hierdie gasheer oop en gebruik Pasmaak om te kies watter kaarte gewys word.",
- "quickActionsLabel": "Vinnige Aksies",
- "quickActionsToolbar": "Vinnige aksies verskyn as knoppies in die Host Metrics-nutsbalk vir die uitvoering van opdragte met een klik.",
- "noQuickActions": "Geen vinnige aksies nog nie.",
- "buttonLabel": "Knoppie-etiket",
- "selectSnippetPlaceholder": "Kies uittreksel...",
- "addActionBtn": "Voeg Aksie by",
"hostNotFound": "Gasheer nie gevind nie",
"loadingHost": "Laai gasheer..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Skyf"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Kanselleer",
- "confirmRunButton": "Hardloop",
- "confirmRunMessage": "Begin \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Verbonde"
}
diff --git a/plugins/host-metrics/locales/translated/ar_SA.json b/plugins/host-metrics/locales/translated/ar_SA.json
index d8c9f111d..26ec0935d 100644
--- a/plugins/host-metrics/locales/translated/ar_SA.json
+++ b/plugins/host-metrics/locales/translated/ar_SA.json
@@ -51,10 +51,6 @@
"processesRunning": "قيد التشغيل",
"loginStats": "إحصائيات تسجيل الدخول عبر SSH",
"noRecentLoginData": "لا توجد بيانات تسجيل دخول حديثة",
- "executingQuickAction": "جارٍ تنفيذ {{name}}...",
- "quickActionSuccess": "تم إكمال {{name}} بنجاح",
- "quickActionFailed": "فشل {{name}}",
- "quickActionError": "فشل تنفيذ {{name}}",
"ports": {
"title": "المنافذ المستمعة",
"protocol": "البروتوكول",
@@ -235,12 +231,6 @@
"addExcludedMount": "يضيف",
"visibleWidgets": "الأدوات المرئية",
"widgetsMovedToHostMetrics": "تتم الآن إضافة البطاقات وترتيبها وتغيير حجمها مباشرةً في علامة تبويب مقاييس المضيف. افتح مقاييس المضيف لهذا المضيف واستخدم تخصيص لاختيار البطاقات التي ستظهر.",
- "quickActionsLabel": "إجراءات سريعة",
- "quickActionsToolbar": "تظهر الإجراءات السريعة كأزرار في شريط أدوات مقاييس المضيف لتنفيذ الأوامر بنقرة واحدة.",
- "noQuickActions": "لا توجد إجراءات سريعة بعد.",
- "buttonLabel": "تسمية الزر",
- "selectSnippetPlaceholder": "اختر مقتطفًا...",
- "addActionBtn": "إضافة إجراء",
"hostNotFound": "المضيف غير موجود",
"loadingHost": "جارٍ تحميل المضيف..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "القرص"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "إلغاء",
- "confirmRunButton": "تشغيل",
- "confirmRunMessage": "تشغيل \"{{name}}\"؟"
- }
- }
- },
"terminal": {
"connected": "متصل"
}
diff --git a/plugins/host-metrics/locales/translated/bg_BG.json b/plugins/host-metrics/locales/translated/bg_BG.json
index 5322ceace..de408ee68 100644
--- a/plugins/host-metrics/locales/translated/bg_BG.json
+++ b/plugins/host-metrics/locales/translated/bg_BG.json
@@ -51,10 +51,6 @@
"processesRunning": "бягане",
"loginStats": "Статистика за SSH вход",
"noRecentLoginData": "Няма скорошни данни за вход",
- "executingQuickAction": "Изпълнява се {{name}}...",
- "quickActionSuccess": "{{name}} завършено успешно",
- "quickActionFailed": "{{name}} неуспешно",
- "quickActionError": "Неуспешно изпълнение на {{name}}",
"ports": {
"title": "Портове за слушане",
"protocol": "Протокол",
@@ -235,12 +231,6 @@
"addExcludedMount": "Добавяне",
"visibleWidgets": "Видими джаджи",
"widgetsMovedToHostMetrics": "Картите вече се добавят, подреждат и преоразмеряват директно в раздела „Показатели за хоста“. Отворете „Показатели за хоста“ за този хост и използвайте „Персонализиране“, за да изберете кои карти да се показват.",
- "quickActionsLabel": "Бързи действия",
- "quickActionsToolbar": "Бързите действия се показват като бутони в лентата с инструменти Host Metrics за изпълнение на команди с едно щракване.",
- "noQuickActions": "Все още няма бързи действия.",
- "buttonLabel": "Етикет на бутона",
- "selectSnippetPlaceholder": "Изберете фрагмент...",
- "addActionBtn": "Добавяне на действие",
"hostNotFound": "Хостът не е намерен",
"loadingHost": "Зареждане на хоста..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Диск"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Отказ",
- "confirmRunButton": "Бягане",
- "confirmRunMessage": "Изпълни „{{name}}“?"
- }
- }
- },
"terminal": {
"connected": "Свързан"
}
diff --git a/plugins/host-metrics/locales/translated/bn_BD.json b/plugins/host-metrics/locales/translated/bn_BD.json
index 8f7447a44..29e6b258d 100644
--- a/plugins/host-metrics/locales/translated/bn_BD.json
+++ b/plugins/host-metrics/locales/translated/bn_BD.json
@@ -51,10 +51,6 @@
"processesRunning": "দৌড়ানো",
"loginStats": "SSH লগইন পরিসংখ্যান",
"noRecentLoginData": "সাম্প্রতিক কোনো লগইন ডেটা নেই",
- "executingQuickAction": "{{name}} কার্যকর করা হচ্ছে ...",
- "quickActionSuccess": "{{name}} সফলভাবে সম্পন্ন হয়েছে",
- "quickActionFailed": "{{name}} ব্যর্থ হয়েছে",
- "quickActionError": "{{name}} কার্যকর করতে ব্যর্থ হয়েছে",
"ports": {
"title": "লিসেনিং পোর্ট",
"protocol": "প্রোটোকল",
@@ -235,12 +231,6 @@
"addExcludedMount": "যোগ করুন",
"visibleWidgets": "দৃশ্যমান উইজেট",
"widgetsMovedToHostMetrics": "এখন কার্ডগুলি সরাসরি হোস্ট মেট্রিক্স ট্যাবে যোগ, সাজানো এবং আকার পরিবর্তন করা যায়। এই হোস্টের জন্য হোস্ট মেট্রিক্স খুলুন এবং কোন কার্ডগুলি দেখানো হবে তা বেছে নিতে কাস্টমাইজ ব্যবহার করুন।",
- "quickActionsLabel": "দ্রুত পদক্ষেপ",
- "quickActionsToolbar": "এক ক্লিকে কমান্ড কার্যকর করার জন্য কুইক অ্যাকশনগুলো হোস্ট মেট্রিক্স টুলবারে বাটন হিসেবে প্রদর্শিত হয়।",
- "noQuickActions": "এখনো কোনো দ্রুত পদক্ষেপ নেওয়া হয়নি।",
- "buttonLabel": "বোতাম লেবেল",
- "selectSnippetPlaceholder": "স্নিপেট নির্বাচন করুন...",
- "addActionBtn": "অ্যাকশন যোগ করুন",
"hostNotFound": "হোস্ট খুঁজে পাওয়া যায়নি",
"loadingHost": "হোস্ট লোড হচ্ছে..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "ডিস্ক"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "বাতিল করুন",
- "confirmRunButton": "দৌড়",
- "confirmRunMessage": "\"{{name}} \" চালান?"
- }
- }
- },
"terminal": {
"connected": "সংযুক্ত"
}
diff --git a/plugins/host-metrics/locales/translated/ca_ES.json b/plugins/host-metrics/locales/translated/ca_ES.json
index 284c80128..e8dc580bd 100644
--- a/plugins/host-metrics/locales/translated/ca_ES.json
+++ b/plugins/host-metrics/locales/translated/ca_ES.json
@@ -51,10 +51,6 @@
"processesRunning": "corrent",
"loginStats": "Estadístiques d'inici de sessió SSH",
"noRecentLoginData": "Sense dades d'inici de sessió recents",
- "executingQuickAction": "Executant {{name}}...",
- "quickActionSuccess": "{{name}} s'ha completat correctament",
- "quickActionFailed": "{{name}} ha fallat",
- "quickActionError": "No s'ha pogut executar {{name}}",
"ports": {
"title": "Ports d'escolta",
"protocol": "Protocol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Afegeix",
"visibleWidgets": "Widgets visibles",
"widgetsMovedToHostMetrics": "Ara les targetes s'afegeixen, s'organitzen i es canvien de mida directament a la pestanya Mètriques de l'amfitrió. Obriu Mètriques de l'amfitrió per a aquest amfitrió i utilitzeu Personalitza per triar quines targetes es mostren.",
- "quickActionsLabel": "Accions ràpides",
- "quickActionsToolbar": "Les accions ràpides apareixen com a botons a la barra d'eines de mètriques de l'amfitrió per executar ordres amb un sol clic.",
- "noQuickActions": "Encara no hi ha accions ràpides.",
- "buttonLabel": "Etiqueta del botó",
- "selectSnippetPlaceholder": "Selecciona un fragment...",
- "addActionBtn": "Afegeix una acció",
"hostNotFound": "No s'ha trobat l'amfitrió",
"loadingHost": "S'està carregant l'amfitrió..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disc"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Cancel·la",
- "confirmRunButton": "Corre",
- "confirmRunMessage": "Executar \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Connectat"
}
diff --git a/plugins/host-metrics/locales/translated/cs_CZ.json b/plugins/host-metrics/locales/translated/cs_CZ.json
index 69ab3e17b..ac42894b4 100644
--- a/plugins/host-metrics/locales/translated/cs_CZ.json
+++ b/plugins/host-metrics/locales/translated/cs_CZ.json
@@ -51,10 +51,6 @@
"processesRunning": "běh",
"loginStats": "Statistiky přihlášení SSH",
"noRecentLoginData": "Žádné nedávné přihlašovací údaje",
- "executingQuickAction": "Spouštění {{name}}...",
- "quickActionSuccess": "{{name}} úspěšně dokončeno",
- "quickActionFailed": "{{name}} selhalo",
- "quickActionError": "Nepodařilo se spustit {{name}}",
"ports": {
"title": "Poslouchající porty",
"protocol": "Protokol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Přidat",
"visibleWidgets": "Viditelné widgety",
"widgetsMovedToHostMetrics": "Karty se nyní přidávají, uspořádávají a mění jejich velikost přímo na kartě Metriky hostitele. Otevřete Metriky hostitele pro tohoto hostitele a pomocí možnosti Přizpůsobit vyberte, které karty se zobrazí.",
- "quickActionsLabel": "Rychlé akce",
- "quickActionsToolbar": "Rychlé akce se zobrazují jako tlačítka v panelu nástrojů Metriky hostitele pro spuštění příkazů jedním kliknutím.",
- "noQuickActions": "Zatím žádné rychlé akce.",
- "buttonLabel": "Štítek tlačítka",
- "selectSnippetPlaceholder": "Vybrat úryvek...",
- "addActionBtn": "Přidat akci",
"hostNotFound": "Hostitel nenalezen",
"loadingHost": "Načítání hostitele..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Zrušit",
- "confirmRunButton": "Běh",
- "confirmRunMessage": "Spustit „{{name}}“?"
- }
- }
- },
"terminal": {
"connected": "Připojeno"
}
diff --git a/plugins/host-metrics/locales/translated/da_DK.json b/plugins/host-metrics/locales/translated/da_DK.json
index bb4dc6cb3..ae747dac1 100644
--- a/plugins/host-metrics/locales/translated/da_DK.json
+++ b/plugins/host-metrics/locales/translated/da_DK.json
@@ -51,10 +51,6 @@
"processesRunning": "løb",
"loginStats": "SSH-loginstatistik",
"noRecentLoginData": "Ingen nylige logindata",
- "executingQuickAction": "Udfører {{name}}...",
- "quickActionSuccess": "{{name}} gennemført",
- "quickActionFailed": "{{name}} mislykkedes",
- "quickActionError": "Kunne ikke udføre {{name}}",
"ports": {
"title": "Lytteporte",
"protocol": "Protokol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Tilføje",
"visibleWidgets": "Synlige widgets",
"widgetsMovedToHostMetrics": "Kort tilføjes, arrangeres og skaleres nu direkte i fanen Værtsmål. Åbn Værtsmål for denne vært, og brug Tilpas til at vælge, hvilke kort der vises.",
- "quickActionsLabel": "Hurtige handlinger",
- "quickActionsToolbar": "Hurtige handlinger vises som knapper i værktøjslinjen Host Metrics til udførelse af kommandoer med et enkelt klik.",
- "noQuickActions": "Ingen hurtige handlinger endnu.",
- "buttonLabel": "Knapmærke",
- "selectSnippetPlaceholder": "Vælg uddrag...",
- "addActionBtn": "Tilføj handling",
"hostNotFound": "Værten blev ikke fundet",
"loadingHost": "Indlæser vært..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Ophæve",
- "confirmRunButton": "Løbe",
- "confirmRunMessage": "Kør \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Forbundet"
}
diff --git a/plugins/host-metrics/locales/translated/de_DE.json b/plugins/host-metrics/locales/translated/de_DE.json
index a656a90a4..628603074 100644
--- a/plugins/host-metrics/locales/translated/de_DE.json
+++ b/plugins/host-metrics/locales/translated/de_DE.json
@@ -51,10 +51,6 @@
"processesRunning": "laufend",
"loginStats": "SSH-Anmeldestatistiken",
"noRecentLoginData": "Keine aktuellen Anmeldedaten",
- "executingQuickAction": "{{name}} wird ausgeführt...",
- "quickActionSuccess": "{{name}} erfolgreich abgeschlossen",
- "quickActionFailed": "{{name}} fehlgeschlagen",
- "quickActionError": "Fehler beim Ausführen von {{name}}",
"ports": {
"title": "Lauschende Ports",
"protocol": "Protokoll",
@@ -235,12 +231,6 @@
"addExcludedMount": "Hinzufügen",
"visibleWidgets": "Sichtbare Widgets",
"widgetsMovedToHostMetrics": "Karten werden jetzt direkt im Tab „Host-Metriken“ hinzugefügt, angeordnet und in der Größe verändert. Öffnen Sie die Host-Metriken für diesen Host und verwenden Sie „Anpassen“, um auszuwählen, welche Karten angezeigt werden.",
- "quickActionsLabel": "Schnellaktionen",
- "quickActionsToolbar": "Schnellaktionen erscheinen als Schaltflächen in der Host-Metriken-Symbolleiste zur Ausführung von Befehlen mit einem Klick.",
- "noQuickActions": "Noch keine Schnellaktionen.",
- "buttonLabel": "Schaltflächenbeschriftung",
- "selectSnippetPlaceholder": "Snippet auswählen...",
- "addActionBtn": "Aktion hinzufügen",
"hostNotFound": "Host nicht gefunden",
"loadingHost": "Host wird geladen..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Datenträger"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Abbrechen",
- "confirmRunButton": "Ausführen",
- "confirmRunMessage": "„{{name}}“ ausführen?"
- }
- }
- },
"terminal": {
"connected": "Verbunden"
}
diff --git a/plugins/host-metrics/locales/translated/el_GR.json b/plugins/host-metrics/locales/translated/el_GR.json
index c561a0926..f7481a0d3 100644
--- a/plugins/host-metrics/locales/translated/el_GR.json
+++ b/plugins/host-metrics/locales/translated/el_GR.json
@@ -51,10 +51,6 @@
"processesRunning": "τρέξιμο",
"loginStats": "Στατιστικά στοιχεία σύνδεσης SSH",
"noRecentLoginData": "Δεν υπάρχουν πρόσφατα δεδομένα σύνδεσης",
- "executingQuickAction": "Εκτέλεση {{name}}...",
- "quickActionSuccess": "{{name}} ολοκληρώθηκε με επιτυχία",
- "quickActionFailed": "{{name}} απέτυχε",
- "quickActionError": "Αποτυχία εκτέλεσης {{name}}",
"ports": {
"title": "Θύρες ακρόασης",
"protocol": "Πρωτόκολλο",
@@ -235,12 +231,6 @@
"addExcludedMount": "Προσθέτω",
"visibleWidgets": "Ορατά γραφικά στοιχεία",
"widgetsMovedToHostMetrics": "Οι κάρτες προστίθενται, ταξινομούνται και αλλάζουν μέγεθος απευθείας στην καρτέλα Μετρήσεις κεντρικού υπολογιστή. Ανοίξτε τις Μετρήσεις κεντρικού υπολογιστή για αυτόν τον κεντρικό υπολογιστή και χρησιμοποιήστε την Προσαρμογή για να επιλέξετε ποιες κάρτες θα εμφανίζονται.",
- "quickActionsLabel": "Γρήγορες ενέργειες",
- "quickActionsToolbar": "Οι γρήγορες ενέργειες εμφανίζονται ως κουμπιά στη γραμμή εργαλείων Μετρήσεων κεντρικού υπολογιστή για εκτέλεση εντολών με ένα κλικ.",
- "noQuickActions": "Δεν υπάρχουν ακόμη γρήγορες ενέργειες.",
- "buttonLabel": "Ετικέτα κουμπιού",
- "selectSnippetPlaceholder": "Επιλογή αποσπάσματος...",
- "addActionBtn": "Προσθήκη ενέργειας",
"hostNotFound": "Δεν βρέθηκε κεντρικός υπολογιστής",
"loadingHost": "Φόρτωση κεντρικού υπολογιστή..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Δίσκος"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Ματαίωση",
- "confirmRunButton": "Τρέξιμο",
- "confirmRunMessage": "Εκτέλεση \"{{name}}\";"
- }
- }
- },
"terminal": {
"connected": "Συνδεδεμένος"
}
diff --git a/plugins/host-metrics/locales/translated/es_ES.json b/plugins/host-metrics/locales/translated/es_ES.json
index 1bc356fa9..e396565dc 100644
--- a/plugins/host-metrics/locales/translated/es_ES.json
+++ b/plugins/host-metrics/locales/translated/es_ES.json
@@ -51,10 +51,6 @@
"processesRunning": "en ejecución",
"loginStats": "Estadísticas de inicio de sesión SSH",
"noRecentLoginData": "No hay datos de inicio de sesión recientes",
- "executingQuickAction": "Ejecutando {{name}}...",
- "quickActionSuccess": "{{name}} completado correctamente",
- "quickActionFailed": "{{name}} falló",
- "quickActionError": "Error al ejecutar {{name}}",
"ports": {
"title": "Puertos en escucha",
"protocol": "Protocolo",
@@ -235,12 +231,6 @@
"addExcludedMount": "Agregar",
"visibleWidgets": "Widgets visibles",
"widgetsMovedToHostMetrics": "Ahora las tarjetas se añaden, organizan y redimensionan directamente en la pestaña Métricas del host. Abre Métricas del host para este host y usa Personalizar para elegir qué tarjetas se muestran.",
- "quickActionsLabel": "Acciones rápidas",
- "quickActionsToolbar": "Las acciones rápidas aparecen como botones en la barra de herramientas de Métricas del host para la ejecución de comandos con un solo clic.",
- "noQuickActions": "Aún no hay acciones rápidas.",
- "buttonLabel": "Etiqueta del botón",
- "selectSnippetPlaceholder": "Seleccionar fragmento...",
- "addActionBtn": "Añadir acción",
"hostNotFound": "Host no encontrado",
"loadingHost": "Cargando host..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disco"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Cancelar",
- "confirmRunButton": "Ejecutar",
- "confirmRunMessage": "¿Ejecutar \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Conectado"
}
diff --git a/plugins/host-metrics/locales/translated/fi_FI.json b/plugins/host-metrics/locales/translated/fi_FI.json
index 92295782a..85593950c 100644
--- a/plugins/host-metrics/locales/translated/fi_FI.json
+++ b/plugins/host-metrics/locales/translated/fi_FI.json
@@ -51,10 +51,6 @@
"processesRunning": "juokseminen",
"loginStats": "SSH-kirjautumistilastot",
"noRecentLoginData": "Ei viimeaikaisia kirjautumistietoja",
- "executingQuickAction": "Suoritetaan {{name}}...",
- "quickActionSuccess": "{{name}} suoritettu onnistuneesti",
- "quickActionFailed": "{{name}} epäonnistui",
- "quickActionError": "Suoritus epäonnistui {{name}}",
"ports": {
"title": "Kuunteluportit",
"protocol": "Protokolla",
@@ -235,12 +231,6 @@
"addExcludedMount": "Lisätä",
"visibleWidgets": "Näkyvät widgetit",
"widgetsMovedToHostMetrics": "Kortteja lisätään, järjestetään ja niiden kokoa muutetaan nyt suoraan Isännän mittarit -välilehdellä. Avaa tämän isännän Isännän mittarit ja valitse näytettävät kortit Mukauta-toiminnolla.",
- "quickActionsLabel": "Pikatoiminnot",
- "quickActionsToolbar": "Pikatoiminnot näkyvät painikkeina isäntämetriikkatyökalurivillä komentojen suorittamista varten yhdellä napsautuksella.",
- "noQuickActions": "Ei vielä nopeita toimia.",
- "buttonLabel": "Painikkeen nimi",
- "selectSnippetPlaceholder": "Valitse katkelma...",
- "addActionBtn": "Lisää toiminto",
"hostNotFound": "Isäntää ei löydy",
"loadingHost": "Ladataan isäntää..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Levy"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Peruuttaa",
- "confirmRunButton": "Juokse",
- "confirmRunMessage": "Suorittaa \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Yhdistetty"
}
diff --git a/plugins/host-metrics/locales/translated/fr_FR.json b/plugins/host-metrics/locales/translated/fr_FR.json
index a2247ec6c..19aebaec8 100644
--- a/plugins/host-metrics/locales/translated/fr_FR.json
+++ b/plugins/host-metrics/locales/translated/fr_FR.json
@@ -51,10 +51,6 @@
"processesRunning": "en cours",
"loginStats": "Statistiques de connexion SSH",
"noRecentLoginData": "Aucune donnée de connexion récente",
- "executingQuickAction": "Exécution de {{name}}...",
- "quickActionSuccess": "{{name}} terminé avec succès",
- "quickActionFailed": "Échec de {{name}}",
- "quickActionError": "Échec de l'exécution de {{name}}",
"ports": {
"title": "Ports en écoute",
"protocol": "Protocole",
@@ -235,12 +231,6 @@
"addExcludedMount": "Ajouter",
"visibleWidgets": "Widgets visibles",
"widgetsMovedToHostMetrics": "Les cartes sont désormais ajoutées, organisées et redimensionnées directement dans l'onglet Métriques de l'hôte. Ouvrez les métriques de cet hôte et utilisez Personnaliser pour choisir les cartes à afficher.",
- "quickActionsLabel": "Actions rapides",
- "quickActionsToolbar": "Les actions rapides apparaissent sous forme de boutons dans la barre d'outils Métriques de l'hôte pour une exécution en un clic.",
- "noQuickActions": "Aucune action rapide pour le moment.",
- "buttonLabel": "Étiquette du bouton",
- "selectSnippetPlaceholder": "Sélectionner un snippet...",
- "addActionBtn": "Ajouter une action",
"hostNotFound": "Hôte introuvable",
"loadingHost": "Chargement de l'hôte..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disque"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Annuler",
- "confirmRunButton": "Exécuter",
- "confirmRunMessage": "Exécuter « {{name}} » ?"
- }
- }
- },
"terminal": {
"connected": "Connecté"
}
diff --git a/plugins/host-metrics/locales/translated/he_IL.json b/plugins/host-metrics/locales/translated/he_IL.json
index a6af986a9..4112714b3 100644
--- a/plugins/host-metrics/locales/translated/he_IL.json
+++ b/plugins/host-metrics/locales/translated/he_IL.json
@@ -51,10 +51,6 @@
"processesRunning": "רִיצָה",
"loginStats": "סטטיסטיקות כניסה ל-SSH",
"noRecentLoginData": "אין נתוני התחברות אחרונים",
- "executingQuickAction": "מבצע {{name}}...",
- "quickActionSuccess": "{{name}} הושלם בהצלחה",
- "quickActionFailed": "{{name}} נכשל",
- "quickActionError": "נכשל בביצוע {{name}}",
"ports": {
"title": "יציאות האזנה",
"protocol": "פּרוֹטוֹקוֹל",
@@ -235,12 +231,6 @@
"addExcludedMount": "לְהוֹסִיף",
"visibleWidgets": "ווידג'טים גלויים",
"widgetsMovedToHostMetrics": "כעת ניתן להוסיף, לסידור ולשנות את גודל הכרטיסים ישירות בכרטיסייה 'מדדי מארח'. פתחו את 'מדדי מארח' עבור מארח זה והשתמשו באפשרות 'התאמה אישית' כדי לבחור אילו כרטיסים יוצגו.",
- "quickActionsLabel": "פעולות מהירות",
- "quickActionsToolbar": "פעולות מהירות מופיעות ככפתורים בסרגל הכלים של Host Metrics לביצוע פקודה בלחיצה אחת.",
- "noQuickActions": "אין עדיין פעולות מהירות.",
- "buttonLabel": "תווית כפתור",
- "selectSnippetPlaceholder": "בחר קטע...",
- "addActionBtn": "הוסף פעולה",
"hostNotFound": "המארח לא נמצא",
"loadingHost": "טוען מארח..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "דִיסק"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "לְבַטֵל",
- "confirmRunButton": "לָרוּץ",
- "confirmRunMessage": "להפעיל את \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "מְחוּבָּר"
}
diff --git a/plugins/host-metrics/locales/translated/hi_IN.json b/plugins/host-metrics/locales/translated/hi_IN.json
index 005133f7f..e1bbc92f7 100644
--- a/plugins/host-metrics/locales/translated/hi_IN.json
+++ b/plugins/host-metrics/locales/translated/hi_IN.json
@@ -51,10 +51,6 @@
"processesRunning": "चल रहे",
"loginStats": "SSH लॉगिन आँकड़े",
"noRecentLoginData": "कोई हालिया लॉगिन डेटा नहीं",
- "executingQuickAction": "{{name}} निष्पादित हो रहा है...",
- "quickActionSuccess": "{{name}} सफलतापूर्वक पूर्ण हुआ",
- "quickActionFailed": "{{name}} विफल रहा",
- "quickActionError": "{{name}} निष्पादित करने में विफल",
"ports": {
"title": "लिसनिंग पोर्ट",
"protocol": "प्रोटोकॉल",
@@ -235,12 +231,6 @@
"addExcludedMount": "जोड़ना",
"visibleWidgets": "दृश्यमान विजेट्स",
"widgetsMovedToHostMetrics": "कार्ड अब सीधे होस्ट मेट्रिक्स टैब में जोड़े, व्यवस्थित और आकार बदले जा सकते हैं। इस होस्ट के लिए होस्ट मेट्रिक्स खोलें और यह चुनने के लिए कस्टमाइज़ का उपयोग करें कि कौन से कार्ड दिखाए जाएँ।",
- "quickActionsLabel": "त्वरित कार्रवाइयाँ",
- "quickActionsToolbar": "त्वरित कार्रवाइयाँ एक-क्लिक कमांड निष्पादन के लिए होस्ट मेट्रिक्स टूलबार में बटन के रूप में दिखाई देती हैं।",
- "noQuickActions": "अभी तक कोई त्वरित कार्रवाई नहीं।",
- "buttonLabel": "बटन लेबल",
- "selectSnippetPlaceholder": "स्निपेट चुनें...",
- "addActionBtn": "कार्रवाई जोड़ें",
"hostNotFound": "होस्ट नहीं मिला",
"loadingHost": "होस्ट लोड हो रहा है..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "डिस्क"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "रद्द करें",
- "confirmRunButton": "चलाएँ",
- "confirmRunMessage": "\"{{name}}\" चलाएँ?"
- }
- }
- },
"terminal": {
"connected": "कनेक्टेड"
}
diff --git a/plugins/host-metrics/locales/translated/hu_HU.json b/plugins/host-metrics/locales/translated/hu_HU.json
index 0deb6c830..bbe384c1d 100644
--- a/plugins/host-metrics/locales/translated/hu_HU.json
+++ b/plugins/host-metrics/locales/translated/hu_HU.json
@@ -51,10 +51,6 @@
"processesRunning": "futás",
"loginStats": "SSH bejelentkezési statisztikák",
"noRecentLoginData": "Nincsenek friss bejelentkezési adatok",
- "executingQuickAction": "{{name}} végrehajtása ...",
- "quickActionSuccess": "{{name}} sikeresen befejeződött",
- "quickActionFailed": "{{name}} sikertelen",
- "quickActionError": "Nem sikerült végrehajtani a {{name}} parancsot.",
"ports": {
"title": "Figyelő portok",
"protocol": "Jegyzőkönyv",
@@ -235,12 +231,6 @@
"addExcludedMount": "Hozzáadás",
"visibleWidgets": "Látható widgetek",
"widgetsMovedToHostMetrics": "A kártyák mostantól közvetlenül a Gazdagép metrikái lapon adhatók hozzá, rendezhetők el és méretezhetők át. Nyissa meg a Gazdagép metrikái oldalt ehhez a gazdagéphez, és a Testreszabás gombbal válassza ki, hogy mely kártyák jelenjenek meg.",
- "quickActionsLabel": "Gyors műveletek",
- "quickActionsToolbar": "A gyors műveletek gombokként jelennek meg a Host Metrics eszköztáron, így egyetlen kattintással végrehajthatók a parancsok.",
- "noQuickActions": "Még nincsenek gyors intézkedések.",
- "buttonLabel": "Gomb felirata",
- "selectSnippetPlaceholder": "Válasszon részletet...",
- "addActionBtn": "Művelet hozzáadása",
"hostNotFound": "Nem található gazdagép",
"loadingHost": "Gazdagép betöltése..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Korong"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Mégsem",
- "confirmRunButton": "Fut",
- "confirmRunMessage": "Futtassa a \"{{name}} \" programot?"
- }
- }
- },
"terminal": {
"connected": "Csatlakoztatva"
}
diff --git a/plugins/host-metrics/locales/translated/id_ID.json b/plugins/host-metrics/locales/translated/id_ID.json
index 646f1d2e3..79f135831 100644
--- a/plugins/host-metrics/locales/translated/id_ID.json
+++ b/plugins/host-metrics/locales/translated/id_ID.json
@@ -51,10 +51,6 @@
"processesRunning": "berlari",
"loginStats": "Statistik Login SSH",
"noRecentLoginData": "Tidak ada data login terbaru.",
- "executingQuickAction": "Menjalankan {{name}}...",
- "quickActionSuccess": "{{name}} berhasil diselesaikan",
- "quickActionFailed": "{{name}} gagal",
- "quickActionError": "Gagal mengeksekusi {{name}}",
"ports": {
"title": "Port Pendengaran",
"protocol": "Protokol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Menambahkan",
"visibleWidgets": "Widget yang Terlihat",
"widgetsMovedToHostMetrics": "Kartu sekarang ditambahkan, diatur, dan diubah ukurannya langsung di tab Metrik Host. Buka Metrik Host untuk host ini dan gunakan Sesuaikan untuk memilih kartu mana yang akan ditampilkan.",
- "quickActionsLabel": "Tindakan Cepat",
- "quickActionsToolbar": "Tindakan cepat muncul sebagai tombol di bilah alat Metrik Host untuk eksekusi perintah sekali klik.",
- "noQuickActions": "Belum ada tindakan cepat.",
- "buttonLabel": "Label tombol",
- "selectSnippetPlaceholder": "Pilih cuplikan...",
- "addActionBtn": "Tambahkan Tindakan",
"hostNotFound": "Host tidak ditemukan",
"loadingHost": "Memuat host..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Membatalkan",
- "confirmRunButton": "Berlari",
- "confirmRunMessage": "Jalankan \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Terhubung"
}
diff --git a/plugins/host-metrics/locales/translated/it_IT.json b/plugins/host-metrics/locales/translated/it_IT.json
index ce65596e7..8e3807949 100644
--- a/plugins/host-metrics/locales/translated/it_IT.json
+++ b/plugins/host-metrics/locales/translated/it_IT.json
@@ -51,10 +51,6 @@
"processesRunning": "in esecuzione",
"loginStats": "Statistiche di accesso SSH",
"noRecentLoginData": "Nessun dato di accesso recente",
- "executingQuickAction": "Esecuzione di {{name}}...",
- "quickActionSuccess": "{{name}} completato con successo",
- "quickActionFailed": "{{name}} non riuscito",
- "quickActionError": "Impossibile eseguire {{name}}",
"ports": {
"title": "Porte in ascolto",
"protocol": "Protocollo",
@@ -235,12 +231,6 @@
"addExcludedMount": "Aggiungere",
"visibleWidgets": "Widget visibili",
"widgetsMovedToHostMetrics": "Le schede ora vengono aggiunte, disposte e ridimensionate direttamente nella scheda Metriche host. Apri Metriche host per questo host e usa Personalizza per scegliere quali schede mostrare.",
- "quickActionsLabel": "Azioni Rapide",
- "quickActionsToolbar": "Le azioni rapide appaiono come pulsanti nella barra delle metriche host per l'esecuzione di comandi con un clic.",
- "noQuickActions": "Nessuna azione rapida ancora.",
- "buttonLabel": "Etichetta pulsante",
- "selectSnippetPlaceholder": "Seleziona snippet...",
- "addActionBtn": "Aggiungi Azione",
"hostNotFound": "Host non trovato",
"loadingHost": "Caricamento host..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disco"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Annulla",
- "confirmRunButton": "Esegui",
- "confirmRunMessage": "Eseguire \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Connesso"
}
diff --git a/plugins/host-metrics/locales/translated/ja_JP.json b/plugins/host-metrics/locales/translated/ja_JP.json
index 2dafa5075..04607d19d 100644
--- a/plugins/host-metrics/locales/translated/ja_JP.json
+++ b/plugins/host-metrics/locales/translated/ja_JP.json
@@ -51,10 +51,6 @@
"processesRunning": "実行中",
"loginStats": "SSHログイン統計",
"noRecentLoginData": "最近のログインデータがありません",
- "executingQuickAction": "{{name}} を実行中...",
- "quickActionSuccess": "{{name}} が正常に完了しました",
- "quickActionFailed": "{{name}} が失敗しました",
- "quickActionError": "{{name}} の実行に失敗しました",
"ports": {
"title": "リスニングポート",
"protocol": "プロトコル",
@@ -235,12 +231,6 @@
"addExcludedMount": "追加",
"visibleWidgets": "表示するウィジェット",
"widgetsMovedToHostMetrics": "カードは、ホストメトリクスタブで直接追加、配置、サイズ変更できるようになりました。このホストのホストメトリクスを開き、カスタマイズを使用して表示するカードを選択してください。",
- "quickActionsLabel": "クイックアクション",
- "quickActionsToolbar": "クイックアクションはホストメトリクスツールバーにボタンとして表示され、ワンクリックでコマンドを実行できます。",
- "noQuickActions": "クイックアクションはまだありません",
- "buttonLabel": "ボタンラベル",
- "selectSnippetPlaceholder": "スニペットを選択...",
- "addActionBtn": "アクションを追加",
"hostNotFound": "ホストが見つかりません",
"loadingHost": "ホストを読み込み中..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "ディスク"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "キャンセル",
- "confirmRunButton": "実行",
- "confirmRunMessage": "「{{name}}」を実行しますか?"
- }
- }
- },
"terminal": {
"connected": "接続中"
}
diff --git a/plugins/host-metrics/locales/translated/ko_KR.json b/plugins/host-metrics/locales/translated/ko_KR.json
index 2cc9ba92d..07177ee58 100644
--- a/plugins/host-metrics/locales/translated/ko_KR.json
+++ b/plugins/host-metrics/locales/translated/ko_KR.json
@@ -51,10 +51,6 @@
"processesRunning": "실행 중",
"loginStats": "SSH 로그인 통계",
"noRecentLoginData": "최근 로그인 데이터가 없습니다.",
- "executingQuickAction": "{{name}} 실행 중...",
- "quickActionSuccess": "{{name}}이(가) 성공적으로 완료되었습니다.",
- "quickActionFailed": "{{name}} 실패",
- "quickActionError": "{{name}} 실행에 실패했습니다.",
"ports": {
"title": "수신 대기 포트",
"protocol": "프로토콜",
@@ -235,12 +231,6 @@
"addExcludedMount": "추가하다",
"visibleWidgets": "표시 위젯",
"widgetsMovedToHostMetrics": "카드는 이제 호스트 메트릭 탭에서 직접 추가, 정렬 및 크기 조정됩니다. 이 호스트의 호스트 메트릭을 열고 사용자 정의를 사용하여 표시할 카드를 선택하세요.",
- "quickActionsLabel": "빠른 작업",
- "quickActionsToolbar": "빠른 작업은 한 번의 클릭으로 명령을 실행할 수 있도록 호스트 메트릭 도구 모음에 버튼으로 표시됩니다.",
- "noQuickActions": "아직 빠른 작업이 없습니다.",
- "buttonLabel": "버튼 레이블",
- "selectSnippetPlaceholder": "스니펫 선택...",
- "addActionBtn": "작업 추가",
"hostNotFound": "호스트를 찾을 수 없습니다",
"loadingHost": "호스트 로드 중..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "디스크"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "취소",
- "confirmRunButton": "실행",
- "confirmRunMessage": "\"{{name}}\"을(를) 실행하시겠습니까?"
- }
- }
- },
"terminal": {
"connected": "연결됨"
}
diff --git a/plugins/host-metrics/locales/translated/nl_NL.json b/plugins/host-metrics/locales/translated/nl_NL.json
index fb37c8c92..532137c7c 100644
--- a/plugins/host-metrics/locales/translated/nl_NL.json
+++ b/plugins/host-metrics/locales/translated/nl_NL.json
@@ -51,10 +51,6 @@
"processesRunning": "rennen",
"loginStats": "SSH-aanmeldingsstatistieken",
"noRecentLoginData": "Geen recente inloggegevens",
- "executingQuickAction": "Het uitvoeren van {{name}}...",
- "quickActionSuccess": "{{name}} succesvol voltooid",
- "quickActionFailed": "{{name}} mislukt",
- "quickActionError": "Het uitvoeren van {{name}} is mislukt.",
"ports": {
"title": "Luisterpoorten",
"protocol": "Protocol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Toevoegen",
"visibleWidgets": "Zichtbare widgets",
"widgetsMovedToHostMetrics": "Kaarten kunnen nu rechtstreeks in het tabblad Hoststatistieken worden toegevoegd, gerangschikt en van formaat worden veranderd. Open Hoststatistieken voor deze host en gebruik Aanpassen om te kiezen welke kaarten worden weergegeven.",
- "quickActionsLabel": "Snelle acties",
- "quickActionsToolbar": "Snelle acties verschijnen als knoppen in de Host Metrics-werkbalk, zodat u opdrachten met één klik kunt uitvoeren.",
- "noQuickActions": "Nog geen snelle acties ondernomen.",
- "buttonLabel": "Knoplabel",
- "selectSnippetPlaceholder": "Selecteer fragment...",
- "addActionBtn": "Actie toevoegen",
"hostNotFound": "Host niet gevonden",
"loadingHost": "Host wordt geladen..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Schijf"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Annuleren",
- "confirmRunButton": "Loop",
- "confirmRunMessage": "Run \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Aangesloten"
}
diff --git a/plugins/host-metrics/locales/translated/no_NO.json b/plugins/host-metrics/locales/translated/no_NO.json
index 9ea9715dd..1a08e5167 100644
--- a/plugins/host-metrics/locales/translated/no_NO.json
+++ b/plugins/host-metrics/locales/translated/no_NO.json
@@ -51,10 +51,6 @@
"processesRunning": "løping",
"loginStats": "SSH-påloggingsstatistikk",
"noRecentLoginData": "Ingen nylige påloggingsdata",
- "executingQuickAction": "Utfører {{name}}...",
- "quickActionSuccess": "{{name}} fullført",
- "quickActionFailed": "{{name}} mislyktes",
- "quickActionError": "Kunne ikke utføre {{name}}",
"ports": {
"title": "Lytteporter",
"protocol": "Protokoll",
@@ -235,12 +231,6 @@
"addExcludedMount": "Legge til",
"visibleWidgets": "Synlige widgeter",
"widgetsMovedToHostMetrics": "Kort legges nå til, arrangeres og endres i størrelse direkte i fanen Vertsmålinger. Åpne Vertsmålinger for denne verten og bruk Tilpass for å velge hvilke kort som vises.",
- "quickActionsLabel": "Hurtighandlinger",
- "quickActionsToolbar": "Hurtighandlinger vises som knapper i verktøylinjen for vertsmålinger for utførelse av kommandoer med ett klikk.",
- "noQuickActions": "Ingen hurtighandlinger ennå.",
- "buttonLabel": "Knappetikett",
- "selectSnippetPlaceholder": "Velg utdrag...",
- "addActionBtn": "Legg til handling",
"hostNotFound": "Verten ble ikke funnet",
"loadingHost": "Laster inn vert ..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Kansellere",
- "confirmRunButton": "Løp",
- "confirmRunMessage": "Kjør \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Tilkoblet"
}
diff --git a/plugins/host-metrics/locales/translated/pl_PL.json b/plugins/host-metrics/locales/translated/pl_PL.json
index a3f1a10c3..0b6d8a70f 100644
--- a/plugins/host-metrics/locales/translated/pl_PL.json
+++ b/plugins/host-metrics/locales/translated/pl_PL.json
@@ -51,10 +51,6 @@
"processesRunning": "działa",
"loginStats": "Statystyki logowania SSH",
"noRecentLoginData": "Brak ostatnich danych logowania",
- "executingQuickAction": "Wykonywanie {{name}}...",
- "quickActionSuccess": "{{name}} ukończono pomyślnie",
- "quickActionFailed": "{{name}} nie powiodło się",
- "quickActionError": "Nie udało się wykonać {{name}}",
"ports": {
"title": "Porty nasłuchujące",
"protocol": "Protokół",
@@ -235,12 +231,6 @@
"addExcludedMount": "Dodaj",
"visibleWidgets": "Widoczne widżety",
"widgetsMovedToHostMetrics": "Karty dodaje się, układa i skaluje bezpośrednio na karcie „Metryki hosta”. Otwórz Metryki hosta dla tego hosta i użyj opcji „Dostosuj”, aby wybrać wyświetlane karty.",
- "quickActionsLabel": "Szybkie akcje",
- "quickActionsToolbar": "Szybkie akcje pojawiają się jako przyciski na pasku narzędzi Metryki hosta, umożliwiając wykonanie polecenia jednym kliknięciem.",
- "noQuickActions": "Brak szybkich akcji.",
- "buttonLabel": "Etykieta przycisku",
- "selectSnippetPlaceholder": "Wybierz snippet...",
- "addActionBtn": "Dodaj akcję",
"hostNotFound": "Nie znaleziono hosta",
"loadingHost": "Ładowanie hosta..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Dysk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Anuluj",
- "confirmRunButton": "Uruchom",
- "confirmRunMessage": "Uruchomić „{{name}}”?"
- }
- }
- },
"terminal": {
"connected": "Połączono"
}
diff --git a/plugins/host-metrics/locales/translated/pt_BR.json b/plugins/host-metrics/locales/translated/pt_BR.json
index 4c7343950..2813920ce 100644
--- a/plugins/host-metrics/locales/translated/pt_BR.json
+++ b/plugins/host-metrics/locales/translated/pt_BR.json
@@ -51,10 +51,6 @@
"processesRunning": "correndo",
"loginStats": "Estatísticas de login SSH",
"noRecentLoginData": "Nenhum dado de login recente",
- "executingQuickAction": "Executando {{name}}...",
- "quickActionSuccess": "{{name}} concluído com sucesso",
- "quickActionFailed": "{{name}} falhou",
- "quickActionError": "Falha ao executar {{name}}",
"ports": {
"title": "Portas de escuta",
"protocol": "Protocolo",
@@ -235,12 +231,6 @@
"addExcludedMount": "Adicionar",
"visibleWidgets": "Widgets Visíveis",
"widgetsMovedToHostMetrics": "Agora, os cartões podem ser adicionados, organizados e redimensionados diretamente na guia Métricas do Host. Abra as Métricas do Host para este host e use a opção Personalizar para escolher quais cartões serão exibidos.",
- "quickActionsLabel": "Ações rápidas",
- "quickActionsToolbar": "As ações rápidas aparecem como botões na barra de ferramentas do Host Metrics para execução de comandos com um único clique.",
- "noQuickActions": "Nenhuma ação imediata ainda.",
- "buttonLabel": "Rótulo do botão",
- "selectSnippetPlaceholder": "Selecione o trecho...",
- "addActionBtn": "Adicionar ação",
"hostNotFound": "Host não encontrado",
"loadingHost": "Carregando host..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disco"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Cancelar",
- "confirmRunButton": "Correr",
- "confirmRunMessage": "Executar \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Conectado"
}
diff --git a/plugins/host-metrics/locales/translated/pt_PT.json b/plugins/host-metrics/locales/translated/pt_PT.json
index 108a76e10..8458a4232 100644
--- a/plugins/host-metrics/locales/translated/pt_PT.json
+++ b/plugins/host-metrics/locales/translated/pt_PT.json
@@ -51,10 +51,6 @@
"processesRunning": "em execução",
"loginStats": "Estatísticas de início de sessão SSH",
"noRecentLoginData": "Sem dados de início de sessão recentes",
- "executingQuickAction": "A executar {{name}}...",
- "quickActionSuccess": "{{name}} concluído com sucesso",
- "quickActionFailed": "{{name}} falhou",
- "quickActionError": "Falha ao executar {{name}}",
"ports": {
"title": "Portas em escuta",
"protocol": "Protocolo",
@@ -235,12 +231,6 @@
"addExcludedMount": "Adicionar",
"visibleWidgets": "Widgets Visíveis",
"widgetsMovedToHostMetrics": "Os cartões são agora adicionados, organizados e redimensionados diretamente no separador Métricas do Host. Abra as Métricas do Host para este host e use Personalizar para escolher quais os cartões a mostrar.",
- "quickActionsLabel": "Ações Rápidas",
- "quickActionsToolbar": "As ações rápidas aparecem como botões na barra de ferramentas de Métricas do Host para execução de comandos com um clique.",
- "noQuickActions": "Ainda não existem ações rápidas.",
- "buttonLabel": "Texto do botão",
- "selectSnippetPlaceholder": "Selecionar snippet...",
- "addActionBtn": "Adicionar Ação",
"hostNotFound": "Host não encontrado",
"loadingHost": "A carregar o host..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disco"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Cancelar",
- "confirmRunButton": "Executar",
- "confirmRunMessage": "Executar \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Ligado"
}
diff --git a/plugins/host-metrics/locales/translated/ro_RO.json b/plugins/host-metrics/locales/translated/ro_RO.json
index 454a5a0d1..a20a26d78 100644
--- a/plugins/host-metrics/locales/translated/ro_RO.json
+++ b/plugins/host-metrics/locales/translated/ro_RO.json
@@ -51,10 +51,6 @@
"processesRunning": "funcţionare",
"loginStats": "Statistici de conectare SSH",
"noRecentLoginData": "Nu există date de conectare recente",
- "executingQuickAction": "Se execută {{name}}...",
- "quickActionSuccess": "{{name}} finalizat cu succes",
- "quickActionFailed": "{{name}} a eșuat",
- "quickActionError": "Executarea {{name}} a eșuat",
"ports": {
"title": "Porturi de ascultare",
"protocol": "Protocol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Adăuga",
"visibleWidgets": "Widgeturi vizibile",
"widgetsMovedToHostMetrics": "Cardurile sunt acum adăugate, aranjate și redimensionate direct în fila Indicatori gazdă. Deschideți Indicatori gazdă pentru această gazdă și utilizați Personalizare pentru a alege ce carduri sunt afișate.",
- "quickActionsLabel": "Acțiuni rapide",
- "quickActionsToolbar": "Acțiunile rapide apar sub formă de butoane în bara de instrumente Host Metrics pentru executarea comenzilor cu un singur clic.",
- "noQuickActions": "Nicio acțiune rapidă încă.",
- "buttonLabel": "Etichetă buton",
- "selectSnippetPlaceholder": "Selectați fragmentul...",
- "addActionBtn": "Adăugați o acțiune",
"hostNotFound": "Gazdă negăsită",
"loadingHost": "Se încarcă gazda..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disc"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Anula",
- "confirmRunButton": "Aleargă",
- "confirmRunMessage": "Executați „{{name}}”?"
- }
- }
- },
"terminal": {
"connected": "Conectat"
}
diff --git a/plugins/host-metrics/locales/translated/ru_RU.json b/plugins/host-metrics/locales/translated/ru_RU.json
index ce1049e18..a3c33f7dd 100644
--- a/plugins/host-metrics/locales/translated/ru_RU.json
+++ b/plugins/host-metrics/locales/translated/ru_RU.json
@@ -51,10 +51,6 @@
"processesRunning": "выполняется",
"loginStats": "Статистика входов SSH",
"noRecentLoginData": "Нет данных о недавних входах",
- "executingQuickAction": "Выполняется {{name}}...",
- "quickActionSuccess": "{{name}} успешно завершено",
- "quickActionFailed": "{{name}} не удалось",
- "quickActionError": "Не удалось выполнить {{name}}",
"ports": {
"title": "Прослушиваемые порты",
"protocol": "Протокол",
@@ -235,12 +231,6 @@
"addExcludedMount": "Добавлять",
"visibleWidgets": "Видимые виджеты",
"widgetsMovedToHostMetrics": "Карточки теперь добавляются, упорядочиваются и изменяются в размере непосредственно на вкладке «Метрики хоста». Откройте метрики для этого хоста и используйте «Настроить», чтобы выбрать отображаемые карточки.",
- "quickActionsLabel": "Быстрые действия",
- "quickActionsToolbar": "Быстрые действия отображаются в виде кнопок на панели инструментов метрик хоста для выполнения команд в один клик.",
- "noQuickActions": "Пока нет быстрых действий.",
- "buttonLabel": "Надпись кнопки",
- "selectSnippetPlaceholder": "Выбрать сниппет...",
- "addActionBtn": "Добавить действие",
"hostNotFound": "Хост не найден",
"loadingHost": "Загрузка хоста..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Диск"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Отмена",
- "confirmRunButton": "Запустить",
- "confirmRunMessage": "Запустить \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Подключено"
}
diff --git a/plugins/host-metrics/locales/translated/sr_SP.json b/plugins/host-metrics/locales/translated/sr_SP.json
index a3dd81af5..1c780c245 100644
--- a/plugins/host-metrics/locales/translated/sr_SP.json
+++ b/plugins/host-metrics/locales/translated/sr_SP.json
@@ -51,10 +51,6 @@
"processesRunning": "трчање",
"loginStats": "Статистика SSH пријава",
"noRecentLoginData": "Нема скорашњих података за пријављивање",
- "executingQuickAction": "Извршава се {{name}}...",
- "quickActionSuccess": "{{name}} успешно завршено",
- "quickActionFailed": "{{name}} није успело",
- "quickActionError": "Није успело извршавање {{name}}",
"ports": {
"title": "Портови за слушање",
"protocol": "Протокол",
@@ -235,12 +231,6 @@
"addExcludedMount": "Додај",
"visibleWidgets": "Видљиви виџети",
"widgetsMovedToHostMetrics": "Картице се сада додају, распоређују и мењају им се величина директно на картици „Host Metrics“. Отворите „Host Metrics“ за овог хоста и користите „Customize“ да бисте изабрали које ће картице бити приказане.",
- "quickActionsLabel": "Брзе акције",
- "quickActionsToolbar": "Брзе акције се појављују као дугмад у траци са алаткама Host Metrics за извршавање команди једним кликом.",
- "noQuickActions": "Још нема брзих акција.",
- "buttonLabel": "Ознака дугмета",
- "selectSnippetPlaceholder": "Изаберите исечак...",
- "addActionBtn": "Додај акцију",
"hostNotFound": "Хост није пронађен",
"loadingHost": "Учитавање хоста..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Диск"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Откажи",
- "confirmRunButton": "Трчи",
- "confirmRunMessage": "Покрени „{{name}}“?"
- }
- }
- },
"terminal": {
"connected": "Повезано"
}
diff --git a/plugins/host-metrics/locales/translated/sv_SE.json b/plugins/host-metrics/locales/translated/sv_SE.json
index f0e52eb54..e93c47f9b 100644
--- a/plugins/host-metrics/locales/translated/sv_SE.json
+++ b/plugins/host-metrics/locales/translated/sv_SE.json
@@ -51,10 +51,6 @@
"processesRunning": "spring",
"loginStats": "SSH-inloggningsstatistik",
"noRecentLoginData": "Inga aktuella inloggningsuppgifter",
- "executingQuickAction": "Kör {{name}}...",
- "quickActionSuccess": "{{name}} slutfört",
- "quickActionFailed": "{{name}} misslyckades",
- "quickActionError": "Misslyckades med att köra {{name}}",
"ports": {
"title": "Lyssningsportar",
"protocol": "Protokoll",
@@ -235,12 +231,6 @@
"addExcludedMount": "Tillägga",
"visibleWidgets": "Synliga widgetar",
"widgetsMovedToHostMetrics": "Kort läggs nu till, ordnas och ändras i storlek direkt på fliken Värdstatistik. Öppna Värdstatistik för den här värden och använd Anpassa för att välja vilka kort som visas.",
- "quickActionsLabel": "Snabbåtgärder",
- "quickActionsToolbar": "Snabbåtgärder visas som knappar i verktygsfältet Host Metrics för att köra kommandon med ett klick.",
- "noQuickActions": "Inga snabba åtgärder ännu.",
- "buttonLabel": "Knappetikett",
- "selectSnippetPlaceholder": "Välj utdrag...",
- "addActionBtn": "Lägg till åtgärd",
"hostNotFound": "Värden hittades inte",
"loadingHost": "Laddar värd..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Avboka",
- "confirmRunButton": "Sikt",
- "confirmRunMessage": "Kör \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Ansluten"
}
diff --git a/plugins/host-metrics/locales/translated/th_TH.json b/plugins/host-metrics/locales/translated/th_TH.json
index 4dacdc78a..627442521 100644
--- a/plugins/host-metrics/locales/translated/th_TH.json
+++ b/plugins/host-metrics/locales/translated/th_TH.json
@@ -51,10 +51,6 @@
"processesRunning": "วิ่ง",
"loginStats": "สถิติการเข้าสู่ระบบ SSH",
"noRecentLoginData": "ไม่มีข้อมูลการเข้าสู่ระบบล่าสุด",
- "executingQuickAction": "กำลังดำเนินการ {{name}}...",
- "quickActionSuccess": "{{name}} เสร็จสมบูรณ์เรียบร้อยแล้ว",
- "quickActionFailed": "{{name}} ล้มเหลว",
- "quickActionError": "ไม่สามารถดำเนินการ {{name}} ได้",
"ports": {
"title": "พอร์ตรับฟัง",
"protocol": "โปรโตคอล",
@@ -235,12 +231,6 @@
"addExcludedMount": "เพิ่ม",
"visibleWidgets": "วิดเจ็ตที่มองเห็นได้",
"widgetsMovedToHostMetrics": "ขณะนี้สามารถเพิ่ม จัดเรียง และปรับขนาดการ์ดได้โดยตรงในแท็บเมตริกโฮสต์ เปิดเมตริกโฮสต์สำหรับโฮสต์นี้แล้วใช้ ปรับแต่ง เพื่อเลือกการ์ดที่จะแสดง",
- "quickActionsLabel": "การดำเนินการด่วน",
- "quickActionsToolbar": "การดำเนินการด่วนจะปรากฏเป็นปุ่มในแถบเครื่องมือ Host Metrics เพื่อให้สามารถเรียกใช้คำสั่งได้ด้วยการคลิกเพียงครั้งเดียว",
- "noQuickActions": "ยังไม่มีการดำเนินการใดๆ อย่างเร่งด่วนในขณะนี้",
- "buttonLabel": "ป้ายกำกับปุ่ม",
- "selectSnippetPlaceholder": "เลือกส่วนย่อย...",
- "addActionBtn": "เพิ่มการดำเนินการ",
"hostNotFound": "ไม่พบโฮสต์",
"loadingHost": "กำลังโหลดโฮสต์..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "ดิสก์"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "ยกเลิก",
- "confirmRunButton": "วิ่ง",
- "confirmRunMessage": "เรียกใช้ \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "เชื่อมต่อแล้ว"
}
diff --git a/plugins/host-metrics/locales/translated/tr_TR.json b/plugins/host-metrics/locales/translated/tr_TR.json
index c51215ce0..083491d7c 100644
--- a/plugins/host-metrics/locales/translated/tr_TR.json
+++ b/plugins/host-metrics/locales/translated/tr_TR.json
@@ -51,10 +51,6 @@
"processesRunning": "çalışan",
"loginStats": "SSH Giriş İstatistikleri",
"noRecentLoginData": "Son giriş verisi yok",
- "executingQuickAction": "{{name}} çalıştırılıyor...",
- "quickActionSuccess": "{{name}} başarıyla tamamlandı",
- "quickActionFailed": "{{name}} başarısız oldu",
- "quickActionError": "{{name}} çalıştırılamadı",
"ports": {
"title": "Dinlenen Portlar",
"protocol": "Protokol",
@@ -235,12 +231,6 @@
"addExcludedMount": "Eklemek",
"visibleWidgets": "Görünür Widget'lar",
"widgetsMovedToHostMetrics": "Kartlar artık doğrudan Sunucu Metrikleri sekmesinde eklenir, düzenlenir ve yeniden boyutlandırılır. Bu sunucu için Sunucu Metrikleri'ni açın ve hangi kartların gösterileceğini seçmek için Özelleştir'i kullanın.",
- "quickActionsLabel": "Hızlı Eylemler",
- "quickActionsToolbar": "Hızlı eylemler, tek tıklamayla komut çalıştırmak için Ana Bilgisayar Metrikleri araç çubuğunda düğme olarak görünür.",
- "noQuickActions": "Henüz hızlı eylem yok.",
- "buttonLabel": "Düğme etiketi",
- "selectSnippetPlaceholder": "Parçacık seç...",
- "addActionBtn": "Eylem Ekle",
"hostNotFound": "Host bulunamadı",
"loadingHost": "Host yükleniyor..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Disk"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "İptal",
- "confirmRunButton": "Çalıştır",
- "confirmRunMessage": "\"{{name}}\" çalıştırılsın mı?"
- }
- }
- },
"terminal": {
"connected": "Bağlandı"
}
diff --git a/plugins/host-metrics/locales/translated/uk_UA.json b/plugins/host-metrics/locales/translated/uk_UA.json
index 2c66178ba..f7c2471a8 100644
--- a/plugins/host-metrics/locales/translated/uk_UA.json
+++ b/plugins/host-metrics/locales/translated/uk_UA.json
@@ -51,10 +51,6 @@
"processesRunning": "біг",
"loginStats": "Статистика входу SSH",
"noRecentLoginData": "Немає останніх даних для входу",
- "executingQuickAction": "Виконання {{name}}...",
- "quickActionSuccess": "{{name}} успішно завершено",
- "quickActionFailed": "{{name}} не вдалося",
- "quickActionError": "Не вдалося виконати {{name}}",
"ports": {
"title": "Порти прослуховування",
"protocol": "Протокол",
@@ -235,12 +231,6 @@
"addExcludedMount": "Додати",
"visibleWidgets": "Видимі віджети",
"widgetsMovedToHostMetrics": "Тепер картки додаються, упорядковуються та змінюються за розміром безпосередньо на вкладці «Метрики хоста». Відкрийте «Метрики хоста» для цього хоста та скористайтеся функцією «Налаштувати», щоб вибрати, які картки відображатимуться.",
- "quickActionsLabel": "Швидкі дії",
- "quickActionsToolbar": "Швидкі дії відображаються як кнопки на панелі інструментів «Метрики хоста» для виконання команд одним клацанням миші.",
- "noQuickActions": "Поки що жодних швидких дій.",
- "buttonLabel": "Мітка кнопки",
- "selectSnippetPlaceholder": "Вибрати фрагмент...",
- "addActionBtn": "Додати дію",
"hostNotFound": "Хост не знайдено",
"loadingHost": "Завантаження хоста..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Диск"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Скасувати",
- "confirmRunButton": "Бігти",
- "confirmRunMessage": "Запустити \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Підключено"
}
diff --git a/plugins/host-metrics/locales/translated/vi_VN.json b/plugins/host-metrics/locales/translated/vi_VN.json
index 53828e648..9529d4bd1 100644
--- a/plugins/host-metrics/locales/translated/vi_VN.json
+++ b/plugins/host-metrics/locales/translated/vi_VN.json
@@ -51,10 +51,6 @@
"processesRunning": "đang chạy",
"loginStats": "Thống kê đăng nhập SSH",
"noRecentLoginData": "Không có dữ liệu đăng nhập gần đây",
- "executingQuickAction": "Đang thực thi {{name}}...",
- "quickActionSuccess": "{{name}} đã hoàn tất thành công",
- "quickActionFailed": "{{name}} thất bại",
- "quickActionError": "Thực thi {{name}} thất bại",
"ports": {
"title": "Cổng đang lắng nghe",
"protocol": "Giao thức",
@@ -235,12 +231,6 @@
"addExcludedMount": "Thêm vào",
"visibleWidgets": "Widget hiển thị",
"widgetsMovedToHostMetrics": "Các thẻ hiện được thêm, sắp xếp và thay đổi kích thước trực tiếp trong tab Số liệu máy. Mở Số liệu máy cho máy này và dùng Tùy chỉnh để chọn thẻ hiển thị.",
- "quickActionsLabel": "Hành động nhanh",
- "quickActionsToolbar": "Hành động nhanh xuất hiện dưới dạng nút trên thanh công cụ Chỉ số máy để thực thi lệnh một lần nhấp.",
- "noQuickActions": "Chưa có hành động nhanh nào.",
- "buttonLabel": "Nhãn nút",
- "selectSnippetPlaceholder": "Chọn đoạn mã...",
- "addActionBtn": "Thêm hành động",
"hostNotFound": "Không tìm thấy máy chủ",
"loadingHost": "Đang tải máy chủ..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "Ổ đĩa"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "Hủy",
- "confirmRunButton": "Chạy",
- "confirmRunMessage": "Chạy \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "Đã kết nối"
}
diff --git a/plugins/host-metrics/locales/translated/zh_CN.json b/plugins/host-metrics/locales/translated/zh_CN.json
index 9736ea220..cdcfd000f 100644
--- a/plugins/host-metrics/locales/translated/zh_CN.json
+++ b/plugins/host-metrics/locales/translated/zh_CN.json
@@ -51,10 +51,6 @@
"processesRunning": "运行中",
"loginStats": "SSH 登录统计",
"noRecentLoginData": "无最近登录数据",
- "executingQuickAction": "正在执行 {{name}}...",
- "quickActionSuccess": "{{name}} 执行成功",
- "quickActionFailed": "{{name}} 执行失败",
- "quickActionError": "执行 {{name}} 失败",
"ports": {
"title": "监听端口",
"protocol": "协议",
@@ -235,12 +231,6 @@
"addExcludedMount": "添加",
"visibleWidgets": "可见小部件",
"widgetsMovedToHostMetrics": "卡片现在可在“主机指标”选项卡中直接添加、排列和调整大小。打开此主机的“主机指标”,使用“自定义”选择要显示的卡片。",
- "quickActionsLabel": "快速操作",
- "quickActionsToolbar": "快速操作以按钮形式显示在主机指标工具栏中,以便一键执行命令。",
- "noQuickActions": "尚无快速操作。",
- "buttonLabel": "按钮标签",
- "selectSnippetPlaceholder": "选择代码片段...",
- "addActionBtn": "添加操作",
"hostNotFound": "未找到主机",
"loadingHost": "正在加载主机..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "磁盘"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "取消",
- "confirmRunButton": "运行",
- "confirmRunMessage": "运行“{{name}}”?"
- }
- }
- },
"terminal": {
"connected": "已连接"
}
diff --git a/plugins/host-metrics/locales/translated/zh_TW.json b/plugins/host-metrics/locales/translated/zh_TW.json
index db704c54c..5b35e1d39 100644
--- a/plugins/host-metrics/locales/translated/zh_TW.json
+++ b/plugins/host-metrics/locales/translated/zh_TW.json
@@ -51,10 +51,6 @@
"processesRunning": "執行中",
"loginStats": "SSH 登入統計資訊",
"noRecentLoginData": "沒有最近的登入資料",
- "executingQuickAction": "正在執行 {{name}}...",
- "quickActionSuccess": "{{name}} 已成功完成",
- "quickActionFailed": "{{name}} 失敗",
- "quickActionError": "無法執行 {{name}}",
"ports": {
"title": "監聽中的連接埠",
"protocol": "通訊協定",
@@ -235,12 +231,6 @@
"addExcludedMount": "添加",
"visibleWidgets": "可見的小工具",
"widgetsMovedToHostMetrics": "現在卡片可直接在主機指標分頁中新增、排列和調整大小。開啟此主機的主機指標,然後使用自訂功能選擇要顯示的卡片。",
- "quickActionsLabel": "快速動作",
- "quickActionsToolbar": "快速動作會以按鈕形式顯示在主機計量工具列上,可一鍵執行指令。",
- "noQuickActions": "尚無快速動作。",
- "buttonLabel": "按鈕標籤",
- "selectSnippetPlaceholder": "選取程式碼片段...",
- "addActionBtn": "新增動作",
"hostNotFound": "找不到主機",
"loadingHost": "正在載入主機..."
},
@@ -282,15 +272,6 @@
"dashboardTab": {
"disk": "磁碟"
},
- "newUi": {
- "sidebar": {
- "snippets": {
- "cancel": "取消",
- "confirmRunButton": "執行",
- "confirmRunMessage": "執行 \"{{name}}\"?"
- }
- }
- },
"terminal": {
"connected": "已連線"
}
diff --git a/plugins/host-metrics/manifest.json b/plugins/host-metrics/manifest.json
index 337f4ce03..3de1c0be7 100644
--- a/plugins/host-metrics/manifest.json
+++ b/plugins/host-metrics/manifest.json
@@ -137,6 +137,10 @@
{
"id": "host-metrics.managers",
"accepts": ["component"]
+ },
+ {
+ "id": "host-metrics.toolbar",
+ "accepts": ["component"]
}
],
"uiPresets": {
diff --git a/plugins/host-metrics/src/frontend/HostEditorStatsTab.tsx b/plugins/host-metrics/src/frontend/HostEditorStatsTab.tsx
index 207d896d4..2a586a4ce 100644
--- a/plugins/host-metrics/src/frontend/HostEditorStatsTab.tsx
+++ b/plugins/host-metrics/src/frontend/HostEditorStatsTab.tsx
@@ -4,7 +4,6 @@ import {
SectionCard,
SettingRow,
FakeSwitch,
- Select2,
} from "@termix/plugin-sdk/ui";
import { useEffect, useState } from "react";
import {
@@ -12,33 +11,24 @@ import {
useTranslation,
type HostEditorSectionProps,
} from "@termix/plugin-sdk/frontend";
-import {
- HardDrive,
- LayoutDashboard,
- Plus,
- Server,
- Trash2,
- Zap,
-} from "lucide-react";
+import { HardDrive, LayoutDashboard, Plus, Server, Trash2 } from "lucide-react";
import { readHostMetricsSettings } from "../shared/stats-widgets.js";
const PLUGIN_ID = "host-metrics";
type PluginSettingsBag = Record>;
-type QuickActionRow = { name: string; snippetId: string };
/**
* The Host Metrics section of the host editor. Its values are this plugin's
* host settings, carried on form.pluginSettings and saved by the editor after
- * the host itself. Quick actions are a core host field.
+ * the host itself.
*/
export function HostStatsTab({
form,
setField,
updateForm,
host,
- snippets,
}: HostEditorSectionProps) {
const { t } = useTranslation();
const [newMount, setNewMount] = useState("");
@@ -49,8 +39,6 @@ export function HostStatsTab({
];
const settings = readHostMetricsSettings(bag);
const { excludedMounts, monitoredMounts } = settings;
- const quickActions = (form.quickActions ?? []) as QuickActionRow[];
- const snippetOptions = (snippets ?? []) as { id: number; name: string }[];
const api = usePluginApi();
const patch = (values: Record) =>
@@ -288,84 +276,6 @@ export function HostStatsTab({
- }
- action={
-
- setField("quickActions", [
- ...quickActions,
- { name: "", snippetId: "" },
- ])
- }
- >
- {t("hosts.addActionBtn")}
-
- }
- >
-
-
- {t("hosts.quickActionsToolbar")}
-
- {quickActions.length === 0 && (
-
-
- {t("hosts.noQuickActions")}
-
- )}
- {quickActions.map((a, i) => (
-
- {
- const updated = [...quickActions];
- updated[i] = { ...updated[i], name: e.target.value };
- setField("quickActions", updated);
- }}
- />
- {
- const updated = [...quickActions];
- updated[i] = {
- ...updated[i],
- snippetId: e.target.value,
- };
- setField("quickActions", updated);
- }}
- >
- {t("hosts.selectSnippetPlaceholder")}
- {snippetOptions.map((s) => (
-
- {s.name}
-
- ))}
-
-
- setField(
- "quickActions",
- quickActions.filter((_, idx) => idx !== i),
- )
- }
- >
-
-
-
- ))}
-
-
>
);
}
diff --git a/plugins/host-metrics/src/frontend/HostMetricsTab.tsx b/plugins/host-metrics/src/frontend/HostMetricsTab.tsx
index 6c4ab74d7..9ac059f98 100644
--- a/plugins/host-metrics/src/frontend/HostMetricsTab.tsx
+++ b/plugins/host-metrics/src/frontend/HostMetricsTab.tsx
@@ -1,7 +1,6 @@
import {
- Separator,
Button,
- useConfirmation,
+ ComponentSlot,
TOTPDialog,
useTabsSafe,
ConnectionLogProvider,
@@ -11,7 +10,6 @@ import {
CardGridCanvas,
ColumnCountStepper,
type GridCardCatalogEntry,
- SnippetVariablesDialog,
} from "@termix/plugin-sdk/ui";
import { readHostMetricsSettings } from "../shared/stats-widgets.js";
import {
@@ -23,33 +21,12 @@ import type { ServerMetrics } from "../shared/metrics.js";
import React from "react";
import { useHostMetricsApi } from "./host-metrics-api";
-interface Snippet {
- id: number;
- name: string;
- content: string;
-}
-
-function errorText(error: unknown): string {
- if (error instanceof Error && error.message) return error.message;
- return typeof error === "string" ? error : "";
-}
-
-// Mirrors the snippets plugin's own hasSnippetInputs: a pure check for
-// whether a snippet still has $INPUT_n placeholders once host variables are
-// substituted, so the quick-action dialog knows to ask before running.
-const SNIPPET_INPUT_PATTERN =
- /\$\{INPUT_(\d+)(?::([^}$]+))?\}|\$INPUT_(\d+)(?![a-zA-Z0-9_])/g;
-function hasSnippetInputs(content: string): boolean {
- SNIPPET_INPUT_PATTERN.lastIndex = 0;
- return SNIPPET_INPUT_PATTERN.test(content);
-}
import {
useTranslation,
useSlotContributions,
useConnectionRetry,
useHost,
useHostStatus,
- invokeAction,
logActivity,
usePluginUiPreferences,
} from "@termix/plugin-sdk/frontend";
@@ -70,11 +47,6 @@ import { metricsChangeKey } from "./metrics-change-key";
const HISTORY_LEN = 30;
-interface QuickAction {
- name: string;
- snippetId: number;
-}
-
type ConnectionLogPayload = Parameters<
ReturnType["addLog"]
>[0];
@@ -87,7 +59,6 @@ interface HostConfig {
name: string;
ip: string;
username: string;
- quickActions?: QuickAction[];
statusCheckEnabled?: boolean;
pluginSettings?: Record>;
authType?: string;
@@ -127,9 +98,6 @@ function HostMetricsInner({
});
const [currentHostConfig, setCurrentHostConfig] = React.useState(hostConfig);
const [isRefreshing, setIsRefreshing] = React.useState(false);
- const [executingActions, setExecutingActions] = React.useState>(
- new Set(),
- );
const [totpRequired, setTotpRequired] = React.useState(false);
const [totpSessionId, setTotpSessionId] = React.useState(null);
const [totpPrompt, setTotpPrompt] = React.useState("");
@@ -139,15 +107,9 @@ function HostMetricsInner({
null,
);
const [editMode, setEditMode] = React.useState(false);
- const [runningAction, setRunningAction] = React.useState<{
- action: QuickAction;
- snippet: Snippet;
- } | null>(null);
- const { confirmWithToast } = useConfirmation();
const activityLoggedRef = React.useRef(false);
const activityLoggingRef = React.useRef(false);
- const snippetsCacheRef = React.useRef(new Map());
const statsConfig = React.useMemo(() => {
const settings = readHostMetricsSettings(
@@ -532,102 +494,6 @@ function HostMetricsInner({
}
};
- async function executeQuickAction(
- action: QuickAction,
- inputValues: Record,
- ) {
- if (!currentHostConfig) return;
- setExecutingActions((prev) => new Set(prev).add(action.snippetId));
- toast.loading(
- t("hostMetrics.executingQuickAction", { name: action.name }),
- {
- id: `quick-action-${action.snippetId}`,
- },
- );
- try {
- const result = (await invokeAction(
- "snippets.execute",
- action.snippetId,
- currentHostConfig.id,
- Object.keys(inputValues).length > 0 ? inputValues : undefined,
- )) as { success: boolean; output?: string; error?: string } | undefined;
- if (!result) {
- toast.error(t("hostMetrics.quickActionError", { name: action.name }), {
- id: `quick-action-${action.snippetId}`,
- description: t("hostMetrics.snippetsUnavailable"),
- duration: 5000,
- });
- } else if (result.success) {
- toast.success(
- t("hostMetrics.quickActionSuccess", { name: action.name }),
- {
- id: `quick-action-${action.snippetId}`,
- description: result.output?.substring(0, 200),
- duration: 5000,
- },
- );
- } else {
- toast.error(t("hostMetrics.quickActionFailed", { name: action.name }), {
- id: `quick-action-${action.snippetId}`,
- description: result.error || result.output,
- duration: 5000,
- });
- }
- } catch (error) {
- toast.error(t("hostMetrics.quickActionError", { name: action.name }), {
- id: `quick-action-${action.snippetId}`,
- description: errorText(error),
- duration: 5000,
- });
- } finally {
- setExecutingActions((prev) => {
- const next = new Set(prev);
- next.delete(action.snippetId);
- return next;
- });
- }
- }
-
- function runQuickActionWithConfirm(
- action: QuickAction,
- inputValues: Record,
- ) {
- const shouldConfirm =
- localStorage.getItem("confirmSnippetExecution") === "true";
- const run = () => void executeQuickAction(action, inputValues);
- if (!shouldConfirm) {
- run();
- return;
- }
- confirmWithToast(
- t("newUi.sidebar.snippets.confirmRunMessage", { name: action.name }),
- run,
- t("newUi.sidebar.snippets.confirmRunButton"),
- t("newUi.sidebar.snippets.cancel"),
- { confirmOnEnter: true, duration: 6000 },
- );
- }
-
- async function runQuickAction(action: QuickAction) {
- if (!currentHostConfig) return;
- try {
- let snippet = snippetsCacheRef.current.get(action.snippetId);
- if (!snippet) {
- snippet =
- ((await invokeAction("snippets.get", action.snippetId)) as
- Snippet | null | undefined) ?? undefined;
- if (snippet) snippetsCacheRef.current.set(snippet.id, snippet);
- }
- if (snippet && hasSnippetInputs(snippet.content)) {
- setRunningAction({ action, snippet });
- return;
- }
- } catch {
- // fall through and run with no inputs if the snippet lookup fails
- }
- runQuickActionWithConfirm(action, {});
- }
-
const showCards =
metricsEnabled && metricsRetry.status === "connected" && metrics;
const showOffline =
@@ -653,43 +519,13 @@ function HostMetricsInner({
{title}
- {currentHostConfig?.quickActions &&
- currentHostConfig.quickActions.length > 0 && (
- <>
-
- {currentHostConfig.quickActions.map(
- (action, index) => {
- const isExecuting = executingActions.has(
- action.snippetId,
- );
- return (
- void runQuickAction(action)}
- >
- {isExecuting ? (
- <>
-
- {action.name}
- >
- ) : (
- action.name
- )}
-
- );
- },
- )}
-
-
- >
- )}
+
{editMode && (
<>
-
- {runningAction && (
-
[0]["snippet"]
- }
- host={
- currentHostConfig
- ? {
- ip: currentHostConfig.ip,
- username: currentHostConfig.username,
- port: currentHostConfig.port,
- name: currentHostConfig.name,
- }
- : null
- }
- onCancel={() => setRunningAction(null)}
- onConfirm={(_resolvedContent, inputValues) => {
- const action = runningAction.action;
- setRunningAction(null);
- runQuickActionWithConfirm(action, inputValues);
- }}
- />
- )}
);
}
diff --git a/plugins/host-metrics/src/frontend/index.tsx b/plugins/host-metrics/src/frontend/index.tsx
index e690d1a2c..13555a756 100644
--- a/plugins/host-metrics/src/frontend/index.tsx
+++ b/plugins/host-metrics/src/frontend/index.tsx
@@ -140,4 +140,10 @@ export async function activate(app: TermixApp): Promise {
id: "host-metrics.managers",
accepts: ["component"],
});
+ // Buttons before the toolbar's own controls, drawn with { hostId, host }.
+ // The snippets plugin puts a host's quick actions here.
+ app.declareActionSlot({
+ id: "host-metrics.toolbar",
+ accepts: ["component"],
+ });
}
diff --git a/plugins/host-metrics/tests/frontend/HostEditorStatsTab.test.tsx b/plugins/host-metrics/tests/frontend/HostEditorStatsTab.test.tsx
index 59fa6b779..fb2c7c4a8 100644
--- a/plugins/host-metrics/tests/frontend/HostEditorStatsTab.test.tsx
+++ b/plugins/host-metrics/tests/frontend/HostEditorStatsTab.test.tsx
@@ -7,7 +7,6 @@ afterEach(cleanup);
function renderSection(pluginSettings: Record = {}) {
let form: Record = {
- quickActions: [],
pluginSettings: { "host-metrics": pluginSettings, other: { keep: 1 } },
};
const updateForm = vi.fn(
@@ -21,7 +20,6 @@ function renderSection(pluginSettings: Record = {}) {
setField={vi.fn()}
updateForm={updateForm as never}
host={{ id: "7", name: "h", ip: "10.0.0.7", port: 22 }}
- snippets={[]}
protocols={{ ssh: true }}
/>,
);
diff --git a/plugins/snippets/locales/en.json b/plugins/snippets/locales/en.json
index cdca75ada..44c199485 100644
--- a/plugins/snippets/locales/en.json
+++ b/plugins/snippets/locales/en.json
@@ -195,5 +195,37 @@
"shareFolderHint": "Shares every snippet in this folder. Snippets added later are not shared automatically.",
"shareFolderSummary": "Shared {{count}} snippet(s) in this folder",
"grantedBy": "Granted by",
- "expired": "Expired"
+ "expired": "Expired",
+ "variablesDialogTitle": "Run \"{{name}}\"",
+ "variablesDialogDescription": "Fill in the values this snippet needs before running it.",
+ "variablesPreviewLabel": "Preview",
+ "variablesConfirmButton": "Run",
+ "keybindings": {
+ "runSnippet": "Run existing snippet",
+ "snippetLabel": "Snippet",
+ "selectSnippet": "Select a snippet",
+ "snippetMissing": "snippet not found",
+ "snippetRequired": "Select a snippet"
+ },
+ "paletteHintRun": "Run in the active terminal tab",
+ "host": {
+ "startupTitle": "Startup Snippet",
+ "quickActionsTitle": "Quick Actions",
+ "quickActionsDescription": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.",
+ "noQuickActions": "No quick actions yet.",
+ "buttonLabel": "Button label",
+ "selectSnippet": "Select snippet...",
+ "addQuickAction": "Add Action",
+ "tabTitle": "Snippets",
+ "startupDescription": "Runs in the terminal each time it connects to this host. Variables like $HOST are filled in; a snippet that asks for inputs is skipped.",
+ "none": "None",
+ "removeQuickAction": "Remove quick action"
+ },
+ "quickActions": {
+ "running": "Executing {{name}}...",
+ "succeeded": "{{name}} completed successfully",
+ "failed": "{{name}} failed",
+ "error": "Failed to execute {{name}}"
+ },
+ "paletteHintPaste": "Paste in the active terminal tab"
}
diff --git a/plugins/snippets/locales/translated/af_ZA.json b/plugins/snippets/locales/translated/af_ZA.json
new file mode 100644
index 000000000..e811c6320
--- /dev/null
+++ b/plugins/snippets/locales/translated/af_ZA.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Begin \"{{name}}\"",
+ "variablesDialogDescription": "Vul die waardes in wat hierdie brokkie benodig voordat dit uitgevoer word.",
+ "variablesPreviewLabel": "Voorskou",
+ "variablesConfirmButton": "Hardloop",
+ "keybindings": {
+ "runSnippet": "Voer bestaande brokkie uit",
+ "snippetLabel": "Brokkie",
+ "selectSnippet": "Kies 'n uittreksel",
+ "snippetMissing": "brokkie nie gevind nie",
+ "snippetRequired": "Kies 'n uittreksel"
+ },
+ "paletteHintRun": "Begin in die aktiewe terminaal-oortjie",
+ "host": {
+ "startupTitle": "Opstart-brokkie",
+ "quickActionsTitle": "Vinnige Aksies",
+ "quickActionsDescription": "Vinnige aksies verskyn as knoppies in die Host Metrics-nutsbalk vir die uitvoering van opdragte met een klik.",
+ "noQuickActions": "Geen vinnige aksies nog nie.",
+ "buttonLabel": "Knoppie-etiket",
+ "selectSnippet": "Kies uittreksel...",
+ "addQuickAction": "Voeg Aksie by"
+ },
+ "quickActions": {
+ "running": "Voer tans {{name}} uit ...",
+ "succeeded": "{{name}} suksesvol voltooi",
+ "failed": "{{name}} het misluk",
+ "error": "Kon nie {{name}} uitvoer nie"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ar_SA.json b/plugins/snippets/locales/translated/ar_SA.json
new file mode 100644
index 000000000..91c3e6487
--- /dev/null
+++ b/plugins/snippets/locales/translated/ar_SA.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "تشغيل \"{{name}}\"",
+ "variablesDialogDescription": "أدخل القيم التي يحتاجها هذا المقتطف البرمجي قبل تشغيله.",
+ "variablesPreviewLabel": "معاينة",
+ "variablesConfirmButton": "يجري",
+ "keybindings": {
+ "runSnippet": "تشغيل المقتطف البرمجي الحالي",
+ "snippetLabel": "مقتطف",
+ "selectSnippet": "حدد جزءًا من التعليمات البرمجية",
+ "snippetMissing": "لم يتم العثور على المقتطف",
+ "snippetRequired": "حدد جزءًا من التعليمات البرمجية"
+ },
+ "paletteHintRun": "قم بتشغيله في علامة تبويب الطرفية النشطة",
+ "host": {
+ "startupTitle": "مقتطف بدء التشغيل",
+ "quickActionsTitle": "إجراءات سريعة",
+ "quickActionsDescription": "تظهر الإجراءات السريعة كأزرار في شريط أدوات مقاييس المضيف لتنفيذ الأوامر بنقرة واحدة.",
+ "noQuickActions": "لا توجد إجراءات سريعة بعد.",
+ "buttonLabel": "تسمية الزر",
+ "selectSnippet": "اختر مقتطفًا...",
+ "addQuickAction": "إضافة إجراء"
+ },
+ "quickActions": {
+ "running": "جارٍ تنفيذ {{name}}...",
+ "succeeded": "تم إكمال {{name}} بنجاح",
+ "failed": "فشل {{name}}",
+ "error": "فشل تنفيذ {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/bg_BG.json b/plugins/snippets/locales/translated/bg_BG.json
new file mode 100644
index 000000000..a276c3d56
--- /dev/null
+++ b/plugins/snippets/locales/translated/bg_BG.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Изпълни „{{name}}“",
+ "variablesDialogDescription": "Попълнете стойностите, необходими на този фрагмент, преди да го изпълните.",
+ "variablesPreviewLabel": "Преглед",
+ "variablesConfirmButton": "Бягане",
+ "keybindings": {
+ "runSnippet": "Изпълнете съществуващия фрагмент",
+ "snippetLabel": "Фрагмент",
+ "selectSnippet": "Изберете фрагмент",
+ "snippetMissing": "фрагмент не е намерен",
+ "snippetRequired": "Изберете фрагмент"
+ },
+ "paletteHintRun": "Изпълняване в активния раздел на терминала",
+ "host": {
+ "startupTitle": "Стартов фрагмент",
+ "quickActionsTitle": "Бързи действия",
+ "quickActionsDescription": "Бързите действия се показват като бутони в лентата с инструменти Host Metrics за изпълнение на команди с едно щракване.",
+ "noQuickActions": "Все още няма бързи действия.",
+ "buttonLabel": "Етикет на бутона",
+ "selectSnippet": "Изберете фрагмент...",
+ "addQuickAction": "Добавяне на действие"
+ },
+ "quickActions": {
+ "running": "Изпълнява се {{name}}...",
+ "succeeded": "{{name}} завършено успешно",
+ "failed": "{{name}} неуспешно",
+ "error": "Неуспешно изпълнение на {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/bn_BD.json b/plugins/snippets/locales/translated/bn_BD.json
new file mode 100644
index 000000000..66478dc76
--- /dev/null
+++ b/plugins/snippets/locales/translated/bn_BD.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "দৌড়াও \"{{name}}\"",
+ "variablesDialogDescription": "এই কোড স্নিপেটটি চালানোর আগে প্রয়োজনীয় মানগুলি পূরণ করুন।",
+ "variablesPreviewLabel": "প্রিভিউ",
+ "variablesConfirmButton": "দৌড়",
+ "keybindings": {
+ "runSnippet": "বিদ্যমান কোড চালান",
+ "snippetLabel": "স্নিপেট",
+ "selectSnippet": "একটি স্নিপেট নির্বাচন করুন",
+ "snippetMissing": "স্নিপেটটি খুঁজে পাওয়া যায়নি",
+ "snippetRequired": "একটি স্নিপেট নির্বাচন করুন"
+ },
+ "paletteHintRun": "সক্রিয় টার্মিনাল ট্যাবে চালান",
+ "host": {
+ "startupTitle": "স্টার্টআপ স্নিপেট",
+ "quickActionsTitle": "দ্রুত পদক্ষেপ",
+ "quickActionsDescription": "এক ক্লিকে কমান্ড কার্যকর করার জন্য কুইক অ্যাকশনগুলো হোস্ট মেট্রিক্স টুলবারে বাটন হিসেবে প্রদর্শিত হয়।",
+ "noQuickActions": "এখনো কোনো দ্রুত পদক্ষেপ নেওয়া হয়নি।",
+ "buttonLabel": "বোতাম লেবেল",
+ "selectSnippet": "স্নিপেট নির্বাচন করুন...",
+ "addQuickAction": "অ্যাকশন যোগ করুন"
+ },
+ "quickActions": {
+ "running": "{{name}} কার্যকর করা হচ্ছে ...",
+ "succeeded": "{{name}} সফলভাবে সম্পন্ন হয়েছে",
+ "failed": "{{name}} ব্যর্থ হয়েছে",
+ "error": "{{name}} কার্যকর করতে ব্যর্থ হয়েছে"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ca_ES.json b/plugins/snippets/locales/translated/ca_ES.json
new file mode 100644
index 000000000..4af8288a4
--- /dev/null
+++ b/plugins/snippets/locales/translated/ca_ES.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Executa \"{{name}}\"",
+ "variablesDialogDescription": "Empleneu els valors que necessita aquest fragment abans d'executar-lo.",
+ "variablesPreviewLabel": "Vista prèvia",
+ "variablesConfirmButton": "Corre",
+ "keybindings": {
+ "runSnippet": "Executa el fragment existent",
+ "snippetLabel": "Fragment",
+ "selectSnippet": "Selecciona un fragment",
+ "snippetMissing": "fragment no trobat",
+ "snippetRequired": "Selecciona un fragment"
+ },
+ "paletteHintRun": "Executar a la pestanya activa del terminal",
+ "host": {
+ "startupTitle": "Fragment d'inici",
+ "quickActionsTitle": "Accions ràpides",
+ "quickActionsDescription": "Les accions ràpides apareixen com a botons a la barra d'eines de mètriques de l'amfitrió per executar ordres amb un sol clic.",
+ "noQuickActions": "Encara no hi ha accions ràpides.",
+ "buttonLabel": "Etiqueta del botó",
+ "selectSnippet": "Selecciona un fragment...",
+ "addQuickAction": "Afegeix una acció"
+ },
+ "quickActions": {
+ "running": "Executant {{name}}...",
+ "succeeded": "{{name}} s'ha completat correctament",
+ "failed": "{{name}} ha fallat",
+ "error": "No s'ha pogut executar {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/cs_CZ.json b/plugins/snippets/locales/translated/cs_CZ.json
new file mode 100644
index 000000000..f2a1b63f7
--- /dev/null
+++ b/plugins/snippets/locales/translated/cs_CZ.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Spustit „{{name}}“",
+ "variablesDialogDescription": "Před spuštěním úryvku kódu vyplňte jeho potřebné hodnoty.",
+ "variablesPreviewLabel": "Náhled",
+ "variablesConfirmButton": "Běh",
+ "keybindings": {
+ "runSnippet": "Spustit existující úryvek kódu",
+ "snippetLabel": "Úryvek",
+ "selectSnippet": "Vyberte úryvek",
+ "snippetMissing": "úryvek nenalezen",
+ "snippetRequired": "Vyberte úryvek"
+ },
+ "paletteHintRun": "Spustit v aktivní kartě terminálu",
+ "host": {
+ "startupTitle": "Úryvek pro spuštění",
+ "quickActionsTitle": "Rychlé akce",
+ "quickActionsDescription": "Rychlé akce se zobrazují jako tlačítka v panelu nástrojů Metriky hostitele pro spuštění příkazů jedním kliknutím.",
+ "noQuickActions": "Zatím žádné rychlé akce.",
+ "buttonLabel": "Štítek tlačítka",
+ "selectSnippet": "Vybrat úryvek...",
+ "addQuickAction": "Přidat akci"
+ },
+ "quickActions": {
+ "running": "Spouštění {{name}}...",
+ "succeeded": "{{name}} úspěšně dokončeno",
+ "failed": "{{name}} selhalo",
+ "error": "Nepodařilo se spustit {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/da_DK.json b/plugins/snippets/locales/translated/da_DK.json
new file mode 100644
index 000000000..13281893e
--- /dev/null
+++ b/plugins/snippets/locales/translated/da_DK.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Kør \"{{name}}\"",
+ "variablesDialogDescription": "Udfyld de værdier, som dette kodestykke skal bruge, før det køres.",
+ "variablesPreviewLabel": "Forhåndsvisning",
+ "variablesConfirmButton": "Løbe",
+ "keybindings": {
+ "runSnippet": "Kør eksisterende kodestykke",
+ "snippetLabel": "Uddrag",
+ "selectSnippet": "Vælg et uddrag",
+ "snippetMissing": "uddrag ikke fundet",
+ "snippetRequired": "Vælg et uddrag"
+ },
+ "paletteHintRun": "Kør i den aktive terminalfane",
+ "host": {
+ "startupTitle": "Opstartskodestykke",
+ "quickActionsTitle": "Hurtige handlinger",
+ "quickActionsDescription": "Hurtige handlinger vises som knapper i værktøjslinjen Host Metrics til udførelse af kommandoer med et enkelt klik.",
+ "noQuickActions": "Ingen hurtige handlinger endnu.",
+ "buttonLabel": "Knapmærke",
+ "selectSnippet": "Vælg uddrag...",
+ "addQuickAction": "Tilføj handling"
+ },
+ "quickActions": {
+ "running": "Udfører {{name}}...",
+ "succeeded": "{{name}} gennemført",
+ "failed": "{{name}} mislykkedes",
+ "error": "Kunne ikke udføre {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/de_DE.json b/plugins/snippets/locales/translated/de_DE.json
new file mode 100644
index 000000000..d755deded
--- /dev/null
+++ b/plugins/snippets/locales/translated/de_DE.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Run \"{{name}}\"",
+ "variablesDialogDescription": "Füllen Sie die für diesen Codeabschnitt erforderlichen Werte aus, bevor Sie ihn ausführen.",
+ "variablesPreviewLabel": "Vorschau",
+ "variablesConfirmButton": "Laufen",
+ "keybindings": {
+ "runSnippet": "Vorhandenen Codeausschnitt ausführen",
+ "snippetLabel": "Ausschnitt",
+ "selectSnippet": "Wählen Sie einen Ausschnitt aus",
+ "snippetMissing": "Ausschnitt nicht gefunden",
+ "snippetRequired": "Wählen Sie einen Ausschnitt aus"
+ },
+ "paletteHintRun": "Im aktiven Terminal-Tab ausführen",
+ "host": {
+ "startupTitle": "Startup-Snippet",
+ "quickActionsTitle": "Schnellaktionen",
+ "quickActionsDescription": "Schnellaktionen erscheinen als Schaltflächen in der Host-Metriken-Symbolleiste zur Ausführung von Befehlen mit einem Klick.",
+ "noQuickActions": "Noch keine Schnellaktionen.",
+ "buttonLabel": "Schaltflächenbeschriftung",
+ "selectSnippet": "Snippet auswählen...",
+ "addQuickAction": "Aktion hinzufügen"
+ },
+ "quickActions": {
+ "running": "{{name}} wird ausgeführt...",
+ "succeeded": "{{name}} erfolgreich abgeschlossen",
+ "failed": "{{name}} fehlgeschlagen",
+ "error": "Fehler beim Ausführen von {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/el_GR.json b/plugins/snippets/locales/translated/el_GR.json
new file mode 100644
index 000000000..8911f21e4
--- /dev/null
+++ b/plugins/snippets/locales/translated/el_GR.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Εκτέλεση \"{{name}}\"",
+ "variablesDialogDescription": "Συμπληρώστε τις τιμές που χρειάζεται αυτό το τμήμα κώδικα πριν το εκτελέσετε.",
+ "variablesPreviewLabel": "Πρεμιέρα",
+ "variablesConfirmButton": "Τρέξιμο",
+ "keybindings": {
+ "runSnippet": "Εκτέλεση υπάρχοντος αποσπάσματος",
+ "snippetLabel": "Απόσπασμα",
+ "selectSnippet": "Επιλέξτε ένα απόσπασμα",
+ "snippetMissing": "το απόσπασμα δεν βρέθηκε",
+ "snippetRequired": "Επιλέξτε ένα απόσπασμα"
+ },
+ "paletteHintRun": "Εκτέλεση στην ενεργή καρτέλα τερματικού",
+ "host": {
+ "startupTitle": "Απόσπασμα εκκίνησης",
+ "quickActionsTitle": "Γρήγορες ενέργειες",
+ "quickActionsDescription": "Οι γρήγορες ενέργειες εμφανίζονται ως κουμπιά στη γραμμή εργαλείων Μετρήσεων κεντρικού υπολογιστή για εκτέλεση εντολών με ένα κλικ.",
+ "noQuickActions": "Δεν υπάρχουν ακόμη γρήγορες ενέργειες.",
+ "buttonLabel": "Ετικέτα κουμπιού",
+ "selectSnippet": "Επιλογή αποσπάσματος...",
+ "addQuickAction": "Προσθήκη ενέργειας"
+ },
+ "quickActions": {
+ "running": "Εκτέλεση {{name}}...",
+ "succeeded": "{{name}} ολοκληρώθηκε με επιτυχία",
+ "failed": "{{name}} απέτυχε",
+ "error": "Αποτυχία εκτέλεσης {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/es_ES.json b/plugins/snippets/locales/translated/es_ES.json
new file mode 100644
index 000000000..c0b10bb16
--- /dev/null
+++ b/plugins/snippets/locales/translated/es_ES.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Ejecutar \"{{name}}\"",
+ "variablesDialogDescription": "Rellena los valores que necesita este fragmento de código antes de ejecutarlo.",
+ "variablesPreviewLabel": "Avance",
+ "variablesConfirmButton": "Correr",
+ "keybindings": {
+ "runSnippet": "Ejecutar el fragmento existente",
+ "snippetLabel": "Retazo",
+ "selectSnippet": "Seleccione un fragmento",
+ "snippetMissing": "Fragmento no encontrado",
+ "snippetRequired": "Seleccione un fragmento"
+ },
+ "paletteHintRun": "Ejecutar en la pestaña de terminal activa",
+ "host": {
+ "startupTitle": "Fragmento de inicio",
+ "quickActionsTitle": "Acciones rápidas",
+ "quickActionsDescription": "Las acciones rápidas aparecen como botones en la barra de herramientas de Métricas del host para la ejecución de comandos con un solo clic.",
+ "noQuickActions": "Aún no hay acciones rápidas.",
+ "buttonLabel": "Etiqueta del botón",
+ "selectSnippet": "Seleccionar fragmento...",
+ "addQuickAction": "Añadir acción"
+ },
+ "quickActions": {
+ "running": "Ejecutando {{name}}...",
+ "succeeded": "{{name}} completado correctamente",
+ "failed": "{{name}} falló",
+ "error": "Error al ejecutar {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/fi_FI.json b/plugins/snippets/locales/translated/fi_FI.json
new file mode 100644
index 000000000..52bf7e1d2
--- /dev/null
+++ b/plugins/snippets/locales/translated/fi_FI.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Suorita \"{{name}}\"",
+ "variablesDialogDescription": "Täytä tämän koodinpätkän tarvitsemat arvot ennen sen suorittamista.",
+ "variablesPreviewLabel": "Esikatselu",
+ "variablesConfirmButton": "Juokse",
+ "keybindings": {
+ "runSnippet": "Suorita olemassa oleva koodinpätkä",
+ "snippetLabel": "Katkelma",
+ "selectSnippet": "Valitse katkelma",
+ "snippetMissing": "katkelmaa ei löytynyt",
+ "snippetRequired": "Valitse katkelma"
+ },
+ "paletteHintRun": "Suorita aktiivisessa päätevälilehdessä",
+ "host": {
+ "startupTitle": "Käynnistyskoodinpätkä",
+ "quickActionsTitle": "Pikatoiminnot",
+ "quickActionsDescription": "Pikatoiminnot näkyvät painikkeina isäntämetriikkatyökalurivillä komentojen suorittamista varten yhdellä napsautuksella.",
+ "noQuickActions": "Ei vielä nopeita toimia.",
+ "buttonLabel": "Painikkeen nimi",
+ "selectSnippet": "Valitse katkelma...",
+ "addQuickAction": "Lisää toiminto"
+ },
+ "quickActions": {
+ "running": "Suoritetaan {{name}}...",
+ "succeeded": "{{name}} suoritettu onnistuneesti",
+ "failed": "{{name}} epäonnistui",
+ "error": "Suoritus epäonnistui {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/fr_FR.json b/plugins/snippets/locales/translated/fr_FR.json
new file mode 100644
index 000000000..c054a997d
--- /dev/null
+++ b/plugins/snippets/locales/translated/fr_FR.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Exécutez \"{{name}}\"",
+ "variablesDialogDescription": "Renseignez les valeurs nécessaires à cet extrait de code avant de l'exécuter.",
+ "variablesPreviewLabel": "Aperçu",
+ "variablesConfirmButton": "Courir",
+ "keybindings": {
+ "runSnippet": "Exécuter l'extrait de code existant",
+ "snippetLabel": "Fragment",
+ "selectSnippet": "Sélectionnez un extrait",
+ "snippetMissing": "extrait introuvable",
+ "snippetRequired": "Sélectionnez un extrait"
+ },
+ "paletteHintRun": "Exécuter dans l'onglet du terminal actif",
+ "host": {
+ "startupTitle": "Extrait de démarrage",
+ "quickActionsTitle": "Actions rapides",
+ "quickActionsDescription": "Les actions rapides apparaissent sous forme de boutons dans la barre d'outils Métriques de l'hôte pour une exécution en un clic.",
+ "noQuickActions": "Aucune action rapide pour le moment.",
+ "buttonLabel": "Étiquette du bouton",
+ "selectSnippet": "Sélectionner un snippet...",
+ "addQuickAction": "Ajouter une action"
+ },
+ "quickActions": {
+ "running": "Exécution de {{name}}...",
+ "succeeded": "{{name}} terminé avec succès",
+ "failed": "Échec de {{name}}",
+ "error": "Échec de l'exécution de {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/he_IL.json b/plugins/snippets/locales/translated/he_IL.json
new file mode 100644
index 000000000..8da7ee869
--- /dev/null
+++ b/plugins/snippets/locales/translated/he_IL.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "הפעל \"{{name}}\"",
+ "variablesDialogDescription": "מלא את הערכים הדרושים לקטע זה לפני הפעלתו.",
+ "variablesPreviewLabel": "תצוגה מקדימה",
+ "variablesConfirmButton": "לָרוּץ",
+ "keybindings": {
+ "runSnippet": "הפעלת קטע קוד קיים",
+ "snippetLabel": "קטע",
+ "selectSnippet": "בחר קטע",
+ "snippetMissing": "קטע לא נמצא",
+ "snippetRequired": "בחר קטע"
+ },
+ "paletteHintRun": "הפעל בלשונית הטרמינל הפעילה",
+ "host": {
+ "startupTitle": "קטע הפעלה",
+ "quickActionsTitle": "פעולות מהירות",
+ "quickActionsDescription": "פעולות מהירות מופיעות ככפתורים בסרגל הכלים של Host Metrics לביצוע פקודה בלחיצה אחת.",
+ "noQuickActions": "אין עדיין פעולות מהירות.",
+ "buttonLabel": "תווית כפתור",
+ "selectSnippet": "בחר קטע...",
+ "addQuickAction": "הוסף פעולה"
+ },
+ "quickActions": {
+ "running": "מבצע {{name}}...",
+ "succeeded": "{{name}} הושלם בהצלחה",
+ "failed": "{{name}} נכשל",
+ "error": "נכשל בביצוע {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/hi_IN.json b/plugins/snippets/locales/translated/hi_IN.json
new file mode 100644
index 000000000..e7851af3e
--- /dev/null
+++ b/plugins/snippets/locales/translated/hi_IN.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "रन \"{{name}}\"",
+ "variablesDialogDescription": "इस कोड स्निपेट को चलाने से पहले इसमें आवश्यक मान भरें।",
+ "variablesPreviewLabel": "पूर्व दर्शन",
+ "variablesConfirmButton": "दौड़ना",
+ "keybindings": {
+ "runSnippet": "मौजूदा स्निपेट चलाएँ",
+ "snippetLabel": "टुकड़ा",
+ "selectSnippet": "एक अंश का चयन करें",
+ "snippetMissing": "अंश नहीं मिला",
+ "snippetRequired": "एक अंश का चयन करें"
+ },
+ "paletteHintRun": "सक्रिय टर्मिनल टैब में चलाएँ",
+ "host": {
+ "startupTitle": "स्टार्टअप स्निपेट",
+ "quickActionsTitle": "त्वरित कार्रवाइयाँ",
+ "quickActionsDescription": "त्वरित कार्रवाइयाँ एक-क्लिक कमांड निष्पादन के लिए होस्ट मेट्रिक्स टूलबार में बटन के रूप में दिखाई देती हैं।",
+ "noQuickActions": "अभी तक कोई त्वरित कार्रवाई नहीं।",
+ "buttonLabel": "बटन लेबल",
+ "selectSnippet": "स्निपेट चुनें...",
+ "addQuickAction": "कार्रवाई जोड़ें"
+ },
+ "quickActions": {
+ "running": "{{name}} निष्पादित हो रहा है...",
+ "succeeded": "{{name}} सफलतापूर्वक पूर्ण हुआ",
+ "failed": "{{name}} विफल रहा",
+ "error": "{{name}} निष्पादित करने में विफल"
+ }
+}
diff --git a/plugins/snippets/locales/translated/hu_HU.json b/plugins/snippets/locales/translated/hu_HU.json
new file mode 100644
index 000000000..c6b107d7a
--- /dev/null
+++ b/plugins/snippets/locales/translated/hu_HU.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Futtassa a \"{{name}}\"",
+ "variablesDialogDescription": "Töltse ki a kódrészlet futtatása előtt szükséges értékeket.",
+ "variablesPreviewLabel": "Előnézet",
+ "variablesConfirmButton": "Fut",
+ "keybindings": {
+ "runSnippet": "Meglévő kódrészlet futtatása",
+ "snippetLabel": "Töredék",
+ "selectSnippet": "Válasszon ki egy részletet",
+ "snippetMissing": "részlet nem található",
+ "snippetRequired": "Válasszon ki egy részletet"
+ },
+ "paletteHintRun": "Futtatás az aktív terminálfülön",
+ "host": {
+ "startupTitle": "Indítási kódrészlet",
+ "quickActionsTitle": "Gyors műveletek",
+ "quickActionsDescription": "A gyors műveletek gombokként jelennek meg a Host Metrics eszköztáron, így egyetlen kattintással végrehajthatók a parancsok.",
+ "noQuickActions": "Még nincsenek gyors intézkedések.",
+ "buttonLabel": "Gomb felirata",
+ "selectSnippet": "Válasszon részletet...",
+ "addQuickAction": "Művelet hozzáadása"
+ },
+ "quickActions": {
+ "running": "{{name}} végrehajtása ...",
+ "succeeded": "{{name}} sikeresen befejeződött",
+ "failed": "{{name}} sikertelen",
+ "error": "Nem sikerült végrehajtani a {{name}} parancsot."
+ }
+}
diff --git a/plugins/snippets/locales/translated/id_ID.json b/plugins/snippets/locales/translated/id_ID.json
new file mode 100644
index 000000000..4c6551675
--- /dev/null
+++ b/plugins/snippets/locales/translated/id_ID.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Jalankan \"{{name}}\"",
+ "variablesDialogDescription": "Isi nilai-nilai yang dibutuhkan cuplikan kode ini sebelum menjalankannya.",
+ "variablesPreviewLabel": "Pratinjau",
+ "variablesConfirmButton": "Berlari",
+ "keybindings": {
+ "runSnippet": "Jalankan cuplikan kode yang sudah ada.",
+ "snippetLabel": "Potongan",
+ "selectSnippet": "Pilih cuplikan",
+ "snippetMissing": "cuplikan tidak ditemukan",
+ "snippetRequired": "Pilih cuplikan"
+ },
+ "paletteHintRun": "Jalankan di tab terminal aktif",
+ "host": {
+ "startupTitle": "Cuplikan Startup",
+ "quickActionsTitle": "Tindakan Cepat",
+ "quickActionsDescription": "Tindakan cepat muncul sebagai tombol di bilah alat Metrik Host untuk eksekusi perintah sekali klik.",
+ "noQuickActions": "Belum ada tindakan cepat.",
+ "buttonLabel": "Label tombol",
+ "selectSnippet": "Pilih cuplikan...",
+ "addQuickAction": "Tambahkan Tindakan"
+ },
+ "quickActions": {
+ "running": "Menjalankan {{name}}...",
+ "succeeded": "{{name}} berhasil diselesaikan",
+ "failed": "{{name}} gagal",
+ "error": "Gagal mengeksekusi {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/it_IT.json b/plugins/snippets/locales/translated/it_IT.json
new file mode 100644
index 000000000..b600320d9
--- /dev/null
+++ b/plugins/snippets/locales/translated/it_IT.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Esegui \"{{name}}\"",
+ "variablesDialogDescription": "Inserisci i valori richiesti da questo frammento di codice prima di eseguirlo.",
+ "variablesPreviewLabel": "Anteprima",
+ "variablesConfirmButton": "Correre",
+ "keybindings": {
+ "runSnippet": "Esegui il frammento di codice esistente",
+ "snippetLabel": "Frammento",
+ "selectSnippet": "Seleziona un frammento",
+ "snippetMissing": "frammento non trovato",
+ "snippetRequired": "Seleziona un frammento"
+ },
+ "paletteHintRun": "Esegui nella scheda del terminale attiva",
+ "host": {
+ "startupTitle": "Frammento di avvio",
+ "quickActionsTitle": "Azioni Rapide",
+ "quickActionsDescription": "Le azioni rapide appaiono come pulsanti nella barra delle metriche host per l'esecuzione di comandi con un clic.",
+ "noQuickActions": "Nessuna azione rapida ancora.",
+ "buttonLabel": "Etichetta pulsante",
+ "selectSnippet": "Seleziona snippet...",
+ "addQuickAction": "Aggiungi Azione"
+ },
+ "quickActions": {
+ "running": "Esecuzione di {{name}}...",
+ "succeeded": "{{name}} completato con successo",
+ "failed": "{{name}} non riuscito",
+ "error": "Impossibile eseguire {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ja_JP.json b/plugins/snippets/locales/translated/ja_JP.json
new file mode 100644
index 000000000..d715062b6
--- /dev/null
+++ b/plugins/snippets/locales/translated/ja_JP.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "「{{name}} を実行」",
+ "variablesDialogDescription": "このコードスニペットを実行する前に、必要な値を入力してください。",
+ "variablesPreviewLabel": "プレビュー",
+ "variablesConfirmButton": "走る",
+ "keybindings": {
+ "runSnippet": "既存のスニペットを実行する",
+ "snippetLabel": "抜粋",
+ "selectSnippet": "抜粋を選択してください",
+ "snippetMissing": "スニペットが見つかりません",
+ "snippetRequired": "抜粋を選択してください"
+ },
+ "paletteHintRun": "アクティブなターミナルタブで実行します",
+ "host": {
+ "startupTitle": "起動時スニペット",
+ "quickActionsTitle": "クイックアクション",
+ "quickActionsDescription": "クイックアクションはホストメトリクスツールバーにボタンとして表示され、ワンクリックでコマンドを実行できます。",
+ "noQuickActions": "クイックアクションはまだありません",
+ "buttonLabel": "ボタンラベル",
+ "selectSnippet": "スニペットを選択...",
+ "addQuickAction": "アクションを追加"
+ },
+ "quickActions": {
+ "running": "{{name}} を実行中...",
+ "succeeded": "{{name}} が正常に完了しました",
+ "failed": "{{name}} が失敗しました",
+ "error": "{{name}} の実行に失敗しました"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ko_KR.json b/plugins/snippets/locales/translated/ko_KR.json
new file mode 100644
index 000000000..8c91a6722
--- /dev/null
+++ b/plugins/snippets/locales/translated/ko_KR.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "\"{{name}} \" 실행",
+ "variablesDialogDescription": "이 코드 조각을 실행하기 전에 필요한 값을 입력하세요.",
+ "variablesPreviewLabel": "시사",
+ "variablesConfirmButton": "달리다",
+ "keybindings": {
+ "runSnippet": "기존 코드 조각을 실행하세요",
+ "snippetLabel": "단편",
+ "selectSnippet": "코드 조각을 선택하세요",
+ "snippetMissing": "스니펫을 찾을 수 없습니다.",
+ "snippetRequired": "코드 조각을 선택하세요"
+ },
+ "paletteHintRun": "현재 활성화된 터미널 탭에서 실행하세요",
+ "host": {
+ "startupTitle": "시작 스니펫",
+ "quickActionsTitle": "빠른 작업",
+ "quickActionsDescription": "빠른 작업은 한 번의 클릭으로 명령을 실행할 수 있도록 호스트 메트릭 도구 모음에 버튼으로 표시됩니다.",
+ "noQuickActions": "아직 빠른 작업이 없습니다.",
+ "buttonLabel": "버튼 레이블",
+ "selectSnippet": "스니펫 선택...",
+ "addQuickAction": "작업 추가"
+ },
+ "quickActions": {
+ "running": "{{name}} 실행 중...",
+ "succeeded": "{{name}}이(가) 성공적으로 완료되었습니다.",
+ "failed": "{{name}} 실패",
+ "error": "{{name}} 실행에 실패했습니다."
+ }
+}
diff --git a/plugins/snippets/locales/translated/nl_NL.json b/plugins/snippets/locales/translated/nl_NL.json
new file mode 100644
index 000000000..148d3d51d
--- /dev/null
+++ b/plugins/snippets/locales/translated/nl_NL.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Uitvoeren \"{{name}}\"",
+ "variablesDialogDescription": "Vul de waarden in die dit codefragment nodig heeft voordat je het uitvoert.",
+ "variablesPreviewLabel": "Voorbeeld",
+ "variablesConfirmButton": "Loop",
+ "keybindings": {
+ "runSnippet": "Voer het bestaande codefragment uit.",
+ "snippetLabel": "Fragment",
+ "selectSnippet": "Selecteer een fragment",
+ "snippetMissing": "fragment niet gevonden",
+ "snippetRequired": "Selecteer een fragment"
+ },
+ "paletteHintRun": "Voer dit uit in het actieve terminaltabblad.",
+ "host": {
+ "startupTitle": "Opstartfragment",
+ "quickActionsTitle": "Snelle acties",
+ "quickActionsDescription": "Snelle acties verschijnen als knoppen in de Host Metrics-werkbalk, zodat u opdrachten met één klik kunt uitvoeren.",
+ "noQuickActions": "Nog geen snelle acties ondernomen.",
+ "buttonLabel": "Knoplabel",
+ "selectSnippet": "Selecteer fragment...",
+ "addQuickAction": "Actie toevoegen"
+ },
+ "quickActions": {
+ "running": "Het uitvoeren van {{name}}...",
+ "succeeded": "{{name}} succesvol voltooid",
+ "failed": "{{name}} mislukt",
+ "error": "Het uitvoeren van {{name}} is mislukt."
+ }
+}
diff --git a/plugins/snippets/locales/translated/no_NO.json b/plugins/snippets/locales/translated/no_NO.json
new file mode 100644
index 000000000..9ea421c90
--- /dev/null
+++ b/plugins/snippets/locales/translated/no_NO.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Kjør «{{name}}»",
+ "variablesDialogDescription": "Fyll inn verdiene dette kodestykket trenger før du kjører det.",
+ "variablesPreviewLabel": "Forhåndsvisning",
+ "variablesConfirmButton": "Løp",
+ "keybindings": {
+ "runSnippet": "Kjør eksisterende kodebit",
+ "snippetLabel": "Utdrag",
+ "selectSnippet": "Velg et utdrag",
+ "snippetMissing": "utdraget ikke funnet",
+ "snippetRequired": "Velg et utdrag"
+ },
+ "paletteHintRun": "Kjør i den aktive terminalfanen",
+ "host": {
+ "startupTitle": "Oppstartsutdrag",
+ "quickActionsTitle": "Hurtighandlinger",
+ "quickActionsDescription": "Hurtighandlinger vises som knapper i verktøylinjen for vertsmålinger for utførelse av kommandoer med ett klikk.",
+ "noQuickActions": "Ingen hurtighandlinger ennå.",
+ "buttonLabel": "Knappetikett",
+ "selectSnippet": "Velg utdrag...",
+ "addQuickAction": "Legg til handling"
+ },
+ "quickActions": {
+ "running": "Utfører {{name}}...",
+ "succeeded": "{{name}} fullført",
+ "failed": "{{name}} mislyktes",
+ "error": "Kunne ikke utføre {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/pl_PL.json b/plugins/snippets/locales/translated/pl_PL.json
new file mode 100644
index 000000000..c9f02abf3
--- /dev/null
+++ b/plugins/snippets/locales/translated/pl_PL.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Uruchom „{{name}}”",
+ "variablesDialogDescription": "Przed uruchomieniem snippetu wprowadź wymagane wartości.",
+ "variablesPreviewLabel": "Podgląd",
+ "variablesConfirmButton": "Uruchom",
+ "keybindings": {
+ "runSnippet": "Uruchom istniejący snippet",
+ "snippetLabel": "Snippet",
+ "selectSnippet": "Wybierz snippet",
+ "snippetMissing": "nie znaleziono snippetu",
+ "snippetRequired": "Wybierz snippet"
+ },
+ "paletteHintRun": "Uruchom w aktywnej karcie terminala",
+ "host": {
+ "startupTitle": "Snippet startowy",
+ "quickActionsTitle": "Szybkie akcje",
+ "quickActionsDescription": "Szybkie akcje pojawiają się jako przyciski na pasku narzędzi Metryki hosta, umożliwiając wykonanie polecenia jednym kliknięciem.",
+ "noQuickActions": "Brak szybkich akcji.",
+ "buttonLabel": "Etykieta przycisku",
+ "selectSnippet": "Wybierz snippet...",
+ "addQuickAction": "Dodaj akcję"
+ },
+ "quickActions": {
+ "running": "Wykonywanie {{name}}...",
+ "succeeded": "{{name}} ukończono pomyślnie",
+ "failed": "{{name}} nie powiodło się",
+ "error": "Nie udało się wykonać {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/pt_BR.json b/plugins/snippets/locales/translated/pt_BR.json
new file mode 100644
index 000000000..183a6d3d3
--- /dev/null
+++ b/plugins/snippets/locales/translated/pt_BR.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Executar \"{{name}}\"",
+ "variablesDialogDescription": "Preencha os valores necessários para este trecho de código antes de executá-lo.",
+ "variablesPreviewLabel": "Pré-visualização",
+ "variablesConfirmButton": "Correr",
+ "keybindings": {
+ "runSnippet": "Executar trecho de código existente",
+ "snippetLabel": "Trecho",
+ "selectSnippet": "Selecione um trecho",
+ "snippetMissing": "trecho não encontrado",
+ "snippetRequired": "Selecione um trecho"
+ },
+ "paletteHintRun": "Execute na aba do terminal ativo",
+ "host": {
+ "startupTitle": "Trecho de startup",
+ "quickActionsTitle": "Ações rápidas",
+ "quickActionsDescription": "As ações rápidas aparecem como botões na barra de ferramentas do Host Metrics para execução de comandos com um único clique.",
+ "noQuickActions": "Nenhuma ação imediata ainda.",
+ "buttonLabel": "Rótulo do botão",
+ "selectSnippet": "Selecione o trecho...",
+ "addQuickAction": "Adicionar ação"
+ },
+ "quickActions": {
+ "running": "Executando {{name}}...",
+ "succeeded": "{{name}} concluído com sucesso",
+ "failed": "{{name}} falhou",
+ "error": "Falha ao executar {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/pt_PT.json b/plugins/snippets/locales/translated/pt_PT.json
new file mode 100644
index 000000000..9e10219cf
--- /dev/null
+++ b/plugins/snippets/locales/translated/pt_PT.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Executar \"{{name}}\"",
+ "variablesDialogDescription": "Preencha os valores necessários para este excerto de código antes de o executar.",
+ "variablesPreviewLabel": "Pré-visualização",
+ "variablesConfirmButton": "Correr",
+ "keybindings": {
+ "runSnippet": "Executar excerto de código existente",
+ "snippetLabel": "Trecho",
+ "selectSnippet": "Selecione um excerto",
+ "snippetMissing": "trecho não encontrado",
+ "snippetRequired": "Selecione um excerto"
+ },
+ "paletteHintRun": "Execute no separador do terminal ativo",
+ "host": {
+ "startupTitle": "Fragmento de inicialização",
+ "quickActionsTitle": "Ações Rápidas",
+ "quickActionsDescription": "As ações rápidas aparecem como botões na barra de ferramentas de Métricas do Host para execução de comandos com um clique.",
+ "noQuickActions": "Ainda não existem ações rápidas.",
+ "buttonLabel": "Texto do botão",
+ "selectSnippet": "Selecionar snippet...",
+ "addQuickAction": "Adicionar Ação"
+ },
+ "quickActions": {
+ "running": "A executar {{name}}...",
+ "succeeded": "{{name}} concluído com sucesso",
+ "failed": "{{name}} falhou",
+ "error": "Falha ao executar {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ro_RO.json b/plugins/snippets/locales/translated/ro_RO.json
new file mode 100644
index 000000000..10f3f92ad
--- /dev/null
+++ b/plugins/snippets/locales/translated/ro_RO.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Executați „{{name}}”",
+ "variablesDialogDescription": "Completați valorile necesare acestui fragment de cod înainte de a-l rula.",
+ "variablesPreviewLabel": "Previzualizare",
+ "variablesConfirmButton": "Aleargă",
+ "keybindings": {
+ "runSnippet": "Execută fragmentul existent",
+ "snippetLabel": "Fragment",
+ "selectSnippet": "Selectați un fragment",
+ "snippetMissing": "fragmentul nu a fost găsit",
+ "snippetRequired": "Selectați un fragment"
+ },
+ "paletteHintRun": "Execută în fila activă a terminalului",
+ "host": {
+ "startupTitle": "Fragment de pornire",
+ "quickActionsTitle": "Acțiuni rapide",
+ "quickActionsDescription": "Acțiunile rapide apar sub formă de butoane în bara de instrumente Host Metrics pentru executarea comenzilor cu un singur clic.",
+ "noQuickActions": "Nicio acțiune rapidă încă.",
+ "buttonLabel": "Etichetă buton",
+ "selectSnippet": "Selectați fragmentul...",
+ "addQuickAction": "Adăugați o acțiune"
+ },
+ "quickActions": {
+ "running": "Se execută {{name}}...",
+ "succeeded": "{{name}} finalizat cu succes",
+ "failed": "{{name}} a eșuat",
+ "error": "Executarea {{name}} a eșuat"
+ }
+}
diff --git a/plugins/snippets/locales/translated/ru_RU.json b/plugins/snippets/locales/translated/ru_RU.json
new file mode 100644
index 000000000..605c77264
--- /dev/null
+++ b/plugins/snippets/locales/translated/ru_RU.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Запуск \"{{name}}\"",
+ "variablesDialogDescription": "Перед запуском заполните необходимые для этого фрагмента значения.",
+ "variablesPreviewLabel": "Предварительный просмотр",
+ "variablesConfirmButton": "Бегать",
+ "keybindings": {
+ "runSnippet": "Запустить существующий фрагмент",
+ "snippetLabel": "Фрагмент",
+ "selectSnippet": "Выберите фрагмент",
+ "snippetMissing": "фрагмент не найден",
+ "snippetRequired": "Выберите фрагмент"
+ },
+ "paletteHintRun": "Запустите в активной вкладке терминала.",
+ "host": {
+ "startupTitle": "Фрагмент запуска",
+ "quickActionsTitle": "Быстрые действия",
+ "quickActionsDescription": "Быстрые действия отображаются в виде кнопок на панели инструментов метрик хоста для выполнения команд в один клик.",
+ "noQuickActions": "Пока нет быстрых действий.",
+ "buttonLabel": "Надпись кнопки",
+ "selectSnippet": "Выбрать сниппет...",
+ "addQuickAction": "Добавить действие"
+ },
+ "quickActions": {
+ "running": "Выполняется {{name}}...",
+ "succeeded": "{{name}} успешно завершено",
+ "failed": "{{name}} не удалось",
+ "error": "Не удалось выполнить {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/sr_SP.json b/plugins/snippets/locales/translated/sr_SP.json
new file mode 100644
index 000000000..1c4608d18
--- /dev/null
+++ b/plugins/snippets/locales/translated/sr_SP.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Покрени „{{name}}“",
+ "variablesDialogDescription": "Унесите вредности које су потребне овом фрагменту пре него што га покренете.",
+ "variablesPreviewLabel": "Преглед",
+ "variablesConfirmButton": "Трчи",
+ "keybindings": {
+ "runSnippet": "Покрени постојећи исечак кода",
+ "snippetLabel": "Исечак",
+ "selectSnippet": "Изаберите исечак",
+ "snippetMissing": "фрагмент није пронађен",
+ "snippetRequired": "Изаберите исечак"
+ },
+ "paletteHintRun": "Покрени у активној картици терминала",
+ "host": {
+ "startupTitle": "Исечак за покретање",
+ "quickActionsTitle": "Брзе акције",
+ "quickActionsDescription": "Брзе акције се појављују као дугмад у траци са алаткама Host Metrics за извршавање команди једним кликом.",
+ "noQuickActions": "Још нема брзих акција.",
+ "buttonLabel": "Ознака дугмета",
+ "selectSnippet": "Изаберите исечак...",
+ "addQuickAction": "Додај акцију"
+ },
+ "quickActions": {
+ "running": "Извршава се {{name}}...",
+ "succeeded": "{{name}} успешно завршено",
+ "failed": "{{name}} није успело",
+ "error": "Није успело извршавање {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/sv_SE.json b/plugins/snippets/locales/translated/sv_SE.json
new file mode 100644
index 000000000..e3b9b0a3f
--- /dev/null
+++ b/plugins/snippets/locales/translated/sv_SE.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Kör \"{{name}}\"",
+ "variablesDialogDescription": "Fyll i de värden som detta kodavsnitt behöver innan du kör det.",
+ "variablesPreviewLabel": "Förhandsvisning",
+ "variablesConfirmButton": "Sikt",
+ "keybindings": {
+ "runSnippet": "Kör befintligt kodavsnitt",
+ "snippetLabel": "Utdrag",
+ "selectSnippet": "Välj ett utdrag",
+ "snippetMissing": "utdraget hittades inte",
+ "snippetRequired": "Välj ett utdrag"
+ },
+ "paletteHintRun": "Kör i den aktiva terminalfliken",
+ "host": {
+ "startupTitle": "Startkodavsnitt",
+ "quickActionsTitle": "Snabbåtgärder",
+ "quickActionsDescription": "Snabbåtgärder visas som knappar i verktygsfältet Host Metrics för att köra kommandon med ett klick.",
+ "noQuickActions": "Inga snabba åtgärder ännu.",
+ "buttonLabel": "Knappetikett",
+ "selectSnippet": "Välj utdrag...",
+ "addQuickAction": "Lägg till åtgärd"
+ },
+ "quickActions": {
+ "running": "Kör {{name}}...",
+ "succeeded": "{{name}} slutfört",
+ "failed": "{{name}} misslyckades",
+ "error": "Misslyckades med att köra {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/th_TH.json b/plugins/snippets/locales/translated/th_TH.json
new file mode 100644
index 000000000..201f5469b
--- /dev/null
+++ b/plugins/snippets/locales/translated/th_TH.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "วิ่ง \"{{name}}\"",
+ "variablesDialogDescription": "กรอกค่าที่จำเป็นสำหรับโค้ดส่วนนี้ก่อนที่จะเรียกใช้งาน",
+ "variablesPreviewLabel": "ตัวอย่าง",
+ "variablesConfirmButton": "วิ่ง",
+ "keybindings": {
+ "runSnippet": "เรียกใช้โค้ดตัวอย่างที่มีอยู่",
+ "snippetLabel": "ส่วนหนึ่ง",
+ "selectSnippet": "เลือกส่วนย่อย",
+ "snippetMissing": "ไม่พบส่วนย่อย",
+ "snippetRequired": "เลือกส่วนย่อย"
+ },
+ "paletteHintRun": "เรียกใช้ในแท็บเทอร์มินัลที่ใช้งานอยู่",
+ "host": {
+ "startupTitle": "ตัวอย่างโค้ดสำหรับเริ่มต้น",
+ "quickActionsTitle": "การดำเนินการด่วน",
+ "quickActionsDescription": "การดำเนินการด่วนจะปรากฏเป็นปุ่มในแถบเครื่องมือ Host Metrics เพื่อให้สามารถเรียกใช้คำสั่งได้ด้วยการคลิกเพียงครั้งเดียว",
+ "noQuickActions": "ยังไม่มีการดำเนินการใดๆ อย่างเร่งด่วนในขณะนี้",
+ "buttonLabel": "ป้ายกำกับปุ่ม",
+ "selectSnippet": "เลือกส่วนย่อย...",
+ "addQuickAction": "เพิ่มการดำเนินการ"
+ },
+ "quickActions": {
+ "running": "กำลังดำเนินการ {{name}}...",
+ "succeeded": "{{name}} เสร็จสมบูรณ์เรียบร้อยแล้ว",
+ "failed": "{{name}} ล้มเหลว",
+ "error": "ไม่สามารถดำเนินการ {{name}} ได้"
+ }
+}
diff --git a/plugins/snippets/locales/translated/tr_TR.json b/plugins/snippets/locales/translated/tr_TR.json
new file mode 100644
index 000000000..0dbae4dc2
--- /dev/null
+++ b/plugins/snippets/locales/translated/tr_TR.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Çalıştır \"{{name}}\"",
+ "variablesDialogDescription": "Bu kod parçasını çalıştırmadan önce gerekli değerleri girin.",
+ "variablesPreviewLabel": "Önizleme",
+ "variablesConfirmButton": "Koşmak",
+ "keybindings": {
+ "runSnippet": "Mevcut kod parçasını çalıştır",
+ "snippetLabel": "Alıntı",
+ "selectSnippet": "Bir parça seçin",
+ "snippetMissing": "Kod parçası bulunamadı",
+ "snippetRequired": "Bir parça seçin"
+ },
+ "paletteHintRun": "Etkin terminal sekmesinde çalıştırın.",
+ "host": {
+ "startupTitle": "Başlangıç Betiği",
+ "quickActionsTitle": "Hızlı Eylemler",
+ "quickActionsDescription": "Hızlı eylemler, tek tıklamayla komut çalıştırmak için Ana Bilgisayar Metrikleri araç çubuğunda düğme olarak görünür.",
+ "noQuickActions": "Henüz hızlı eylem yok.",
+ "buttonLabel": "Düğme etiketi",
+ "selectSnippet": "Parçacık seç...",
+ "addQuickAction": "Eylem Ekle"
+ },
+ "quickActions": {
+ "running": "{{name}} çalıştırılıyor...",
+ "succeeded": "{{name}} başarıyla tamamlandı",
+ "failed": "{{name}} başarısız oldu",
+ "error": "{{name}} çalıştırılamadı"
+ }
+}
diff --git a/plugins/snippets/locales/translated/uk_UA.json b/plugins/snippets/locales/translated/uk_UA.json
new file mode 100644
index 000000000..bb9429c4b
--- /dev/null
+++ b/plugins/snippets/locales/translated/uk_UA.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Виконати \"{{name}}\"",
+ "variablesDialogDescription": "Заповніть значення, необхідні цьому фрагменту, перш ніж запускати його.",
+ "variablesPreviewLabel": "Попередній перегляд",
+ "variablesConfirmButton": "Бігти",
+ "keybindings": {
+ "runSnippet": "Запустити існуючий фрагмент коду",
+ "snippetLabel": "Фрагмент",
+ "selectSnippet": "Виберіть фрагмент",
+ "snippetMissing": "фрагмент не знайдено",
+ "snippetRequired": "Виберіть фрагмент"
+ },
+ "paletteHintRun": "Запустити в активній вкладці терміналу",
+ "host": {
+ "startupTitle": "Фрагмент запуску",
+ "quickActionsTitle": "Швидкі дії",
+ "quickActionsDescription": "Швидкі дії відображаються як кнопки на панелі інструментів «Метрики хоста» для виконання команд одним клацанням миші.",
+ "noQuickActions": "Поки що жодних швидких дій.",
+ "buttonLabel": "Мітка кнопки",
+ "selectSnippet": "Вибрати фрагмент...",
+ "addQuickAction": "Додати дію"
+ },
+ "quickActions": {
+ "running": "Виконання {{name}}...",
+ "succeeded": "{{name}} успішно завершено",
+ "failed": "{{name}} не вдалося",
+ "error": "Не вдалося виконати {{name}}"
+ }
+}
diff --git a/plugins/snippets/locales/translated/vi_VN.json b/plugins/snippets/locales/translated/vi_VN.json
new file mode 100644
index 000000000..7bc5af0fa
--- /dev/null
+++ b/plugins/snippets/locales/translated/vi_VN.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "Chạy \"{{name}}\"",
+ "variablesDialogDescription": "Hãy điền đầy đủ các giá trị cần thiết cho đoạn mã này trước khi chạy.",
+ "variablesPreviewLabel": "Xem trước",
+ "variablesConfirmButton": "Chạy",
+ "keybindings": {
+ "runSnippet": "Chạy đoạn mã hiện có",
+ "snippetLabel": "Đoạn trích",
+ "selectSnippet": "Chọn một đoạn trích",
+ "snippetMissing": "Không tìm thấy đoạn mã.",
+ "snippetRequired": "Chọn một đoạn trích"
+ },
+ "paletteHintRun": "Chạy trong tab cửa sổ terminal đang hoạt động",
+ "host": {
+ "startupTitle": "Đoạn mã khởi động",
+ "quickActionsTitle": "Hành động nhanh",
+ "quickActionsDescription": "Hành động nhanh xuất hiện dưới dạng nút trên thanh công cụ Chỉ số máy để thực thi lệnh một lần nhấp.",
+ "noQuickActions": "Chưa có hành động nhanh nào.",
+ "buttonLabel": "Nhãn nút",
+ "selectSnippet": "Chọn đoạn mã...",
+ "addQuickAction": "Thêm hành động"
+ },
+ "quickActions": {
+ "running": "Đang thực thi {{name}}...",
+ "succeeded": "{{name}} đã hoàn tất thành công",
+ "failed": "{{name}} thất bại",
+ "error": "Thực thi {{name}} thất bại"
+ }
+}
diff --git a/plugins/snippets/locales/translated/zh_CN.json b/plugins/snippets/locales/translated/zh_CN.json
new file mode 100644
index 000000000..4391aa8f8
--- /dev/null
+++ b/plugins/snippets/locales/translated/zh_CN.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "运行“{{name}}”",
+ "variablesDialogDescription": "运行此代码片段之前,请填写所需的值。",
+ "variablesPreviewLabel": "预览",
+ "variablesConfirmButton": "运行",
+ "keybindings": {
+ "runSnippet": "运行现有代码片段",
+ "snippetLabel": "片段",
+ "selectSnippet": "选择代码片段",
+ "snippetMissing": "未找到代码片段",
+ "snippetRequired": "选择代码片段"
+ },
+ "paletteHintRun": "在活动终端标签页中运行",
+ "host": {
+ "startupTitle": "启动代码片段",
+ "quickActionsTitle": "快速操作",
+ "quickActionsDescription": "快速操作以按钮形式显示在主机指标工具栏中,以便一键执行命令。",
+ "noQuickActions": "尚无快速操作。",
+ "buttonLabel": "按钮标签",
+ "selectSnippet": "选择代码片段...",
+ "addQuickAction": "添加操作"
+ },
+ "quickActions": {
+ "running": "正在执行 {{name}}...",
+ "succeeded": "{{name}} 执行成功",
+ "failed": "{{name}} 执行失败",
+ "error": "执行 {{name}} 失败"
+ }
+}
diff --git a/plugins/snippets/locales/translated/zh_TW.json b/plugins/snippets/locales/translated/zh_TW.json
new file mode 100644
index 000000000..cec161a77
--- /dev/null
+++ b/plugins/snippets/locales/translated/zh_TW.json
@@ -0,0 +1,29 @@
+{
+ "variablesDialogTitle": "運行“{{name}}",
+ "variablesDialogDescription": "在運行此程式碼片段之前,請填寫所需的值。",
+ "variablesPreviewLabel": "預覽",
+ "variablesConfirmButton": "跑步",
+ "keybindings": {
+ "runSnippet": "運行現有程式碼片段",
+ "snippetLabel": "片段",
+ "selectSnippet": "選擇一段文字",
+ "snippetMissing": "未找到程式碼片段",
+ "snippetRequired": "選擇一段文字"
+ },
+ "paletteHintRun": "在活動終端選項卡中運行",
+ "host": {
+ "startupTitle": "啟動程式碼片段",
+ "quickActionsTitle": "快速動作",
+ "quickActionsDescription": "快速動作會以按鈕形式顯示在主機計量工具列上,可一鍵執行指令。",
+ "noQuickActions": "尚無快速動作。",
+ "buttonLabel": "按鈕標籤",
+ "selectSnippet": "選取程式碼片段...",
+ "addQuickAction": "新增動作"
+ },
+ "quickActions": {
+ "running": "正在執行 {{name}}...",
+ "succeeded": "{{name}} 已成功完成",
+ "failed": "{{name}} 失敗",
+ "error": "無法執行 {{name}}"
+ }
+}
diff --git a/plugins/snippets/manifest.json b/plugins/snippets/manifest.json
index 6ef88d7fb..17a0e2a05 100644
--- a/plugins/snippets/manifest.json
+++ b/plugins/snippets/manifest.json
@@ -95,8 +95,51 @@
"descriptionKey": "settings.confirmExecution.description",
"default": false
}
- ]
+ ],
+ "host": {
+ "editorGroup": "ssh",
+ "editorOrder": 15,
+ "fields": [
+ {
+ "key": "startupSnippetId",
+ "type": "number",
+ "labelKey": "host.startupTitle",
+ "descriptionKey": "host.startupDescription",
+ "hidden": true,
+ "min": 1,
+ "shareRead": "view"
+ },
+ {
+ "key": "quickActions",
+ "type": "json",
+ "labelKey": "host.quickActionsTitle",
+ "descriptionKey": "host.quickActionsDescription",
+ "hidden": true,
+ "default": [],
+ "shareRead": "view"
+ }
+ ]
+ }
},
- "syncEntities": ["snippetFolders", "snippets"]
+ "syncEntities": ["snippetFolders", "snippets"],
+ "keybindingActions": [
+ {
+ "id": "runSnippet",
+ "params": {
+ "snippetId": {
+ "type": "string",
+ "pattern": "^[0-9]+$",
+ "maxLength": 20,
+ "syncEntity": "snippets"
+ },
+ "appendEnter": {
+ "type": "boolean"
+ }
+ }
+ }
+ ],
+ "http": {
+ "adminImpersonation": true
+ }
}
}
diff --git a/plugins/snippets/src/backend/execution.ts b/plugins/snippets/src/backend/execution.ts
index 99da7c0a1..3fba5054a 100644
--- a/plugins/snippets/src/backend/execution.ts
+++ b/plugins/snippets/src/backend/execution.ts
@@ -4,58 +4,8 @@ export interface SnippetExecutionResult {
error?: string;
}
-export interface SnippetHostVars {
- ip?: string;
- username?: string;
- port?: number | string;
- name?: string;
-}
-
-const INPUT_PATTERN =
- /\$\{INPUT_(\d+)(?::([^}$]+))?\}|\$INPUT_(\d+)(?![a-zA-Z0-9_])/g;
-
-function replaceVar(content: string, name: string, value?: string): string {
- if (value === undefined) return content;
- const pattern = new RegExp(`\\$\\{?${name}\\}?`, "g");
- return content.replace(pattern, value);
-}
-
-/**
- * Mirrors src/ui/lib/snippet-variables.ts resolveSnippetContent. Frontend and
- * backend are separate builds, so this is kept as a small standalone copy
- * rather than a shared package for one pure function.
- */
-export function resolveSnippetCommand(
- content: string,
- host: SnippetHostVars | null,
- inputValues: Record = {},
-): string {
- let resolved = content;
-
- resolved = replaceVar(resolved, "HOST", host?.ip);
- resolved = replaceVar(resolved, "USER", host?.username);
- resolved = replaceVar(
- resolved,
- "PORT",
- host?.port !== undefined ? String(host.port) : undefined,
- );
- resolved = replaceVar(resolved, "NAME", host?.name);
-
- resolved = resolved.replace(
- INPUT_PATTERN,
- (
- fullMatch,
- braceDigits: string | undefined,
- _label,
- plainDigits: string | undefined,
- ) => {
- const key = `INPUT_${braceDigits ?? plainDigits}`;
- return key in inputValues ? inputValues[key] : fullMatch;
- },
- );
-
- return resolved;
-}
+export { resolveSnippetContent as resolveSnippetCommand } from "../shared/variables.js";
+export type { SnippetHostContext as SnippetHostVars } from "../shared/variables.js";
export function getSnippetExecutionTimeoutMs(
value = process.env.SNIPPET_EXECUTION_TIMEOUT_SECONDS,
diff --git a/plugins/snippets/src/backend/host-import.ts b/plugins/snippets/src/backend/host-import.ts
new file mode 100644
index 000000000..9af3cd611
--- /dev/null
+++ b/plugins/snippets/src/backend/host-import.ts
@@ -0,0 +1,117 @@
+import {
+ PLUGIN_ID,
+ readQuickActions,
+ readStartupSnippetId,
+} from "../shared/host-settings.js";
+
+/**
+ * Registered as "snippets.hostImportNormalizer": a 2.8 or early 2.9 export
+ * carried quick actions as a host field and the startup snippet inside
+ * terminalConfig. An export that already carries this plugin's
+ * pluginSettings has had them written by core.
+ */
+export function hostImportNormalizer(
+ raw: Record,
+): Record | null {
+ const carried = (raw.pluginSettings as Record | undefined)?.[
+ PLUGIN_ID
+ ];
+ if (
+ carried &&
+ typeof carried === "object" &&
+ Object.keys(carried).length > 0
+ ) {
+ return null;
+ }
+
+ const values: Record = {};
+ const quickActions = readQuickActions(raw.quickActions);
+ if (quickActions.length > 0) values.quickActions = quickActions;
+
+ let terminalConfig = raw.terminalConfig;
+ if (typeof terminalConfig === "string") {
+ try {
+ terminalConfig = JSON.parse(terminalConfig);
+ } catch {
+ terminalConfig = null;
+ }
+ }
+ const startup = readStartupSnippetId(
+ (terminalConfig as { startupSnippetId?: unknown } | null)?.startupSnippetId,
+ );
+ if (startup !== null) values.startupSnippetId = startup;
+
+ return Object.keys(values).length > 0 ? values : null;
+}
+
+/**
+ * Registered as "snippets.hostPayloadLegacy": quickActions and
+ * terminalConfig.startupSnippetId in their 2.8 shape, for clients that
+ * still read them (Termix-Mobile). Remove once the mobile app reads
+ * pluginSettings.snippets.
+ */
+export function hostPayloadLegacy(
+ values: Record,
+): Record {
+ const out: Record = {
+ quickActions: readQuickActions(values.quickActions),
+ };
+ const startup = readStartupSnippetId(values.startupSnippetId);
+ if (startup !== null) out.terminalConfig = { startupSnippetId: startup };
+ return out;
+}
+
+/** What hostSettingsSync needs from the repository. */
+export interface SnippetSyncLookup {
+ findSyncIdById: (id: number) => Promise;
+ findIdBySyncId: (syncId: string) => Promise;
+}
+
+/**
+ * Registered as "snippets.hostSettingsSync": both host settings hold local
+ * snippet ids, so remote sync carries the snippets' sync ids instead, which
+ * are the same on both sides.
+ */
+export function createHostSettingsSync(lookup: SnippetSyncLookup) {
+ const toSyncId = async (id: number) => lookup.findSyncIdById(id);
+ const toId = async (value: unknown) =>
+ typeof value === "string" && value ? lookup.findIdBySyncId(value) : null;
+
+ return {
+ exportValue: async (key: string, value: unknown) => {
+ if (key === "startupSnippetId") {
+ const id = readStartupSnippetId(value);
+ return id === null ? null : await toSyncId(id);
+ }
+ if (key === "quickActions") {
+ const actions = readQuickActions(value);
+ const out = [];
+ for (const action of actions) {
+ const syncId = await toSyncId(action.snippetId);
+ if (syncId) out.push({ name: action.name, snippetId: syncId });
+ }
+ return out;
+ }
+ return value;
+ },
+ importValue: async (key: string, value: unknown) => {
+ if (key === "startupSnippetId") return await toId(value);
+ if (key === "quickActions") {
+ if (!Array.isArray(value)) return [];
+ const out = [];
+ for (const entry of value) {
+ const row = (entry ?? {}) as { name?: unknown; snippetId?: unknown };
+ const id = await toId(row.snippetId);
+ if (id !== null) {
+ out.push({
+ name: typeof row.name === "string" ? row.name : "",
+ snippetId: id,
+ });
+ }
+ }
+ return out;
+ }
+ return value;
+ },
+ };
+}
diff --git a/plugins/snippets/src/backend/index.ts b/plugins/snippets/src/backend/index.ts
index 6402a05e0..ec66438ea 100644
--- a/plugins/snippets/src/backend/index.ts
+++ b/plugins/snippets/src/backend/index.ts
@@ -3,7 +3,15 @@ import type { PluginContext } from "@termix/plugin-sdk/backend";
import { snippetAccess, snippetFolders, snippets } from "./tables.js";
import { createSnippetRepository } from "./repository.js";
import { registerSnippetRoutes } from "./routes.js";
-import { resolveSnippetCommand } from "./execution.js";
+import {
+ extractSnippetInputs,
+ resolveSnippetContent,
+} from "../shared/variables.js";
+import {
+ createHostSettingsSync,
+ hostImportNormalizer,
+ hostPayloadLegacy,
+} from "./host-import.js";
/** What other plugins get from ctx.services.get("snippets.access", {userId}). */
export interface SnippetsService {
@@ -74,8 +82,24 @@ export async function activate(ctx: PluginContext) {
type: "snippets",
table: snippetsTable,
order: 60,
- // Host quick actions and keybindings in core point at snippets by this.
- answersTo: ["commandSnippet"],
+ });
+
+ // Host settings that name a snippet by its local id, and the 2.8 host
+ // fields they came from. Keybindings name one through the runSnippet
+ // declaration in the manifest, which core translates over sync.
+ ctx.registry.provide("snippets.hostImportNormalizer", hostImportNormalizer);
+ ctx.registry.provide("snippets.hostPayloadLegacy", hostPayloadLegacy);
+ ctx.registry.provide(
+ "snippets.hostSettingsSync",
+ createHostSettingsSync(repo),
+ );
+
+ // $HOST and $INPUT_n handling for commands other plugins run (fleets). A
+ // pure function, so it goes through the registry rather than a service
+ // gated on snippets.view.
+ ctx.registry.provide("snippets.variables", {
+ resolve: resolveSnippetContent,
+ extractInputs: extractSnippetInputs,
});
// The user row survives a password-reset data wipe, so it never triggers
@@ -133,7 +157,7 @@ export async function activate(ctx: PluginContext) {
if (!userId) return null;
const snippet = await findAccessible(userId, id);
if (!snippet) return null;
- return resolveSnippetCommand(snippet.content, vars, inputValues);
+ return resolveSnippetContent(snippet.content, vars, inputValues);
},
create: async (input) => {
const userId = ctx.currentActor();
diff --git a/plugins/snippets/src/backend/repository.ts b/plugins/snippets/src/backend/repository.ts
index 716b5a4ed..290bf5e1b 100644
--- a/plugins/snippets/src/backend/repository.ts
+++ b/plugins/snippets/src/backend/repository.ts
@@ -882,6 +882,27 @@ export function createSnippetRepository(
return rows[0] ?? null;
},
+ /** A snippet's sync id, for a host setting that names it over sync. */
+ async findSyncIdById(snippetId: number): Promise {
+ const drizzle = await client();
+ const rows = await drizzle
+ .select({ syncId: snippets.syncId })
+ .from(snippets)
+ .where(eq(snippets.id, snippetId))
+ .limit(1);
+ return (rows[0]?.syncId as string | null | undefined) ?? null;
+ },
+
+ async findIdBySyncId(syncId: string): Promise {
+ const drizzle = await client();
+ const rows = await drizzle
+ .select({ id: snippets.id })
+ .from(snippets)
+ .where(eq(snippets.syncId, syncId))
+ .limit(1);
+ return (rows[0]?.id as number | undefined) ?? null;
+ },
+
/** A user's role ids, for the sharing filters above. */
async listUserRoleIds(userId: string): Promise {
const drizzle = await client();
diff --git a/plugins/snippets/src/frontend/HostSnippetsSection.tsx b/plugins/snippets/src/frontend/HostSnippetsSection.tsx
new file mode 100644
index 000000000..d609fdd09
--- /dev/null
+++ b/plugins/snippets/src/frontend/HostSnippetsSection.tsx
@@ -0,0 +1,171 @@
+import { Plus, Play, Trash2, Zap } from "lucide-react";
+import {
+ useTranslation,
+ type HostEditorSectionProps,
+} from "@termix/plugin-sdk/frontend";
+import { Button, Input, SectionCard, Select2 } from "@termix/plugin-sdk/ui";
+import { PLUGIN_ID, readStartupSnippetId } from "../shared/host-settings.js";
+import { useSnippetOptions } from "./use-snippet-options";
+
+type PluginSettingsBag = Record>;
+
+/** A quick action row while it is being edited: the snippet may be unset. */
+interface QuickActionRow {
+ name: string;
+ snippetId: number | null;
+}
+
+function readRows(value: unknown): QuickActionRow[] {
+ if (!Array.isArray(value)) return [];
+ return value.map((entry) => {
+ const row = (entry ?? {}) as { name?: unknown; snippetId?: unknown };
+ const id = Number(row.snippetId);
+ return {
+ name: typeof row.name === "string" ? row.name : "",
+ snippetId: Number.isInteger(id) && id > 0 ? id : null,
+ };
+ });
+}
+
+/**
+ * The host editor's Snippets tab: the snippet a terminal runs when it
+ * connects, and the quick action buttons Host Metrics shows for the host.
+ * Both are this plugin's host settings, saved with the host.
+ */
+export function HostSnippetsSection({
+ form,
+ updateForm,
+ adminTargetUserId,
+}: HostEditorSectionProps) {
+ const { t } = useTranslation();
+ const { options } = useSnippetOptions(adminTargetUserId);
+ const own =
+ (form.pluginSettings as PluginSettingsBag | undefined)?.[PLUGIN_ID] ?? {};
+ const startupSnippetId = readStartupSnippetId(own.startupSnippetId);
+ const quickActions = readRows(own.quickActions);
+
+ const write = (values: Record) =>
+ updateForm((current) => {
+ const all =
+ (current.pluginSettings as PluginSettingsBag | undefined) ?? {};
+ return {
+ ...current,
+ pluginSettings: {
+ ...all,
+ [PLUGIN_ID]: { ...(all[PLUGIN_ID] ?? {}), ...values },
+ },
+ };
+ });
+
+ const setQuickActions = (rows: QuickActionRow[]) =>
+ write({ quickActions: rows });
+
+ return (
+ <>
+ }
+ >
+
+
+ {t("host.startupDescription")}
+
+
+ write({
+ startupSnippetId: e.target.value
+ ? Number(e.target.value)
+ : null,
+ })
+ }
+ className="flex h-9 w-full border border-border bg-background px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
+ >
+ {t("host.none")}
+ {options.map((snippet) => (
+
+ {snippet.name}
+
+ ))}
+
+
+
+ }
+ action={
+
+ setQuickActions([...quickActions, { name: "", snippetId: null }])
+ }
+ >
+ {t("host.addQuickAction")}
+
+ }
+ >
+
+
+ {t("host.quickActionsDescription")}
+
+ {quickActions.length === 0 && (
+
+
+ {t("host.noQuickActions")}
+
+ )}
+ {quickActions.map((row, index) => (
+
+ {
+ const next = [...quickActions];
+ next[index] = { ...row, name: e.target.value };
+ setQuickActions(next);
+ }}
+ />
+ {
+ const next = [...quickActions];
+ next[index] = {
+ ...row,
+ snippetId: e.target.value ? Number(e.target.value) : null,
+ };
+ setQuickActions(next);
+ }}
+ >
+ {t("host.selectSnippet")}
+ {options.map((snippet) => (
+
+ {snippet.name}
+
+ ))}
+
+
+ setQuickActions(quickActions.filter((_, i) => i !== index))
+ }
+ >
+
+
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/plugins/snippets/src/frontend/QuickActionButtons.tsx b/plugins/snippets/src/frontend/QuickActionButtons.tsx
new file mode 100644
index 000000000..f0bfaaffb
--- /dev/null
+++ b/plugins/snippets/src/frontend/QuickActionButtons.tsx
@@ -0,0 +1,147 @@
+import { useMemo, useState } from "react";
+import { RefreshCw } from "lucide-react";
+import { toast } from "sonner";
+import {
+ usePermission,
+ usePluginApi,
+ useTranslation,
+} from "@termix/plugin-sdk/frontend";
+import { Button, Separator } from "@termix/plugin-sdk/ui";
+import {
+ readQuickActions,
+ snippetHostSettings,
+ type QuickAction,
+} from "../shared/host-settings.js";
+import { hasSnippetInputs } from "../shared/variables.js";
+import { askForInputs, shouldConfirmExecution } from "./prompt-store";
+import { createSnippetsApi } from "./snippets-api";
+import { errorMessage } from "./types";
+
+interface ToolbarHost {
+ id?: number | string;
+ name?: string;
+ ip?: string;
+ username?: string;
+ port?: number;
+ pluginSettings?: Record>;
+}
+
+/**
+ * The host's quick actions as buttons in the Host Metrics toolbar (the
+ * host-metrics.toolbar slot). Each runs its snippet on the host over a fresh
+ * SSH connection and reports the result in a toast.
+ */
+export function QuickActionButtons(props: Record) {
+ const host = props.host as ToolbarHost | undefined;
+ const { t } = useTranslation();
+ const api = usePluginApi();
+ const client = useMemo(() => createSnippetsApi(api), [api]);
+ const canView = usePermission("view");
+ const [running, setRunning] = useState>(new Set());
+ const actions = readQuickActions(snippetHostSettings(host).quickActions);
+ const hostId = Number(host?.id);
+
+ if (!canView || actions.length === 0 || !Number.isInteger(hostId)) {
+ return null;
+ }
+
+ const setBusy = (snippetId: number, busy: boolean) =>
+ setRunning((prev) => {
+ const next = new Set(prev);
+ if (busy) next.add(snippetId);
+ else next.delete(snippetId);
+ return next;
+ });
+
+ async function execute(
+ action: QuickAction,
+ inputValues: Record,
+ ) {
+ const toastId = `quick-action-${hostId}-${action.snippetId}`;
+ setBusy(action.snippetId, true);
+ toast.loading(t("quickActions.running", { name: action.name }), {
+ id: toastId,
+ });
+ try {
+ const result = await client.execute(
+ action.snippetId,
+ hostId,
+ Object.keys(inputValues).length > 0 ? inputValues : undefined,
+ );
+ if (result.success) {
+ toast.success(t("quickActions.succeeded", { name: action.name }), {
+ id: toastId,
+ description: result.output?.substring(0, 200),
+ duration: 5000,
+ });
+ } else {
+ toast.error(t("quickActions.failed", { name: action.name }), {
+ id: toastId,
+ description: result.error || result.output,
+ duration: 5000,
+ });
+ }
+ } catch (error) {
+ toast.error(t("quickActions.error", { name: action.name }), {
+ id: toastId,
+ description: errorMessage(error, t("executionFailed")),
+ duration: 5000,
+ });
+ } finally {
+ setBusy(action.snippetId, false);
+ }
+ }
+
+ async function run(action: QuickAction) {
+ let inputValues: Record = {};
+ const snippet = await client.get(action.snippetId).catch(() => null);
+ if (snippet && hasSnippetInputs(snippet.content)) {
+ const values = await askForInputs(
+ { name: action.name || snippet.name, content: snippet.content },
+ host
+ ? {
+ ip: host.ip,
+ username: host.username,
+ port: host.port,
+ name: host.name,
+ }
+ : null,
+ );
+ if (!values) return;
+ inputValues = values;
+ }
+ const go = () => void execute(action, inputValues);
+ if (!shouldConfirmExecution()) {
+ go();
+ return;
+ }
+ toast(t("confirmRunMessage", { name: action.name }), {
+ action: { label: t("confirmRunButton"), onClick: go },
+ duration: 6000,
+ });
+ }
+
+ return (
+ <>
+
+ {actions.map((action, index) => {
+ const busy = running.has(action.snippetId);
+ return (
+ void run(action)}
+ >
+ {busy && }
+ {action.name}
+
+ );
+ })}
+
+
+ >
+ );
+}
diff --git a/plugins/snippets/src/frontend/SnippetOverlay.tsx b/plugins/snippets/src/frontend/SnippetOverlay.tsx
new file mode 100644
index 000000000..0a776a69a
--- /dev/null
+++ b/plugins/snippets/src/frontend/SnippetOverlay.tsx
@@ -0,0 +1,32 @@
+import { useEffect } from "react";
+import { useSettings } from "@termix/plugin-sdk/frontend";
+import { SnippetVariablesDialog } from "./SnippetVariablesDialog";
+import { readSnippetSettings } from "./settings";
+import { setConfirmExecution, usePendingPrompt } from "./prompt-store";
+
+/**
+ * Always mounted at the shell's root (the shell.overlay slot), so a snippet
+ * started from the command palette, a keybinding or a host button can ask
+ * for its inputs after whatever started it has closed.
+ */
+export function SnippetOverlay() {
+ const settings = useSettings("user");
+ const prompt = usePendingPrompt();
+ const confirmExecution = readSnippetSettings(
+ settings.values,
+ ).confirmExecution;
+
+ useEffect(() => {
+ setConfirmExecution(confirmExecution);
+ }, [confirmExecution]);
+
+ if (!prompt) return null;
+ return (
+ prompt.settle(null)}
+ onConfirm={(_resolved, values) => prompt.settle(values)}
+ />
+ );
+}
diff --git a/src/ui/components/SnippetVariablesDialog.tsx b/plugins/snippets/src/frontend/SnippetVariablesDialog.tsx
similarity index 79%
rename from src/ui/components/SnippetVariablesDialog.tsx
rename to plugins/snippets/src/frontend/SnippetVariablesDialog.tsx
index 3d1938765..7d9a0fec4 100644
--- a/src/ui/components/SnippetVariablesDialog.tsx
+++ b/plugins/snippets/src/frontend/SnippetVariablesDialog.tsx
@@ -1,25 +1,30 @@
import { useState, useEffect, useMemo } from "react";
-import { useTranslation } from "react-i18next";
-import { Button } from "@/components/button";
-import { Input } from "@/components/input";
+import { useTranslation } from "@termix/plugin-sdk/frontend";
import {
+ Button,
+ Input,
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
-} from "@/components/dialog";
+} from "@termix/plugin-sdk/ui";
import {
extractSnippetInputs,
resolveSnippetContent,
type SnippetHostContext,
-} from "@/lib/snippet-variables";
-import type { Snippet } from "@/types/ui-types";
+} from "../shared/variables.js";
+
+/** Enough of a snippet to fill in its inputs. */
+export interface VariablesDialogSnippet {
+ name: string;
+ content: string;
+}
/**
- * Shown before running a snippet that contains $INPUT_n placeholders --
+ * Shown before running a snippet that contains $INPUT_n placeholders:
* collects a value per placeholder and previews the fully resolved command
- * (host vars + inputs) before handing the result back to the caller.
+ * (host vars and inputs) before handing the result back to the caller.
*/
export function SnippetVariablesDialog({
snippet,
@@ -27,7 +32,7 @@ export function SnippetVariablesDialog({
onCancel,
onConfirm,
}: {
- snippet: Snippet;
+ snippet: VariablesDialogSnippet;
host: SnippetHostContext | null;
onCancel: () => void;
onConfirm: (
@@ -53,12 +58,10 @@ export function SnippetVariablesDialog({
- {t("newUi.sidebar.snippets.variablesDialogTitle", {
- name: snippet.name,
- })}
+ {t("variablesDialogTitle", { name: snippet.name })}
- {t("newUi.sidebar.snippets.variablesDialogDescription")}
+ {t("variablesDialogDescription")}
@@ -79,7 +82,7 @@ export function SnippetVariablesDialog({
))}
- {t("newUi.sidebar.snippets.variablesPreviewLabel")}
+ {t("variablesPreviewLabel")}
{preview}
@@ -88,14 +91,14 @@ export function SnippetVariablesDialog({
- {t("newUi.sidebar.snippets.cancel")}
+ {t("cancel")}
onConfirm(preview, values)}
>
- {t("newUi.sidebar.snippets.variablesConfirmButton")}
+ {t("variablesConfirmButton")}
diff --git a/plugins/snippets/src/frontend/index.tsx b/plugins/snippets/src/frontend/index.tsx
index 210d54ae3..c12f06284 100644
--- a/plugins/snippets/src/frontend/index.tsx
+++ b/plugins/snippets/src/frontend/index.tsx
@@ -1,15 +1,53 @@
-import { Play } from "lucide-react";
-import type { PanelProps, TermixApp } from "@termix/plugin-sdk/frontend";
+import { Clipboard, Play } from "lucide-react";
+import type {
+ PaletteItem,
+ PanelProps,
+ PluginHostRecord,
+ TermixApp,
+} from "@termix/plugin-sdk/frontend";
+import type { Snippet } from "./types";
import { SnippetsPanel } from "./SnippetsPanel";
import { createSnippetsApi } from "./snippets-api";
-import { resolveSnippetContent, hasSnippetInputs } from "./snippet-variables";
import {
- AdminUserSnippets,
- adminOptions,
- mapSnippets,
-} from "./AdminUserSnippets";
+ extractSnippetInputs,
+ resolveSnippetContent,
+ type SnippetHostContext,
+} from "../shared/variables.js";
+import {
+ readStartupSnippetId,
+ snippetHostSettings,
+} from "../shared/host-settings.js";
+import { AdminUserSnippets } from "./AdminUserSnippets";
+import { SnippetOverlay } from "./SnippetOverlay";
+import { HostSnippetsSection } from "./HostSnippetsSection";
+import { QuickActionButtons } from "./QuickActionButtons";
+import { resetPrompts } from "./prompt-store";
+import { fetchSnippet, runInSession } from "./run-flows";
+import {
+ RUN_SNIPPET_ACTION,
+ RunSnippetEditor,
+ RunSnippetSummary,
+ runSnippetBinding,
+ validateRunSnippet,
+} from "./keybinding";
+
+function hostContext(host: unknown): SnippetHostContext | null {
+ if (!host || typeof host !== "object") return null;
+ const record = host as Record
;
+ return {
+ ip: typeof record.ip === "string" ? record.ip : undefined,
+ username: typeof record.username === "string" ? record.username : undefined,
+ port:
+ typeof record.port === "number" || typeof record.port === "string"
+ ? record.port
+ : undefined,
+ name: typeof record.name === "string" ? record.name : undefined,
+ };
+}
export function activate(app: TermixApp): void {
+ app.onDispose(resetPrompts);
+
app.registerRailItem({
id: "snippets",
icon: Play,
@@ -33,6 +71,73 @@ export function activate(app: TermixApp): void {
},
});
+ // Every snippet in the command palette, run in the terminal the user is
+ // working in.
+ app.registerPaletteGroup({
+ id: "snippets",
+ titleKey: "nav.snippets",
+ order: 10,
+ load: async (): Promise => {
+ if (!(await app.hasPermission("view"))) return [];
+ const snippets: Snippet[] = await createSnippetsApi(app.api)
+ .list()
+ .catch(() => []);
+ return snippets.map((snippet) => ({
+ id: String(snippet.id),
+ title: snippet.name,
+ description: snippet.content,
+ icon: snippet.isNote ? Clipboard : Play,
+ keywords: [snippet.description ?? ""],
+ needsTarget: true,
+ hint: app.t(snippet.isNote ? "paletteHintPaste" : "paletteHintRun"),
+ run: ({ targetTab }) => {
+ if (!targetTab) return;
+ void runInSession(
+ app,
+ snippet,
+ { sessionId: targetTab.id },
+ hostContext(targetTab.host),
+ { announce: true },
+ );
+ },
+ }));
+ },
+ });
+
+ // Keeps the input dialog and the confirm setting alive at the shell root.
+ app.registerSlotContribution("shell.overlay", {
+ actionId: "snippets.overlay",
+ titleKey: "nav.snippets",
+ kind: "component",
+ component: SnippetOverlay as never,
+ });
+
+ app.registerKeybindingAction({
+ id: RUN_SNIPPET_ACTION,
+ titleKey: "keybindings.runSnippet",
+ editor: RunSnippetEditor,
+ summary: RunSnippetSummary,
+ validate: validateRunSnippet,
+ run: (action, context) => runSnippetBinding(app, action, context),
+ });
+
+ app.registerHostEditorSection({
+ id: "snippets",
+ group: "ssh",
+ titleKey: "host.tabTitle",
+ icon: Play,
+ order: 15,
+ component: HostSnippetsSection,
+ });
+
+ // Host Metrics shows a host's quick actions as toolbar buttons.
+ app.registerSlotContribution("host-metrics.toolbar", {
+ actionId: "snippets.quickActions",
+ titleKey: "host.quickActionsTitle",
+ kind: "component",
+ component: QuickActionButtons,
+ });
+
app.registerSlotContribution("onboarding.features", {
actionId: "snippets.feature",
titleKey: "onboarding.feature_snippets",
@@ -49,71 +154,17 @@ export function activate(app: TermixApp): void {
order: 10,
});
- // Core's snippet pickers (the host editor's startup snippet, keybindings,
- // the command palette) read the list through this. An admin editing
- // another user's host passes that user's id.
- app.registerAction("snippet.list", (async (options?: {
- targetUserId?: string;
- }) => {
- const response = options?.targetUserId
- ? await app.api.get("/", adminOptions(options.targetUserId))
- : await app.api.get("/");
- const raw = response.data as unknown;
- const list = Array.isArray(raw)
- ? raw
- : ((raw as { snippets?: unknown[] })?.snippets ?? []);
- return (list as Record[]).map((row) => ({
- ...mapSnippets([row])[0],
- isNote: row.isNote === true,
- }));
- }) as never);
-
- // Resolves a snippet by id for a terminal (startup snippet, custom
- // keybindings, the command palette), so core never imports snippet code.
- const resolveForTerminal = async (
- snippetId: number,
- host: {
- ip?: string;
- username?: string;
- port?: number;
- name?: string;
- } | null,
- inputValues?: Record,
+ // The command a terminal types when it connects to a host: the host's
+ // startup snippet with its variables filled in, or null.
+ app.registerAction("snippets.startupCommand", (async (
+ host: PluginHostRecord | null,
+ vars?: SnippetHostContext,
) => {
- const api = createSnippetsApi(app.api);
- const snippet = await api.get(snippetId).catch(() => null);
+ const id = readStartupSnippetId(snippetHostSettings(host).startupSnippetId);
+ if (id === null) return null;
+ const snippet = await fetchSnippet(app, id);
if (!snippet) return null;
- if (
- hasSnippetInputs(snippet.content) &&
- (!inputValues || Object.keys(inputValues).length === 0)
- ) {
- return {
- needsInputs: true,
- content: snippet.content,
- isNote: snippet.isNote,
- };
- }
- return {
- needsInputs: false,
- content: resolveSnippetContent(snippet.content, host, inputValues ?? {}),
- isNote: snippet.isNote,
- };
- };
- app.registerAction("snippet.resolveForTerminal", resolveForTerminal as never);
- app.registerAction(
- "snippets.resolveForTerminal",
- resolveForTerminal as never,
- );
-
- // For other plugins' quick actions (host metrics): the snippet's content,
- // so the caller can ask for $INPUT_n values, and a run on a host.
- app.registerAction("snippets.get", (async (snippetId: number) => {
- const snippet = await createSnippetsApi(app.api)
- .get(snippetId)
- .catch(() => null);
- return snippet
- ? { id: snippet.id, name: snippet.name, content: snippet.content }
- : null;
+ return resolveSnippetContent(snippet.content, vars ?? hostContext(host));
}) as never);
// For pickers in other plugins (the AI assistant's @-mentions): id and name.
@@ -124,14 +175,7 @@ export function activate(app: TermixApp): void {
return snippets.map((snippet) => ({ id: snippet.id, name: snippet.name }));
}) as never);
- app.registerAction("snippets.execute", (async (
- snippetId: number,
- hostId: number,
- inputValues?: Record,
- ) =>
- createSnippetsApi(app.api).execute(
- snippetId,
- hostId,
- inputValues,
- )) as never);
+ // Variable handling for commands other plugins run (fleets' run box).
+ app.registerAction("snippets.extractInputs", ((content: string) =>
+ extractSnippetInputs(typeof content === "string" ? content : "")) as never);
}
diff --git a/plugins/snippets/src/frontend/keybinding.tsx b/plugins/snippets/src/frontend/keybinding.tsx
new file mode 100644
index 000000000..bde972c84
--- /dev/null
+++ b/plugins/snippets/src/frontend/keybinding.tsx
@@ -0,0 +1,89 @@
+import {
+ useTranslation,
+ type KeybindingAction,
+ type KeybindingActionEditorProps,
+ type KeybindingRunContext,
+ type TermixApp,
+} from "@termix/plugin-sdk/frontend";
+import { fetchSnippet, runInSession } from "./run-flows";
+import { useSnippetOptions } from "./use-snippet-options";
+
+/** Stored as action.type; kept from 2.8 so saved bindings keep working. */
+export const RUN_SNIPPET_ACTION = "runSnippet";
+
+function snippetIdOf(action: KeybindingAction): string {
+ return typeof action.snippetId === "string" ? action.snippetId : "";
+}
+
+/** Picks the snippet a "Run snippet" binding runs. */
+export function RunSnippetEditor({
+ action,
+ onChange,
+}: KeybindingActionEditorProps) {
+ const { t } = useTranslation();
+ const { options } = useSnippetOptions();
+ return (
+
+
+ {t("keybindings.snippetLabel")}
+
+
+ onChange({
+ type: RUN_SNIPPET_ACTION,
+ snippetId: e.target.value || undefined,
+ appendEnter: true,
+ })
+ }
+ className="h-8 border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
+ >
+ {t("keybindings.selectSnippet")}
+ {options.map((snippet) => (
+
+ {snippet.name}
+
+ ))}
+
+
+ );
+}
+
+/** The bound snippet's name in the binding list, or a warning once it is gone. */
+export function RunSnippetSummary({ action }: KeybindingActionEditorProps) {
+ const { t } = useTranslation();
+ const { options, loaded } = useSnippetOptions();
+ if (!loaded) return null;
+ const snippet = options.find((s) => String(s.id) === snippetIdOf(action));
+ if (snippet) return ({snippet.name}) ;
+ return (
+
+ ({t("keybindings.snippetMissing")})
+
+ );
+}
+
+export function validateRunSnippet(action: KeybindingAction): string | null {
+ return /^\d+$/.test(snippetIdOf(action))
+ ? null
+ : "keybindings.snippetRequired";
+}
+
+/** Runs a bound snippet in the terminal the key was pressed in. */
+export function runSnippetBinding(
+ app: Pick,
+ action: KeybindingAction,
+ context: KeybindingRunContext,
+): void {
+ void fetchSnippet(app, Number(snippetIdOf(action))).then((snippet) => {
+ if (!snippet) return;
+ void runInSession(
+ app,
+ { ...snippet, isNote: false },
+ { sessionId: context.sessionId, send: context.send },
+ context.host ?? null,
+ { pressEnter: action.appendEnter !== false },
+ );
+ });
+}
diff --git a/plugins/snippets/src/frontend/prompt-store.ts b/plugins/snippets/src/frontend/prompt-store.ts
new file mode 100644
index 000000000..af4dfd33f
--- /dev/null
+++ b/plugins/snippets/src/frontend/prompt-store.ts
@@ -0,0 +1,72 @@
+import { useSyncExternalStore } from "react";
+import type { SnippetHostContext } from "../shared/variables.js";
+
+export interface PendingPrompt {
+ snippet: { name: string; content: string };
+ host: SnippetHostContext | null;
+ settle: (values: Record | null) => void;
+}
+
+let pending: PendingPrompt | null = null;
+let confirmExecution = false;
+const listeners = new Set<() => void>();
+
+function notify(): void {
+ for (const listener of [...listeners]) listener();
+}
+
+function subscribe(listener: () => void): () => void {
+ listeners.add(listener);
+ return () => listeners.delete(listener);
+}
+
+/**
+ * Asks for a snippet's $INPUT_n values through the dialog the overlay draws.
+ * Resolves null when the user cancels. A second request cancels the first.
+ */
+export function askForInputs(
+ snippet: { name: string; content: string },
+ host: SnippetHostContext | null,
+): Promise | null> {
+ pending?.settle(null);
+ return new Promise((resolve) => {
+ const prompt: PendingPrompt = {
+ snippet,
+ host,
+ settle: (values) => {
+ if (pending === prompt) {
+ pending = null;
+ notify();
+ }
+ resolve(values);
+ },
+ };
+ pending = prompt;
+ notify();
+ });
+}
+
+export function usePendingPrompt(): PendingPrompt | null {
+ return useSyncExternalStore(
+ subscribe,
+ () => pending,
+ () => pending,
+ );
+}
+
+/** Kept current by the overlay from the user's confirmExecution setting. */
+export function setConfirmExecution(value: boolean): void {
+ confirmExecution = value;
+}
+
+export function shouldConfirmExecution(): boolean {
+ return confirmExecution;
+}
+
+/** Cancels anything waiting, for deactivate. */
+export function resetPrompts(): void {
+ pending?.settle(null);
+ pending = null;
+ confirmExecution = false;
+ notify();
+}
diff --git a/plugins/snippets/src/frontend/run-flows.ts b/plugins/snippets/src/frontend/run-flows.ts
new file mode 100644
index 000000000..2de490468
--- /dev/null
+++ b/plugins/snippets/src/frontend/run-flows.ts
@@ -0,0 +1,121 @@
+import { toast } from "sonner";
+import { invokeAction, type TermixApp } from "@termix/plugin-sdk/frontend";
+import {
+ hasSnippetInputs,
+ resolveSnippetContent,
+ type SnippetHostContext,
+} from "../shared/variables.js";
+import { askForInputs, shouldConfirmExecution } from "./prompt-store";
+import { createSnippetsApi } from "./snippets-api";
+
+export interface RunnableSnippet {
+ id: number;
+ name: string;
+ content: string;
+ isNote?: boolean;
+}
+
+/** Where a run sends its text: a session by id, or a raw writer. */
+export interface RunTarget {
+ sessionId?: string;
+ send?: (data: string) => void;
+}
+
+/**
+ * The snippet with its variables filled in, asking for $INPUT_n values when
+ * it has any. Null when the user cancels.
+ */
+export async function resolveForRun(
+ snippet: { name: string; content: string },
+ host: SnippetHostContext | null,
+): Promise {
+ if (!hasSnippetInputs(snippet.content)) {
+ return resolveSnippetContent(snippet.content, host);
+ }
+ const values = await askForInputs(snippet, host);
+ if (!values) return null;
+ return resolveSnippetContent(snippet.content, host, values);
+}
+
+/** Runs `execute` now, or after the user confirms when they asked to. */
+export function confirmThenRun(
+ app: Pick,
+ name: string,
+ execute: () => void,
+): void {
+ if (!shouldConfirmExecution()) {
+ execute();
+ return;
+ }
+ toast(app.t("confirmRunMessage", { name }), {
+ action: { label: app.t("confirmRunButton"), onClick: execute },
+ duration: 6000,
+ });
+}
+
+/**
+ * Types a snippet into a terminal session: resolves its variables, asks for
+ * inputs and confirmation as the user's settings say, then sends it. A note
+ * is pasted without running.
+ */
+export async function runInSession(
+ app: Pick,
+ snippet: RunnableSnippet,
+ target: RunTarget,
+ host: SnippetHostContext | null,
+ options: { pressEnter?: boolean; announce?: boolean } = {},
+): Promise {
+ const content = await resolveForRun(snippet, host);
+ if (content === null) return;
+ const run = !snippet.isNote && options.pressEnter !== false;
+ const send = async () => {
+ let sent = true;
+ if (target.send) {
+ target.send(content + (run ? "\r" : ""));
+ } else if (target.sessionId) {
+ sent =
+ (await invokeAction(
+ "terminal.sendToSession",
+ target.sessionId,
+ content,
+ {
+ run,
+ },
+ )) === true;
+ } else {
+ sent = false;
+ }
+ if (!options.announce) return;
+ if (!sent) {
+ toast.error(app.t("noTerminalTabsOpen"));
+ return;
+ }
+ toast.success(
+ app.t(snippet.isNote ? "pasteSuccess" : "runSuccess", {
+ name: snippet.name,
+ count: 1,
+ }),
+ );
+ };
+ if (snippet.isNote) void send();
+ else confirmThenRun(app, snippet.name, () => void send());
+}
+
+/** One snippet by id, or null when it is gone or unreadable. */
+export async function fetchSnippet(
+ app: Pick,
+ id: number,
+): Promise {
+ if (!Number.isInteger(id) || id <= 0) return null;
+ const snippet = await createSnippetsApi(app.api)
+ .get(id)
+ .catch(() => null);
+ return snippet
+ ? {
+ id: snippet.id,
+ name: snippet.name,
+ content: snippet.content,
+ isNote: snippet.isNote,
+ }
+ : null;
+}
diff --git a/plugins/snippets/src/frontend/use-snippet-options.ts b/plugins/snippets/src/frontend/use-snippet-options.ts
new file mode 100644
index 000000000..bb0d226c9
--- /dev/null
+++ b/plugins/snippets/src/frontend/use-snippet-options.ts
@@ -0,0 +1,53 @@
+import { useEffect, useState } from "react";
+import { usePluginApi } from "@termix/plugin-sdk/frontend";
+import { adminOptions } from "./AdminUserSnippets";
+
+export interface SnippetOption {
+ id: number;
+ name: string;
+}
+
+/**
+ * The user's snippets as picker options: their own, or the target user's
+ * while an admin edits that user's host. `loaded` turns true once the list
+ * came back, so a picker can tell "no snippets" from "not loaded yet".
+ */
+export function useSnippetOptions(targetUserId?: string): {
+ options: SnippetOption[];
+ loaded: boolean;
+} {
+ const api = usePluginApi();
+ const [state, setState] = useState<{
+ options: SnippetOption[];
+ loaded: boolean;
+ }>({ options: [], loaded: false });
+
+ useEffect(() => {
+ let cancelled = false;
+ const request = targetUserId
+ ? api.get("/", adminOptions(targetUserId))
+ : api.get("/");
+ request
+ .then((response) => {
+ const raw = response.data as unknown;
+ const list = Array.isArray(raw)
+ ? raw
+ : ((raw as { snippets?: unknown[] })?.snippets ?? []);
+ const options = (list as Array<{ id?: unknown; name?: unknown }>)
+ .filter((row) => typeof row.id === "number")
+ .map((row) => ({
+ id: row.id as number,
+ name: typeof row.name === "string" ? row.name : `#${row.id}`,
+ }));
+ if (!cancelled) setState({ options, loaded: true });
+ })
+ .catch(() => {
+ if (!cancelled) setState({ options: [], loaded: true });
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [api, targetUserId]);
+
+ return state;
+}
diff --git a/plugins/snippets/src/frontend/use-snippet-runner.tsx b/plugins/snippets/src/frontend/use-snippet-runner.tsx
index 00f89d784..2ddd82d7a 100644
--- a/plugins/snippets/src/frontend/use-snippet-runner.tsx
+++ b/plugins/snippets/src/frontend/use-snippet-runner.tsx
@@ -5,12 +5,12 @@ import {
useTranslation,
} from "@termix/plugin-sdk/frontend";
import { toast } from "sonner";
-import { SnippetVariablesDialog } from "@termix/plugin-sdk/ui";
import {
hasSnippetInputs,
resolveSnippetContent,
type SnippetHostContext,
-} from "./snippet-variables";
+} from "../shared/variables.js";
+import { SnippetVariablesDialog } from "./SnippetVariablesDialog";
import { createSnippetsApi } from "./snippets-api";
import {
ExecutionResultsDialog,
@@ -259,7 +259,7 @@ export function useSnippetRunner(confirmExecution = false) {
const variablesDialog = runningSnippet ? (
setRunningSnippet(null)}
onConfirm={runningSnippet.onConfirm}
diff --git a/plugins/snippets/src/shared/host-settings.ts b/plugins/snippets/src/shared/host-settings.ts
new file mode 100644
index 000000000..0a1a9ce31
--- /dev/null
+++ b/plugins/snippets/src/shared/host-settings.ts
@@ -0,0 +1,48 @@
+/**
+ * The snippets plugin's host settings: the startup snippet a terminal runs
+ * when it connects, and the quick action buttons Host Metrics shows.
+ */
+
+export const PLUGIN_ID = "snippets";
+
+export interface QuickAction {
+ name: string;
+ snippetId: number;
+}
+
+function snippetId(value: unknown): number | null {
+ const id = typeof value === "string" ? Number(value) : value;
+ return typeof id === "number" && Number.isInteger(id) && id > 0 ? id : null;
+}
+
+/** Quick actions in their stored shape, dropping any without a snippet. */
+export function readQuickActions(value: unknown): QuickAction[] {
+ let list = value;
+ if (typeof list === "string") {
+ try {
+ list = JSON.parse(list);
+ } catch {
+ return [];
+ }
+ }
+ if (!Array.isArray(list)) return [];
+ return list.flatMap((entry): QuickAction[] => {
+ if (!entry || typeof entry !== "object") return [];
+ const id = snippetId((entry as { snippetId?: unknown }).snippetId);
+ if (id === null) return [];
+ const name = (entry as { name?: unknown }).name;
+ return [{ name: typeof name === "string" ? name : "", snippetId: id }];
+ });
+}
+
+export function readStartupSnippetId(value: unknown): number | null {
+ return snippetId(value);
+}
+
+/** This plugin's slice of a host record's pluginSettings. */
+export function snippetHostSettings(host: unknown): Record {
+ const all = (host as { pluginSettings?: Record } | null)
+ ?.pluginSettings;
+ const own = all?.[PLUGIN_ID];
+ return own && typeof own === "object" ? (own as Record) : {};
+}
diff --git a/plugins/snippets/src/frontend/snippet-variables.ts b/plugins/snippets/src/shared/variables.ts
similarity index 90%
rename from plugins/snippets/src/frontend/snippet-variables.ts
rename to plugins/snippets/src/shared/variables.ts
index 9cf81db63..4f0a0869d 100644
--- a/plugins/snippets/src/frontend/snippet-variables.ts
+++ b/plugins/snippets/src/shared/variables.ts
@@ -1,3 +1,9 @@
+/**
+ * $HOST, $USER, $PORT, $NAME and $INPUT_n in a command. The one copy: the
+ * frontend, the backend and other plugins (fleets, through the
+ * "snippets.variables" registry entry and action) all use this.
+ */
+
export interface SnippetInput {
key: string;
label: string;
diff --git a/plugins/snippets/tests/backend/activate.test.ts b/plugins/snippets/tests/backend/activate.test.ts
index 134756e4c..d0290f64a 100644
--- a/plugins/snippets/tests/backend/activate.test.ts
+++ b/plugins/snippets/tests/backend/activate.test.ts
@@ -98,6 +98,28 @@ describe("snippets activate", () => {
expect(after).toHaveLength(0);
});
+ it("registers its host settings hooks and the variables helper", async () => {
+ server = await startServer();
+ const registry = server.mock.ctx.registry;
+ for (const key of [
+ "snippets.hostImportNormalizer",
+ "snippets.hostPayloadLegacy",
+ "snippets.hostSettingsSync",
+ ]) {
+ expect(registry.consume(key), key).toBeDefined();
+ }
+ const variables = registry.consume<{
+ resolve: (c: string, h: { ip?: string } | null) => string;
+ extractInputs: (c: string) => Array<{ key: string }>;
+ }>("snippets.variables");
+ expect(variables?.resolve("ping $HOST", { ip: "10.0.0.1" })).toBe(
+ "ping 10.0.0.1",
+ );
+ expect(variables?.extractInputs("echo $INPUT_1")).toEqual([
+ { key: "INPUT_1", label: "Input 1" },
+ ]);
+ });
+
it("fails closed without db:own", async () => {
db = await createTestDb(pluginDir);
const mock = createMockCtx({
diff --git a/plugins/snippets/tests/backend/host-import.test.ts b/plugins/snippets/tests/backend/host-import.test.ts
new file mode 100644
index 000000000..90cbfce32
--- /dev/null
+++ b/plugins/snippets/tests/backend/host-import.test.ts
@@ -0,0 +1,79 @@
+import { describe, expect, it } from "vitest";
+import {
+ createHostSettingsSync,
+ hostImportNormalizer,
+ hostPayloadLegacy,
+} from "../../src/backend/host-import.js";
+
+describe("hostImportNormalizer", () => {
+ it("reads a 2.8 export's quick actions and startup snippet", () => {
+ expect(
+ hostImportNormalizer({
+ quickActions: [
+ { name: "Restart", snippetId: "3" },
+ { name: "Broken", snippetId: "" },
+ ],
+ terminalConfig: JSON.stringify({ startupSnippetId: 5, fontSize: 12 }),
+ }),
+ ).toEqual({
+ quickActions: [{ name: "Restart", snippetId: 3 }],
+ startupSnippetId: 5,
+ });
+ });
+
+ it("leaves an export that carries this plugin's settings to core", () => {
+ expect(
+ hostImportNormalizer({
+ pluginSettings: { snippets: { startupSnippetId: 1 } },
+ terminalConfig: { startupSnippetId: 5 },
+ }),
+ ).toBeNull();
+ });
+
+ it("writes nothing for a host without either", () => {
+ expect(hostImportNormalizer({ name: "web" })).toBeNull();
+ });
+});
+
+describe("hostPayloadLegacy", () => {
+ it("puts the 2.8 fields back for older clients", () => {
+ expect(
+ hostPayloadLegacy({
+ quickActions: [{ name: "Up", snippetId: 2 }],
+ startupSnippetId: 7,
+ }),
+ ).toEqual({
+ quickActions: [{ name: "Up", snippetId: 2 }],
+ terminalConfig: { startupSnippetId: 7 },
+ });
+ expect(hostPayloadLegacy({})).toEqual({ quickActions: [] });
+ });
+});
+
+describe("hostSettingsSync", () => {
+ const sync = createHostSettingsSync({
+ findSyncIdById: async (id) => (id === 2 ? "sync-2" : null),
+ findIdBySyncId: async (syncId) => (syncId === "sync-2" ? 9 : null),
+ });
+
+ it("sends sync ids and drops what does not map", async () => {
+ expect(await sync.exportValue("startupSnippetId", 2)).toBe("sync-2");
+ expect(await sync.exportValue("startupSnippetId", 4)).toBeNull();
+ expect(
+ await sync.exportValue("quickActions", [
+ { name: "A", snippetId: 2 },
+ { name: "B", snippetId: 4 },
+ ]),
+ ).toEqual([{ name: "A", snippetId: "sync-2" }]);
+ });
+
+ it("maps sync ids back to this side's rows", async () => {
+ expect(await sync.importValue("startupSnippetId", "sync-2")).toBe(9);
+ expect(await sync.importValue("startupSnippetId", "missing")).toBeNull();
+ expect(
+ await sync.importValue("quickActions", [
+ { name: "A", snippetId: "sync-2" },
+ ]),
+ ).toEqual([{ name: "A", snippetId: 9 }]);
+ });
+});
diff --git a/plugins/snippets/tests/frontend/snippet-variables.test.ts b/plugins/snippets/tests/backend/variables.test.ts
similarity index 98%
rename from plugins/snippets/tests/frontend/snippet-variables.test.ts
rename to plugins/snippets/tests/backend/variables.test.ts
index ccb84b1d0..bdf0e16ab 100644
--- a/plugins/snippets/tests/frontend/snippet-variables.test.ts
+++ b/plugins/snippets/tests/backend/variables.test.ts
@@ -3,7 +3,7 @@ import {
extractSnippetInputs,
hasSnippetInputs,
resolveSnippetContent,
-} from "../../src/frontend/snippet-variables";
+} from "../../src/shared/variables.js";
describe("extractSnippetInputs", () => {
it("extracts a single unlabeled input", () => {
diff --git a/plugins/snippets/tests/frontend/activate.test.tsx b/plugins/snippets/tests/frontend/activate.test.tsx
index 615c77dee..9dba7ba10 100644
--- a/plugins/snippets/tests/frontend/activate.test.tsx
+++ b/plugins/snippets/tests/frontend/activate.test.tsx
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vitest";
-import { screen } from "@testing-library/react";
+import { act, fireEvent, screen } from "@testing-library/react";
+import { runKeybindingAction } from "@termix/plugin-sdk/frontend";
import {
renderWithApp,
type RenderedPluginApp,
@@ -41,11 +42,24 @@ describe(`${manifest.id} activate`, () => {
expect(railItem?.permission).toBe("snippets.view");
});
- it("registers the snippets.resolveForTerminal action", async () => {
+ it("registers what other surfaces reach it through", async () => {
rendered = await renderWithApp(plugin, { manifest, locales });
- expect(rendered.registered.actions()).toContain(
- "snippets.resolveForTerminal",
+ expect(rendered.registered.actions()).toEqual(
+ expect.arrayContaining([
+ "snippets.startupCommand",
+ "snippets.list",
+ "snippets.extractInputs",
+ ]),
);
+ expect(rendered.registered.paletteGroups()).toEqual(["snippets"]);
+ expect(rendered.registered.keybindingActions()).toEqual(["runSnippet"]);
+ expect(rendered.registered.hostEditorSections()).toEqual(["snippets"]);
+ expect(rendered.registered.slot("host-metrics.toolbar")).toEqual([
+ "snippets.quickActions",
+ ]);
+ expect(rendered.registered.slot("shell.overlay")).toEqual([
+ "snippets.overlay",
+ ]);
});
it("removes everything it registered on deactivate", async () => {
@@ -54,6 +68,143 @@ describe(`${manifest.id} activate`, () => {
expect(app.registered.panels()).toEqual([]);
expect(app.registered.railItems()).toEqual([]);
expect(app.registered.actions()).toEqual([]);
+ expect(app.registered.paletteGroups()).toEqual([]);
+ expect(app.registered.keybindingActions()).toEqual([]);
+ });
+});
+
+const deploy = {
+ id: 3,
+ name: "Deploy",
+ content: "deploy $HOST",
+ description: null,
+ isNote: false,
+};
+
+describe("snippets in the command palette", () => {
+ it("lists snippets that run in the target tab", async () => {
+ const api = { get: vi.fn(async () => ({ data: [deploy] })) };
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ api: api as never,
+ permissions: ["snippets.view"],
+ });
+ const items = await rendered.loadPaletteGroup("snippets");
+ expect(items).toEqual([
+ expect.objectContaining({
+ id: "3",
+ title: "Deploy",
+ description: "deploy $HOST",
+ needsTarget: true,
+ }),
+ ]);
+ });
+
+ it("offers nothing without the view permission", async () => {
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ permissions: [],
+ });
+ expect(await rendered.loadPaletteGroup("snippets")).toEqual([]);
+ });
+});
+
+describe("the runSnippet keybinding", () => {
+ it("types the resolved snippet into the session it was pressed in", async () => {
+ const api = { get: vi.fn(async () => ({ data: deploy })) };
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ api: api as never,
+ });
+ const send = vi.fn();
+ const handled = runKeybindingAction(
+ { type: "runSnippet", snippetId: "3", appendEnter: true },
+ { host: { ip: "10.0.0.9" }, send },
+ );
+ expect(handled).toBe(true);
+ await vi.waitFor(() =>
+ expect(send).toHaveBeenCalledWith("deploy 10.0.0.9\r"),
+ );
+ expect(api.get).toHaveBeenCalledWith("/3");
+ });
+});
+
+describe("the startup command", () => {
+ it("resolves the host's startup snippet, or nothing without one", async () => {
+ const api = { get: vi.fn(async () => ({ data: deploy })) };
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ api: api as never,
+ });
+ const host = {
+ id: "1",
+ name: "web",
+ ip: "10.0.0.2",
+ port: 22,
+ pluginSettings: { snippets: { startupSnippetId: 3 } },
+ };
+ expect(
+ await rendered.app.invokeAction("snippets.startupCommand", host),
+ ).toBe("deploy 10.0.0.2");
+ expect(
+ await rendered.app.invokeAction("snippets.startupCommand", {
+ ...host,
+ pluginSettings: {},
+ }),
+ ).toBeNull();
+ });
+});
+
+describe("the host editor's Snippets tab", () => {
+ it("writes the startup snippet and quick actions into this plugin's settings", async () => {
+ const api = { get: vi.fn(async () => ({ data: [deploy] })) };
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ api: api as never,
+ });
+ let form: Record = {
+ pluginSettings: { other: { keep: 1 } },
+ };
+ const updateForm = (
+ patch: (current: Record) => Record,
+ ) => {
+ form = patch(form);
+ };
+ rendered.renderHostEditorSection("snippets", { form, updateForm });
+ const startup = await screen.findByRole("combobox", {
+ name: "Startup Snippet",
+ });
+ await screen.findAllByRole("option", { name: "Deploy" });
+ act(() => {
+ fireEvent.change(startup, { target: { value: "3" } });
+ });
+ expect(form.pluginSettings).toEqual({
+ other: { keep: 1 },
+ snippets: { startupSnippetId: 3 },
+ });
+ });
+});
+
+describe("the Host Metrics quick action buttons", () => {
+ it("draws a button per quick action and nothing without any", async () => {
+ rendered = await renderWithApp(plugin, {
+ manifest,
+ locales,
+ permissions: ["snippets.view"],
+ });
+ const host = {
+ id: 4,
+ pluginSettings: {
+ snippets: { quickActions: [{ name: "Restart", snippetId: 3 }] },
+ },
+ };
+ rendered.renderSlot("host-metrics.toolbar", { hostId: 4, host });
+ expect(await screen.findByRole("button", { name: "Restart" })).toBeTruthy();
});
});
diff --git a/plugins/snippets/tests/frontend/admin-user-snippets.test.tsx b/plugins/snippets/tests/frontend/admin-user-snippets.test.tsx
index 70789d04e..75637d0a8 100644
--- a/plugins/snippets/tests/frontend/admin-user-snippets.test.tsx
+++ b/plugins/snippets/tests/frontend/admin-user-snippets.test.tsx
@@ -31,22 +31,23 @@ afterEach(async () => {
rendered = null;
});
-describe("snippet.list", () => {
- it("lists the user's own snippets", async () => {
+describe("snippet pickers while an admin edits another user's host", () => {
+ it("list the user's own snippets", async () => {
const api = fakeApi([{ id: 1, name: "a", content: "ls", isNote: true }]);
rendered = await renderWithApp(plugin, { manifest, locales, api });
- const rows = await rendered.app.invokeAction("snippet.list");
+ rendered.renderHostEditorSection("snippets", { form: {} });
+ await screen.findAllByRole("option", { name: "a" });
expect(api.get).toHaveBeenCalledWith("/");
- expect(rows).toEqual([
- { id: 1, name: "a", content: "ls", folder: null, isNote: true },
- ]);
});
- it("lists another user's snippets for an admin", async () => {
+ it("list the target user's snippets", async () => {
const api = fakeApi([]);
rendered = await renderWithApp(plugin, { manifest, locales, api });
- await rendered.app.invokeAction("snippet.list", { targetUserId: "u2" });
- expect(api.get).toHaveBeenCalledWith("/", TARGET);
+ rendered.renderHostEditorSection("snippets", {
+ form: {},
+ adminTargetUserId: "u2",
+ });
+ await waitFor(() => expect(api.get).toHaveBeenCalledWith("/", TARGET));
});
});
diff --git a/plugins/ssh-terminal/locales/en.json b/plugins/ssh-terminal/locales/en.json
index 903ae161e..816b3b7b2 100644
--- a/plugins/ssh-terminal/locales/en.json
+++ b/plugins/ssh-terminal/locales/en.json
@@ -490,7 +490,6 @@
"savedThemesLabel": "Saved Themes",
"scrollbackBufferLabel": "Scrollback Buffer",
"scrollbackMaxLines": "Maximum number of lines kept in history",
- "startupSnippetLabel": "Startup Snippet",
"sudoPasswordAutoFill": "Sudo Password Auto-Fill",
"sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.",
"sudoPasswordAutoFillLabel": "Sudo Password Auto-fill",
@@ -520,7 +519,8 @@
"history": "History",
"localTerminal": "Local Terminal",
"sshTools": "SSH Tools",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Type here to broadcast keystrokes...",
@@ -551,5 +551,122 @@
"savedThemePlaceholder": "Choose a saved theme",
"title": "Connection Defaults",
"apply": "Apply"
+ },
+ "keybindings": {
+ "copy": "Copy selection",
+ "paste": "Paste",
+ "sendControlCode": "Send Ctrl+letter signal",
+ "sendText": "Send text",
+ "controlCodeLabel": "Letter",
+ "textLabel": "Text to send",
+ "appendEnterLabel": "Press Enter after sending",
+ "clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers.",
+ "textRequired": "Enter the text to send",
+ "controlCodeRequired": "Enter a single letter for the control code",
+ "builtIn": {
+ "copyCtrlC": "Copy selection (Ctrl+C, only when text is selected)",
+ "copyCtrlShiftC": "Copy selection (Ctrl+Shift+C)",
+ "copyCmdC": "Copy selection (Cmd+C)",
+ "pasteCtrlShiftV": "Paste from clipboard (Ctrl+Shift+V)",
+ "ctrlW": "Send Ctrl+W to shell (blocked from closing browser tab)",
+ "ctrlT": "Send Ctrl+T to shell (blocked from opening browser tab)",
+ "ctrlN": "Send Ctrl+N to shell (blocked from opening browser window)",
+ "ctrlQ": "Send Ctrl+Q to shell (blocked from quitting browser)"
+ }
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Run a saved series of commands in a terminal, and react to what the server sends back.",
+ "target": "Target",
+ "noTerminal": "No terminal open",
+ "empty": "No macros yet",
+ "emptyHint": "Create a macro to automate a terminal flow, like logging in, restarting a service, or answering prompts.",
+ "create": "New macro",
+ "back": "Back to macros",
+ "name": "Name",
+ "namePlaceholder": "Restart web server",
+ "nameRequired": "Give the macro a name before saving",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "What this macro does (optional)",
+ "unsaved": "Unsaved changes",
+ "save": "Save",
+ "saved": "Macro saved",
+ "saveFailed": "Could not save the macro",
+ "delete": "Delete",
+ "deleteTitle": "Delete this macro?",
+ "deleteBody": "\"{{name}}\" will be permanently deleted. This cannot be undone.",
+ "deleted": "Macro deleted",
+ "cancel": "Cancel",
+ "run": "Run",
+ "stop": "Stop",
+ "running": "Running...",
+ "completed": "Macro finished",
+ "failed": "Macro failed",
+ "loadFailed": "Could not load macros",
+ "needTerminal": "Open and connect a terminal first",
+ "noSteps": "This macro has no steps yet. Add one below.",
+ "addStep": "Add step",
+ "stepNumber": "Step {{number}}",
+ "removeStep": "Remove step",
+ "confirmRunTitle": "Run this macro?",
+ "confirmRunBody": "\"{{name}}\" will send commands to {{host}}.",
+ "confirmRunAction": "Run macro",
+ "stepTypes": {
+ "send": {
+ "label": "Send command",
+ "help": "Types text into the terminal, the same as if you typed it."
+ },
+ "wait": {
+ "label": "Wait for output",
+ "help": "Pauses until the server prints the text you name, like a password prompt."
+ },
+ "delay": {
+ "label": "Pause",
+ "help": "Waits a fixed amount of time before the next step."
+ },
+ "if": {
+ "label": "If output contains",
+ "help": "Checks everything the server has printed so far, then runs one of two branches."
+ },
+ "repeat": {
+ "label": "Repeat",
+ "help": "Runs the steps inside it a set number of times."
+ }
+ },
+ "send": {
+ "text": "Command or text",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Press Enter after sending",
+ "pressEnterHelp": "Turn this off to type text without submitting it."
+ },
+ "wait": {
+ "pattern": "Wait until the output contains",
+ "patternPlaceholder": "password:",
+ "timeout": "Give up after",
+ "onTimeout": "If it never appears",
+ "stop": "Stop the macro",
+ "continue": "Keep going anyway"
+ },
+ "delay": {
+ "duration": "Wait for"
+ },
+ "ifStep": {
+ "pattern": "If the output contains",
+ "patternPlaceholder": "Active: active (running)",
+ "then": "Then do this",
+ "else": "Otherwise do this",
+ "emptyBranch": "No steps"
+ },
+ "repeat": {
+ "count": "Number of times",
+ "times": "times"
+ },
+ "match": {
+ "useRegex": "Match with a regular expression",
+ "useRegexHelp": "Leave this off to match the text exactly as written.",
+ "invalidRegex": "Not a valid regular expression, it will be matched as plain text"
+ },
+ "seconds": "seconds",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/af_ZA.json b/plugins/ssh-terminal/locales/translated/af_ZA.json
index 7eb7e1202..2530ce4c1 100644
--- a/plugins/ssh-terminal/locales/translated/af_ZA.json
+++ b/plugins/ssh-terminal/locales/translated/af_ZA.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gestoorde temas",
"scrollbackBufferLabel": "Terugrolbuffer",
"scrollbackMaxLines": "Maksimum aantal lyne wat in die geskiedenis gehou word",
- "startupSnippetLabel": "Opstart-brokkie",
"sudoPasswordAutoFill": "Sudo Wagwoord Outomatiese Vul",
"sudoPasswordAutoFillDesc": "Stoor 'n sudo-wagwoord sodat gasheermetrieke, terminaalprompts en ander outomaties bevoorregte opdragte kan uitvoer.",
"sudoPasswordAutoFillLabel": "Sudo Wagwoord Outomatiese Vul",
@@ -335,7 +334,8 @@
"history": "Geskiedenis",
"localTerminal": "Plaaslike Terminaal",
"sshTools": "SSH-gereedskap",
- "terminal": "Terminaal"
+ "terminal": "Terminaal",
+ "macros": "Makro's"
},
"sshTools": {
"broadcastInputPlaceholder": "Tik hier om toetsaanslagen uit te saai...",
@@ -363,5 +363,112 @@
"off": "Af",
"on": "Aan",
"title": "Verbindingsverstekke"
+ },
+ "keybindings": {
+ "copy": "Kopieer seleksie",
+ "paste": "Plak",
+ "sendControlCode": "Stuur Ctrl+letter sein",
+ "sendText": "Stuur SMS",
+ "controlCodeLabel": "Brief",
+ "textLabel": "SMS om te stuur",
+ "appendEnterLabel": "Druk Enter na stuur",
+ "clipboardPermissionNote": "Kan in sommige blaaiers met die eerste gebruik vir knipbordtoestemming vra.",
+ "textRequired": "Voer die teks in om te stuur",
+ "controlCodeRequired": "Voer 'n enkele letter vir die beheerkode in"
+ },
+ "macros": {
+ "title": "Makro's",
+ "description": "Voer 'n gestoorde reeks opdragte in 'n terminaal uit en reageer op wat die bediener terugstuur.",
+ "target": "Teiken",
+ "noTerminal": "Geen terminaal oop nie",
+ "empty": "Geen makro's nog nie",
+ "emptyHint": "Skep 'n makro om 'n terminaalvloei te outomatiseer, soos om aan te meld, 'n diens te herbegin of aanwysings te beantwoord.",
+ "create": "Nuwe makro",
+ "back": "Terug na makro's",
+ "name": "Naam",
+ "namePlaceholder": "Herbegin die webbediener",
+ "nameRequired": "Gee die makro 'n naam voordat jy dit stoor",
+ "descriptionLabel": "Beskrywing",
+ "descriptionPlaceholder": "Wat hierdie makro doen (opsioneel)",
+ "unsaved": "Ongestoorde veranderinge",
+ "save": "Stoor",
+ "saved": "Makro gestoor",
+ "saveFailed": "Kon nie die makro stoor nie",
+ "delete": "Vee uit",
+ "deleteTitle": "Vee hierdie makro uit?",
+ "deleteBody": "\"{{name}}\" sal permanent verwyder word. Dit kan nie ongedaan gemaak word nie.",
+ "deleted": "Makro uitgevee",
+ "cancel": "Kanselleer",
+ "run": "Hardloop",
+ "stop": "Stop",
+ "running": "Hardloop...",
+ "completed": "Makro klaar",
+ "failed": "Makro het misluk",
+ "loadFailed": "Kon nie makro's laai nie",
+ "needTerminal": "Maak eers 'n terminaal oop en koppel dit",
+ "noSteps": "Hierdie makro het nog geen stappe nie. Voeg een hieronder by.",
+ "addStep": "Voeg stap by",
+ "stepNumber": "Stap {{number}}",
+ "removeStep": "Verwyder stap",
+ "confirmRunTitle": "Voer hierdie makro uit?",
+ "confirmRunBody": "\"{{name}}\" sal bevele stuur na {{host}}.",
+ "confirmRunAction": "Begin makro",
+ "stepTypes": {
+ "send": {
+ "label": "Stuur opdrag",
+ "help": "Tik teks in die terminaal, dieselfde asof jy dit getik het."
+ },
+ "wait": {
+ "label": "Wag vir uitvoer",
+ "help": "Pouseer totdat die bediener die teks wat jy noem, druk, soos 'n wagwoordaanwysing."
+ },
+ "delay": {
+ "label": "Pouseer",
+ "help": "Wag 'n vaste hoeveelheid tyd voor die volgende stap."
+ },
+ "if": {
+ "label": "Indien die uitvoer bevat",
+ "help": "Kontroleer alles wat die bediener tot dusver gedruk het, en voer dan een van twee takke uit."
+ },
+ "repeat": {
+ "label": "Herhaal",
+ "help": "Voer die stappe daarin 'n vasgestelde aantal kere uit."
+ }
+ },
+ "send": {
+ "text": "Opdrag of teks",
+ "textPlaceholder": "systemctl herbegin nginx",
+ "pressEnter": "Druk Enter na stuur",
+ "pressEnterHelp": "Skakel dit af om teks te tik sonder om dit in te dien."
+ },
+ "wait": {
+ "pattern": "Wag totdat die uitvoer bevat",
+ "patternPlaceholder": "wagwoord:",
+ "timeout": "Gee op daarna",
+ "onTimeout": "As dit nooit verskyn nie",
+ "stop": "Stop die makro",
+ "continue": "Gaan in elk geval voort"
+ },
+ "delay": {
+ "duration": "Wag vir"
+ },
+ "ifStep": {
+ "pattern": "Indien die uitvoer bevat",
+ "patternPlaceholder": "Aktief: aktief (hardloop)",
+ "then": "Doen dan hierdie",
+ "else": "Andersins doen dit",
+ "emptyBranch": "Geen stappe nie"
+ },
+ "repeat": {
+ "count": "Aantal kere",
+ "times": "tye"
+ },
+ "match": {
+ "useRegex": "Stem ooreen met 'n gereelde uitdrukking",
+ "useRegexHelp": "Los dit af om die teks presies soos geskryf te laat ooreenstem.",
+ "invalidRegex": "Nie 'n geldige gereelde uitdrukking nie, dit sal as gewone teks ooreenstem"
+ },
+ "seconds": "sekondes",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ar_SA.json b/plugins/ssh-terminal/locales/translated/ar_SA.json
index de9d3d9aa..33fb6bc1c 100644
--- a/plugins/ssh-terminal/locales/translated/ar_SA.json
+++ b/plugins/ssh-terminal/locales/translated/ar_SA.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "السمات المحفوظة",
"scrollbackBufferLabel": "مخزن التمرير المؤقت",
"scrollbackMaxLines": "الحد الأقصى لعدد الأسطر المحفوظة في السجل",
- "startupSnippetLabel": "مقتطف بدء التشغيل",
"sudoPasswordAutoFill": "التعبئة التلقائية لكلمة مرور Sudo",
"sudoPasswordAutoFillDesc": "تخزين كلمة مرور sudo لتشغيل مقاييس المضيف ومطالبات الطرفية وغيرها من الأوامر المميزة تلقائياً.",
"sudoPasswordAutoFillLabel": "الملء التلقائي لكلمة مرور sudo",
@@ -335,7 +334,8 @@
"history": "السجل",
"localTerminal": "المحطة المحلية",
"sshTools": "أدوات SSH",
- "terminal": "الطرفية"
+ "terminal": "الطرفية",
+ "macros": "وحدات الماكرو"
},
"sshTools": {
"broadcastInputPlaceholder": "اكتب هنا لبث ضغطات المفاتيح...",
@@ -363,5 +363,112 @@
"off": "عن",
"on": "على",
"title": "الإعدادات الافتراضية للاتصال"
+ },
+ "keybindings": {
+ "copy": "تحديد النص",
+ "paste": "لصق",
+ "sendControlCode": "إرسال إشارة Ctrl+حرف",
+ "sendText": "أرسل رسالة نصية",
+ "controlCodeLabel": "خطاب",
+ "textLabel": "نص لإرساله",
+ "appendEnterLabel": "اضغط على زر الإدخال بعد الإرسال",
+ "clipboardPermissionNote": "قد يطلب منك بعض المتصفحات منح إذن الوصول إلى الحافظة عند الاستخدام الأول.",
+ "textRequired": "أدخل النص المراد إرساله",
+ "controlCodeRequired": "أدخل حرفًا واحدًا لرمز التحكم"
+ },
+ "macros": {
+ "title": "وحدات الماكرو",
+ "description": "قم بتشغيل سلسلة من الأوامر المحفوظة في طرفية، وتفاعل مع ما يرسله الخادم.",
+ "target": "هدف",
+ "noTerminal": "لا يوجد طرفية مفتوحة",
+ "empty": "لا توجد وحدات ماكرو حتى الآن",
+ "emptyHint": "أنشئ ماكرو لأتمتة تدفق طرفية، مثل تسجيل الدخول أو إعادة تشغيل خدمة أو الإجابة على المطالبات.",
+ "create": "ماكرو جديد",
+ "back": "العودة إلى وحدات الماكرو",
+ "name": "اسم",
+ "namePlaceholder": "أعد تشغيل خادم الويب",
+ "nameRequired": "قم بتسمية الماكرو قبل حفظه",
+ "descriptionLabel": "وصف",
+ "descriptionPlaceholder": "ما يفعله هذا الماكرو (اختياري)",
+ "unsaved": "التغييرات غير المحفوظة",
+ "save": "يحفظ",
+ "saved": "تم حفظ الماكرو",
+ "saveFailed": "تعذر حفظ الماكرو",
+ "delete": "يمسح",
+ "deleteTitle": "هل تريد حذف هذا الماكرو؟",
+ "deleteBody": "سيتم حذف \"{{name}}\" نهائياً. لا يمكن التراجع عن هذا الإجراء.",
+ "deleted": "تم حذف الماكرو",
+ "cancel": "يلغي",
+ "run": "يجري",
+ "stop": "قف",
+ "running": "جري...",
+ "completed": "اللمسة النهائية الدقيقة",
+ "failed": "فشل الماكرو",
+ "loadFailed": "تعذر تحميل وحدات الماكرو",
+ "needTerminal": "افتح وقم بتوصيل طرفية أولاً",
+ "noSteps": "لا يحتوي هذا الماكرو على أي خطوات حتى الآن. أضف خطوة واحدة أدناه.",
+ "addStep": "خطوات إضافية",
+ "stepNumber": "الخطوة {{number}}",
+ "removeStep": "خطوة الإزالة",
+ "confirmRunTitle": "هل تريد تشغيل هذا الماكرو؟",
+ "confirmRunBody": "\"{{name}}\" سيرسل أوامر إلى {{host}}.",
+ "confirmRunAction": "تشغيل الماكرو",
+ "stepTypes": {
+ "send": {
+ "label": "إرسال الأمر",
+ "help": "يكتب النص في الطرفية، تمامًا كما لو كنت أنت من كتبته."
+ },
+ "wait": {
+ "label": "انتظر حتى يتم إخراج البيانات",
+ "help": "يتوقف مؤقتًا حتى يقوم الخادم بطباعة النص الذي تحدده، مثل مطالبة كلمة المرور."
+ },
+ "delay": {
+ "label": "يوقف",
+ "help": "ينتظر فترة زمنية محددة قبل الخطوة التالية."
+ },
+ "if": {
+ "label": "إذا احتوى الناتج",
+ "help": "يقوم بفحص كل ما طبعه الخادم حتى الآن، ثم يقوم بتشغيل أحد الفرعين."
+ },
+ "repeat": {
+ "label": "يكرر",
+ "help": "يقوم بتنفيذ الخطوات الموجودة بداخله لعدد محدد من المرات."
+ }
+ },
+ "send": {
+ "text": "أمر أو نص",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "اضغط على زر الإدخال بعد الإرسال",
+ "pressEnterHelp": "قم بإيقاف تشغيل هذا الخيار لكتابة نص دون إرساله."
+ },
+ "wait": {
+ "pattern": "انتظر حتى يحتوي الناتج على",
+ "patternPlaceholder": "كلمة المرور:",
+ "timeout": "الاستسلام بعد",
+ "onTimeout": "إذا لم يظهر أبداً",
+ "stop": "أوقف الماكرو",
+ "continue": "استمر على أي حال"
+ },
+ "delay": {
+ "duration": "انتظر"
+ },
+ "ifStep": {
+ "pattern": "إذا احتوى الناتج",
+ "patternPlaceholder": "نشط: نشط (قيد التشغيل)",
+ "then": "ثم افعل هذا",
+ "else": "وإلا فافعل هذا",
+ "emptyBranch": "لا توجد"
+ },
+ "repeat": {
+ "count": "عدد المرات",
+ "times": "أوقات"
+ },
+ "match": {
+ "useRegex": "مطابقة مع تعبير نمطي",
+ "useRegexHelp": "اترك هذا الخيار فارغًا لمطابقة النص تمامًا كما هو مكتوب.",
+ "invalidRegex": "هذا ليس تعبيرًا نمطيًا صالحًا، وسيتم مطابقته كنص عادي."
+ },
+ "seconds": "ثوانٍ",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/bg_BG.json b/plugins/ssh-terminal/locales/translated/bg_BG.json
index e0a77cfc6..5f367021d 100644
--- a/plugins/ssh-terminal/locales/translated/bg_BG.json
+++ b/plugins/ssh-terminal/locales/translated/bg_BG.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Запазени теми",
"scrollbackBufferLabel": "Буфер за превъртане назад",
"scrollbackMaxLines": "Максимален брой редове, съхранявани в историята",
- "startupSnippetLabel": "Стартов фрагмент",
"sudoPasswordAutoFill": "Автоматично попълване на парола за Sudo",
"sudoPasswordAutoFillDesc": "Съхранявайте парола за sudo, така че показателите за хоста, подканите на терминала и други да могат да изпълняват привилегировани команди автоматично.",
"sudoPasswordAutoFillLabel": "Автоматично попълване на парола за Sudo",
@@ -335,7 +334,8 @@
"history": "История",
"localTerminal": "Местен терминал",
"sshTools": "SSH инструменти",
- "terminal": "Терминал"
+ "terminal": "Терминал",
+ "macros": "Макроси"
},
"sshTools": {
"broadcastInputPlaceholder": "Въведете тук, за да излъчвате натисканията на клавиши...",
@@ -363,5 +363,112 @@
"off": "Изключено",
"on": "Включено",
"title": "Настройки на връзката по подразбиране"
+ },
+ "keybindings": {
+ "copy": "Копиране на селекцията",
+ "paste": "Паста",
+ "sendControlCode": "Изпрати сигнал Ctrl+буква",
+ "sendText": "Изпращане на текст",
+ "controlCodeLabel": "Писмо",
+ "textLabel": "Текст за изпращане",
+ "appendEnterLabel": "Натиснете Enter след изпращане",
+ "clipboardPermissionNote": "Може да поиска разрешение за достъп до клипборда при първото използване в някои браузъри.",
+ "textRequired": "Въведете текста за изпращане",
+ "controlCodeRequired": "Въведете една буква за контролния код"
+ },
+ "macros": {
+ "title": "Макроси",
+ "description": "Изпълнете запазена серия от команди в терминал и реагирайте на това, което сървърът изпраща обратно.",
+ "target": "Цел",
+ "noTerminal": "Няма отворен терминал",
+ "empty": "Все още няма макроси",
+ "emptyHint": "Създайте макрос за автоматизиране на процес в терминал, като например влизане в системата, рестартиране на услуга или отговаряне на подкани.",
+ "create": "Нов макрос",
+ "back": "Обратно към макросите",
+ "name": "Име",
+ "namePlaceholder": "Рестартирайте уеб сървъра",
+ "nameRequired": "Дайте име на макроса, преди да го запазите",
+ "descriptionLabel": "Описание",
+ "descriptionPlaceholder": "Какво прави този макрос (по избор)",
+ "unsaved": "Незапазени промени",
+ "save": "Запазване",
+ "saved": "Макросът е запазен",
+ "saveFailed": "Макросът не можа да се запази",
+ "delete": "Изтриване",
+ "deleteTitle": "Да се изтрие ли този макрос?",
+ "deleteBody": "„{{name}}“ ще бъде изтрито за постоянно. Това не може да бъде отменено.",
+ "deleted": "Макросът е изтрит",
+ "cancel": "Отказ",
+ "run": "Бягане",
+ "stop": "Спри",
+ "running": "Бягане...",
+ "completed": "Макрото е завършено",
+ "failed": "Макросът не беше успешен",
+ "loadFailed": "Не можаха да се заредят макроси",
+ "needTerminal": "Първо отворете и свържете терминал",
+ "noSteps": "Този макрос все още няма стъпки. Добавете една по-долу.",
+ "addStep": "Добавяне на стъпка",
+ "stepNumber": "Стъпка {{number}}",
+ "removeStep": "Премахване на стъпка",
+ "confirmRunTitle": "Да се изпълни ли този макрос?",
+ "confirmRunBody": "„{{name}}“ ще изпраща команди до {{host}}.",
+ "confirmRunAction": "Изпълнете макрос",
+ "stepTypes": {
+ "send": {
+ "label": "Изпрати команда",
+ "help": "Въвежда текст в терминала, по същия начин, както ако сте го написали."
+ },
+ "wait": {
+ "label": "Изчакайте изхода",
+ "help": "Пауза, докато сървърът отпечата текста, който посочите, като например подкана за парола."
+ },
+ "delay": {
+ "label": "Пауза",
+ "help": "Изчаква фиксирано време преди следващата стъпка."
+ },
+ "if": {
+ "label": "Ако изходът съдържа",
+ "help": "Проверява всичко, което сървърът е отпечатал досега, след което изпълнява един от двата клона."
+ },
+ "repeat": {
+ "label": "Повторете",
+ "help": "Изпълнява стъпките вътре в него определен брой пъти."
+ }
+ },
+ "send": {
+ "text": "Команда или текст",
+ "textPlaceholder": "systemctl рестартиране на nginx",
+ "pressEnter": "Натиснете Enter след изпращане",
+ "pressEnterHelp": "Изключете това, за да пишете текст, без да го изпращате."
+ },
+ "wait": {
+ "pattern": "Изчакайте, докато изходът съдържа",
+ "patternPlaceholder": "парола:",
+ "timeout": "Откажи се след",
+ "onTimeout": "Ако никога не се появи",
+ "stop": "Спиране на макроса",
+ "continue": "Продължавай все пак"
+ },
+ "delay": {
+ "duration": "Изчакайте"
+ },
+ "ifStep": {
+ "pattern": "Ако изходът съдържа",
+ "patternPlaceholder": "Активен: активен (работи)",
+ "then": "Тогава направете това",
+ "else": "В противен случай направете това",
+ "emptyBranch": "Без стъпала"
+ },
+ "repeat": {
+ "count": "Брой пъти",
+ "times": "пъти"
+ },
+ "match": {
+ "useRegex": "Съвпадение с регулярен израз",
+ "useRegexHelp": "Не включвайте това, за да съвпада текстът точно както е написан.",
+ "invalidRegex": "Не е валиден регулярен израз, ще бъде съпоставен като обикновен текст"
+ },
+ "seconds": "секунди",
+ "secondsShort": "{{count}}с"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/bn_BD.json b/plugins/ssh-terminal/locales/translated/bn_BD.json
index b23953e90..7417ec0f1 100644
--- a/plugins/ssh-terminal/locales/translated/bn_BD.json
+++ b/plugins/ssh-terminal/locales/translated/bn_BD.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "সংরক্ষিত থিম",
"scrollbackBufferLabel": "স্ক্রোলব্যাক বাফার",
"scrollbackMaxLines": "ইতিহাসে সংরক্ষিত সর্বাধিক সংখ্যক লাইন",
- "startupSnippetLabel": "স্টার্টআপ স্নিপেট",
"sudoPasswordAutoFill": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ",
"sudoPasswordAutoFillDesc": "একটি sudo পাসওয়ার্ড সংরক্ষণ করুন যাতে হোস্ট মেট্রিক্স, টার্মিনাল প্রম্পট এবং অন্যান্যরা স্বয়ংক্রিয়ভাবে বিশেষাধিকারপ্রাপ্ত কমান্ড চালাতে পারে।",
"sudoPasswordAutoFillLabel": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ",
@@ -335,7 +334,8 @@
"history": "ইতিহাস",
"localTerminal": "স্থানীয় টার্মিনাল",
"sshTools": "SSH টুলস",
- "terminal": "টার্মিনাল"
+ "terminal": "টার্মিনাল",
+ "macros": "ম্যাক্রো"
},
"sshTools": {
"broadcastInputPlaceholder": "কীস্ট্রোক সম্প্রচার করতে এখানে টাইপ করুন...",
@@ -363,5 +363,112 @@
"off": "বন্ধ",
"on": "চালু",
"title": "সংযোগের ডিফল্ট"
+ },
+ "keybindings": {
+ "copy": "কপি নির্বাচন",
+ "paste": "পেস্ট",
+ "sendControlCode": "Ctrl+অক্ষর সংকেত পাঠান",
+ "sendText": "টেক্সট পাঠান",
+ "controlCodeLabel": "চিঠি",
+ "textLabel": "পাঠানোর জন্য টেক্সট",
+ "appendEnterLabel": "পাঠানোর পর এন্টার চাপুন",
+ "clipboardPermissionNote": "কিছু ব্রাউজারে প্রথমবার ব্যবহারের সময় ক্লিপবোর্ডের অনুমতি চাইতে পারে।",
+ "textRequired": "পাঠানোর জন্য টেক্সটটি লিখুন।",
+ "controlCodeRequired": "কন্ট্রোল কোডের জন্য একটি অক্ষর লিখুন।"
+ },
+ "macros": {
+ "title": "ম্যাক্রো",
+ "description": "টার্মিনালে সংরক্ষিত কমান্ডের একটি সিরিজ চালান এবং সার্ভার যা ফেরত পাঠায় তার উপর প্রতিক্রিয়া জানান।",
+ "target": "লক্ষ্য",
+ "noTerminal": "কোন টার্মিনাল খোলা নেই",
+ "empty": "এখনো কোনো ম্যাক্রো নেই",
+ "emptyHint": "টার্মিনালের কোনো কাজ, যেমন লগ ইন করা, কোনো সার্ভিস রিস্টার্ট করা বা প্রম্পটের উত্তর দেওয়া, স্বয়ংক্রিয় করতে একটি ম্যাক্রো তৈরি করুন।",
+ "create": "নতুন ম্যাক্রো",
+ "back": "ম্যাক্রোতে ফিরে যান",
+ "name": "নাম",
+ "namePlaceholder": "ওয়েব সার্ভার পুনরায় চালু করুন",
+ "nameRequired": "সংরক্ষণ করার আগে ম্যাক্রোটির একটি নাম দিন।",
+ "descriptionLabel": "বর্ণনা",
+ "descriptionPlaceholder": "এই ম্যাক্রোটি কী করে (ঐচ্ছিক)",
+ "unsaved": "সংরক্ষিত নয় এমন পরিবর্তন",
+ "save": "সংরক্ষণ করুন",
+ "saved": "ম্যাক্রো সংরক্ষিত",
+ "saveFailed": "ম্যাক্রোটি সংরক্ষণ করা যায়নি।",
+ "delete": "মুছে ফেলুন",
+ "deleteTitle": "এই ম্যাক্রোটি মুছে ফেলবেন?",
+ "deleteBody": "\"{{name}}\" স্থায়ীভাবে মুছে ফেলা হবে। এটি আর পূর্বাবস্থায় ফেরানো যাবে না।",
+ "deleted": "ম্যাক্রো মুছে ফেলা হয়েছে",
+ "cancel": "বাতিল করুন",
+ "run": "দৌড়",
+ "stop": "থামুন",
+ "running": "দৌড়াচ্ছে...",
+ "completed": "ম্যাক্রো শেষ হয়েছে",
+ "failed": "ম্যাক্রো ব্যর্থ হয়েছে",
+ "loadFailed": "ম্যাক্রো লোড করা যায়নি",
+ "needTerminal": "প্রথমে একটি টার্মিনাল খুলুন এবং সংযোগ করুন।",
+ "noSteps": "এই ম্যাক্রোতে এখনও কোনো ধাপ নেই। নিচে একটি যোগ করুন।",
+ "addStep": "ধাপ যোগ করুন",
+ "stepNumber": "ধাপ {{number}}",
+ "removeStep": "ধাপটি সরান",
+ "confirmRunTitle": "এই ম্যাক্রোটি চালাবেন?",
+ "confirmRunBody": "\"{{name}}\" {{host}} -এ কমান্ড পাঠাবে।",
+ "confirmRunAction": "ম্যাক্রো চালান",
+ "stepTypes": {
+ "send": {
+ "label": "কমান্ড পাঠান",
+ "help": "টার্মিনালে টেক্সট টাইপ করে, ঠিক যেন আপনি নিজেই টাইপ করেছেন।"
+ },
+ "wait": {
+ "label": "আউটপুটের জন্য অপেক্ষা করুন",
+ "help": "সার্ভার আপনার দেওয়া টেক্সটটি প্রিন্ট না করা পর্যন্ত এটি থেমে থাকে, অনেকটা পাসওয়ার্ড দেওয়ার সময়কার মতো।"
+ },
+ "delay": {
+ "label": "বিরতি",
+ "help": "পরবর্তী ধাপে যাওয়ার আগে একটি নির্দিষ্ট পরিমাণ সময় অপেক্ষা করে।"
+ },
+ "if": {
+ "label": "যদি আউটপুটে থাকে",
+ "help": "সার্ভার এ পর্যন্ত যা যা প্রিন্ট করেছে, তা যাচাই করে দুটি শাখার মধ্যে একটি চালায়।"
+ },
+ "repeat": {
+ "label": "পুনরাবৃত্তি",
+ "help": "এর ভেতরের ধাপগুলো একটি নির্দিষ্ট সংখ্যক বার চালায়।"
+ }
+ },
+ "send": {
+ "text": "কমান্ড বা টেক্সট",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "পাঠানোর পর এন্টার চাপুন",
+ "pressEnterHelp": "সাবমিট না করে টেক্সট টাইপ করতে এটি বন্ধ করুন।"
+ },
+ "wait": {
+ "pattern": "আউটপুটে থাকা পর্যন্ত অপেক্ষা করুন",
+ "patternPlaceholder": "পাসওয়ার্ড:",
+ "timeout": "হাল ছেড়ে দেওয়ার পর",
+ "onTimeout": "যদি এটি কখনও প্রদর্শিত না হয়",
+ "stop": "ম্যাক্রোটি বন্ধ করুন",
+ "continue": "তবুও এগিয়ে যান।"
+ },
+ "delay": {
+ "duration": "অপেক্ষা করুন"
+ },
+ "ifStep": {
+ "pattern": "যদি আউটপুটে থাকে",
+ "patternPlaceholder": "সক্রিয়: সক্রিয় (চলমান)",
+ "then": "তারপর এটা করুন",
+ "else": "অন্যথায় এটা করুন",
+ "emptyBranch": "কোন পদক্ষেপ নেই"
+ },
+ "repeat": {
+ "count": "সংখ্যা",
+ "times": "সময়"
+ },
+ "match": {
+ "useRegex": "রেগুলার এক্সপ্রেশনের সাথে মেলান",
+ "useRegexHelp": "লেখাটির সাথে হুবহু মিল রাখতে এটি বাদ দিন।",
+ "invalidRegex": "এটি একটি বৈধ রেগুলার এক্সপ্রেশন নয়, এটিকে সাধারণ টেক্সট হিসেবে মেলানো হবে।"
+ },
+ "seconds": "সেকেন্ড",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ca_ES.json b/plugins/ssh-terminal/locales/translated/ca_ES.json
index 3a7441565..5967cd855 100644
--- a/plugins/ssh-terminal/locales/translated/ca_ES.json
+++ b/plugins/ssh-terminal/locales/translated/ca_ES.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temes desats",
"scrollbackBufferLabel": "Buffer de desplaçament enrere",
"scrollbackMaxLines": "Nombre màxim de línies que es conserven a l'historial",
- "startupSnippetLabel": "Fragment d'inici",
"sudoPasswordAutoFill": "Emplenament automàtic de contrasenya de Sudo",
"sudoPasswordAutoFillDesc": "Emmagatzema una contrasenya sudo perquè les mètriques de l'amfitrió, les indicacions del terminal i altres puguin executar ordres privilegiades automàticament.",
"sudoPasswordAutoFillLabel": "Autoemplenament de contrasenya de Sudo",
@@ -335,7 +334,8 @@
"history": "Història",
"localTerminal": "Terminal local",
"sshTools": "Eines SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Escriu aquí per emetre les pulsacions de tecles...",
@@ -363,5 +363,112 @@
"off": "Desactivat",
"on": "Activat",
"title": "Valors predeterminats de connexió"
+ },
+ "keybindings": {
+ "copy": "Copia la selecció",
+ "paste": "Enganxa",
+ "sendControlCode": "Envia el senyal Ctrl+lletra",
+ "sendText": "Enviar text",
+ "controlCodeLabel": "Carta",
+ "textLabel": "Text per enviar",
+ "appendEnterLabel": "Premeu Intro després d'enviar",
+ "clipboardPermissionNote": "Pot demanar permisos al porta-retalls en el primer ús en alguns navegadors.",
+ "textRequired": "Introduïu el text per enviar",
+ "controlCodeRequired": "Introduïu una sola lletra per al codi de control"
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Executa una sèrie d'ordres desades en un terminal i reacciona al que el servidor envia de tornada.",
+ "target": "Objectiu",
+ "noTerminal": "Cap terminal obert",
+ "empty": "Encara no hi ha macros",
+ "emptyHint": "Crea una macro per automatitzar un flux de terminal, com ara iniciar la sessió, reiniciar un servei o respondre a les indicacions.",
+ "create": "Nova macro",
+ "back": "Torna a les macros",
+ "name": "Nom",
+ "namePlaceholder": "Reinicia el servidor web",
+ "nameRequired": "Doneu un nom a la macro abans de desar-la",
+ "descriptionLabel": "Descripció",
+ "descriptionPlaceholder": "Què fa aquesta macro (opcional)",
+ "unsaved": "Canvis no desats",
+ "save": "Desa",
+ "saved": "Macro desada",
+ "saveFailed": "No s'ha pogut desar la macro",
+ "delete": "Suprimeix",
+ "deleteTitle": "Voleu suprimir aquesta macro?",
+ "deleteBody": "«{{name}}» s'eliminarà permanentment. Això no es pot desfer.",
+ "deleted": "Macro suprimida",
+ "cancel": "Cancel·la",
+ "run": "Corre",
+ "stop": "Atura",
+ "running": "Corrent...",
+ "completed": "Macro acabada",
+ "failed": "La macro ha fallat",
+ "loadFailed": "No s'han pogut carregar les macros",
+ "needTerminal": "Obriu i connecteu primer un terminal",
+ "noSteps": "Aquesta macro encara no té cap pas. Afegeix-ne un a continuació.",
+ "addStep": "Afegeix un pas",
+ "stepNumber": "Pas {{number}}",
+ "removeStep": "Elimina el pas",
+ "confirmRunTitle": "Voleu executar aquesta macro?",
+ "confirmRunBody": "\"{{name}}\" enviarà ordres a {{host}}.",
+ "confirmRunAction": "Executa macro",
+ "stepTypes": {
+ "send": {
+ "label": "Envia l'ordre",
+ "help": "Escriu text al terminal, igual que si l'haguessis escrit."
+ },
+ "wait": {
+ "label": "Espera la sortida",
+ "help": "Es posa en pausa fins que el servidor imprimeix el text que anomeneu, com si fos una sol·licitud de contrasenya."
+ },
+ "delay": {
+ "label": "Pausa",
+ "help": "Espera un temps fix abans del següent pas."
+ },
+ "if": {
+ "label": "Si la sortida conté",
+ "help": "Comprova tot el que el servidor ha imprès fins ara i després executa una de les dues branques."
+ },
+ "repeat": {
+ "label": "Repetir",
+ "help": "Executa els passos que hi ha dins un nombre determinat de vegades."
+ }
+ },
+ "send": {
+ "text": "Ordre o text",
+ "textPlaceholder": "reinici de systemctl a nginx",
+ "pressEnter": "Premeu Intro després d'enviar",
+ "pressEnterHelp": "Desactiva això per escriure text sense enviar-lo."
+ },
+ "wait": {
+ "pattern": "Espereu fins que la sortida contingui",
+ "patternPlaceholder": "contrasenya:",
+ "timeout": "Renunciar després",
+ "onTimeout": "Si mai apareix",
+ "stop": "Atura la macro",
+ "continue": "Continua igualment"
+ },
+ "delay": {
+ "duration": "Espera"
+ },
+ "ifStep": {
+ "pattern": "Si la sortida conté",
+ "patternPlaceholder": "Actiu: actiu (en funcionament)",
+ "then": "Aleshores fes això",
+ "else": "Si no, fes això",
+ "emptyBranch": "Sense esglaons"
+ },
+ "repeat": {
+ "count": "Nombre de vegades",
+ "times": "vegades"
+ },
+ "match": {
+ "useRegex": "Coincideix amb una expressió regular",
+ "useRegexHelp": "Deixa això desactivat perquè el text coincideixi exactament com està escrit.",
+ "invalidRegex": "No és una expressió regular vàlida, es trobarà com a text sense format."
+ },
+ "seconds": "segons",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/cs_CZ.json b/plugins/ssh-terminal/locales/translated/cs_CZ.json
index be8627a94..86d0728b1 100644
--- a/plugins/ssh-terminal/locales/translated/cs_CZ.json
+++ b/plugins/ssh-terminal/locales/translated/cs_CZ.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Uložená témata",
"scrollbackBufferLabel": "Vyrovnávací paměť pro rolování zpět",
"scrollbackMaxLines": "Maximální počet řádků uchovávaných v historii",
- "startupSnippetLabel": "Úryvek pro spuštění",
"sudoPasswordAutoFill": "Automatické vyplňování hesla Sudo",
"sudoPasswordAutoFillDesc": "Uložte heslo sudo, aby metriky hostitele, výzvy terminálu a další mohly automaticky spouštět privilegované příkazy.",
"sudoPasswordAutoFillLabel": "Automatické vyplňování hesla Sudo",
@@ -335,7 +334,8 @@
"history": "Dějiny",
"localTerminal": "Místní terminál",
"sshTools": "SSH nástroje",
- "terminal": "Terminál"
+ "terminal": "Terminál",
+ "macros": "Makra"
},
"sshTools": {
"broadcastInputPlaceholder": "Zde napište pro vysílání stisknutí kláves...",
@@ -363,5 +363,112 @@
"off": "Vypnuto",
"on": "Na",
"title": "Výchozí nastavení připojení"
+ },
+ "keybindings": {
+ "copy": "Kopírovat výběr",
+ "paste": "Pasta",
+ "sendControlCode": "Odeslat signál Ctrl+písmeno",
+ "sendText": "Odeslat text",
+ "controlCodeLabel": "Dopis",
+ "textLabel": "Text k odeslání",
+ "appendEnterLabel": "Po odeslání stiskněte Enter",
+ "clipboardPermissionNote": "V některých prohlížečích se může při prvním použití zobrazit výzva k zadání povolení ke schránce.",
+ "textRequired": "Zadejte text k odeslání",
+ "controlCodeRequired": "Zadejte jedno písmeno pro kontrolní kód"
+ },
+ "macros": {
+ "title": "Makra",
+ "description": "Spusťte v terminálu uloženou sérii příkazů a reagujte na to, co server odešle zpět.",
+ "target": "Cíl",
+ "noTerminal": "Žádný otevřený terminál",
+ "empty": "Zatím žádná makra",
+ "emptyHint": "Vytvořte makro pro automatizaci procesů v terminálu, jako je přihlašování, restartování služby nebo odpovídání na výzvy.",
+ "create": "Nové makro",
+ "back": "Zpět k makrům",
+ "name": "Jméno",
+ "namePlaceholder": "Restartujte webový server",
+ "nameRequired": "Před uložením zadejte makro název",
+ "descriptionLabel": "Popis",
+ "descriptionPlaceholder": "Co toto makro dělá (volitelné)",
+ "unsaved": "Neuložené změny",
+ "save": "Uložit",
+ "saved": "Makro uloženo",
+ "saveFailed": "Makro se nepodařilo uložit.",
+ "delete": "Vymazat",
+ "deleteTitle": "Smazat toto makro?",
+ "deleteBody": "Soubor „{{name}}“ bude trvale smazán. Tuto akci nelze vrátit zpět.",
+ "deleted": "Makro smazáno",
+ "cancel": "Zrušit",
+ "run": "Běh",
+ "stop": "Zastávka",
+ "running": "Běh...",
+ "completed": "Makro dokončeno",
+ "failed": "Makro selhalo",
+ "loadFailed": "Nepodařilo se načíst makra",
+ "needTerminal": "Nejprve otevřete a připojte terminál",
+ "noSteps": "Toto makro zatím nemá žádné kroky. Přidejte jeden níže.",
+ "addStep": "Přidat krok",
+ "stepNumber": "Krok {{number}}",
+ "removeStep": "Odebrat krok",
+ "confirmRunTitle": "Spustit toto makro?",
+ "confirmRunBody": "„{{name}}“ odešle příkazy pro {{host}}.",
+ "confirmRunAction": "Spustit makro",
+ "stepTypes": {
+ "send": {
+ "label": "Odeslat příkaz",
+ "help": "Zadá text do terminálu, stejně jako byste ho napsali."
+ },
+ "wait": {
+ "label": "Počkejte na výstup",
+ "help": "Pozastaví se, dokud server nevypíše text, který pojmenujete, například výzvu k zadání hesla."
+ },
+ "delay": {
+ "label": "Pauza",
+ "help": "Čeká pevně stanovenou dobu před dalším krokem."
+ },
+ "if": {
+ "label": "Pokud výstup obsahuje",
+ "help": "Zkontroluje vše, co server dosud vytiskl, a poté spustí jednu ze dvou větví."
+ },
+ "repeat": {
+ "label": "Opakovat",
+ "help": "Spustí kroky uvnitř něj tolikrát, kolikrát je nastaveno."
+ }
+ },
+ "send": {
+ "text": "Příkaz nebo text",
+ "textPlaceholder": "restartování systemctl v Nginxu",
+ "pressEnter": "Po odeslání stiskněte Enter",
+ "pressEnterHelp": "Vypněte tuto funkci, chcete-li psát text bez jeho odesílání."
+ },
+ "wait": {
+ "pattern": "Počkejte, dokud výstup neobsahuje",
+ "patternPlaceholder": "heslo:",
+ "timeout": "Vzdej se po",
+ "onTimeout": "Pokud se to nikdy neobjeví",
+ "stop": "Zastavit makro",
+ "continue": "Stejně pokračuj"
+ },
+ "delay": {
+ "duration": "Čekat na"
+ },
+ "ifStep": {
+ "pattern": "Pokud výstup obsahuje",
+ "patternPlaceholder": "Aktivní: aktivní (běží)",
+ "then": "Pak udělej tohle",
+ "else": "Jinak to udělej",
+ "emptyBranch": "Žádné schody"
+ },
+ "repeat": {
+ "count": "Počet opakování",
+ "times": "krát"
+ },
+ "match": {
+ "useRegex": "Porovnání s regulárním výrazem",
+ "useRegexHelp": "Nechte toto vypnuté, aby text přesně odpovídal napsanému textu.",
+ "invalidRegex": "Není to platný regulární výraz, bude nalezen jako prostý text."
+ },
+ "seconds": "sekundy",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/da_DK.json b/plugins/ssh-terminal/locales/translated/da_DK.json
index 42d135bf5..c64990186 100644
--- a/plugins/ssh-terminal/locales/translated/da_DK.json
+++ b/plugins/ssh-terminal/locales/translated/da_DK.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gemte temaer",
"scrollbackBufferLabel": "Scrollback-buffer",
"scrollbackMaxLines": "Maksimalt antal linjer gemt i historikken",
- "startupSnippetLabel": "Opstartskodestykke",
"sudoPasswordAutoFill": "Sudo-adgangskode automatisk udfyldning",
"sudoPasswordAutoFillDesc": "Gem en sudo-adgangskode, så værtsmålinger, terminalprompter og andre kan køre privilegerede kommandoer automatisk.",
"sudoPasswordAutoFillLabel": "Sudo-adgangskode automatisk udfyldning",
@@ -335,7 +334,8 @@
"history": "Historie",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-værktøjer",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makroer"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv her for at udsende tastetryk...",
@@ -363,5 +363,112 @@
"off": "Slukket",
"on": "På",
"title": "Standardindstillinger for forbindelse"
+ },
+ "keybindings": {
+ "copy": "Kopiér valg",
+ "paste": "Indsæt",
+ "sendControlCode": "Send Ctrl+bogstavsignal",
+ "sendText": "Send sms",
+ "controlCodeLabel": "Bogstav",
+ "textLabel": "Tekst der skal sendes",
+ "appendEnterLabel": "Tryk Enter efter afsendelse",
+ "clipboardPermissionNote": "Kan bede om tilladelse til udklipsholder ved første brug i nogle browsere.",
+ "textRequired": "Indtast den tekst, der skal sendes",
+ "controlCodeRequired": "Indtast et enkelt bogstav for kontrolkoden"
+ },
+ "macros": {
+ "title": "Makroer",
+ "description": "Kør en gemt række kommandoer i en terminal, og reager på, hvad serveren sender tilbage.",
+ "target": "Mål",
+ "noTerminal": "Ingen terminal åben",
+ "empty": "Ingen makroer endnu",
+ "emptyHint": "Opret en makro for at automatisere et terminalflow, f.eks. at logge ind, genstarte en tjeneste eller besvare prompts.",
+ "create": "Ny makro",
+ "back": "Tilbage til makroer",
+ "name": "Navn",
+ "namePlaceholder": "Genstart webserveren",
+ "nameRequired": "Giv makroen et navn, før du gemmer den",
+ "descriptionLabel": "Beskrivelse",
+ "descriptionPlaceholder": "Hvad denne makro gør (valgfrit)",
+ "unsaved": "Ikke-gemte ændringer",
+ "save": "Spare",
+ "saved": "Makro gemt",
+ "saveFailed": "Makroen kunne ikke gemmes",
+ "delete": "Slet",
+ "deleteTitle": "Slette denne makro?",
+ "deleteBody": "\"{{name}}\" vil blive slettet permanent. Dette kan ikke fortrydes.",
+ "deleted": "Makro slettet",
+ "cancel": "Ophæve",
+ "run": "Løbe",
+ "stop": "Stop",
+ "running": "Løber...",
+ "completed": "Makro færdig",
+ "failed": "Makro mislykkedes",
+ "loadFailed": "Makroer kunne ikke indlæses",
+ "needTerminal": "Åbn og tilslut først en terminal",
+ "noSteps": "Denne makro har endnu ingen trin. Tilføj et nedenfor.",
+ "addStep": "Tilføj trin",
+ "stepNumber": "Trin {{number}}",
+ "removeStep": "Fjern trin",
+ "confirmRunTitle": "Køre denne makro?",
+ "confirmRunBody": "\"{{name}}\" sender kommandoer til {{host}}.",
+ "confirmRunAction": "Kør makro",
+ "stepTypes": {
+ "send": {
+ "label": "Send kommando",
+ "help": "Skriver tekst i terminalen, på samme måde som hvis du havde skrevet den."
+ },
+ "wait": {
+ "label": "Vent på output",
+ "help": "Holder pause, indtil serveren udskriver den tekst, du navngiver, f.eks. en adgangskodeprompt."
+ },
+ "delay": {
+ "label": "Pause",
+ "help": "Venter et fast tidsrum før næste trin."
+ },
+ "if": {
+ "label": "Hvis outputtet indeholder",
+ "help": "Kontrollerer alt, hvad serveren har udskrevet indtil videre, og kører derefter en af to grene."
+ },
+ "repeat": {
+ "label": "Gentage",
+ "help": "Kører trinnene inde i den et bestemt antal gange."
+ }
+ },
+ "send": {
+ "text": "Kommando eller tekst",
+ "textPlaceholder": "systemctl genstart nginx",
+ "pressEnter": "Tryk Enter efter afsendelse",
+ "pressEnterHelp": "Slå dette fra for at skrive tekst uden at indsende den."
+ },
+ "wait": {
+ "pattern": "Vent indtil outputtet indeholder",
+ "patternPlaceholder": "adgangskode:",
+ "timeout": "Giv op efter",
+ "onTimeout": "Hvis det aldrig dukker op",
+ "stop": "Stop makroen",
+ "continue": "Fortsæt alligevel"
+ },
+ "delay": {
+ "duration": "Vent på"
+ },
+ "ifStep": {
+ "pattern": "Hvis outputtet indeholder",
+ "patternPlaceholder": "Aktiv: aktiv (løber)",
+ "then": "Så gør dette",
+ "else": "Ellers gør dette",
+ "emptyBranch": "Ingen trin"
+ },
+ "repeat": {
+ "count": "Antal gange",
+ "times": "gange"
+ },
+ "match": {
+ "useRegex": "Match med et regulært udtryk",
+ "useRegexHelp": "Slå dette fra for at teksten skal være præcis som den er skrevet.",
+ "invalidRegex": "Ikke et gyldigt regulært udtryk, det vil blive matchet som almindelig tekst"
+ },
+ "seconds": "sekunder",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/de_DE.json b/plugins/ssh-terminal/locales/translated/de_DE.json
index 413711661..c33d9c3c7 100644
--- a/plugins/ssh-terminal/locales/translated/de_DE.json
+++ b/plugins/ssh-terminal/locales/translated/de_DE.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gespeicherte Designs",
"scrollbackBufferLabel": "Rückrollpuffer",
"scrollbackMaxLines": "Maximale Anzahl der im Verlauf gespeicherten Zeilen",
- "startupSnippetLabel": "Startup-Snippet",
"sudoPasswordAutoFill": "Sudo-Passwort automatisch ausfüllen",
"sudoPasswordAutoFillDesc": "Sudo-Passwort speichern, damit Hostmetriken, Terminal-Eingabeaufforderungen und andere privilegierte Befehle automatisch ausgeführt werden können.",
"sudoPasswordAutoFillLabel": "Sudo-Passwort automatisch ausfüllen",
@@ -335,7 +334,8 @@
"history": "Verlauf",
"localTerminal": "Lokales Terminal",
"sshTools": "SSH-Tools",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makros"
},
"sshTools": {
"broadcastInputPlaceholder": "Hier tippen, um Tastenanschläge zu senden...",
@@ -363,5 +363,112 @@
"off": "Aus",
"on": "An",
"title": "Standardeinstellungen für die Verbindung"
+ },
+ "keybindings": {
+ "copy": "Auswahl kopieren",
+ "paste": "Paste",
+ "sendControlCode": "Sende das Signal Strg+Buchstabe",
+ "sendText": "SMS senden",
+ "controlCodeLabel": "Brief",
+ "textLabel": "Text zum Senden",
+ "appendEnterLabel": "Nach dem Absenden die Eingabetaste drücken",
+ "clipboardPermissionNote": "Bei einigen Browsern kann es bei der ersten Verwendung zu einer Anfrage nach der Berechtigung zum Ablegen der Zwischenablage kommen.",
+ "textRequired": "Geben Sie den zu sendenden Text ein.",
+ "controlCodeRequired": "Geben Sie einen einzelnen Buchstaben als Kontrollcode ein."
+ },
+ "macros": {
+ "title": "Makros",
+ "description": "Führe eine gespeicherte Befehlsfolge in einem Terminal aus und reagiere auf die Antwort des Servers.",
+ "target": "Ziel",
+ "noTerminal": "Kein Terminal geöffnet",
+ "empty": "Noch keine Makros",
+ "emptyHint": "Erstellen Sie ein Makro, um einen Terminal-Workflow zu automatisieren, z. B. das Anmelden, das Neustarten eines Dienstes oder das Beantworten von Eingabeaufforderungen.",
+ "create": "Neues Makro",
+ "back": "Zurück zu den Makros",
+ "name": "Name",
+ "namePlaceholder": "Webserver neu starten",
+ "nameRequired": "Gib dem Makro vor dem Speichern einen Namen.",
+ "descriptionLabel": "Beschreibung",
+ "descriptionPlaceholder": "Was dieses Makro bewirkt (optional)",
+ "unsaved": "Nicht gespeicherte Änderungen",
+ "save": "Speichern",
+ "saved": "Makro gespeichert",
+ "saveFailed": "Makro konnte nicht gespeichert werden",
+ "delete": "Löschen",
+ "deleteTitle": "Dieses Makro löschen?",
+ "deleteBody": "\"{{name}}\" wird endgültig gelöscht. Dies kann nicht rückgängig gemacht werden.",
+ "deleted": "Makro gelöscht",
+ "cancel": "Stornieren",
+ "run": "Laufen",
+ "stop": "Stoppen",
+ "running": "Läuft...",
+ "completed": "Makro beendet",
+ "failed": "Makro fehlgeschlagen",
+ "loadFailed": "Makros konnten nicht geladen werden.",
+ "needTerminal": "Öffnen und verbinden Sie zuerst ein Terminal.",
+ "noSteps": "Dieses Makro hat noch keine Schritte. Füge unten einen hinzu.",
+ "addStep": "Schritt hinzufügen",
+ "stepNumber": "Schritt {{number}}",
+ "removeStep": "Schritt entfernen",
+ "confirmRunTitle": "Dieses Makro ausführen?",
+ "confirmRunBody": "\"{{name}}\" sendet Befehle an {{host}}.",
+ "confirmRunAction": "Makro ausführen",
+ "stepTypes": {
+ "send": {
+ "label": "Befehl senden",
+ "help": "Gibt den Text im Terminal ein, genau so, als ob Sie ihn eingetippt hätten."
+ },
+ "wait": {
+ "label": "Auf Ausgabe warten",
+ "help": "Die Ausgabe wird so lange unterbrochen, bis der Server den von Ihnen angegebenen Text ausgibt, beispielsweise eine Passwortabfrage."
+ },
+ "delay": {
+ "label": "Pause",
+ "help": "Wartet eine festgelegte Zeitspanne, bevor der nächste Schritt ausgeführt wird."
+ },
+ "if": {
+ "label": "Wenn die Ausgabe enthält",
+ "help": "Prüft alles, was der Server bisher ausgegeben hat, und führt dann einen von zwei Zweigen aus."
+ },
+ "repeat": {
+ "label": "Wiederholen",
+ "help": "Führt die darin enthaltenen Schritte eine festgelegte Anzahl von Malen aus."
+ }
+ },
+ "send": {
+ "text": "Befehl oder Text",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Nach dem Absenden die Eingabetaste drücken",
+ "pressEnterHelp": "Deaktivieren Sie diese Option, um Text einzugeben, ohne ihn abzusenden."
+ },
+ "wait": {
+ "pattern": "Warten Sie, bis die Ausgabe Folgendes enthält",
+ "patternPlaceholder": "Passwort:",
+ "timeout": "Aufgeben nach",
+ "onTimeout": "Wenn es nie erscheint",
+ "stop": "Stoppe das Makro",
+ "continue": "Mach trotzdem weiter."
+ },
+ "delay": {
+ "duration": "Warten Sie auf"
+ },
+ "ifStep": {
+ "pattern": "Wenn die Ausgabe enthält",
+ "patternPlaceholder": "Aktiv: aktiv (läuft)",
+ "then": "Dann tu Folgendes:",
+ "else": "Andernfalls tun Sie Folgendes:",
+ "emptyBranch": "Keine Stufen"
+ },
+ "repeat": {
+ "count": "Anzahl der Male",
+ "times": "mal"
+ },
+ "match": {
+ "useRegex": "Übereinstimmung mit einem regulären Ausdruck",
+ "useRegexHelp": "Lassen Sie dies weg, damit der Text genau so bleibt, wie er geschrieben steht.",
+ "invalidRegex": "Kein gültiger regulärer Ausdruck, wird als Klartext erkannt."
+ },
+ "seconds": "Sekunden",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/el_GR.json b/plugins/ssh-terminal/locales/translated/el_GR.json
index 0df5bd174..f277f94ad 100644
--- a/plugins/ssh-terminal/locales/translated/el_GR.json
+++ b/plugins/ssh-terminal/locales/translated/el_GR.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Αποθηκευμένα Θέματα",
"scrollbackBufferLabel": "Ενδιάμεσο buffer κύλισης προς τα πίσω",
"scrollbackMaxLines": "Μέγιστος αριθμός γραμμών που διατηρούνται στο ιστορικό",
- "startupSnippetLabel": "Απόσπασμα εκκίνησης",
"sudoPasswordAutoFill": "Αυτόματη συμπλήρωση κωδικού πρόσβασης Sudo",
"sudoPasswordAutoFillDesc": "Αποθηκεύστε έναν κωδικό πρόσβασης sudo, ώστε οι μετρήσεις του κεντρικού υπολογιστή, οι προτροπές του τερματικού και άλλα να μπορούν να εκτελούν αυτόματα προνομιακές εντολές.",
"sudoPasswordAutoFillLabel": "Αυτόματη συμπλήρωση κωδικού πρόσβασης Sudo",
@@ -335,7 +334,8 @@
"history": "Ιστορία",
"localTerminal": "Τοπικό τερματικό",
"sshTools": "Εργαλεία SSH",
- "terminal": "Τερματικό"
+ "terminal": "Τερματικό",
+ "macros": "Μακροεντολές"
},
"sshTools": {
"broadcastInputPlaceholder": "Πληκτρολογήστε εδώ για να μεταδώσετε πληκτρολογήσεις...",
@@ -363,5 +363,112 @@
"off": "Μακριά από",
"on": "Επί",
"title": "Προεπιλογές σύνδεσης"
+ },
+ "keybindings": {
+ "copy": "Αντιγραφή επιλογής",
+ "paste": "Πάστα",
+ "sendControlCode": "Αποστολή σήματος Ctrl+γράμμα",
+ "sendText": "Αποστολή κειμένου",
+ "controlCodeLabel": "Επιστολή",
+ "textLabel": "Μήνυμα προς αποστολή",
+ "appendEnterLabel": "Πατήστε Enter μετά την αποστολή",
+ "clipboardPermissionNote": "Ενδέχεται να ζητηθεί άδεια για το πρόχειρο κατά την πρώτη χρήση σε ορισμένα προγράμματα περιήγησης.",
+ "textRequired": "Εισαγάγετε το κείμενο που θέλετε να στείλετε",
+ "controlCodeRequired": "Εισαγάγετε ένα μόνο γράμμα για τον κωδικό ελέγχου"
+ },
+ "macros": {
+ "title": "Μακροεντολές",
+ "description": "Εκτελέστε μια αποθηκευμένη σειρά εντολών σε ένα τερματικό και αντιδράστε σε ό,τι στέλνει πίσω ο διακομιστής.",
+ "target": "Στόχος",
+ "noTerminal": "Δεν υπάρχει ανοιχτό τερματικό",
+ "empty": "Δεν υπάρχουν ακόμη μακροεντολές",
+ "emptyHint": "Δημιουργήστε μια μακροεντολή για να αυτοματοποιήσετε μια ροή τερματικού, όπως σύνδεση, επανεκκίνηση μιας υπηρεσίας ή απάντηση σε μηνύματα.",
+ "create": "Νέα μακροεντολή",
+ "back": "Επιστροφή στις μακροεντολές",
+ "name": "Ονομα",
+ "namePlaceholder": "Επανεκκίνηση διακομιστή ιστού",
+ "nameRequired": "Δώστε ένα όνομα στη μακροεντολή πριν από την αποθήκευση",
+ "descriptionLabel": "Περιγραφή",
+ "descriptionPlaceholder": "Τι κάνει αυτή η μακροεντολή (προαιρετικό)",
+ "unsaved": "Μη αποθηκευμένες αλλαγές",
+ "save": "Εκτός",
+ "saved": "Η μακροεντολή αποθηκεύτηκε",
+ "saveFailed": "Δεν ήταν δυνατή η αποθήκευση της μακροεντολής",
+ "delete": "Διαγράφω",
+ "deleteTitle": "Διαγραφή αυτής της μακροεντολής;",
+ "deleteBody": "Το \"{{name}}\" θα διαγραφεί οριστικά. Δεν είναι δυνατή η αναίρεση αυτής της ενέργειας.",
+ "deleted": "Η μακροεντολή διαγράφηκε",
+ "cancel": "Ματαίωση",
+ "run": "Τρέξιμο",
+ "stop": "Στάση",
+ "running": "Τρέξιμο...",
+ "completed": "Η μακροεντολή ολοκληρώθηκε",
+ "failed": "Η μακροεντολή απέτυχε.",
+ "loadFailed": "Δεν ήταν δυνατή η φόρτωση μακροεντολών",
+ "needTerminal": "Ανοίξτε και συνδέστε πρώτα ένα τερματικό",
+ "noSteps": "Αυτή η μακροεντολή δεν έχει ακόμη βήματα. Προσθέστε ένα παρακάτω.",
+ "addStep": "Προσθήκη βήματος",
+ "stepNumber": "Βήμα {{number}}",
+ "removeStep": "Αφαίρεση βήματος",
+ "confirmRunTitle": "Εκτέλεση αυτής της μακροεντολής;",
+ "confirmRunBody": "Η εντολή \"{{name}}\" θα στείλει εντολές στον χρήστη {{host}}.",
+ "confirmRunAction": "Εκτέλεση μακροεντολής",
+ "stepTypes": {
+ "send": {
+ "label": "Αποστολή εντολής",
+ "help": "Πληκτρολογεί κείμενο στο τερματικό, όπως θα το γράφατε."
+ },
+ "wait": {
+ "label": "Περιμένετε για έξοδο",
+ "help": "Παύει μέχρι ο διακομιστής να εκτυπώσει το κείμενο που ονομάζετε, σαν να σας ζητείται κωδικός πρόσβασης."
+ },
+ "delay": {
+ "label": "Παύση",
+ "help": "Περιμένει ένα συγκεκριμένο χρονικό διάστημα πριν από το επόμενο βήμα."
+ },
+ "if": {
+ "label": "Εάν η έξοδος περιέχει",
+ "help": "Ελέγχει όλα όσα έχει εκτυπώσει μέχρι στιγμής ο διακομιστής και, στη συνέχεια, εκτελεί μία από τις δύο διακλαδώσεις."
+ },
+ "repeat": {
+ "label": "Επαναλαμβάνω",
+ "help": "Εκτελεί τα βήματα στο εσωτερικό του έναν καθορισμένο αριθμό φορών."
+ }
+ },
+ "send": {
+ "text": "Εντολή ή κείμενο",
+ "textPlaceholder": "επανεκκίνηση systemctl nginx",
+ "pressEnter": "Πατήστε Enter μετά την αποστολή",
+ "pressEnterHelp": "Απενεργοποιήστε αυτήν τη λειτουργία για να πληκτρολογείτε κείμενο χωρίς να το υποβάλλετε."
+ },
+ "wait": {
+ "pattern": "Περιμένετε μέχρι η έξοδος να περιέχει",
+ "patternPlaceholder": "σύνθημα:",
+ "timeout": "Παραιτήσου μετά",
+ "onTimeout": "Αν δεν εμφανιστεί ποτέ",
+ "stop": "Διακοπή της μακροεντολής",
+ "continue": "Συνέχισε ούτως ή άλλως"
+ },
+ "delay": {
+ "duration": "Περιμένω για"
+ },
+ "ifStep": {
+ "pattern": "Εάν η έξοδος περιέχει",
+ "patternPlaceholder": "Ενεργό: ενεργό (σε λειτουργία)",
+ "then": "Στη συνέχεια, κάντε αυτό",
+ "else": "Διαφορετικά, κάντε αυτό",
+ "emptyBranch": "Χωρίς σκαλοπάτια"
+ },
+ "repeat": {
+ "count": "Αριθμός φορών",
+ "times": "φορές"
+ },
+ "match": {
+ "useRegex": "Ταίριασμα με μια κανονική έκφραση",
+ "useRegexHelp": "Αφήστε το απενεργοποιημένο για να ταιριάζει ακριβώς με το κείμενο όπως είναι γραμμένο.",
+ "invalidRegex": "Δεν είναι έγκυρη κανονική έκφραση, θα αντιστοιχιστεί ως απλό κείμενο"
+ },
+ "seconds": "δευτερόλεπτα",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/es_ES.json b/plugins/ssh-terminal/locales/translated/es_ES.json
index 452993d9e..714f4b69c 100644
--- a/plugins/ssh-terminal/locales/translated/es_ES.json
+++ b/plugins/ssh-terminal/locales/translated/es_ES.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas guardados",
"scrollbackBufferLabel": "Búfer de retroceso",
"scrollbackMaxLines": "Número máximo de líneas conservadas en el historial",
- "startupSnippetLabel": "Fragmento de inicio",
"sudoPasswordAutoFill": "Autocompletado de contraseña de sudo",
"sudoPasswordAutoFillDesc": "Almacenar una contraseña sudo para que las métricas del host, las solicitudes del terminal y otros puedan ejecutar comandos privilegiados automáticamente.",
"sudoPasswordAutoFillLabel": "Autocompletado de contraseña sudo",
@@ -335,7 +334,8 @@
"history": "Historial",
"localTerminal": "Terminal local",
"sshTools": "Herramientas SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Escriba aquí para transmitir pulsaciones...",
@@ -363,5 +363,112 @@
"off": "Apagado",
"on": "En",
"title": "Valores predeterminados de conexión"
+ },
+ "keybindings": {
+ "copy": "Copiar selección",
+ "paste": "Pasta",
+ "sendControlCode": "Enviar señal Ctrl+letra",
+ "sendText": "Enviar mensaje de texto",
+ "controlCodeLabel": "Carta",
+ "textLabel": "Texto para enviar",
+ "appendEnterLabel": "Presione Enter después de enviar",
+ "clipboardPermissionNote": "En algunos navegadores, puede solicitar permiso para copiar y pegar el contenido del portapapeles la primera vez que se utilice.",
+ "textRequired": "Introduce el texto que deseas enviar",
+ "controlCodeRequired": "Introduzca una sola letra para el código de control."
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Ejecuta una serie de comandos guardados en una terminal y reacciona a la respuesta que envíe el servidor.",
+ "target": "Objetivo",
+ "noTerminal": "No hay terminal abierta",
+ "empty": "Aún no hay macros",
+ "emptyHint": "Crea una macro para automatizar un flujo de terminal, como iniciar sesión, reiniciar un servicio o responder a preguntas.",
+ "create": "Nueva macro",
+ "back": "Volver a las macros",
+ "name": "Nombre",
+ "namePlaceholder": "Reiniciar el servidor web",
+ "nameRequired": "Dale un nombre a la macro antes de guardarla.",
+ "descriptionLabel": "Descripción",
+ "descriptionPlaceholder": "Qué hace esta macro (opcional)",
+ "unsaved": "Cambios no guardados",
+ "save": "Ahorrar",
+ "saved": "Macro guardada",
+ "saveFailed": "No se pudo guardar la macro",
+ "delete": "Borrar",
+ "deleteTitle": "¿Eliminar esta macro?",
+ "deleteBody": "\"{{name}}\" se eliminará permanentemente. Esto no se puede deshacer.",
+ "deleted": "Macro eliminada",
+ "cancel": "Cancelar",
+ "run": "Correr",
+ "stop": "Detener",
+ "running": "Correr...",
+ "completed": "Macro terminado",
+ "failed": "La macro falló",
+ "loadFailed": "No se pudieron cargar las macros",
+ "needTerminal": "Primero, abre y conecta una terminal.",
+ "noSteps": "Esta macro aún no tiene pasos. Añada uno a continuación.",
+ "addStep": "Agregar paso",
+ "stepNumber": "Paso {{number}}",
+ "removeStep": "Eliminar paso",
+ "confirmRunTitle": "¿Ejecutar esta macro?",
+ "confirmRunBody": "\"{{name}}\" enviará comandos a {{host}}.",
+ "confirmRunAction": "Ejecutar macro",
+ "stepTypes": {
+ "send": {
+ "label": "Enviar comando",
+ "help": "Escribe el texto en la terminal, igual que si lo hubieras escrito tú."
+ },
+ "wait": {
+ "label": "Espere la salida",
+ "help": "Hace una pausa hasta que el servidor imprime el texto que usted especifique, como si fuera una solicitud de contraseña."
+ },
+ "delay": {
+ "label": "Pausa",
+ "help": "Espera un tiempo fijo antes de pasar al siguiente paso."
+ },
+ "if": {
+ "label": "Si la salida contiene",
+ "help": "Comprueba todo lo que el servidor ha impreso hasta el momento y, a continuación, ejecuta una de las dos ramas."
+ },
+ "repeat": {
+ "label": "Repetir",
+ "help": "Reproduce los pasos que contiene un número determinado de veces."
+ }
+ },
+ "send": {
+ "text": "Comando o texto",
+ "textPlaceholder": "systemctl reiniciar nginx",
+ "pressEnter": "Presione Enter después de enviar",
+ "pressEnterHelp": "Desactive esta opción para escribir texto sin enviarlo."
+ },
+ "wait": {
+ "pattern": "Espere hasta que la salida contenga",
+ "patternPlaceholder": "contraseña:",
+ "timeout": "Rendirse después",
+ "onTimeout": "Si nunca aparece",
+ "stop": "Detén la macro",
+ "continue": "Sigue adelante de todos modos."
+ },
+ "delay": {
+ "duration": "Esperar"
+ },
+ "ifStep": {
+ "pattern": "Si la salida contiene",
+ "patternPlaceholder": "Activo: activo (corriendo)",
+ "then": "Entonces haz esto",
+ "else": "De lo contrario, haz esto",
+ "emptyBranch": "Sin escalones"
+ },
+ "repeat": {
+ "count": "Número de veces",
+ "times": "veces"
+ },
+ "match": {
+ "useRegex": "Coincidencia con una expresión regular",
+ "useRegexHelp": "Omita esta opción para que coincida exactamente con el texto escrito.",
+ "invalidRegex": "No es una expresión regular válida, se interpretará como texto plano."
+ },
+ "seconds": "artículos de segunda clase",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/fi_FI.json b/plugins/ssh-terminal/locales/translated/fi_FI.json
index 5f603b0a3..e104fd4b4 100644
--- a/plugins/ssh-terminal/locales/translated/fi_FI.json
+++ b/plugins/ssh-terminal/locales/translated/fi_FI.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Tallennetut teemat",
"scrollbackBufferLabel": "Vieroituspuskuri",
"scrollbackMaxLines": "Historiassa säilytettävien rivien enimmäismäärä",
- "startupSnippetLabel": "Käynnistyskoodinpätkä",
"sudoPasswordAutoFill": "Sudo-salasanan automaattinen täyttö",
"sudoPasswordAutoFillDesc": "Tallenna sudo-salasana, jotta isäntämittarit, päätekehotteet ja muut voivat suorittaa etuoikeutettuja komentoja automaattisesti.",
"sudoPasswordAutoFillLabel": "Sudo-salasanan automaattinen täyttö",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Paikallinen terminaali",
"sshTools": "SSH-työkalut",
- "terminal": "Terminaali"
+ "terminal": "Terminaali",
+ "macros": "Makrot"
},
"sshTools": {
"broadcastInputPlaceholder": "Kirjoita tähän lähettääksesi näppäinpainalluksia...",
@@ -363,5 +363,112 @@
"off": "Pois",
"on": "Päällä",
"title": "Yhteysoletukset"
+ },
+ "keybindings": {
+ "copy": "Kopioi valinta",
+ "paste": "Liitä",
+ "sendControlCode": "Lähetä Ctrl+kirjain-signaali",
+ "sendText": "Lähetä tekstiviesti",
+ "controlCodeLabel": "Kirje",
+ "textLabel": "Lähetettävä tekstiviesti",
+ "appendEnterLabel": "Paina Enter lähetyksen jälkeen",
+ "clipboardPermissionNote": "Saattaa pyytää leikepöydän käyttöoikeutta ensimmäisellä käyttökerralla joissakin selaimissa.",
+ "textRequired": "Kirjoita lähetettävä teksti",
+ "controlCodeRequired": "Syötä yksi kirjain ohjauskoodille"
+ },
+ "macros": {
+ "title": "Makrot",
+ "description": "Suorita tallennettu komentosarja terminaalissa ja reagoi palvelimen lähettämiin komentoihin.",
+ "target": "Kohde",
+ "noTerminal": "Ei auki olevaa terminaalia",
+ "empty": "Ei vielä makroja",
+ "emptyHint": "Luo makro automatisoidaksesi päätetyönkulun, kuten sisäänkirjautumisen, palvelun uudelleenkäynnistyksen tai kehotteisiin vastaamisen.",
+ "create": "Uusi makro",
+ "back": "Takaisin makroihin",
+ "name": "Nimi",
+ "namePlaceholder": "Käynnistä web-palvelin uudelleen",
+ "nameRequired": "Anna makrolle nimi ennen tallentamista",
+ "descriptionLabel": "Kuvaus",
+ "descriptionPlaceholder": "Mitä tämä makro tekee (valinnainen)",
+ "unsaved": "Tallentamattomat muutokset",
+ "save": "Tallentaa",
+ "saved": "Makro tallennettu",
+ "saveFailed": "Makroa ei voitu tallentaa",
+ "delete": "Poistaa",
+ "deleteTitle": "Poistetaanko tämä makro?",
+ "deleteBody": "\"{{name}}\" poistetaan pysyvästi. Tätä ei voi perua.",
+ "deleted": "Makro poistettu",
+ "cancel": "Peruuttaa",
+ "run": "Juokse",
+ "stop": "Stop",
+ "running": "Juoksemassa...",
+ "completed": "Makro valmis",
+ "failed": "Makro epäonnistui",
+ "loadFailed": "Makrojen lataaminen epäonnistui",
+ "needTerminal": "Avaa ja kytke ensin päätelaite",
+ "noSteps": "Tällä makrolla ei ole vielä vaiheita. Lisää yksi alle.",
+ "addStep": "Lisää vaihe",
+ "stepNumber": "Vaihe {{number}}",
+ "removeStep": "Poista vaihe",
+ "confirmRunTitle": "Suoritetaanko tämä makro?",
+ "confirmRunBody": "\"{{name}}\" lähettää komentoja {{host}}:een.",
+ "confirmRunAction": "Suorita makro",
+ "stepTypes": {
+ "send": {
+ "label": "Lähetä komento",
+ "help": "Kirjoittaa tekstiä terminaaliin aivan kuin olisit kirjoittanut sen."
+ },
+ "wait": {
+ "label": "Odota tulostetta",
+ "help": "Pysähtyy, kunnes palvelin tulostaa nimeämäsi tekstin, kuten salasanakehotteen."
+ },
+ "delay": {
+ "label": "Tauko",
+ "help": "Odottaa tietyn ajan ennen seuraavaa vaihetta."
+ },
+ "if": {
+ "label": "Jos tuloste sisältää",
+ "help": "Tarkistaa kaiken, mitä palvelin on tähän mennessä tulostanut, ja suorittaa sitten toisen kahdesta haarasta."
+ },
+ "repeat": {
+ "label": "Toistaa",
+ "help": "Suorittaa sen sisällä olevat vaiheet tietyn määrän kertoja."
+ }
+ },
+ "send": {
+ "text": "Komento tai teksti",
+ "textPlaceholder": "systemctl käynnistä nginx uudelleen",
+ "pressEnter": "Paina Enter lähetyksen jälkeen",
+ "pressEnterHelp": "Poista tämä käytöstä kirjoittaaksesi tekstiä lähettämättä sitä."
+ },
+ "wait": {
+ "pattern": "Odota, kunnes tuloste sisältää",
+ "patternPlaceholder": "salasana:",
+ "timeout": "Luovuta jälkeenpäin",
+ "onTimeout": "Jos se ei koskaan ilmesty",
+ "stop": "Pysäytä makro",
+ "continue": "Jatka joka tapauksessa"
+ },
+ "delay": {
+ "duration": "Odota"
+ },
+ "ifStep": {
+ "pattern": "Jos tuloste sisältää",
+ "patternPlaceholder": "Aktiivinen: aktiivinen (käynnissä)",
+ "then": "Tee sitten tämä",
+ "else": "Muuten tee näin",
+ "emptyBranch": "Ei portaita"
+ },
+ "repeat": {
+ "count": "Kuinka monta kertaa",
+ "times": "kertaa"
+ },
+ "match": {
+ "useRegex": "Säännöllisen lausekkeen mukainen osuma",
+ "useRegexHelp": "Jätä tämä pois, jotta teksti vastaa täysin kirjoitettua tekstiä.",
+ "invalidRegex": "Ei kelvollinen säännöllinen lauseke, se tulkitaan pelkkänä tekstinä"
+ },
+ "seconds": "sekuntia",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/fr_FR.json b/plugins/ssh-terminal/locales/translated/fr_FR.json
index 26e8e4541..316a3b3e5 100644
--- a/plugins/ssh-terminal/locales/translated/fr_FR.json
+++ b/plugins/ssh-terminal/locales/translated/fr_FR.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Thèmes enregistrés",
"scrollbackBufferLabel": "Mémoire de défilement",
"scrollbackMaxLines": "Nombre maximal de lignes conservées dans l'historique",
- "startupSnippetLabel": "Extrait de démarrage",
"sudoPasswordAutoFill": "Remplissage automatique du mot de passe sudo",
"sudoPasswordAutoFillDesc": "Enregistrer un mot de passe sudo pour que les métriques de l'hôte, les invites du terminal et autres puissent exécuter automatiquement des commandes privilégiées.",
"sudoPasswordAutoFillLabel": "Remplissage automatique du mot de passe sudo",
@@ -335,7 +334,8 @@
"history": "Historique",
"localTerminal": "Terminal local",
"sshTools": "Outils SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Tapez ici pour diffuser les frappes...",
@@ -363,5 +363,112 @@
"off": "Désactivé",
"on": "Sur",
"title": "Paramètres de connexion par défaut"
+ },
+ "keybindings": {
+ "copy": "Copier la sélection",
+ "paste": "Coller",
+ "sendControlCode": "Envoyer le signal Ctrl+lettre",
+ "sendText": "Envoyer un SMS",
+ "controlCodeLabel": "Lettre",
+ "textLabel": "Texte à envoyer",
+ "appendEnterLabel": "Appuyez sur Entrée après l'envoi",
+ "clipboardPermissionNote": "Il se peut que certains navigateurs demandent l'autorisation d'accéder au presse-papiers lors de la première utilisation.",
+ "textRequired": "Saisissez le texte à envoyer",
+ "controlCodeRequired": "Saisissez une seule lettre pour le code de contrôle"
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Exécutez une série de commandes enregistrées dans un terminal et réagissez à la réponse du serveur.",
+ "target": "Cible",
+ "noTerminal": "Aucun terminal ouvert",
+ "empty": "Pas encore de macros",
+ "emptyHint": "Créez une macro pour automatiser un flux de terminal, comme la connexion, le redémarrage d'un service ou la réponse à des invites.",
+ "create": "Nouvelle macro",
+ "back": "Retour aux macros",
+ "name": "Nom",
+ "namePlaceholder": "Redémarrer le serveur web",
+ "nameRequired": "Donnez un nom à la macro avant de l'enregistrer.",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "Fonctionnement de cette macro (facultatif)",
+ "unsaved": "Modifications non enregistrées",
+ "save": "Sauvegarder",
+ "saved": "Macro enregistrée",
+ "saveFailed": "Impossible d'enregistrer la macro",
+ "delete": "Supprimer",
+ "deleteTitle": "Supprimer cette macro ?",
+ "deleteBody": "\"{{name}}\" sera définitivement supprimé. Cette action est irréversible.",
+ "deleted": "Macro supprimée",
+ "cancel": "Annuler",
+ "run": "Courir",
+ "stop": "Arrêt",
+ "running": "En cours d'exécution...",
+ "completed": "Macro terminé",
+ "failed": "Échec de la macro",
+ "loadFailed": "Impossible de charger les macros",
+ "needTerminal": "Ouvrez et connectez d'abord un terminal.",
+ "noSteps": "Cette macro ne comporte pas encore d'étapes. Ajoutez-en une ci-dessous.",
+ "addStep": "Ajouter une étape",
+ "stepNumber": "Étape {{number}}",
+ "removeStep": "Supprimer l'étape",
+ "confirmRunTitle": "Exécuter cette macro ?",
+ "confirmRunBody": "\"{{name}}\" enverra des commandes à {{host}}.",
+ "confirmRunAction": "Exécuter une macro",
+ "stepTypes": {
+ "send": {
+ "label": "Envoyer la commande",
+ "help": "Saisit du texte dans le terminal, comme si vous l'aviez saisi manuellement."
+ },
+ "wait": {
+ "label": "Attendez la sortie",
+ "help": "Il y a une pause jusqu'à ce que le serveur affiche le texte que vous avez indiqué, comme une invite de mot de passe."
+ },
+ "delay": {
+ "label": "Pause",
+ "help": "Attendre un laps de temps fixe avant de passer à l'étape suivante."
+ },
+ "if": {
+ "label": "Si la sortie contient",
+ "help": "Vérifie tout ce que le serveur a imprimé jusqu'à présent, puis exécute l'une des deux branches."
+ },
+ "repeat": {
+ "label": "Répéter",
+ "help": "Exécute les étapes qu'il contient un nombre de fois défini."
+ }
+ },
+ "send": {
+ "text": "Commande ou texte",
+ "textPlaceholder": "systemctl redémarrer nginx",
+ "pressEnter": "Appuyez sur Entrée après l'envoi",
+ "pressEnterHelp": "Désactivez cette option pour saisir du texte sans le soumettre."
+ },
+ "wait": {
+ "pattern": "Attendez que la sortie contienne",
+ "patternPlaceholder": "mot de passe:",
+ "timeout": "Abandonner après",
+ "onTimeout": "S'il n'apparaît jamais",
+ "stop": "Arrêtez la macro",
+ "continue": "Continuez quand même"
+ },
+ "delay": {
+ "duration": "Attendre"
+ },
+ "ifStep": {
+ "pattern": "Si la sortie contient",
+ "patternPlaceholder": "Actif : actif (en cours d'exécution)",
+ "then": "Ensuite, faites ceci",
+ "else": "Sinon, faites ceci.",
+ "emptyBranch": "Pas d'étapes"
+ },
+ "repeat": {
+ "count": "Nombre de fois",
+ "times": "fois"
+ },
+ "match": {
+ "useRegex": "Correspondance avec une expression régulière",
+ "useRegexHelp": "Laissez cette section de côté pour que le texte corresponde exactement à ce qui est écrit.",
+ "invalidRegex": "Il ne s'agit pas d'une expression régulière valide ; elle sera interprétée comme du texte brut."
+ },
+ "seconds": "secondes",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/he_IL.json b/plugins/ssh-terminal/locales/translated/he_IL.json
index 70b13cda7..02aa4b14f 100644
--- a/plugins/ssh-terminal/locales/translated/he_IL.json
+++ b/plugins/ssh-terminal/locales/translated/he_IL.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "ערכות נושא שמורות",
"scrollbackBufferLabel": "מאגר גלילה לאחור",
"scrollbackMaxLines": "מספר השורות המרבי שנשמר בהיסטוריה",
- "startupSnippetLabel": "קטע הפעלה",
"sudoPasswordAutoFill": "מילוי אוטומטי של סיסמאות Sudo",
"sudoPasswordAutoFillDesc": "אחסן סיסמת sudo כדי שמדדי מארח, הנחיות מסוף ואחרים יוכלו להריץ פקודות בעלות הרשאות באופן אוטומטי.",
"sudoPasswordAutoFillLabel": "מילוי אוטומטי של סיסמת סודו",
@@ -335,7 +334,8 @@
"history": "הִיסטוֹרִיָה",
"localTerminal": "מסוף מקומי",
"sshTools": "כלי SSH",
- "terminal": "מָסוֹף"
+ "terminal": "מָסוֹף",
+ "macros": "פקודות מאקרו"
},
"sshTools": {
"broadcastInputPlaceholder": "הקלד כאן כדי לשדר הקשות מקלדת...",
@@ -363,5 +363,112 @@
"off": "כבוי",
"on": "עַל",
"title": "ברירות מחדל של חיבור"
+ },
+ "keybindings": {
+ "copy": "העתקת הבחירה",
+ "paste": "לְהַדבִּיק",
+ "sendControlCode": "שלח אות Ctrl+אות",
+ "sendText": "שלח טקסט",
+ "controlCodeLabel": "מִכְתָב",
+ "textLabel": "טקסט לשליחה",
+ "appendEnterLabel": "לחץ על Enter לאחר השליחה",
+ "clipboardPermissionNote": "ייתכן שתבקש הרשאה ללוח בעת השימוש הראשון בדפדפנים מסוימים.",
+ "textRequired": "הזן את הטקסט לשליחה",
+ "controlCodeRequired": "הזן אות אחת עבור קוד הבקרה"
+ },
+ "macros": {
+ "title": "פקודות מאקרו",
+ "description": "הפעל סדרה שמורה של פקודות בטרמינל, והגב למה שהשרת שולח בחזרה.",
+ "target": "יַעַד",
+ "noTerminal": "אין טרמינל פתוח",
+ "empty": "עדיין אין פקודות מאקרו",
+ "emptyHint": "צור מאקרו כדי להפוך זרימת מסוף לאוטומטית, כמו כניסה, הפעלה מחדש של שירות או מענה לבקשות.",
+ "create": "מאקרו חדש",
+ "back": "חזרה למאקרו",
+ "name": "שֵׁם",
+ "namePlaceholder": "הפעל מחדש את שרת האינטרנט",
+ "nameRequired": "תן שם למאקרו לפני השמירה",
+ "descriptionLabel": "תֵאוּר",
+ "descriptionPlaceholder": "מה המאקרו הזה עושה (אופציונלי)",
+ "unsaved": "שינויים שלא נשמרו",
+ "save": "לְהַצִיל",
+ "saved": "מאקרו נשמר",
+ "saveFailed": "לא ניתן היה לשמור את המאקרו",
+ "delete": "לִמְחוֹק",
+ "deleteTitle": "למחוק את המאקרו הזה?",
+ "deleteBody": "\"{{name}}\" יימחק לצמיתות. לא ניתן לבטל פעולה זו.",
+ "deleted": "מאקרו נמחק",
+ "cancel": "לְבַטֵל",
+ "run": "לָרוּץ",
+ "stop": "לְהַפְסִיק",
+ "running": "רִיצָה...",
+ "completed": "מאקרו הסתיים",
+ "failed": "מאקרו נכשל",
+ "loadFailed": "לא ניתן היה לטעון פקודות מאקרו",
+ "needTerminal": "פתח וחבר תחילה טרמינל",
+ "noSteps": "למאקרו הזה עדיין אין שלבים. הוסף אחד למטה.",
+ "addStep": "הוסף שלב",
+ "stepNumber": "שלב {{number}}",
+ "removeStep": "הסר שלב",
+ "confirmRunTitle": "להפעיל את המאקרו הזה?",
+ "confirmRunBody": "\"{{name}}\" ישלח פקודות אל {{host}}.",
+ "confirmRunAction": "הפעל מאקרו",
+ "stepTypes": {
+ "send": {
+ "label": "שלח פקודה",
+ "help": "מקליד טקסט לתוך הטרמינל, אותו הדבר כאילו הקלדת אותו."
+ },
+ "wait": {
+ "label": "המתן לפלט",
+ "help": "מושהה עד שהשרת ידפיס את הטקסט שציינת, כמו בקשת סיסמה."
+ },
+ "delay": {
+ "label": "הַפסָקָה",
+ "help": "ממתין פרק זמן קבוע לפני השלב הבא."
+ },
+ "if": {
+ "label": "אם הפלט מכיל",
+ "help": "בודק את כל מה שהשרת הדפיס עד כה, ולאחר מכן מפעיל אחד משני ענפים."
+ },
+ "repeat": {
+ "label": "לַחֲזוֹר עַל",
+ "help": "מריץ את השלבים בתוכו מספר קבוע של פעמים."
+ }
+ },
+ "send": {
+ "text": "פקודה או טקסט",
+ "textPlaceholder": "הפעל מחדש את nginx של systemctl",
+ "pressEnter": "לחץ על Enter לאחר השליחה",
+ "pressEnterHelp": "כבה אפשרות זו כדי להקליד טקסט מבלי לשלוח אותו."
+ },
+ "wait": {
+ "pattern": "המתן עד שהפלט יכיל",
+ "patternPlaceholder": "סִיסמָה:",
+ "timeout": "לוותר אחרי",
+ "onTimeout": "אם זה אף פעם לא מופיע",
+ "stop": "עצור את המאקרו",
+ "continue": "המשיכו בכל מקרה"
+ },
+ "delay": {
+ "duration": "חכה ל"
+ },
+ "ifStep": {
+ "pattern": "אם הפלט מכיל",
+ "patternPlaceholder": "פעיל: פעיל (רץ)",
+ "then": "אז תעשה את זה",
+ "else": "אחרת תעשו את זה",
+ "emptyBranch": "אין מדרגות"
+ },
+ "repeat": {
+ "count": "מספר פעמים",
+ "times": "פִּי"
+ },
+ "match": {
+ "useRegex": "התאמה עם ביטוי רגולרי",
+ "useRegexHelp": "השאר את זה כבוי כדי שהטקסט יתאים בדיוק כפי שנכתב.",
+ "invalidRegex": "ביטוי רגולרי לא תקין, הוא יתאים כטקסט רגיל"
+ },
+ "seconds": "שניות",
+ "secondsShort": "{{count}}ים"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/hi_IN.json b/plugins/ssh-terminal/locales/translated/hi_IN.json
index 447a7510c..a5054561c 100644
--- a/plugins/ssh-terminal/locales/translated/hi_IN.json
+++ b/plugins/ssh-terminal/locales/translated/hi_IN.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "सहेजे गए विषय",
"scrollbackBufferLabel": "स्क्रॉलबैक बफ़र",
"scrollbackMaxLines": "इतिहास में रखी जाने वाली लाइनों की अधिकतम संख्या",
- "startupSnippetLabel": "स्टार्टअप स्निपेट",
"sudoPasswordAutoFill": "sudo पासवर्ड ऑटो-फिल",
"sudoPasswordAutoFillDesc": "sudo पासवर्ड संग्रहीत करें ताकि होस्ट मेट्रिक्स, टर्मिनल संकेत और अन्य स्वचालित रूप से विशेषाधिकार प्राप्त कमांड चला सकें।",
"sudoPasswordAutoFillLabel": "sudo पासवर्ड स्वतः-भरण",
@@ -335,7 +334,8 @@
"history": "इतिहास",
"localTerminal": "स्थानीय टर्मिनल",
"sshTools": "SSH टूल्स",
- "terminal": "टर्मिनल"
+ "terminal": "टर्मिनल",
+ "macros": "मैक्रो"
},
"sshTools": {
"broadcastInputPlaceholder": "कीस्ट्रोक्स प्रसारित करने के लिए यहाँ टाइप करें...",
@@ -363,5 +363,112 @@
"off": "बंद",
"on": "पर",
"title": "कनेक्शन डिफ़ॉल्ट"
+ },
+ "keybindings": {
+ "copy": "चयन की प्रतिलिपि बनाएँ",
+ "paste": "पेस्ट करें",
+ "sendControlCode": "Ctrl+अक्षर सिग्नल भेजें",
+ "sendText": "संदेश भेजें",
+ "controlCodeLabel": "पत्र",
+ "textLabel": "भेजने के लिए संदेश",
+ "appendEnterLabel": "भेजने के बाद एंटर दबाएँ",
+ "clipboardPermissionNote": "कुछ ब्राउज़रों में पहली बार उपयोग करने पर क्लिपबोर्ड की अनुमति मांगी जा सकती है।",
+ "textRequired": "भेजने के लिए टेक्स्ट दर्ज करें",
+ "controlCodeRequired": "नियंत्रण कोड के लिए एक अक्षर दर्ज करें"
+ },
+ "macros": {
+ "title": "मैक्रो",
+ "description": "टर्मिनल में सहेजे गए कमांडों की एक श्रृंखला चलाएं, और सर्वर द्वारा वापस भेजे गए कमांडों पर प्रतिक्रिया दें।",
+ "target": "लक्ष्य",
+ "noTerminal": "कोई टर्मिनल खुला नहीं है",
+ "empty": "अभी तक कोई मैक्रो नहीं है",
+ "emptyHint": "लॉग इन करना, किसी सेवा को रीस्टार्ट करना या प्रॉम्प्ट का जवाब देना जैसे टर्मिनल कार्यों को स्वचालित करने के लिए एक मैक्रो बनाएं।",
+ "create": "नया मैक्रो",
+ "back": "मैक्रोज़ पर वापस जाएँ",
+ "name": "नाम",
+ "namePlaceholder": "वेब सर्वर को पुनः आरंभ करें",
+ "nameRequired": "मैक्रो को सेव करने से पहले उसे एक नाम दें।",
+ "descriptionLabel": "विवरण",
+ "descriptionPlaceholder": "यह मैक्रो क्या करता है (वैकल्पिक)",
+ "unsaved": "असहेजित परिवर्तन",
+ "save": "बचाना",
+ "saved": "मैक्रो सहेजा गया",
+ "saveFailed": "मैक्रो को सहेजा नहीं जा सका",
+ "delete": "मिटाना",
+ "deleteTitle": "क्या इस मैक्रो को हटा दें?",
+ "deleteBody": "\"{{name}}\" को स्थायी रूप से हटा दिया जाएगा। इसे पूर्ववत नहीं किया जा सकता है।",
+ "deleted": "मैक्रो हटा दिया गया",
+ "cancel": "रद्द करना",
+ "run": "दौड़ना",
+ "stop": "रुकना",
+ "running": "दौड़ना...",
+ "completed": "मैक्रो समाप्त हो गया",
+ "failed": "मैक्रो विफल रहा",
+ "loadFailed": "मैक्रो लोड नहीं हो सके",
+ "needTerminal": "सबसे पहले एक टर्मिनल खोलें और उससे कनेक्ट करें।",
+ "noSteps": "इस मैक्रो में अभी तक कोई चरण नहीं है। कृपया नीचे एक चरण जोड़ें।",
+ "addStep": "चरण जोड़ें",
+ "stepNumber": "चरण {{number}}",
+ "removeStep": "चरण हटाएँ",
+ "confirmRunTitle": "क्या आप इस मैक्रो को चलाना चाहते हैं?",
+ "confirmRunBody": "\"{{name}}\" {{host}} को कमांड भेजेगा।",
+ "confirmRunAction": "मैक्रो चलाएँ",
+ "stepTypes": {
+ "send": {
+ "label": "कमांड भेजें",
+ "help": "यह टर्मिनल में ठीक उसी तरह टेक्स्ट टाइप करता है जैसे आप उसे खुद टाइप करते हैं।"
+ },
+ "wait": {
+ "label": "आउटपुट की प्रतीक्षा करें",
+ "help": "यह तब तक रुका रहता है जब तक सर्वर आपके द्वारा बताए गए टेक्स्ट को प्रिंट नहीं कर देता, जैसे पासवर्ड प्रॉम्प्ट।"
+ },
+ "delay": {
+ "label": "विराम",
+ "help": "अगले चरण से पहले एक निश्चित समय तक प्रतीक्षा करता है।"
+ },
+ "if": {
+ "label": "यदि आउटपुट में शामिल है",
+ "help": "यह सर्वर द्वारा अब तक प्रिंट की गई हर चीज़ की जाँच करता है, फिर दो शाखाओं में से एक को चलाता है।"
+ },
+ "repeat": {
+ "label": "दोहराना",
+ "help": "इसके अंदर के चरणों को निर्धारित संख्या में बार-बार दोहराता है।"
+ }
+ },
+ "send": {
+ "text": "आदेश या पाठ",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "भेजने के बाद एंटर दबाएँ",
+ "pressEnterHelp": "इसे बंद करने पर आपको टेक्स्ट टाइप करने के बाद उसे सबमिट करने की आवश्यकता नहीं होगी।"
+ },
+ "wait": {
+ "pattern": "आउटपुट में यह जानकारी आने तक प्रतीक्षा करें",
+ "patternPlaceholder": "पासवर्ड:",
+ "timeout": "हार मान लो",
+ "onTimeout": "अगर यह कभी प्रकट नहीं होता",
+ "stop": "मैक्रो को रोकें",
+ "continue": "फिर भी आगे बढ़ते रहो।"
+ },
+ "delay": {
+ "duration": "के लिए प्रतीक्षा करें"
+ },
+ "ifStep": {
+ "pattern": "यदि आउटपुट में शामिल है",
+ "patternPlaceholder": "सक्रिय: सक्रिय (चल रहा)",
+ "then": "फिर ऐसा करें",
+ "else": "अन्यथा ऐसा करें",
+ "emptyBranch": "कोई कदम नहीं"
+ },
+ "repeat": {
+ "count": "कितनी बार",
+ "times": "टाइम्स"
+ },
+ "match": {
+ "useRegex": "नियमित अभिव्यक्ति के साथ मिलान करें",
+ "useRegexHelp": "इसे हटा दें ताकि यह लिखे गए पाठ से बिल्कुल मेल खाए।",
+ "invalidRegex": "यह एक मान्य रेगुलर एक्सप्रेशन नहीं है, इसे सादे टेक्स्ट के रूप में ही माना जाएगा।"
+ },
+ "seconds": "सेकंड",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/hu_HU.json b/plugins/ssh-terminal/locales/translated/hu_HU.json
index 43250f4ad..f4787cdef 100644
--- a/plugins/ssh-terminal/locales/translated/hu_HU.json
+++ b/plugins/ssh-terminal/locales/translated/hu_HU.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Mentett témák",
"scrollbackBufferLabel": "Görgetési puffer",
"scrollbackMaxLines": "A történetben tárolt sorok maximális száma",
- "startupSnippetLabel": "Indítási kódrészlet",
"sudoPasswordAutoFill": "Sudo jelszó automatikus kitöltése",
"sudoPasswordAutoFillDesc": "Tároljon egy sudo jelszót, hogy a gazdagép metrikái, a terminálpromptok és mások automatikusan futtathassanak privilegizált parancsokat.",
"sudoPasswordAutoFillLabel": "Sudo jelszó automatikus kitöltése",
@@ -335,7 +334,8 @@
"history": "Történelem",
"localTerminal": "Helyi terminál",
"sshTools": "SSH eszközök",
- "terminal": "Terminál"
+ "terminal": "Terminál",
+ "macros": "Makrók"
},
"sshTools": {
"broadcastInputPlaceholder": "Írjon ide a billentyűleütések küldéséhez...",
@@ -363,5 +363,112 @@
"off": "Le",
"on": "On",
"title": "Kapcsolat alapértelmezései"
+ },
+ "keybindings": {
+ "copy": "Kijelölés másolása",
+ "paste": "Paszta",
+ "sendControlCode": "Ctrl+betűjel küldése",
+ "sendText": "SMS küldése",
+ "controlCodeLabel": "Levél",
+ "textLabel": "Küldendő szöveg",
+ "appendEnterLabel": "Küldés után nyomja meg az Enter billentyűt",
+ "clipboardPermissionNote": "Egyes böngészőkben első használatkor vágólap-engedélyt kérhet.",
+ "textRequired": "Írd be a küldendő szöveget",
+ "controlCodeRequired": "Írjon be egyetlen betűt az ellenőrző kódhoz"
+ },
+ "macros": {
+ "title": "Makrók",
+ "description": "Futtass egy mentett parancssorozatot egy terminálban, és reagálj a szerver által küldöttekre.",
+ "target": "Cél",
+ "noTerminal": "Nincs nyitva terminál",
+ "empty": "Még nincsenek makrók",
+ "emptyHint": "Hozz létre egy makrót egy terminálfolyamat automatizálásához, például bejelentkezéshez, szolgáltatás újraindításához vagy a kérdések megválaszolásához.",
+ "create": "Új makró",
+ "back": "Vissza a makrókhoz",
+ "name": "Név",
+ "namePlaceholder": "Indítsa újra a webszervert",
+ "nameRequired": "Mentés előtt adjon nevet a makrónak",
+ "descriptionLabel": "Leírás",
+ "descriptionPlaceholder": "Mit csinál ez a makró (opcionális)",
+ "unsaved": "Nem mentett változtatások",
+ "save": "Megtakarítás",
+ "saved": "Makró mentve",
+ "saveFailed": "Nem sikerült menteni a makrót",
+ "delete": "Töröl",
+ "deleteTitle": "Törli ezt a makrót?",
+ "deleteBody": "A(z) „{{name}}” véglegesen törlésre kerül. Ez a művelet nem vonható vissza.",
+ "deleted": "Makró törölve",
+ "cancel": "Mégsem",
+ "run": "Fut",
+ "stop": "Stop",
+ "running": "Futás...",
+ "completed": "Makró kész",
+ "failed": "Makró sikertelen",
+ "loadFailed": "Nem sikerült betölteni a makrókat",
+ "needTerminal": "Először nyiss meg és csatlakoztass egy terminált",
+ "noSteps": "Ennek a makrónak még nincsenek lépései. Adjon hozzá egyet alább.",
+ "addStep": "Lépés hozzáadása",
+ "stepNumber": "{{number}} lépés",
+ "removeStep": "Lépés eltávolítása",
+ "confirmRunTitle": "Futtassa ezt a makrót?",
+ "confirmRunBody": "A „{{name}}” parancsokat küld a {{host}} címre.",
+ "confirmRunAction": "Makró futtatása",
+ "stepTypes": {
+ "send": {
+ "label": "Parancs küldése",
+ "help": "Beírja a szöveget a terminálba, ugyanúgy, mintha beírta volna."
+ },
+ "wait": {
+ "label": "Várjon a kimenetre",
+ "help": "Szünetet tart, amíg a szerver ki nem írja a megnevezett szöveget, például egy jelszókérést."
+ },
+ "delay": {
+ "label": "Szünet",
+ "help": "Egy meghatározott ideig vár a következő lépés előtt."
+ },
+ "if": {
+ "label": "Ha a kimenet tartalmaz",
+ "help": "Ellenőrzi a szerver által eddig kinyomtatott összes adatot, majd lefuttatja a két ág egyikét."
+ },
+ "repeat": {
+ "label": "Ismétlés",
+ "help": "Meghatározott számú alkalommal futtatja le a benne található lépéseket."
+ }
+ },
+ "send": {
+ "text": "Parancs vagy szöveg",
+ "textPlaceholder": "systemctl újraindítja az nginx-et",
+ "pressEnter": "Küldés után nyomja meg az Enter billentyűt",
+ "pressEnterHelp": "Kapcsold ki ezt, ha beküldés nélkül szeretnél szöveget beírni."
+ },
+ "wait": {
+ "pattern": "Várjon, amíg a kimenet tartalmazza a",
+ "patternPlaceholder": "jelszó:",
+ "timeout": "Add fel utána",
+ "onTimeout": "Ha soha nem jelenik meg",
+ "stop": "A makró leállítása",
+ "continue": "Folytasd mindenképpen"
+ },
+ "delay": {
+ "duration": "Várj"
+ },
+ "ifStep": {
+ "pattern": "Ha a kimenet tartalmaz",
+ "patternPlaceholder": "Aktív: aktív (fut)",
+ "then": "Akkor csináld ezt",
+ "else": "Egyébként tedd ezt",
+ "emptyBranch": "Nincsenek lépcsők"
+ },
+ "repeat": {
+ "count": "Alkalmak száma",
+ "times": "alkalommal"
+ },
+ "match": {
+ "useRegex": "Egyezés reguláris kifejezéssel",
+ "useRegexHelp": "Hagyd ezt ki, hogy a szöveg pontosan úgy illeszkedjen, ahogyan le van írva.",
+ "invalidRegex": "Nem érvényes reguláris kifejezés, sima szövegként lesz egyezve."
+ },
+ "seconds": "másodperc",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/id_ID.json b/plugins/ssh-terminal/locales/translated/id_ID.json
index 686db05c6..bba785c0d 100644
--- a/plugins/ssh-terminal/locales/translated/id_ID.json
+++ b/plugins/ssh-terminal/locales/translated/id_ID.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Tema Tersimpan",
"scrollbackBufferLabel": "Buffer Gulir Balik",
"scrollbackMaxLines": "Jumlah maksimum baris yang disimpan dalam riwayat",
- "startupSnippetLabel": "Cuplikan Startup",
"sudoPasswordAutoFill": "Isi Otomatis Kata Sandi Sudo",
"sudoPasswordAutoFillDesc": "Simpan kata sandi sudo agar metrik host, perintah terminal, dan lainnya dapat menjalankan perintah istimewa secara otomatis.",
"sudoPasswordAutoFillLabel": "Isi Otomatis Kata Sandi Sudo",
@@ -335,7 +334,8 @@
"history": "Sejarah",
"localTerminal": "Terminal Lokal",
"sshTools": "Alat SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makro"
},
"sshTools": {
"broadcastInputPlaceholder": "Ketik di sini untuk menyiarkan ketukan keyboard...",
@@ -363,5 +363,112 @@
"off": "Mati",
"on": "Pada",
"title": "Pengaturan Koneksi Default"
+ },
+ "keybindings": {
+ "copy": "Salin pilihan",
+ "paste": "Pasta",
+ "sendControlCode": "Kirim sinyal Ctrl+huruf",
+ "sendText": "Kirim pesan teks",
+ "controlCodeLabel": "Surat",
+ "textLabel": "Teks untuk dikirim",
+ "appendEnterLabel": "Tekan Enter setelah mengirim",
+ "clipboardPermissionNote": "Pada beberapa browser, mungkin akan meminta izin akses clipboard saat pertama kali digunakan.",
+ "textRequired": "Masukkan teks yang ingin dikirim",
+ "controlCodeRequired": "Masukkan satu huruf untuk kode kontrol."
+ },
+ "macros": {
+ "title": "Makro",
+ "description": "Jalankan serangkaian perintah yang tersimpan di terminal, dan tanggapi apa yang dikirimkan kembali oleh server.",
+ "target": "Target",
+ "noTerminal": "Tidak ada terminal yang terbuka",
+ "empty": "Belum ada makro",
+ "emptyHint": "Buat makro untuk mengotomatiskan alur terminal, seperti masuk, memulai ulang layanan, atau menjawab perintah.",
+ "create": "Makro baru",
+ "back": "Kembali ke makro",
+ "name": "Nama",
+ "namePlaceholder": "Mulai ulang server web",
+ "nameRequired": "Beri nama makro sebelum menyimpannya.",
+ "descriptionLabel": "Keterangan",
+ "descriptionPlaceholder": "Fungsi makro ini (opsional)",
+ "unsaved": "Perubahan yang belum disimpan",
+ "save": "Menyimpan",
+ "saved": "Makro tersimpan",
+ "saveFailed": "Makro tidak dapat disimpan.",
+ "delete": "Menghapus",
+ "deleteTitle": "Hapus makro ini?",
+ "deleteBody": "\"{{name}}\" akan dihapus secara permanen. Ini tidak dapat dibatalkan.",
+ "deleted": "Makro dihapus",
+ "cancel": "Membatalkan",
+ "run": "Berlari",
+ "stop": "Berhenti",
+ "running": "Berlari...",
+ "completed": "Makro selesai",
+ "failed": "Makro gagal",
+ "loadFailed": "Makro tidak dapat dimuat",
+ "needTerminal": "Buka dan sambungkan terminal terlebih dahulu.",
+ "noSteps": "Makro ini belum memiliki langkah. Tambahkan langkah di bawah ini.",
+ "addStep": "Tambahkan langkah",
+ "stepNumber": "Langkah {{number}}",
+ "removeStep": "Hapus langkah",
+ "confirmRunTitle": "Jalankan makro ini?",
+ "confirmRunBody": "\"{{name}}\" akan mengirimkan perintah ke {{host}}.",
+ "confirmRunAction": "Jalankan makro",
+ "stepTypes": {
+ "send": {
+ "label": "Kirim perintah",
+ "help": "Mengetik teks ke terminal, sama seperti jika Anda mengetiknya."
+ },
+ "wait": {
+ "label": "Tunggu hingga muncul output.",
+ "help": "Akan berhenti sejenak hingga server menampilkan teks yang Anda sebutkan, seperti saat meminta kata sandi."
+ },
+ "delay": {
+ "label": "Berhenti sebentar",
+ "help": "Menunggu dalam jangka waktu tertentu sebelum melanjutkan ke langkah berikutnya."
+ },
+ "if": {
+ "label": "Jika output berisi",
+ "help": "Memeriksa semua yang telah dicetak server sejauh ini, lalu menjalankan salah satu dari dua cabang."
+ },
+ "repeat": {
+ "label": "Mengulang",
+ "help": "Menjalankan langkah-langkah di dalamnya sebanyak jumlah kali yang telah ditentukan."
+ }
+ },
+ "send": {
+ "text": "Perintah atau teks",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Tekan Enter setelah mengirim",
+ "pressEnterHelp": "Matikan opsi ini untuk mengetik teks tanpa mengirimkannya."
+ },
+ "wait": {
+ "pattern": "Tunggu hingga output berisi",
+ "patternPlaceholder": "kata sandi:",
+ "timeout": "Menyerah setelah",
+ "onTimeout": "Jika itu tidak pernah muncul",
+ "stop": "Hentikan makro",
+ "continue": "Tetaplah melanjutkan"
+ },
+ "delay": {
+ "duration": "Tunggu"
+ },
+ "ifStep": {
+ "pattern": "Jika outputnya berisi",
+ "patternPlaceholder": "Aktif: aktif (berjalan)",
+ "then": "Kemudian lakukan ini",
+ "else": "Jika tidak, lakukan ini",
+ "emptyBranch": "Tidak ada tangga"
+ },
+ "repeat": {
+ "count": "Jumlah kali",
+ "times": "waktu"
+ },
+ "match": {
+ "useRegex": "Cocokkan dengan ekspresi reguler",
+ "useRegexHelp": "Hilangkan bagian ini agar sesuai persis dengan teks yang tertulis.",
+ "invalidRegex": "Bukan ekspresi reguler yang valid, akan dicocokkan sebagai teks biasa."
+ },
+ "seconds": "detik",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/it_IT.json b/plugins/ssh-terminal/locales/translated/it_IT.json
index a8505ab96..09bb8c4f7 100644
--- a/plugins/ssh-terminal/locales/translated/it_IT.json
+++ b/plugins/ssh-terminal/locales/translated/it_IT.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temi salvati",
"scrollbackBufferLabel": "Buffer di scorrimento",
"scrollbackMaxLines": "Numero massimo di righe conservate nella cronologia",
- "startupSnippetLabel": "Frammento di avvio",
"sudoPasswordAutoFill": "Compilazione automatica password sudo",
"sudoPasswordAutoFillDesc": "Memorizza una password sudo in modo che le metriche dell'host, i prompt del terminale e altro possano eseguire automaticamente comandi privilegiati.",
"sudoPasswordAutoFillLabel": "Compilazione automatica password sudo",
@@ -335,7 +334,8 @@
"history": "Cronologia",
"localTerminal": "Terminale locale",
"sshTools": "Strumenti SSH",
- "terminal": "Terminale"
+ "terminal": "Terminale",
+ "macros": "Macro"
},
"sshTools": {
"broadcastInputPlaceholder": "Digita qui per trasmettere le pressioni dei tasti...",
@@ -363,5 +363,112 @@
"off": "Spento",
"on": "SU",
"title": "Impostazioni predefinite di connessione"
+ },
+ "keybindings": {
+ "copy": "Copia selezione",
+ "paste": "Impasto",
+ "sendControlCode": "Invia il segnale Ctrl+lettera",
+ "sendText": "Invia testo",
+ "controlCodeLabel": "Lettera",
+ "textLabel": "Testo da inviare",
+ "appendEnterLabel": "Premi Invio dopo aver inviato",
+ "clipboardPermissionNote": "In alcuni browser potrebbe essere richiesta l'autorizzazione per l'apposizione degli appunti al primo utilizzo.",
+ "textRequired": "Inserisci il testo da inviare",
+ "controlCodeRequired": "Inserisci una sola lettera come codice di controllo"
+ },
+ "macros": {
+ "title": "Macro",
+ "description": "Esegui una serie di comandi salvati in un terminale e reagisci a ciò che il server ti invia in risposta.",
+ "target": "Bersaglio",
+ "noTerminal": "Nessun terminale aperto",
+ "empty": "Ancora nessuna macro",
+ "emptyHint": "Crea una macro per automatizzare un flusso di lavoro da terminale, come l'accesso, il riavvio di un servizio o la risposta a richieste.",
+ "create": "Nuova macro",
+ "back": "Torna alle macro",
+ "name": "Nome",
+ "namePlaceholder": "Riavvia il server web",
+ "nameRequired": "Assegna un nome alla macro prima di salvarla.",
+ "descriptionLabel": "Descrizione",
+ "descriptionPlaceholder": "Cosa fa questa macro (facoltativo)",
+ "unsaved": "Modifiche non salvate",
+ "save": "Salva",
+ "saved": "Macro salvata",
+ "saveFailed": "Impossibile salvare la macro",
+ "delete": "Eliminare",
+ "deleteTitle": "Eliminare questa macro?",
+ "deleteBody": "\"{{name}}\" verrà eliminato definitivamente. Questa operazione non può essere annullata.",
+ "deleted": "Macro eliminato",
+ "cancel": "Cancellare",
+ "run": "Correre",
+ "stop": "Fermare",
+ "running": "Corsa...",
+ "completed": "Macro terminato",
+ "failed": "Il fallimento di Macro",
+ "loadFailed": "Impossibile caricare le macro",
+ "needTerminal": "Aprire e collegare prima un terminale",
+ "noSteps": "Questa macro non ha ancora passaggi. Aggiungine uno qui sotto.",
+ "addStep": "Aggiungi passaggio",
+ "stepNumber": "Passo {{number}}",
+ "removeStep": "Rimuovere il passaggio",
+ "confirmRunTitle": "Eseguire questa macro?",
+ "confirmRunBody": "\"{{name}}\" invierà comandi a {{host}}.",
+ "confirmRunAction": "Esegui macro",
+ "stepTypes": {
+ "send": {
+ "label": "inviare comando",
+ "help": "Digita il testo nel terminale, proprio come se lo avessi digitato tu."
+ },
+ "wait": {
+ "label": "Attendi l'output",
+ "help": "Si mette in pausa finché il server non stampa il testo che hai specificato, ad esempio una richiesta di password."
+ },
+ "delay": {
+ "label": "Pausa",
+ "help": "Attende un intervallo di tempo fisso prima di procedere al passo successivo."
+ },
+ "if": {
+ "label": "Se l'output contiene",
+ "help": "Controlla tutto ciò che il server ha stampato finora, quindi esegue uno dei due rami."
+ },
+ "repeat": {
+ "label": "Ripetere",
+ "help": "Esegue i passaggi al suo interno un numero prestabilito di volte."
+ }
+ },
+ "send": {
+ "text": "Comando o testo",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Premi Invio dopo aver inviato",
+ "pressEnterHelp": "Disattiva questa opzione per digitare del testo senza inviarlo."
+ },
+ "wait": {
+ "pattern": "Attendi che l'output contenga",
+ "patternPlaceholder": "password:",
+ "timeout": "Rinunciare dopo",
+ "onTimeout": "Se non appare mai",
+ "stop": "Interrompi la macro",
+ "continue": "Continua comunque"
+ },
+ "delay": {
+ "duration": "Attendi"
+ },
+ "ifStep": {
+ "pattern": "Se l'output contiene",
+ "patternPlaceholder": "Attivo: attivo (in corsa)",
+ "then": "Poi fai questo",
+ "else": "Altrimenti fai così",
+ "emptyBranch": "Nessun gradino"
+ },
+ "repeat": {
+ "count": "Numero di volte",
+ "times": "volte"
+ },
+ "match": {
+ "useRegex": "Corrispondenza con un'espressione regolare",
+ "useRegexHelp": "Lasciare vuoto questo elemento per far sì che il testo corrisponda esattamente a come è stato scritto.",
+ "invalidRegex": "Non è un'espressione regolare valida, verrà interpretata come testo semplice."
+ },
+ "seconds": "secondi",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ja_JP.json b/plugins/ssh-terminal/locales/translated/ja_JP.json
index 29e7a7f48..227500592 100644
--- a/plugins/ssh-terminal/locales/translated/ja_JP.json
+++ b/plugins/ssh-terminal/locales/translated/ja_JP.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "保存済みテーマ",
"scrollbackBufferLabel": "スクロールバッファ",
"scrollbackMaxLines": "履歴に保持する最大行数",
- "startupSnippetLabel": "起動時スニペット",
"sudoPasswordAutoFill": "Sudo パスワードの自動入力",
"sudoPasswordAutoFillDesc": "sudo パスワードを保存して、ホストメトリクス、ターミナルプロンプトなどが自動的に特権コマンドを実行できるようにします",
"sudoPasswordAutoFillLabel": "sudo パスワードの自動入力",
@@ -335,7 +334,8 @@
"history": "履歴",
"localTerminal": "ローカル端末",
"sshTools": "SSHツール",
- "terminal": "ターミナル"
+ "terminal": "ターミナル",
+ "macros": "マクロ"
},
"sshTools": {
"broadcastInputPlaceholder": "ここに入力してキーをブロードキャスト...",
@@ -363,5 +363,112 @@
"off": "オフ",
"on": "の上",
"title": "接続のデフォルト設定"
+ },
+ "keybindings": {
+ "copy": "選択範囲をコピー",
+ "paste": "ペースト",
+ "sendControlCode": "Ctrlキーと文字キーを同時に押して信号を送る",
+ "sendText": "テキストを送信",
+ "controlCodeLabel": "手紙",
+ "textLabel": "送信するテキスト",
+ "appendEnterLabel": "送信後にEnterキーを押してください。",
+ "clipboardPermissionNote": "一部のブラウザでは、初回使用時にクリップボードのアクセス許可を求められる場合があります。",
+ "textRequired": "送信するテキストを入力してください",
+ "controlCodeRequired": "制御コードとして1文字を入力してください"
+ },
+ "macros": {
+ "title": "マクロ",
+ "description": "ターミナルで保存済みのコマンド系列を実行し、サーバーからの応答に応じて処理を行います。",
+ "target": "ターゲット",
+ "noTerminal": "端末が開いていません",
+ "empty": "マクロはまだありません",
+ "emptyHint": "ログイン、サービスの再起動、プロンプトへの応答など、ターミナル操作の流れを自動化するマクロを作成します。",
+ "create": "新しいマクロ",
+ "back": "マクロに戻る",
+ "name": "名前",
+ "namePlaceholder": "ウェブサーバーを再起動します",
+ "nameRequired": "保存する前にマクロに名前を付けてください。",
+ "descriptionLabel": "説明",
+ "descriptionPlaceholder": "このマクロの機能(オプション)",
+ "unsaved": "保存されていない変更",
+ "save": "保存",
+ "saved": "マクロが保存されました",
+ "saveFailed": "マクロを保存できませんでした",
+ "delete": "消去",
+ "deleteTitle": "このマクロを削除しますか?",
+ "deleteBody": "\"{{name}}\" は完全に削除されます。元に戻すことはできません。",
+ "deleted": "マクロが削除されました",
+ "cancel": "キャンセル",
+ "run": "走る",
+ "stop": "停止",
+ "running": "実行中...",
+ "completed": "マクロ撮影終了",
+ "failed": "マクロが失敗しました",
+ "loadFailed": "マクロを読み込めませんでした",
+ "needTerminal": "まずターミナルを開いて接続します。",
+ "noSteps": "このマクロにはまだ手順がありません。以下に手順を追加してください。",
+ "addStep": "ステップを追加する",
+ "stepNumber": "ステップ {{number}}",
+ "removeStep": "削除手順",
+ "confirmRunTitle": "このマクロを実行しますか?",
+ "confirmRunBody": "\"{{name}}\" は {{host}} にコマンドを送信します。",
+ "confirmRunAction": "マクロを実行する",
+ "stepTypes": {
+ "send": {
+ "label": "コマンドを送信",
+ "help": "端末にテキストを入力します。まるで手入力したかのように表示されます。"
+ },
+ "wait": {
+ "label": "出力を待つ",
+ "help": "サーバーが指定したテキスト(パスワード入力画面など)を表示するまで一時停止します。"
+ },
+ "delay": {
+ "label": "一時停止",
+ "help": "次のステップに進む前に、一定時間待機します。"
+ },
+ "if": {
+ "label": "出力に以下が含まれる場合",
+ "help": "サーバーがこれまでに出力したすべての内容を確認し、その後、2つの分岐のうちいずれか1つを実行します。"
+ },
+ "repeat": {
+ "label": "繰り返す",
+ "help": "指定された回数だけ、その中の手順を実行します。"
+ }
+ },
+ "send": {
+ "text": "コマンドまたはテキスト",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "送信後にEnterキーを押してください。",
+ "pressEnterHelp": "テキストを送信せずに入力する場合は、これをオフにしてください。"
+ },
+ "wait": {
+ "pattern": "出力に以下の内容が含まれるまで待機してください",
+ "patternPlaceholder": "パスワード:",
+ "timeout": "諦めた後",
+ "onTimeout": "それが決して現れない場合",
+ "stop": "マクロを停止する",
+ "continue": "とにかく続けて"
+ },
+ "delay": {
+ "duration": "待って"
+ },
+ "ifStep": {
+ "pattern": "出力に以下が含まれる場合",
+ "patternPlaceholder": "アクティブ:アクティブ(実行中)",
+ "then": "次にこれを実行してください",
+ "else": "そうでなければ、これを実行してください。",
+ "emptyBranch": "階段なし"
+ },
+ "repeat": {
+ "count": "回数",
+ "times": "タイムズ"
+ },
+ "match": {
+ "useRegex": "正規表現で一致させる",
+ "useRegexHelp": "原文と完全に一致させるには、この部分を削除してください。",
+ "invalidRegex": "有効な正規表現ではないため、プレーンテキストとしてマッチします。"
+ },
+ "seconds": "秒",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ko_KR.json b/plugins/ssh-terminal/locales/translated/ko_KR.json
index b1e3ec508..480f52609 100644
--- a/plugins/ssh-terminal/locales/translated/ko_KR.json
+++ b/plugins/ssh-terminal/locales/translated/ko_KR.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "저장된 테마",
"scrollbackBufferLabel": "스크롤백 버퍼",
"scrollbackMaxLines": "기록에 유지할 최대 라인 수",
- "startupSnippetLabel": "시작 스니펫",
"sudoPasswordAutoFill": "Sudo 비밀번호 자동 완성",
"sudoPasswordAutoFillDesc": "sudo 비밀번호를 저장하여 호스트 메트릭, 터미널 프롬프트 등이 자동으로 권한 있는 명령을 실행할 수 있도록 합니다",
"sudoPasswordAutoFillLabel": "Sudo 비밀번호 자동 입력",
@@ -335,7 +334,8 @@
"history": "기록",
"localTerminal": "로컬 터미널",
"sshTools": "SSH 도구",
- "terminal": "터미널"
+ "terminal": "터미널",
+ "macros": "매크로"
},
"sshTools": {
"broadcastInputPlaceholder": "키 입력을 브로드캐스트하려면 여기에 입력...",
@@ -363,5 +363,112 @@
"off": "끄다",
"on": "~에",
"title": "연결 기본 설정"
+ },
+ "keybindings": {
+ "copy": "선택 복사",
+ "paste": "반죽",
+ "sendControlCode": "Ctrl+문자 신호를 보내세요",
+ "sendText": "문자 보내기",
+ "controlCodeLabel": "편지",
+ "textLabel": "보낼 텍스트",
+ "appendEnterLabel": "전송 후 Enter 키를 누르세요.",
+ "clipboardPermissionNote": "일부 브라우저에서는 처음 사용 시 클립보드 권한을 요청할 수 있습니다.",
+ "textRequired": "보낼 텍스트를 입력하세요",
+ "controlCodeRequired": "제어 코드에 한 글자를 입력하세요."
+ },
+ "macros": {
+ "title": "매크로",
+ "description": "터미널에서 저장된 일련의 명령어를 실행하고, 서버가 보내는 응답에 반응합니다.",
+ "target": "목표",
+ "noTerminal": "터미널이 열려 있지 않습니다",
+ "empty": "아직 매크로가 없습니다.",
+ "emptyHint": "터미널 로그인, 서비스 재시작 또는 프롬프트 응답과 같은 터미널 작업 흐름을 자동화하는 매크로를 만드세요.",
+ "create": "새로운 매크로",
+ "back": "매크로로 돌아가기",
+ "name": "이름",
+ "namePlaceholder": "웹 서버를 재시작하세요",
+ "nameRequired": "매크로를 저장하기 전에 이름을 지정하세요.",
+ "descriptionLabel": "설명",
+ "descriptionPlaceholder": "이 매크로의 기능 (선택 사항)",
+ "unsaved": "저장되지 않은 변경 사항",
+ "save": "구하다",
+ "saved": "매크로 저장됨",
+ "saveFailed": "매크로를 저장할 수 없습니다.",
+ "delete": "삭제",
+ "deleteTitle": "이 매크로를 삭제하시겠습니까?",
+ "deleteBody": "\"{{name}}\"는 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다.",
+ "deleted": "매크로 삭제됨",
+ "cancel": "취소",
+ "run": "달리다",
+ "stop": "멈추다",
+ "running": "달리기...",
+ "completed": "매크로 완료",
+ "failed": "매크로 실패",
+ "loadFailed": "매크로를 불러올 수 없습니다.",
+ "needTerminal": "먼저 터미널을 열고 연결하세요.",
+ "noSteps": "이 매크로는 아직 단계가 없습니다. 아래에 단계를 추가하세요.",
+ "addStep": "단계 추가",
+ "stepNumber": "단계 {{number}}",
+ "removeStep": "제거 단계",
+ "confirmRunTitle": "이 매크로를 실행하시겠습니까?",
+ "confirmRunBody": "\"{{name}}\"는 {{host}}로 명령을 보냅니다.",
+ "confirmRunAction": "매크로 실행",
+ "stepTypes": {
+ "send": {
+ "label": "명령을 보내세요",
+ "help": "터미널에 텍스트를 입력합니다. 마치 직접 타이핑하는 것과 같습니다."
+ },
+ "wait": {
+ "label": "출력이 나올 때까지 기다리세요",
+ "help": "서버가 사용자가 입력한 텍스트(예: 비밀번호 입력창)를 출력할 때까지 일시 중지됩니다."
+ },
+ "delay": {
+ "label": "정지시키다",
+ "help": "다음 단계로 넘어가기 전에 일정 시간 동안 기다립니다."
+ },
+ "if": {
+ "label": "출력에 다음 내용이 포함되어 있는 경우",
+ "help": "서버에서 지금까지 출력한 모든 내용을 확인한 후 두 가지 분기 중 하나를 실행합니다."
+ },
+ "repeat": {
+ "label": "반복하다",
+ "help": "그 안에 있는 단계들을 정해진 횟수만큼 실행합니다."
+ }
+ },
+ "send": {
+ "text": "명령 또는 텍스트",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "전송 후 Enter 키를 누르세요.",
+ "pressEnterHelp": "텍스트를 입력하되 제출하지 않으려면 이 기능을 끄세요."
+ },
+ "wait": {
+ "pattern": "출력 내용이 포함될 때까지 기다리세요",
+ "patternPlaceholder": "비밀번호:",
+ "timeout": "포기한 후에",
+ "onTimeout": "만약 전혀 나타나지 않는다면",
+ "stop": "매크로를 중지하세요",
+ "continue": "어쨌든 계속 가세요"
+ },
+ "delay": {
+ "duration": "기다리세요"
+ },
+ "ifStep": {
+ "pattern": "출력에 다음 내용이 포함되어 있는 경우",
+ "patternPlaceholder": "활성: 활성(실행 중)",
+ "then": "그럼 이렇게 하세요",
+ "else": "그렇지 않으면 이렇게 하세요.",
+ "emptyBranch": "단계 없음"
+ },
+ "repeat": {
+ "count": "횟수",
+ "times": "타임스"
+ },
+ "match": {
+ "useRegex": "정규 표현식과 일치",
+ "useRegexHelp": "원문과 정확히 일치하려면 이 부분을 삭제하세요.",
+ "invalidRegex": "유효한 정규 표현식이 아니므로 일반 텍스트로 처리됩니다."
+ },
+ "seconds": "초",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/nl_NL.json b/plugins/ssh-terminal/locales/translated/nl_NL.json
index 2f743bd32..b83d0c1bf 100644
--- a/plugins/ssh-terminal/locales/translated/nl_NL.json
+++ b/plugins/ssh-terminal/locales/translated/nl_NL.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Opgeslagen thema's",
"scrollbackBufferLabel": "Terugscrollbuffer",
"scrollbackMaxLines": "Maximaal aantal regels dat in de geschiedenis wordt bewaard",
- "startupSnippetLabel": "Opstartfragment",
"sudoPasswordAutoFill": "Sudo-wachtwoord automatisch invullen",
"sudoPasswordAutoFillDesc": "Sla een sudo-wachtwoord op, zodat hoststatistieken, terminalprompts en andere programma's automatisch geprivilegieerde opdrachten kunnen uitvoeren.",
"sudoPasswordAutoFillLabel": "Sudo-wachtwoord automatisch invullen",
@@ -335,7 +334,8 @@
"history": "Geschiedenis",
"localTerminal": "Lokale terminal",
"sshTools": "SSH-tools",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macro's"
},
"sshTools": {
"broadcastInputPlaceholder": "Typ hier om toetsaanslagen te verzenden...",
@@ -363,5 +363,112 @@
"off": "Uit",
"on": "Op",
"title": "Verbindingsstandaarden"
+ },
+ "keybindings": {
+ "copy": "Selectie kopiëren",
+ "paste": "Plakken",
+ "sendControlCode": "Verzend het Ctrl+letter-signaal",
+ "sendText": "Sms verzenden",
+ "controlCodeLabel": "Brief",
+ "textLabel": "Tekst om te verzenden",
+ "appendEnterLabel": "Druk op Enter na het verzenden.",
+ "clipboardPermissionNote": "In sommige browsers kan bij het eerste gebruik om toestemming voor het gebruik van het klembord worden gevraagd.",
+ "textRequired": "Voer de tekst in die u wilt verzenden",
+ "controlCodeRequired": "Voer één letter in als besturingscode."
+ },
+ "macros": {
+ "title": "Macro's",
+ "description": "Voer een opgeslagen reeks commando's uit in een terminal en reageer op wat de server terugstuurt.",
+ "target": "Doel",
+ "noTerminal": "Geen terminal geopend",
+ "empty": "Nog geen macro's",
+ "emptyHint": "Maak een macro om een terminalproces te automatiseren, zoals inloggen, een service herstarten of prompts beantwoorden.",
+ "create": "Nieuwe macro",
+ "back": "Terug naar macro's",
+ "name": "Naam",
+ "namePlaceholder": "Webserver opnieuw opstarten",
+ "nameRequired": "Geef de macro een naam voordat je hem opslaat.",
+ "descriptionLabel": "Beschrijving",
+ "descriptionPlaceholder": "Wat deze macro doet (optioneel)",
+ "unsaved": "Niet-opgeslagen wijzigingen",
+ "save": "Redden",
+ "saved": "Macro opgeslagen",
+ "saveFailed": "Het lukte niet om de macro op te slaan.",
+ "delete": "Verwijderen",
+ "deleteTitle": "Deze macro verwijderen?",
+ "deleteBody": "\"{{name}}\" wordt permanent verwijderd. Dit kan niet ongedaan gemaakt worden.",
+ "deleted": "Macro verwijderd",
+ "cancel": "Annuleren",
+ "run": "Loop",
+ "stop": "Stop",
+ "running": "Rennen...",
+ "completed": "Macro voltooid",
+ "failed": "Macro mislukt",
+ "loadFailed": "Macro's konden niet worden geladen.",
+ "needTerminal": "Open en verbind eerst een terminal.",
+ "noSteps": "Deze macro bevat nog geen stappen. Voeg er hieronder een toe.",
+ "addStep": "Voeg stap toe",
+ "stepNumber": "Stap {{number}}",
+ "removeStep": "Verwijder stap",
+ "confirmRunTitle": "Voer deze macro uit?",
+ "confirmRunBody": "\"{{name}}\" zal commando's naar {{host}} sturen.",
+ "confirmRunAction": "Macro uitvoeren",
+ "stepTypes": {
+ "send": {
+ "label": "Verzend commando",
+ "help": "Typt tekst in de terminal, net alsof je die zelf hebt ingetypt."
+ },
+ "wait": {
+ "label": "Wacht op uitvoer",
+ "help": "Het programma pauzeert totdat de server de door u opgegeven tekst afdrukt, zoals bij een wachtwoordprompt."
+ },
+ "delay": {
+ "label": "Pauze",
+ "help": "Wacht een vaste tijdsduur voordat de volgende stap wordt uitgevoerd."
+ },
+ "if": {
+ "label": "Als de uitvoer het volgende bevat:",
+ "help": "Controleert alles wat de server tot nu toe heeft afgedrukt en voert vervolgens een van de twee vertakkingen uit."
+ },
+ "repeat": {
+ "label": "Herhalen",
+ "help": "Voert de stappen binnenin een vast aantal keren uit."
+ }
+ },
+ "send": {
+ "text": "Opdracht of tekst",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Druk op Enter na het verzenden.",
+ "pressEnterHelp": "Schakel deze optie uit om tekst te typen zonder deze te verzenden."
+ },
+ "wait": {
+ "pattern": "Wacht tot de uitvoer het volgende bevat:",
+ "patternPlaceholder": "wachtwoord:",
+ "timeout": "Geef het op na",
+ "onTimeout": "Als het nooit verschijnt",
+ "stop": "Stop de macro",
+ "continue": "Ga toch door."
+ },
+ "delay": {
+ "duration": "Wachten op"
+ },
+ "ifStep": {
+ "pattern": "Als de uitvoer het volgende bevat:",
+ "patternPlaceholder": "Actief: actief (lopend)",
+ "then": "Doe dan dit",
+ "else": "Doe anders dit",
+ "emptyBranch": "Geen treden"
+ },
+ "repeat": {
+ "count": "Aantal keren",
+ "times": "tijden"
+ },
+ "match": {
+ "useRegex": "Overeenkomst met een reguliere expressie",
+ "useRegexHelp": "Laat dit weg zodat de tekst exact overeenkomt met de geschreven tekst.",
+ "invalidRegex": "Dit is geen geldige reguliere expressie; het wordt als platte tekst herkend."
+ },
+ "seconds": "seconden",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/no_NO.json b/plugins/ssh-terminal/locales/translated/no_NO.json
index d4dac7d79..0d5baf002 100644
--- a/plugins/ssh-terminal/locales/translated/no_NO.json
+++ b/plugins/ssh-terminal/locales/translated/no_NO.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Lagrede temaer",
"scrollbackBufferLabel": "Tilbakespolingsbuffer",
"scrollbackMaxLines": "Maksimalt antall linjer lagret i historikken",
- "startupSnippetLabel": "Oppstartsutdrag",
"sudoPasswordAutoFill": "Sudo-passord automatisk utfylling",
"sudoPasswordAutoFillDesc": "Lagre et sudo-passord slik at vertsmålinger, terminalledetekster og andre kan kjøre privilegerte kommandoer automatisk.",
"sudoPasswordAutoFillLabel": "Sudo-passord automatisk utfylling",
@@ -335,7 +334,8 @@
"history": "Historie",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-verktøy",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makroer"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv her for å kringkaste tastetrykk...",
@@ -363,5 +363,112 @@
"off": "Av",
"on": "På",
"title": "Standardinnstillinger for tilkobling"
+ },
+ "keybindings": {
+ "copy": "Kopier valg",
+ "paste": "Lim inn",
+ "sendControlCode": "Send Ctrl+bokstavsignal",
+ "sendText": "Send tekstmelding",
+ "controlCodeLabel": "Brev",
+ "textLabel": "Tekst som skal sendes",
+ "appendEnterLabel": "Trykk Enter etter sending",
+ "clipboardPermissionNote": "Kan be om tillatelse til utklippstavlen ved første gangs bruk i noen nettlesere.",
+ "textRequired": "Skriv inn teksten som skal sendes",
+ "controlCodeRequired": "Skriv inn én bokstav for kontrollkoden"
+ },
+ "macros": {
+ "title": "Makroer",
+ "description": "Kjør en lagret serie med kommandoer i en terminal, og reager på det serveren sender tilbake.",
+ "target": "Mål",
+ "noTerminal": "Ingen terminal åpen",
+ "empty": "Ingen makroer ennå",
+ "emptyHint": "Opprett en makro for å automatisere en terminalflyt, som å logge inn, starte en tjeneste på nytt eller svare på forespørsler.",
+ "create": "Ny makro",
+ "back": "Tilbake til makroer",
+ "name": "Navn",
+ "namePlaceholder": "Start nettserveren på nytt",
+ "nameRequired": "Gi makroen et navn før du lagrer",
+ "descriptionLabel": "Beskrivelse",
+ "descriptionPlaceholder": "Hva denne makroen gjør (valgfritt)",
+ "unsaved": "Ulagrede endringer",
+ "save": "Spare",
+ "saved": "Makro lagret",
+ "saveFailed": "Kunne ikke lagre makroen",
+ "delete": "Slett",
+ "deleteTitle": "Slette denne makroen?",
+ "deleteBody": "«{{name}}» vil bli slettet permanent. Dette kan ikke angres.",
+ "deleted": "Makro slettet",
+ "cancel": "Kansellere",
+ "run": "Løp",
+ "stop": "Stoppe",
+ "running": "Løping...",
+ "completed": "Makro ferdig",
+ "failed": "Makroen mislyktes",
+ "loadFailed": "Kunne ikke laste inn makroer",
+ "needTerminal": "Åpne og koble til en terminal først",
+ "noSteps": "Denne makroen har ingen trinn ennå. Legg til et nedenfor.",
+ "addStep": "Legg til trinn",
+ "stepNumber": "Trinn {{number}}",
+ "removeStep": "Fjern trinn",
+ "confirmRunTitle": "Kjøre denne makroen?",
+ "confirmRunBody": "\"{{name}}\" vil sende kommandoer til {{host}}.",
+ "confirmRunAction": "Kjør makro",
+ "stepTypes": {
+ "send": {
+ "label": "Send kommando",
+ "help": "Skriver tekst inn i terminalen, på samme måte som om du hadde skrevet den."
+ },
+ "wait": {
+ "label": "Vent på utdata",
+ "help": "Pauserer til serveren skriver ut teksten du oppgir, som en passordforespørsel."
+ },
+ "delay": {
+ "label": "Pause",
+ "help": "Venter en fast tid før neste trinn."
+ },
+ "if": {
+ "label": "Hvis utdataene inneholder",
+ "help": "Sjekker alt serveren har skrevet ut så langt, og kjører deretter en av to grener."
+ },
+ "repeat": {
+ "label": "Gjenta",
+ "help": "Kjører trinnene inni den et angitt antall ganger."
+ }
+ },
+ "send": {
+ "text": "Kommando eller tekst",
+ "textPlaceholder": "systemctl omstart nginx",
+ "pressEnter": "Trykk Enter etter sending",
+ "pressEnterHelp": "Slå av dette for å skrive tekst uten å sende den inn."
+ },
+ "wait": {
+ "pattern": "Vent til utdataene inneholder",
+ "patternPlaceholder": "passord:",
+ "timeout": "Gi opp etterpå",
+ "onTimeout": "Hvis den aldri dukker opp",
+ "stop": "Stopp makroen",
+ "continue": "Fortsett uansett"
+ },
+ "delay": {
+ "duration": "Vent på"
+ },
+ "ifStep": {
+ "pattern": "Hvis utdataene inneholder",
+ "patternPlaceholder": "Aktiv: aktiv (løper)",
+ "then": "Gjør så dette",
+ "else": "Ellers gjør dette",
+ "emptyBranch": "Ingen trinn"
+ },
+ "repeat": {
+ "count": "Antall ganger",
+ "times": "ganger"
+ },
+ "match": {
+ "useRegex": "Samsvar med et regulært uttrykk",
+ "useRegexHelp": "La dette være av for å få teksten til å se nøyaktig slik den er skrevet.",
+ "invalidRegex": "Ikke et gyldig regulært uttrykk, det vil bli matchet som ren tekst"
+ },
+ "seconds": "sekunder",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/pl_PL.json b/plugins/ssh-terminal/locales/translated/pl_PL.json
index 687f8a70c..e2637385d 100644
--- a/plugins/ssh-terminal/locales/translated/pl_PL.json
+++ b/plugins/ssh-terminal/locales/translated/pl_PL.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Zapisane motywy",
"scrollbackBufferLabel": "Bufor przewijania",
"scrollbackMaxLines": "Maksymalna liczba wierszy przechowywanych w historii",
- "startupSnippetLabel": "Snippet startowy",
"sudoPasswordAutoFill": "Automatyczne uzupełnianie hasła sudo",
"sudoPasswordAutoFillDesc": "Przechowuj hasło sudo, aby metryki hosta, monity terminala i inne narzędzia mogły automatycznie uruchamiać polecenia uprzywilejowane.",
"sudoPasswordAutoFillLabel": "Automatyczne uzupełnianie hasła sudo",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Terminal lokalny",
"sshTools": "Narzędzia SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makra"
},
"sshTools": {
"broadcastInputPlaceholder": "Wpisz tutaj, aby rozesłać naciśnięcia klawiszy...",
@@ -363,5 +363,112 @@
"off": "Wył.",
"on": "Wł.",
"title": "Domyślne ustawienia połączenia"
+ },
+ "keybindings": {
+ "copy": "Kopiuj zaznaczenie",
+ "paste": "Wklej",
+ "sendControlCode": "Wyślij sygnał Ctrl+litera",
+ "sendText": "Wyślij tekst",
+ "controlCodeLabel": "Litera",
+ "textLabel": "Tekst do wysłania",
+ "appendEnterLabel": "Naciśnij Enter po wysłaniu",
+ "clipboardPermissionNote": "Przy pierwszym użyciu w niektórych przeglądarkach może zostać wyświetlony monit o zezwolenie na dostęp do schowka.",
+ "textRequired": "Wprowadź tekst do wysłania",
+ "controlCodeRequired": "Wprowadź pojedynczą literę kodu kontrolnego"
+ },
+ "macros": {
+ "title": "Makra",
+ "description": "Uruchom zapisaną serię poleceń w terminalu i zareaguj na to, co odeśle serwer.",
+ "target": "Cel",
+ "noTerminal": "Brak otwartego terminala",
+ "empty": "Brak makr",
+ "emptyHint": "Utwórz makro, aby zautomatyzować pracę w terminalu: logowanie, restart usługi czy odpowiadanie na monity.",
+ "create": "Nowe makro",
+ "back": "Powrót do makr",
+ "name": "Nazwa",
+ "namePlaceholder": "Uruchom ponownie serwer WWW",
+ "nameRequired": "Przed zapisaniem nadaj makrze nazwę",
+ "descriptionLabel": "Opis",
+ "descriptionPlaceholder": "Co robi to makro (opcjonalnie)",
+ "unsaved": "Niezapisane zmiany",
+ "save": "Zapisz",
+ "saved": "Zapisano makro",
+ "saveFailed": "Nie udało się zapisać makra",
+ "delete": "Usuń",
+ "deleteTitle": "Usunąć to makro?",
+ "deleteBody": "„{{name}}” zostanie trwale usunięte. Nie można tego cofnąć.",
+ "deleted": "Usunięto makro",
+ "cancel": "Anuluj",
+ "run": "Uruchom",
+ "stop": "Zatrzymaj",
+ "running": "Działa...",
+ "completed": "Makro zakończone",
+ "failed": "Makro nie powiodło się",
+ "loadFailed": "Nie można załadować makr",
+ "needTerminal": "Najpierw otwórz i podłącz terminal",
+ "noSteps": "To makro nie ma jeszcze żadnych kroków. Dodaj krok poniżej.",
+ "addStep": "Dodaj krok",
+ "stepNumber": "Krok {{number}}",
+ "removeStep": "Usuń krok",
+ "confirmRunTitle": "Uruchomić to makro?",
+ "confirmRunBody": "„{{name}}” wyśle polecenia do {{host}}.",
+ "confirmRunAction": "Uruchom makro",
+ "stepTypes": {
+ "send": {
+ "label": "Wyślij polecenie",
+ "help": "Wpisuje tekst do terminala, tak samo, jakby wpisano go ręcznie."
+ },
+ "wait": {
+ "label": "Czekaj na wyjście",
+ "help": "Wstrzymuje wykonanie, aż serwer wypisze podany tekst, np. monit o hasło."
+ },
+ "delay": {
+ "label": "Pauza",
+ "help": "Czeka przez ustalony czas przed kolejnym krokiem."
+ },
+ "if": {
+ "label": "Jeśli wyjście zawiera",
+ "help": "Sprawdza wszystko, co serwer wypisał do tej pory, a potem uruchamia jedną z dwóch gałęzi."
+ },
+ "repeat": {
+ "label": "Powtórz",
+ "help": "Uruchamia zawarte w nim kroki ustaloną liczbę razy."
+ }
+ },
+ "send": {
+ "text": "Polecenie lub tekst",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Naciśnij Enter po wysłaniu",
+ "pressEnterHelp": "Wyłącz tę opcję, aby wpisać tekst bez przesyłania go."
+ },
+ "wait": {
+ "pattern": "Czekaj, aż wyjście będzie zawierać",
+ "patternPlaceholder": "password:",
+ "timeout": "Przerwij po",
+ "onTimeout": "Jeśli nigdy się nie pojawi",
+ "stop": "Zatrzymaj makro",
+ "continue": "Kontynuuj mimo wszystko"
+ },
+ "delay": {
+ "duration": "Czekaj"
+ },
+ "ifStep": {
+ "pattern": "Jeśli wyjście zawiera",
+ "patternPlaceholder": "Active: active (running)",
+ "then": "Następnie zrób to",
+ "else": "W przeciwnym razie zrób to",
+ "emptyBranch": "Brak kroków"
+ },
+ "repeat": {
+ "count": "Liczba razy",
+ "times": "razy"
+ },
+ "match": {
+ "useRegex": "Dopasuj do wyrażenia regularnego",
+ "useRegexHelp": "Zostaw wyłączone, aby dopasować tekst dokładnie tak, jak został wpisany.",
+ "invalidRegex": "Nie jest to prawidłowe wyrażenie regularne, zostanie dopasowane jako zwykły tekst"
+ },
+ "seconds": "sekundy",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/pt_BR.json b/plugins/ssh-terminal/locales/translated/pt_BR.json
index e000fcf85..6f090bbd9 100644
--- a/plugins/ssh-terminal/locales/translated/pt_BR.json
+++ b/plugins/ssh-terminal/locales/translated/pt_BR.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas salvos",
"scrollbackBufferLabel": "Buffer de rolagem",
"scrollbackMaxLines": "Número máximo de linhas mantidas no histórico",
- "startupSnippetLabel": "Trecho de startup",
"sudoPasswordAutoFill": "Preenchimento automático de senhas Sudo",
"sudoPasswordAutoFillDesc": "Armazene uma senha de sudo para que as métricas do host, os prompts do terminal e outros possam executar comandos privilegiados automaticamente.",
"sudoPasswordAutoFillLabel": "Preenchimento automático de senha Sudo",
@@ -335,7 +334,8 @@
"history": "História",
"localTerminal": "Terminal local",
"sshTools": "Ferramentas SSH",
- "terminal": "terminal"
+ "terminal": "terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Digite aqui para transmitir as teclas pressionadas...",
@@ -363,5 +363,112 @@
"off": "Desligado",
"on": "Sobre",
"title": "Configurações de conexão padrão"
+ },
+ "keybindings": {
+ "copy": "Copiar seleção",
+ "paste": "Colar",
+ "sendControlCode": "Enviar sinal Ctrl+letra",
+ "sendText": "Enviar mensagem de texto",
+ "controlCodeLabel": "Carta",
+ "textLabel": "Texto a enviar",
+ "appendEnterLabel": "Pressione Enter após enviar",
+ "clipboardPermissionNote": "Pode solicitar permissão para acessar a área de transferência no primeiro uso em alguns navegadores.",
+ "textRequired": "Digite o texto que deseja enviar.",
+ "controlCodeRequired": "Digite uma única letra para o código de controle."
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Execute uma série de comandos salvos em um terminal e reaja ao que o servidor envia de volta.",
+ "target": "Alvo",
+ "noTerminal": "Nenhum terminal aberto",
+ "empty": "Ainda não há macros.",
+ "emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como fazer login, reiniciar um serviço ou responder a solicitações.",
+ "create": "Nova macro",
+ "back": "Voltar às macros",
+ "name": "Nome",
+ "namePlaceholder": "Reinicie o servidor web.",
+ "nameRequired": "Dê um nome à macro antes de salvar.",
+ "descriptionLabel": "Descrição",
+ "descriptionPlaceholder": "O que esta macro faz (opcional)",
+ "unsaved": "Alterações não salvas",
+ "save": "Salvar",
+ "saved": "Macro salvo",
+ "saveFailed": "Não foi possível salvar a macro.",
+ "delete": "Excluir",
+ "deleteTitle": "Excluir esta macro?",
+ "deleteBody": "\"{{name}}\" será excluído permanentemente. Esta ação não pode ser desfeita.",
+ "deleted": "Macro excluída",
+ "cancel": "Cancelar",
+ "run": "Correr",
+ "stop": "Parar",
+ "running": "Correndo...",
+ "completed": "Macro finalizado",
+ "failed": "A macro falhou",
+ "loadFailed": "Não foi possível carregar as macros",
+ "needTerminal": "Primeiro, abra e conecte um terminal.",
+ "noSteps": "Esta macro ainda não possui etapas. Adicione uma abaixo.",
+ "addStep": "Adicionar etapa",
+ "stepNumber": "Passo {{number}}",
+ "removeStep": "Remover etapa",
+ "confirmRunTitle": "Executar esta macro?",
+ "confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
+ "confirmRunAction": "Executar macro",
+ "stepTypes": {
+ "send": {
+ "label": "Enviar comando",
+ "help": "Digita o texto no terminal, da mesma forma que se você o digitasse."
+ },
+ "wait": {
+ "label": "Aguarde a saída",
+ "help": "Aguarda até que o servidor imprima o texto que você digitou, como uma solicitação de senha."
+ },
+ "delay": {
+ "label": "Pausa",
+ "help": "Aguarda um período de tempo fixo antes da próxima etapa."
+ },
+ "if": {
+ "label": "Se a saída contiver",
+ "help": "Verifica tudo o que o servidor imprimiu até o momento e, em seguida, executa uma das duas ramificações."
+ },
+ "repeat": {
+ "label": "Repita",
+ "help": "Executa os passos internos um número definido de vezes."
+ }
+ },
+ "send": {
+ "text": "Comando ou texto",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Pressione Enter após enviar",
+ "pressEnterHelp": "Desative esta opção para digitar texto sem enviá-lo."
+ },
+ "wait": {
+ "pattern": "Aguarde até que a saída contenha",
+ "patternPlaceholder": "senha:",
+ "timeout": "Desistir depois",
+ "onTimeout": "Se nunca aparecer",
+ "stop": "Pare a macro",
+ "continue": "Continue mesmo assim"
+ },
+ "delay": {
+ "duration": "Aguarde"
+ },
+ "ifStep": {
+ "pattern": "Se a saída contiver",
+ "patternPlaceholder": "Ativo: ativo (em execução)",
+ "then": "Então faça isso",
+ "else": "Caso contrário, faça isto",
+ "emptyBranch": "Sem degraus"
+ },
+ "repeat": {
+ "count": "Número de vezes",
+ "times": "vezes"
+ },
+ "match": {
+ "useRegex": "Combinar com uma expressão regular",
+ "useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
+ "invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
+ },
+ "seconds": "segundos",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/pt_PT.json b/plugins/ssh-terminal/locales/translated/pt_PT.json
index 7e82b4ffd..2272d97fe 100644
--- a/plugins/ssh-terminal/locales/translated/pt_PT.json
+++ b/plugins/ssh-terminal/locales/translated/pt_PT.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas guardados",
"scrollbackBufferLabel": "Buffer de Retrocesso",
"scrollbackMaxLines": "Número máximo de linhas guardadas no histórico",
- "startupSnippetLabel": "Fragmento de inicialização",
"sudoPasswordAutoFill": "Preenchimento Automático da Palavra-passe Sudo",
"sudoPasswordAutoFillDesc": "Armazenar uma palavra-passe sudo para que as métricas do host, prompts do terminal e outros possam executar comandos com privilégios automaticamente.",
"sudoPasswordAutoFillLabel": "Preenchimento automático da palavra-passe sudo",
@@ -335,7 +334,8 @@
"history": "Histórico",
"localTerminal": "Terminal local",
"sshTools": "Ferramentas SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Digite aqui para difundir as teclas premidas...",
@@ -363,5 +363,112 @@
"off": "Desligado",
"on": "Em",
"title": "Configurações de ligação padrão"
+ },
+ "keybindings": {
+ "copy": "Copiar seleção",
+ "paste": "Colar",
+ "sendControlCode": "Enviar sinal Ctrl+letra",
+ "sendText": "Enviar mensagem de texto",
+ "controlCodeLabel": "Carta",
+ "textLabel": "Texto a enviar",
+ "appendEnterLabel": "Prima Enter após enviar",
+ "clipboardPermissionNote": "Pode solicitar permissão para aceder à área de transferência na primeira utilização em alguns navegadores.",
+ "textRequired": "Introduza o texto que pretende enviar.",
+ "controlCodeRequired": "Introduza uma única letra para o código de controlo."
+ },
+ "macros": {
+ "title": "Macros",
+ "description": "Execute uma série de comandos guardados num terminal e reaja ao que o servidor envia de volta.",
+ "target": "Objetivo",
+ "noTerminal": "Nenhum terminal aberto",
+ "empty": "Ainda não existem macros.",
+ "emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como por exemplo, iniciar sessão, reiniciar um serviço ou responder a pedidos.",
+ "create": "Nova macro",
+ "back": "Voltar às macros",
+ "name": "Nome",
+ "namePlaceholder": "Reinicie o servidor web.",
+ "nameRequired": "Dê um nome à macro antes de guardar.",
+ "descriptionLabel": "Descrição",
+ "descriptionPlaceholder": "O que faz esta macro (opcional)",
+ "unsaved": "Alterações não guardadas",
+ "save": "Guardar",
+ "saved": "Macro salvo",
+ "saveFailed": "Não foi possível guardar a macro.",
+ "delete": "Eliminar",
+ "deleteTitle": "Apagar esta macro?",
+ "deleteBody": "\"{{name}}\" será eliminado permanentemente. Esta ação não pode ser anulada.",
+ "deleted": "Macro excluída",
+ "cancel": "Cancelar",
+ "run": "Correr",
+ "stop": "Parar",
+ "running": "Corrida...",
+ "completed": "Macro finalizado",
+ "failed": "A macro falhou",
+ "loadFailed": "Não foi possível carregar as macros",
+ "needTerminal": "Primeiro, abra e ligue um terminal.",
+ "noSteps": "Esta macro ainda não tem etapas. Adicione uma abaixo.",
+ "addStep": "Adicionar etapa",
+ "stepNumber": "Passo {{number}}",
+ "removeStep": "Remover etapa",
+ "confirmRunTitle": "Executar esta macro?",
+ "confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
+ "confirmRunAction": "Executar macro",
+ "stepTypes": {
+ "send": {
+ "label": "Enviar comando",
+ "help": "Digita o texto no terminal, da mesma forma que se o digitasse."
+ },
+ "wait": {
+ "label": "Aguarde a saída",
+ "help": "Aguarda até que o servidor imprima o texto que introduziu, como por exemplo um pedido de palavra-passe."
+ },
+ "delay": {
+ "label": "Pausa",
+ "help": "Aguarda um período de tempo fixo antes da etapa seguinte."
+ },
+ "if": {
+ "label": "Se a saída contiver",
+ "help": "Verifica tudo o que o servidor imprimiu até à data e, em seguida, executa uma das duas ramificações."
+ },
+ "repeat": {
+ "label": "Repetir",
+ "help": "Executa os passos internos um número definido de vezes."
+ }
+ },
+ "send": {
+ "text": "Comando ou texto",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Prima Enter após enviar",
+ "pressEnterHelp": "Desative esta opção para digitar texto sem o enviar."
+ },
+ "wait": {
+ "pattern": "Aguarde até que a saída contenha",
+ "patternPlaceholder": "palavra-passe:",
+ "timeout": "Desistir depois",
+ "onTimeout": "Se nunca aparecer",
+ "stop": "Pare a macro",
+ "continue": "Continue mesmo assim"
+ },
+ "delay": {
+ "duration": "Aguarde"
+ },
+ "ifStep": {
+ "pattern": "Se a saída contiver",
+ "patternPlaceholder": "Ativo: ativo (em execução)",
+ "then": "Então faça isso",
+ "else": "Caso contrário, faça-o",
+ "emptyBranch": "Sem degraus"
+ },
+ "repeat": {
+ "count": "Número de vezes",
+ "times": "vezes"
+ },
+ "match": {
+ "useRegex": "Combinar com uma expressão regular",
+ "useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
+ "invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
+ },
+ "seconds": "segundos",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ro_RO.json b/plugins/ssh-terminal/locales/translated/ro_RO.json
index e56932077..8033eed19 100644
--- a/plugins/ssh-terminal/locales/translated/ro_RO.json
+++ b/plugins/ssh-terminal/locales/translated/ro_RO.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Teme salvate",
"scrollbackBufferLabel": "Buffer de derulare înapoi",
"scrollbackMaxLines": "Numărul maxim de linii păstrate în istoric",
- "startupSnippetLabel": "Fragment de pornire",
"sudoPasswordAutoFill": "Completare automată parolă Sudo",
"sudoPasswordAutoFillDesc": "Stocați o parolă sudo, astfel încât metricile gazdei, prompturile terminalului și alte funcții să poată executa automat comenzi privilegiate.",
"sudoPasswordAutoFillLabel": "Completare automată parolă Sudo",
@@ -335,7 +334,8 @@
"history": "Istorie",
"localTerminal": "Terminal local",
"sshTools": "Instrumente SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macrocomenzi"
},
"sshTools": {
"broadcastInputPlaceholder": "Tastați aici pentru a difuza apăsările de taste...",
@@ -363,5 +363,112 @@
"off": "Dezactivat",
"on": "Pe",
"title": "Setări implicite ale conexiunii"
+ },
+ "keybindings": {
+ "copy": "Copiați selecția",
+ "paste": "Pastă",
+ "sendControlCode": "Trimite semnal Ctrl+literă",
+ "sendText": "Trimiteți mesaj text",
+ "controlCodeLabel": "Scrisoare",
+ "textLabel": "Text de trimis",
+ "appendEnterLabel": "Apăsați Enter după trimitere",
+ "clipboardPermissionNote": "În unele browsere, este posibil să solicite permisiunea pentru clipboard la prima utilizare.",
+ "textRequired": "Introduceți textul de trimis",
+ "controlCodeRequired": "Introduceți o singură literă pentru codul de control"
+ },
+ "macros": {
+ "title": "Macrocomenzi",
+ "description": "Executați o serie de comenzi salvate într-un terminal și reacționați la ceea ce serverul trimite înapoi.",
+ "target": "Ţintă",
+ "noTerminal": "Niciun terminal deschis",
+ "empty": "Încă nu există macrocomenzi",
+ "emptyHint": "Creați o macrocomandă pentru a automatiza un flux de lucru în terminal, cum ar fi conectarea, repornirea unui serviciu sau răspunsul la solicitări.",
+ "create": "Macrocomandă nouă",
+ "back": "Înapoi la macrocomenzi",
+ "name": "Nume",
+ "namePlaceholder": "Reporniți serverul web",
+ "nameRequired": "Dați un nume macrocomenzii înainte de salvare",
+ "descriptionLabel": "Descriere",
+ "descriptionPlaceholder": "Ce face această macrocomandă (opțional)",
+ "unsaved": "Modificări nesalvate",
+ "save": "Salva",
+ "saved": "Macrocomandă salvată",
+ "saveFailed": "Nu s-a putut salva macrocomanda",
+ "delete": "Şterge",
+ "deleteTitle": "Ștergeți această macrocomandă?",
+ "deleteBody": "„{{name}}” va fi șters definitiv. Această acțiune nu poate fi anulată.",
+ "deleted": "Macrocomandă ștearsă",
+ "cancel": "Anula",
+ "run": "Aleargă",
+ "stop": "Stop",
+ "running": "Funcţionare...",
+ "completed": "Macrocomandă finalizată",
+ "failed": "Macrocomanda a eșuat",
+ "loadFailed": "Nu s-au putut încărca macrocomenzile",
+ "needTerminal": "Deschideți și conectați mai întâi un terminal",
+ "noSteps": "Această macrocomandă nu are încă pași. Adăugați unul mai jos.",
+ "addStep": "Adăugați un pas",
+ "stepNumber": "Pasul {{number}}",
+ "removeStep": "Eliminați pasul",
+ "confirmRunTitle": "Rulați această macrocomandă?",
+ "confirmRunBody": "„{{name}}” va trimite comenzi către {{host}}.",
+ "confirmRunAction": "Executare macrocomandă",
+ "stepTypes": {
+ "send": {
+ "label": "Trimiteți comanda",
+ "help": "Introduce text în terminal, la fel ca și cum l-ai fi tastat."
+ },
+ "wait": {
+ "label": "Așteptați ieșirea",
+ "help": "Se întrerupe până când serverul afișează textul pe care îl denumiți, ca o solicitare de parolă."
+ },
+ "delay": {
+ "label": "Pauză",
+ "help": "Așteaptă o perioadă fixă de timp înainte de pasul următor."
+ },
+ "if": {
+ "label": "Dacă ieșirea conține",
+ "help": "Verifică tot ce a imprimat serverul până acum, apoi rulează una dintre cele două ramificații."
+ },
+ "repeat": {
+ "label": "Repeta",
+ "help": "Execută pașii din interiorul său de un număr stabilit de ori."
+ }
+ },
+ "send": {
+ "text": "Comandă sau text",
+ "textPlaceholder": "repornirea systemctl a nginx",
+ "pressEnter": "Apăsați Enter după trimitere",
+ "pressEnterHelp": "Dezactivați această opțiune pentru a introduce text fără a-l trimite."
+ },
+ "wait": {
+ "pattern": "Așteptați până când ieșirea conține",
+ "patternPlaceholder": "parolă:",
+ "timeout": "Renunță după",
+ "onTimeout": "Dacă nu apare niciodată",
+ "stop": "Opriți macrocomanda",
+ "continue": "Continuă oricum"
+ },
+ "delay": {
+ "duration": "Așteptați"
+ },
+ "ifStep": {
+ "pattern": "Dacă ieșirea conține",
+ "patternPlaceholder": "Activ: activ (alergând)",
+ "then": "Apoi fă asta",
+ "else": "Altfel, fă asta",
+ "emptyBranch": "Fără trepte"
+ },
+ "repeat": {
+ "count": "Numărul de ori",
+ "times": "ori"
+ },
+ "match": {
+ "useRegex": "Potrivire cu o expresie regulată",
+ "useRegexHelp": "Omiteți această opțiune pentru a corespunde exact textului așa cum este scris.",
+ "invalidRegex": "Nu este o expresie regulată validă, va fi găsită ca text simplu."
+ },
+ "seconds": "secunde",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/ru_RU.json b/plugins/ssh-terminal/locales/translated/ru_RU.json
index f0ac86396..052eb34fd 100644
--- a/plugins/ssh-terminal/locales/translated/ru_RU.json
+++ b/plugins/ssh-terminal/locales/translated/ru_RU.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Сохраненные темы",
"scrollbackBufferLabel": "Буфер прокрутки",
"scrollbackMaxLines": "Максимальное количество строк, сохраняемых в истории",
- "startupSnippetLabel": "Фрагмент запуска",
"sudoPasswordAutoFill": "Автозаполнение пароля sudo",
"sudoPasswordAutoFillDesc": "Хранить пароль sudo, чтобы метрики хоста, запросы терминала и другие могли автоматически выполнять привилегированные команды.",
"sudoPasswordAutoFillLabel": "Автозаполнение пароля sudo",
@@ -335,7 +334,8 @@
"history": "История",
"localTerminal": "Локальный терминал",
"sshTools": "Инструменты SSH",
- "terminal": "Терминал"
+ "terminal": "Терминал",
+ "macros": "Макросы"
},
"sshTools": {
"broadcastInputPlaceholder": "Введите текст для трансляции нажатий...",
@@ -363,5 +363,112 @@
"off": "Выключенный",
"on": "На",
"title": "Настройки подключения по умолчанию"
+ },
+ "keybindings": {
+ "copy": "Скопировать выделенный фрагмент",
+ "paste": "Вставить",
+ "sendControlCode": "Отправить сигнал Ctrl+буква",
+ "sendText": "Отправить текст",
+ "controlCodeLabel": "Письмо",
+ "textLabel": "Текст для отправки",
+ "appendEnterLabel": "Нажмите Enter после отправки",
+ "clipboardPermissionNote": "В некоторых браузерах при первом использовании может запрашиваться разрешение на использование буфера обмена.",
+ "textRequired": "Введите текст для отправки",
+ "controlCodeRequired": "Введите одну букву в качестве управляющего кода."
+ },
+ "macros": {
+ "title": "Макросы",
+ "description": "Выполните сохраненную последовательность команд в терминале и отреагируйте на то, что сервер отправит вам в ответ.",
+ "target": "Цель",
+ "noTerminal": "Терминал не открыт",
+ "empty": "Макросы пока отсутствуют",
+ "emptyHint": "Создайте макрос для автоматизации действий в терминале, таких как вход в систему, перезапуск службы или ответ на запросы.",
+ "create": "Новый макрос",
+ "back": "Вернуться к макроэлементам",
+ "name": "Имя",
+ "namePlaceholder": "Перезапустите веб-сервер",
+ "nameRequired": "Перед сохранением дайте макросу имя.",
+ "descriptionLabel": "Описание",
+ "descriptionPlaceholder": "Что делает этот макрос (необязательно)",
+ "unsaved": "Несохраненные изменения",
+ "save": "Сохранять",
+ "saved": "Макрос сохранен",
+ "saveFailed": "Не удалось сохранить макрос.",
+ "delete": "Удалить",
+ "deleteTitle": "Удалить этот макрос?",
+ "deleteBody": "\"{{name}}\" будет удалено навсегда. Этого нельзя будет отменить.",
+ "deleted": "Макрос удален",
+ "cancel": "Отмена",
+ "run": "Бегать",
+ "stop": "Останавливаться",
+ "running": "Бег...",
+ "completed": "Макро завершен",
+ "failed": "Макрос не сработал",
+ "loadFailed": "Не удалось загрузить макросы.",
+ "needTerminal": "Сначала откройте и подключите терминал.",
+ "noSteps": "В этом макросе пока нет шагов. Добавьте один ниже.",
+ "addStep": "Добавить шаг",
+ "stepNumber": "Шаг {{number}}",
+ "removeStep": "Удалить шаг",
+ "confirmRunTitle": "Запустить этот макрос?",
+ "confirmRunBody": "\"{{name}}\" будет отправлять команды {{host}}.",
+ "confirmRunAction": "Запустить макрос",
+ "stepTypes": {
+ "send": {
+ "label": "Отправить команду",
+ "help": "Вводит текст в терминал так же, как если бы вы его ввели вручную."
+ },
+ "wait": {
+ "label": "Дождитесь вывода",
+ "help": "Приостанавливается до тех пор, пока сервер не выведет указанный вами текст, как запрос пароля."
+ },
+ "delay": {
+ "label": "Пауза",
+ "help": "Перед следующим шагом выполняется ожидание в течение заданного промежутка времени."
+ },
+ "if": {
+ "label": "Если вывод содержит",
+ "help": "Проверяет все данные, выведенные сервером на экран, а затем запускает одну из двух ветвей выполнения."
+ },
+ "repeat": {
+ "label": "Повторить",
+ "help": "Выполняет все шаги, входящие в его состав, заданное количество раз."
+ }
+ },
+ "send": {
+ "text": "Команда или текст",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Нажмите Enter после отправки",
+ "pressEnterHelp": "Отключите эту функцию, чтобы вводить текст без отправки."
+ },
+ "wait": {
+ "pattern": "Дождитесь, пока на выходе не появится",
+ "patternPlaceholder": "пароль:",
+ "timeout": "Сдаться после",
+ "onTimeout": "Если оно никогда не появится",
+ "stop": "Остановите макрос",
+ "continue": "Продолжайте в любом случае."
+ },
+ "delay": {
+ "duration": "Подождите"
+ },
+ "ifStep": {
+ "pattern": "Если в выходных данных содержится",
+ "patternPlaceholder": "Активный: активен (работает)",
+ "then": "Затем сделайте это.",
+ "else": "В противном случае сделайте это.",
+ "emptyBranch": "Без ступенек"
+ },
+ "repeat": {
+ "count": "Количество раз",
+ "times": "времена"
+ },
+ "match": {
+ "useRegex": "Сопоставление с использованием регулярного выражения",
+ "useRegexHelp": "Оставьте этот параметр отключенным, чтобы текст точно соответствовал написанному.",
+ "invalidRegex": "Не является допустимым регулярным выражением, будет сопоставлено как обычный текст."
+ },
+ "seconds": "секунд",
+ "secondsShort": "{{count}}с"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/sr_SP.json b/plugins/ssh-terminal/locales/translated/sr_SP.json
index 3a9dfcea6..4bd320e6b 100644
--- a/plugins/ssh-terminal/locales/translated/sr_SP.json
+++ b/plugins/ssh-terminal/locales/translated/sr_SP.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Сачуване теме",
"scrollbackBufferLabel": "Бафер за померање уназад",
"scrollbackMaxLines": "Максималан број линија сачуваних у историји",
- "startupSnippetLabel": "Исечак за покретање",
"sudoPasswordAutoFill": "Аутоматско попуњавање лозинке за Судо",
"sudoPasswordAutoFillDesc": "Сачувајте лозинку за sudo како би метрике хоста, упити терминала и други могли аутоматски да покрећу привилеговане команде.",
"sudoPasswordAutoFillLabel": "Аутоматско попуњавање лозинке за Sudo",
@@ -335,7 +334,8 @@
"history": "Историја",
"localTerminal": "Локални терминал",
"sshTools": "SSH алати",
- "terminal": "Терминал"
+ "terminal": "Терминал",
+ "macros": "Макрои"
},
"sshTools": {
"broadcastInputPlaceholder": "Куцајте овде да бисте емитовали притиске тастера...",
@@ -363,5 +363,112 @@
"off": "Искључено",
"on": "Укључено",
"title": "Подразумеване вредности везе"
+ },
+ "keybindings": {
+ "copy": "Копирај избор",
+ "paste": "Залепи",
+ "sendControlCode": "Пошаљи сигнал Ctrl+слово",
+ "sendText": "Пошаљи текст",
+ "controlCodeLabel": "Писмо",
+ "textLabel": "Текст за слање",
+ "appendEnterLabel": "Притисните Ентер након слања",
+ "clipboardPermissionNote": "Може затражити дозволу за међуспремник при првој употреби у неким прегледачима.",
+ "textRequired": "Унесите текст за слање",
+ "controlCodeRequired": "Унесите једно слово за контролни код"
+ },
+ "macros": {
+ "title": "Макрои",
+ "description": "Покрените сачувану серију команди у терминалу и реагујте на оно што сервер пошаље назад.",
+ "target": "Циљ",
+ "noTerminal": "Ниједан терминал није отворен",
+ "empty": "Још нема макроа",
+ "emptyHint": "Направите макро за аутоматизацију тока рада терминала, као што је пријављивање, поновно покретање сервиса или одговарање на упите.",
+ "create": "Нови макро",
+ "back": "Назад на макрое",
+ "name": "Име",
+ "namePlaceholder": "Поново покрените веб сервер",
+ "nameRequired": "Дајте макроу име пре чувања",
+ "descriptionLabel": "Опис",
+ "descriptionPlaceholder": "Шта овај макро ради (опционо)",
+ "unsaved": "Несачуване промене",
+ "save": "Сачувај",
+ "saved": "Макро је сачуван",
+ "saveFailed": "Није могуће сачувати макро",
+ "delete": "Обриши",
+ "deleteTitle": "Да ли желите да обришете овај макро?",
+ "deleteBody": "„{{name}}“ ће бити трајно обрисано. Ова радња се не може поништити.",
+ "deleted": "Макро је обрисан",
+ "cancel": "Откажи",
+ "run": "Трчи",
+ "stop": "Заустави",
+ "running": "Трчи се...",
+ "completed": "Макро је завршен",
+ "failed": "Макро није успео",
+ "loadFailed": "Није могуће учитати макрое",
+ "needTerminal": "Прво отворите и повежите терминал",
+ "noSteps": "Овај макро још нема кораке. Додајте један испод.",
+ "addStep": "Додај корак",
+ "stepNumber": "Корак {{number}}",
+ "removeStep": "Уклони корак",
+ "confirmRunTitle": "Покрени овај макро?",
+ "confirmRunBody": "„{{name}}“ ће послати команде на {{host}}.",
+ "confirmRunAction": "Покрени макро",
+ "stepTypes": {
+ "send": {
+ "label": "Пошаљи команду",
+ "help": "Уноси текст у терминал, исто као да сте га откуцали."
+ },
+ "wait": {
+ "label": "Сачекајте излаз",
+ "help": "Паузира док сервер не одштампа текст који наведете, као што је захтев за лозинку."
+ },
+ "delay": {
+ "label": "Пауза",
+ "help": "Чека одређено време пре следећег корака."
+ },
+ "if": {
+ "label": "Ако излаз садржи",
+ "help": "Проверава све што је сервер до сада одштампао, а затим покреће једну од две гране."
+ },
+ "repeat": {
+ "label": "Понови",
+ "help": "Покреће кораке унутар њега одређени број пута."
+ }
+ },
+ "send": {
+ "text": "Команда или текст",
+ "textPlaceholder": "системцтл рестартовање нгинкса",
+ "pressEnter": "Притисните Ентер након слања",
+ "pressEnterHelp": "Искључите ово да бисте куцали текст без његовог слања."
+ },
+ "wait": {
+ "pattern": "Сачекајте док излаз не садржи",
+ "patternPlaceholder": "лозинка:",
+ "timeout": "Одустани после",
+ "onTimeout": "Ако се никада не појави",
+ "stop": "Зауставите макро",
+ "continue": "Настави ипак"
+ },
+ "delay": {
+ "duration": "Сачекајте"
+ },
+ "ifStep": {
+ "pattern": "Ако излаз садржи",
+ "patternPlaceholder": "Активно: активно (покреће се)",
+ "then": "Онда уради ово",
+ "else": "У супротном, урадите ово",
+ "emptyBranch": "Без степеница"
+ },
+ "repeat": {
+ "count": "Број пута",
+ "times": "пута"
+ },
+ "match": {
+ "useRegex": "Упари се са регуларним изразом",
+ "useRegexHelp": "Оставите ово искључено да би текст тачно одговарао написаном тексту.",
+ "invalidRegex": "Није важећи регуларни израз, биће упарен као обичан текст"
+ },
+ "seconds": "секунде",
+ "secondsShort": "{{count}}с"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/sv_SE.json b/plugins/ssh-terminal/locales/translated/sv_SE.json
index 672d790b3..b43e55ac7 100644
--- a/plugins/ssh-terminal/locales/translated/sv_SE.json
+++ b/plugins/ssh-terminal/locales/translated/sv_SE.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Sparade teman",
"scrollbackBufferLabel": "Scrollback-buffert",
"scrollbackMaxLines": "Maximalt antal rader som sparas i historiken",
- "startupSnippetLabel": "Startkodavsnitt",
"sudoPasswordAutoFill": "Sudo-lösenordsautomatik",
"sudoPasswordAutoFillDesc": "Lagra ett sudo-lösenord så att värdmätvärden, terminalprompter och annat kan köra privilegierade kommandon automatiskt.",
"sudoPasswordAutoFillLabel": "Sudo-lösenordsautomatik",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-verktyg",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makron"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv här för att sända tangenttryckningar...",
@@ -363,5 +363,112 @@
"off": "Av",
"on": "På",
"title": "Standardinställningar för anslutning"
+ },
+ "keybindings": {
+ "copy": "Kopiera val",
+ "paste": "Klistra",
+ "sendControlCode": "Skicka Ctrl+bokstavssignal",
+ "sendText": "Skicka sms",
+ "controlCodeLabel": "Brev",
+ "textLabel": "SMS att skicka",
+ "appendEnterLabel": "Tryck Enter efter att du skickat",
+ "clipboardPermissionNote": "Kan be om behörighet för urklipp vid första användningen i vissa webbläsare.",
+ "textRequired": "Ange texten som ska skickas",
+ "controlCodeRequired": "Ange en enda bokstav för kontrollkoden"
+ },
+ "macros": {
+ "title": "Makron",
+ "description": "Kör en sparad serie kommandon i en terminal och reagera på vad servern skickar tillbaka.",
+ "target": "Mål",
+ "noTerminal": "Ingen terminal öppen",
+ "empty": "Inga makron ännu",
+ "emptyHint": "Skapa ett makro för att automatisera ett terminalflöde, som att logga in, starta om en tjänst eller svara på frågor.",
+ "create": "Nytt makro",
+ "back": "Tillbaka till makron",
+ "name": "Namn",
+ "namePlaceholder": "Starta om webbservern",
+ "nameRequired": "Ge makrot ett namn innan du sparar",
+ "descriptionLabel": "Beskrivning",
+ "descriptionPlaceholder": "Vad detta makro gör (valfritt)",
+ "unsaved": "Osparade ändringar",
+ "save": "Spara",
+ "saved": "Makrot sparat",
+ "saveFailed": "Kunde inte spara makrot",
+ "delete": "Radera",
+ "deleteTitle": "Ta bort det här makrot?",
+ "deleteBody": "\"{{name}}\" kommer att raderas permanent. Detta kan inte ångras.",
+ "deleted": "Makro borttaget",
+ "cancel": "Avboka",
+ "run": "Sikt",
+ "stop": "Stopp",
+ "running": "Spring...",
+ "completed": "Makro klart",
+ "failed": "Makrot misslyckades",
+ "loadFailed": "Kunde inte läsa in makron",
+ "needTerminal": "Öppna och anslut en terminal först",
+ "noSteps": "Det här makrot har inga steg ännu. Lägg till ett nedan.",
+ "addStep": "Lägg till steg",
+ "stepNumber": "Steg {{number}}",
+ "removeStep": "Ta bort steg",
+ "confirmRunTitle": "Köra det här makrot?",
+ "confirmRunBody": "\"{{name}}\" skickar kommandon till {{host}}.",
+ "confirmRunAction": "Kör makro",
+ "stepTypes": {
+ "send": {
+ "label": "Skicka kommando",
+ "help": "Skriver text i terminalen, på samma sätt som om du hade skrivit den."
+ },
+ "wait": {
+ "label": "Vänta på utdata",
+ "help": "Pausar tills servern skriver ut texten du anger, som en lösenordsfråga."
+ },
+ "delay": {
+ "label": "Paus",
+ "help": "Väntar en fastställd tid innan nästa steg."
+ },
+ "if": {
+ "label": "Om utdata innehåller",
+ "help": "Kontrollerar allt som servern har skrivit ut hittills och kör sedan en av två grenar."
+ },
+ "repeat": {
+ "label": "Upprepa",
+ "help": "Kör stegen inuti ett visst antal gånger."
+ }
+ },
+ "send": {
+ "text": "Kommando eller text",
+ "textPlaceholder": "systemctl starta om nginx",
+ "pressEnter": "Tryck Enter efter att du skickat",
+ "pressEnterHelp": "Stäng av detta för att skriva text utan att skicka in den."
+ },
+ "wait": {
+ "pattern": "Vänta tills utdata innehåller",
+ "patternPlaceholder": "lösenord:",
+ "timeout": "Ge upp efteråt",
+ "onTimeout": "Om det aldrig dyker upp",
+ "stop": "Stoppa makrot",
+ "continue": "Fortsätt ändå"
+ },
+ "delay": {
+ "duration": "Vänta på"
+ },
+ "ifStep": {
+ "pattern": "Om utdata innehåller",
+ "patternPlaceholder": "Aktiv: aktiv (löper)",
+ "then": "Gör sedan detta",
+ "else": "Annars gör så här",
+ "emptyBranch": "Inga steg"
+ },
+ "repeat": {
+ "count": "Antal gånger",
+ "times": "gånger"
+ },
+ "match": {
+ "useRegex": "Matcha med ett reguljärt uttryck",
+ "useRegexHelp": "Lämna detta av för att texten ska se exakt ut som den är skriven.",
+ "invalidRegex": "Inte ett giltigt reguljärt uttryck, det kommer att matchas som vanlig text"
+ },
+ "seconds": "sekunder",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/th_TH.json b/plugins/ssh-terminal/locales/translated/th_TH.json
index 8b64cea61..0c7f44f81 100644
--- a/plugins/ssh-terminal/locales/translated/th_TH.json
+++ b/plugins/ssh-terminal/locales/translated/th_TH.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "ธีมที่บันทึกไว้",
"scrollbackBufferLabel": "บัฟเฟอร์การเลื่อนกลับ",
"scrollbackMaxLines": "จำนวนบรรทัดสูงสุดที่เก็บไว้ในประวัติ",
- "startupSnippetLabel": "ตัวอย่างโค้ดสำหรับเริ่มต้น",
"sudoPasswordAutoFill": "การกรอกรหัสผ่านอัตโนมัติของ Sudo",
"sudoPasswordAutoFillDesc": "บันทึกรหัสผ่าน sudo เพื่อให้โปรแกรมตรวจสอบข้อมูลโฮสต์ ข้อความแจ้งเตือนในเทอร์มินัล และโปรแกรมอื่นๆ สามารถเรียกใช้คำสั่งที่มีสิทธิ์พิเศษได้โดยอัตโนมัติ",
"sudoPasswordAutoFillLabel": "การกรอกรหัสผ่านอัตโนมัติของ Sudo",
@@ -335,7 +334,8 @@
"history": "ประวัติศาสตร์",
"localTerminal": "สถานีปลายทางท้องถิ่น",
"sshTools": "เครื่องมือ SSH",
- "terminal": "เทอร์มินัล"
+ "terminal": "เทอร์มินัล",
+ "macros": "มาโคร"
},
"sshTools": {
"broadcastInputPlaceholder": "พิมพ์ที่นี่เพื่อถ่ายทอดการกดแป้นพิมพ์...",
@@ -363,5 +363,112 @@
"off": "ปิด",
"on": "บน",
"title": "ค่าเริ่มต้นการเชื่อมต่อ"
+ },
+ "keybindings": {
+ "copy": "คัดลอกการเลือก",
+ "paste": "แปะ",
+ "sendControlCode": "ส่งสัญญาณ Ctrl+ตัวอักษร",
+ "sendText": "ส่งข้อความ",
+ "controlCodeLabel": "จดหมาย",
+ "textLabel": "ข้อความที่จะส่ง",
+ "appendEnterLabel": "กด Enter หลังจากส่งแล้ว",
+ "clipboardPermissionNote": "ในบางเบราว์เซอร์ อาจขออนุญาตคัดลอกและวางข้อมูลลงในคลิปบอร์ดเมื่อใช้งานครั้งแรก",
+ "textRequired": "ป้อนข้อความที่จะส่ง",
+ "controlCodeRequired": "ป้อนตัวอักษรตัวเดียวสำหรับรหัสควบคุม"
+ },
+ "macros": {
+ "title": "มาโคร",
+ "description": "เรียกใช้ชุดคำสั่งที่บันทึกไว้ในเทอร์มินัล แล้วตอบสนองต่อสิ่งที่เซิร์ฟเวอร์ส่งกลับมา",
+ "target": "เป้า",
+ "noTerminal": "ไม่มีเทอร์มินัลเปิดอยู่",
+ "empty": "ยังไม่มีมาโคร",
+ "emptyHint": "สร้างมาโครเพื่อทำให้กระบวนการทำงานบนเทอร์มินัลเป็นไปโดยอัตโนมัติ เช่น การล็อกอิน การรีสตาร์ทบริการ หรือการตอบคำถามต่างๆ",
+ "create": "มาโครใหม่",
+ "back": "กลับไปที่มาโคร",
+ "name": "ชื่อ",
+ "namePlaceholder": "รีสตาร์ทเว็บเซิร์ฟเวอร์",
+ "nameRequired": "ตั้งชื่อมาโครก่อนบันทึก",
+ "descriptionLabel": "คำอธิบาย",
+ "descriptionPlaceholder": "มาโครนี้ทำอะไร (ไม่บังคับ)",
+ "unsaved": "การเปลี่ยนแปลงที่ยังไม่ได้บันทึก",
+ "save": "บันทึก",
+ "saved": "บันทึกมาโครแล้ว",
+ "saveFailed": "ไม่สามารถบันทึกมาโครได้",
+ "delete": "ลบ",
+ "deleteTitle": "ลบมาโครนี้?",
+ "deleteBody": "\"{{name}}\" จะถูกลบอย่างถาวร ไม่สามารถกู้คืนได้",
+ "deleted": "มาโครถูกลบแล้ว",
+ "cancel": "ยกเลิก",
+ "run": "วิ่ง",
+ "stop": "หยุด",
+ "running": "วิ่ง...",
+ "completed": "มาโครเสร็จสิ้น",
+ "failed": "มาโครล้มเหลว",
+ "loadFailed": "ไม่สามารถโหลดมาโครได้",
+ "needTerminal": "เปิดและเชื่อมต่อเทอร์มินัลก่อน",
+ "noSteps": "มาโครนี้ยังไม่มีขั้นตอนใดๆ เพิ่มขั้นตอนด้านล่าง",
+ "addStep": "เพิ่มขั้นตอน",
+ "stepNumber": "ขั้นตอน {{number}}",
+ "removeStep": "ลบขั้นตอน",
+ "confirmRunTitle": "เรียกใช้มาโครนี้หรือไม่?",
+ "confirmRunBody": "\"{{name}}\" จะส่งคำสั่งไปยัง {{host}}",
+ "confirmRunAction": "เรียกใช้มาโคร",
+ "stepTypes": {
+ "send": {
+ "label": "ส่งคำสั่ง",
+ "help": "พิมพ์ข้อความลงในเทอร์มินัล เหมือนกับที่คุณพิมพ์ข้อความนั้นด้วยตนเอง"
+ },
+ "wait": {
+ "label": "รอผลลัพธ์",
+ "help": "หยุดรอจนกว่าเซิร์ฟเวอร์จะแสดงข้อความที่คุณระบุ เช่นเดียวกับการขอให้ป้อนรหัสผ่าน"
+ },
+ "delay": {
+ "label": "หยุดชั่วคราว",
+ "help": "รอเป็นระยะเวลาที่กำหนดก่อนดำเนินการขั้นตอนต่อไป"
+ },
+ "if": {
+ "label": "หากผลลัพธ์ประกอบด้วย",
+ "help": "ตรวจสอบทุกอย่างที่เซิร์ฟเวอร์ได้พิมพ์ออกมาแล้ว จากนั้นจึงเลือกดำเนินการตามหนึ่งในสองทางเลือก"
+ },
+ "repeat": {
+ "label": "ทำซ้ำ",
+ "help": "ดำเนินการตามขั้นตอนภายในตามจำนวนครั้งที่กำหนด"
+ }
+ },
+ "send": {
+ "text": "คำสั่งหรือข้อความ",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "กด Enter หลังจากส่งแล้ว",
+ "pressEnterHelp": "ปิดตัวเลือกนี้เพื่อพิมพ์ข้อความโดยไม่ต้องกดส่ง"
+ },
+ "wait": {
+ "pattern": "รอจนกว่าผลลัพธ์จะประกอบด้วย",
+ "patternPlaceholder": "รหัสผ่าน:",
+ "timeout": "ยอมแพ้หลังจาก",
+ "onTimeout": "หากมันไม่ปรากฏเลย",
+ "stop": "หยุดมาโคร",
+ "continue": "อย่าหยุดแค่นี้"
+ },
+ "delay": {
+ "duration": "รอสักครู่"
+ },
+ "ifStep": {
+ "pattern": "หากผลลัพธ์ประกอบด้วย",
+ "patternPlaceholder": "สถานะ: กำลังทำกิจกรรม (วิ่ง)",
+ "then": "จากนั้นให้ทำแบบนี้",
+ "else": "หรือทำแบบนี้ก็ได้",
+ "emptyBranch": "ไม่มีขั้นตอน"
+ },
+ "repeat": {
+ "count": "จำนวนครั้ง",
+ "times": "ครั้ง"
+ },
+ "match": {
+ "useRegex": "จับคู่ด้วยนิพจน์ปกติ",
+ "useRegexHelp": "ไม่ต้องใส่ส่วนนี้เพื่อให้ตรงกับข้อความที่เขียนไว้ทุกประการ",
+ "invalidRegex": "ไม่ใช่รูปแบบนิพจน์ปกติที่ถูกต้อง ระบบจะถือว่าเป็นข้อความธรรมดา"
+ },
+ "seconds": "วินาที",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/tr_TR.json b/plugins/ssh-terminal/locales/translated/tr_TR.json
index eba01fbbb..df200a3fa 100644
--- a/plugins/ssh-terminal/locales/translated/tr_TR.json
+++ b/plugins/ssh-terminal/locales/translated/tr_TR.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Kaydedilen Temalar",
"scrollbackBufferLabel": "Kaydırma Arabelleği",
"scrollbackMaxLines": "Geçmişte saklanan maksimum satır sayısı",
- "startupSnippetLabel": "Başlangıç Betiği",
"sudoPasswordAutoFill": "Sudo Parolası Otomatik Doldurma",
"sudoPasswordAutoFillDesc": "Sudo parolasını saklayın; böylece ana bilgisayar metrikleri, terminal istemleri ve diğer bileşenler ayrıcalıklı komutları otomatik olarak çalıştırabilir.",
"sudoPasswordAutoFillLabel": "Sudo Parolası Otomatik Doldurma",
@@ -335,7 +334,8 @@
"history": "Geçmiş",
"localTerminal": "Yerel Terminal",
"sshTools": "SSH Araçları",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Makrolar"
},
"sshTools": {
"broadcastInputPlaceholder": "Tuş vuruşlarını yayınlamak için buraya yazın...",
@@ -363,5 +363,112 @@
"off": "Kapalı",
"on": "Açık",
"title": "Bağlantı Varsayılanları"
+ },
+ "keybindings": {
+ "copy": "Seçimi kopyala",
+ "paste": "Yapıştır",
+ "sendControlCode": "Ctrl+harf sinyali gönder",
+ "sendText": "Mesaj gönder",
+ "controlCodeLabel": "Mektup",
+ "textLabel": "Gönderilecek metin",
+ "appendEnterLabel": "Gönderdikten sonra Enter tuşuna basın.",
+ "clipboardPermissionNote": "Bazı tarayıcılarda ilk kullanımda panoya kopyalama izni isteyebilir.",
+ "textRequired": "Göndermek istediğiniz metni girin.",
+ "controlCodeRequired": "Kontrol kodu için tek bir harf girin."
+ },
+ "macros": {
+ "title": "Makrolar",
+ "description": "Önceden kaydedilmiş bir komut dizisini terminalde çalıştırın ve sunucunun geri gönderdiği yanıtlara göre hareket edin.",
+ "target": "Hedef",
+ "noTerminal": "Açık terminal yok.",
+ "empty": "Henüz makro yok",
+ "emptyHint": "Giriş yapma, bir hizmeti yeniden başlatma veya komut istemlerine yanıt verme gibi terminal işlemlerini otomatikleştirmek için bir makro oluşturun.",
+ "create": "Yeni makro",
+ "back": "Makrolara geri dön",
+ "name": "İsim",
+ "namePlaceholder": "Web sunucusunu yeniden başlatın.",
+ "nameRequired": "Kaydetmeden önce makroya bir ad verin.",
+ "descriptionLabel": "Tanım",
+ "descriptionPlaceholder": "Bu makronun ne yaptığı (isteğe bağlı)",
+ "unsaved": "Kaydedilmemiş değişiklikler",
+ "save": "Kaydetmek",
+ "saved": "Makro kaydedildi.",
+ "saveFailed": "Makro kaydedilemedi.",
+ "delete": "Silmek",
+ "deleteTitle": "Bu makroyu silmek ister misiniz?",
+ "deleteBody": "\"{{name}}\" kalıcı olarak silinecektir. Bu işlem geri alınamaz.",
+ "deleted": "Makro silindi",
+ "cancel": "İptal etmek",
+ "run": "Koşmak",
+ "stop": "Durmak",
+ "running": "Koşma...",
+ "completed": "Makro tamamlandı",
+ "failed": "Makro başarısız oldu",
+ "loadFailed": "Makrolar yüklenemedi.",
+ "needTerminal": "Öncelikle bir terminal açın ve bağlayın.",
+ "noSteps": "Bu makronun henüz hiçbir adımı yok. Aşağıya bir adım ekleyin.",
+ "addStep": "Adım ekle",
+ "stepNumber": "Adım {{number}}",
+ "removeStep": "Adımı kaldır",
+ "confirmRunTitle": "Bu makroyu çalıştırayım mı?",
+ "confirmRunBody": "\"{{name}}\" {{host}} adresine komutlar gönderecektir.",
+ "confirmRunAction": "Makroyu çalıştır",
+ "stepTypes": {
+ "send": {
+ "label": "Komut gönder",
+ "help": "Terminale, sanki siz yazmışsınız gibi metin yazar."
+ },
+ "wait": {
+ "label": "Çıktıyı bekleyin",
+ "help": "Sunucu, belirttiğiniz metni (örneğin parola istemi gibi) yazdırana kadar bekler."
+ },
+ "delay": {
+ "label": "Duraklat",
+ "help": "Bir sonraki adıma geçmeden önce belirli bir süre bekler."
+ },
+ "if": {
+ "label": "Çıktı içeriyorsa",
+ "help": "Sunucunun şu ana kadar yazdırdığı her şeyi kontrol eder, ardından iki daldan birini çalıştırır."
+ },
+ "repeat": {
+ "label": "Tekrarlamak",
+ "help": "İçindeki adımları belirtilen sayıda tekrarlar."
+ }
+ },
+ "send": {
+ "text": "Komut veya metin",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Gönderdikten sonra Enter tuşuna basın.",
+ "pressEnterHelp": "Göndermeden metin yazmak için bunu kapatın."
+ },
+ "wait": {
+ "pattern": "Çıktıyı oluşturana kadar bekleyin.",
+ "patternPlaceholder": "şifre:",
+ "timeout": "Vazgeçtikten sonra",
+ "onTimeout": "Eğer hiç ortaya çıkmazsa",
+ "stop": "Makroyu durdur",
+ "continue": "Yine de devam edin."
+ },
+ "delay": {
+ "duration": "Bekleyin"
+ },
+ "ifStep": {
+ "pattern": "Çıktı şunları içeriyorsa:",
+ "patternPlaceholder": "Aktif: aktif (koşmak)",
+ "then": "O halde şunu yapın",
+ "else": "Aksi takdirde şunu yapın",
+ "emptyBranch": "Hiçbir adım yok"
+ },
+ "repeat": {
+ "count": "Sayısı",
+ "times": "kez"
+ },
+ "match": {
+ "useRegex": "Düzenli ifadeyle eşleştirin",
+ "useRegexHelp": "Metnin aynen yazıldığı gibi kalması için bunu atlayın.",
+ "invalidRegex": "Geçerli bir normal ifade değil, düz metin olarak eşleştirilecektir."
+ },
+ "seconds": "saniyeler",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/uk_UA.json b/plugins/ssh-terminal/locales/translated/uk_UA.json
index 2da6b1e3e..a827e523b 100644
--- a/plugins/ssh-terminal/locales/translated/uk_UA.json
+++ b/plugins/ssh-terminal/locales/translated/uk_UA.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Збережені теми",
"scrollbackBufferLabel": "Буфер прокручування",
"scrollbackMaxLines": "Максимальна кількість рядків, що зберігаються в історії",
- "startupSnippetLabel": "Фрагмент запуску",
"sudoPasswordAutoFill": "Автозаповнення пароля Sudo",
"sudoPasswordAutoFillDesc": "Зберігайте пароль sudo, щоб метрики хоста, запити терміналу та інші могли автоматично запускати привілейовані команди.",
"sudoPasswordAutoFillLabel": "Автозаповнення пароля Sudo",
@@ -335,7 +334,8 @@
"history": "Історія",
"localTerminal": "Місцевий термінал",
"sshTools": "SSH-інструменти",
- "terminal": "Термінал"
+ "terminal": "Термінал",
+ "macros": "Макроси"
},
"sshTools": {
"broadcastInputPlaceholder": "Введіть тут, щоб транслювати натискання клавіш...",
@@ -363,5 +363,112 @@
"off": "Вимкнено",
"on": "Увімкнено",
"title": "Налаштування підключення за замовчуванням"
+ },
+ "keybindings": {
+ "copy": "Копіювати виділення",
+ "paste": "Вставити",
+ "sendControlCode": "Надіслати сигнал Ctrl+літера",
+ "sendText": "Надіслати текст",
+ "controlCodeLabel": "Лист",
+ "textLabel": "Текст для надсилання",
+ "appendEnterLabel": "Натисніть Enter після відправлення",
+ "clipboardPermissionNote": "У деяких браузерах може запитуватися дозвіл на використання буфера обміну під час першого використання.",
+ "textRequired": "Введіть текст для надсилання",
+ "controlCodeRequired": "Введіть одну літеру для контрольного коду"
+ },
+ "macros": {
+ "title": "Макроси",
+ "description": "Виконайте збережену серію команд у терміналі та відреагуйте на те, що надсилає сервер.",
+ "target": "Ціль",
+ "noTerminal": "Термінал не відкритий",
+ "empty": "Поки що немає макросів",
+ "emptyHint": "Створіть макрос для автоматизації процесу роботи термінала, такого як вхід у систему, перезапуск служби або відповідь на запити.",
+ "create": "Новий макрос",
+ "back": "Повернутися до макросів",
+ "name": "Ім'я",
+ "namePlaceholder": "Перезапустіть веб-сервер",
+ "nameRequired": "Введіть ім'я макросу перед збереженням",
+ "descriptionLabel": "Опис",
+ "descriptionPlaceholder": "Що робить цей макрос (необов'язково)",
+ "unsaved": "Незбережені зміни",
+ "save": "Зберегти",
+ "saved": "Макрос збережено",
+ "saveFailed": "Не вдалося зберегти макрос",
+ "delete": "Видалити",
+ "deleteTitle": "Видалити цей макрос?",
+ "deleteBody": "«{{name}}» буде видалено назавжди. Цю дію неможливо скасувати.",
+ "deleted": "Макрос видалено",
+ "cancel": "Скасувати",
+ "run": "Бігти",
+ "stop": "СТІЙ",
+ "running": "Біг...",
+ "completed": "Макрос завершено",
+ "failed": "Помилка макросу",
+ "loadFailed": "Не вдалося завантажити макроси",
+ "needTerminal": "Спочатку відкрийте та підключіть термінал",
+ "noSteps": "Цей макрос ще не має кроків. Додайте один нижче.",
+ "addStep": "Додати крок",
+ "stepNumber": "Крок {{number}}",
+ "removeStep": "Видалити крок",
+ "confirmRunTitle": "Запустити цей макрос?",
+ "confirmRunBody": "\"{{name}}\" надсилатиме команди до {{host}}.",
+ "confirmRunAction": "Запустити макрос",
+ "stepTypes": {
+ "send": {
+ "label": "Надіслати команду",
+ "help": "Вводить текст у термінал так само, як і надруковано."
+ },
+ "wait": {
+ "label": "Зачекайте на вивід",
+ "help": "Призупиняється, доки сервер не надрукує вказаний вами текст, наприклад, запит на пароль."
+ },
+ "delay": {
+ "label": "Пауза",
+ "help": "Очікує фіксований проміжок часу перед наступним кроком."
+ },
+ "if": {
+ "label": "Якщо вихід містить",
+ "help": "Перевіряє все, що сервер надрукував до цього часу, а потім запускає одну з двох гілок."
+ },
+ "repeat": {
+ "label": "Повторити",
+ "help": "Виконує кроки всередині нього задану кількість разів."
+ }
+ },
+ "send": {
+ "text": "Команда або текст",
+ "textPlaceholder": "перезапуск systemctl nginx",
+ "pressEnter": "Натисніть Enter після відправлення",
+ "pressEnterHelp": "Вимкніть цю функцію, щоб вводити текст, не надсилаючи його."
+ },
+ "wait": {
+ "pattern": "Зачекайте, поки вихідні дані міститимуть",
+ "patternPlaceholder": "пароль:",
+ "timeout": "Здайся після",
+ "onTimeout": "Якщо воно ніколи не з'явиться",
+ "stop": "Зупинити макрос",
+ "continue": "Продовжуй у будь-якому разі"
+ },
+ "delay": {
+ "duration": "Зачекайте"
+ },
+ "ifStep": {
+ "pattern": "Якщо вихід містить",
+ "patternPlaceholder": "Активний: активний (працює)",
+ "then": "Тоді зробіть це",
+ "else": "Інакше зробіть це",
+ "emptyBranch": "Без сходинок"
+ },
+ "repeat": {
+ "count": "Кількість разів",
+ "times": "рази"
+ },
+ "match": {
+ "useRegex": "Зіставлення з регулярним виразом",
+ "useRegexHelp": "Не виключайте це, щоб текст точно відповідав написаному.",
+ "invalidRegex": "Недійсний регулярний вираз, буде знайдено звичайний текст"
+ },
+ "seconds": "секунди",
+ "secondsShort": "{{count}}с"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/vi_VN.json b/plugins/ssh-terminal/locales/translated/vi_VN.json
index 68ee2e742..6ee7139c4 100644
--- a/plugins/ssh-terminal/locales/translated/vi_VN.json
+++ b/plugins/ssh-terminal/locales/translated/vi_VN.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "Chủ đề đã lưu",
"scrollbackBufferLabel": "Bộ đệm cuộn ngược",
"scrollbackMaxLines": "Số dòng tối đa được giữ trong lịch sử",
- "startupSnippetLabel": "Đoạn mã khởi động",
"sudoPasswordAutoFill": "Tự động điền mật khẩu Sudo",
"sudoPasswordAutoFillDesc": "Lưu trữ mật khẩu sudo để các số liệu máy chủ, lời nhắc terminal và các thành phần khác có thể chạy lệnh đặc quyền tự động.",
"sudoPasswordAutoFillLabel": "Tự động điền mật khẩu sudo",
@@ -335,7 +334,8 @@
"history": "Lịch sử",
"localTerminal": "Ga địa phương",
"sshTools": "Công cụ SSH",
- "terminal": "Terminal"
+ "terminal": "Terminal",
+ "macros": "Macro"
},
"sshTools": {
"broadcastInputPlaceholder": "Nhập vào đây để phát tán thao tác phím...",
@@ -363,5 +363,112 @@
"off": "Tắt",
"on": "TRÊN",
"title": "Cài đặt mặc định kết nối"
+ },
+ "keybindings": {
+ "copy": "Sao chép lựa chọn",
+ "paste": "Dán",
+ "sendControlCode": "Gửi tín hiệu Ctrl+chữ cái",
+ "sendText": "Gửi tin nhắn",
+ "controlCodeLabel": "Thư",
+ "textLabel": "Văn bản để gửi",
+ "appendEnterLabel": "Nhấn Enter sau khi gửi",
+ "clipboardPermissionNote": "Một số trình duyệt có thể yêu cầu quyền truy cập clipboard khi sử dụng lần đầu.",
+ "textRequired": "Nhập văn bản cần gửi",
+ "controlCodeRequired": "Nhập một chữ cái duy nhất cho mã điều khiển."
+ },
+ "macros": {
+ "title": "Macro",
+ "description": "Chạy một chuỗi lệnh đã lưu trong cửa sổ dòng lệnh và phản hồi lại những gì máy chủ gửi về.",
+ "target": "Mục tiêu",
+ "noTerminal": "Không có thiết bị đầu cuối nào đang mở",
+ "empty": "Chưa có macro nào.",
+ "emptyHint": "Tạo macro để tự động hóa quy trình trên thiết bị đầu cuối, chẳng hạn như đăng nhập, khởi động lại dịch vụ hoặc trả lời các lời nhắc.",
+ "create": "Macro mới",
+ "back": "Quay lại phần macro",
+ "name": "Tên",
+ "namePlaceholder": "Khởi động lại máy chủ web",
+ "nameRequired": "Đặt tên cho macro trước khi lưu.",
+ "descriptionLabel": "Sự miêu tả",
+ "descriptionPlaceholder": "Chức năng của macro này (tùy chọn)",
+ "unsaved": "Thay đổi chưa được lưu",
+ "save": "Cứu",
+ "saved": "Macro đã được lưu",
+ "saveFailed": "Không thể lưu macro",
+ "delete": "Xóa bỏ",
+ "deleteTitle": "Xóa macro này?",
+ "deleteBody": "\"{{name}}\" sẽ bị xóa vĩnh viễn. Không thể hoàn tác.",
+ "deleted": "Macro đã bị xóa",
+ "cancel": "Hủy bỏ",
+ "run": "Chạy",
+ "stop": "Dừng lại",
+ "running": "Đang chạy...",
+ "completed": "Macro đã hoàn thành",
+ "failed": "Macro bị lỗi",
+ "loadFailed": "Không thể tải macro",
+ "needTerminal": "Trước tiên hãy mở và kết nối thiết bị đầu cuối.",
+ "noSteps": "Macro này chưa có bước nào. Hãy thêm một bước bên dưới.",
+ "addStep": "Thêm bước",
+ "stepNumber": "Bước {{number}}",
+ "removeStep": "Xóa bước",
+ "confirmRunTitle": "Chạy macro này?",
+ "confirmRunBody": "\"{{name}}\" sẽ gửi lệnh đến {{host}}.",
+ "confirmRunAction": "Chạy macro",
+ "stepTypes": {
+ "send": {
+ "label": "Gửi lệnh",
+ "help": "Nhập văn bản vào cửa sổ dòng lệnh, giống như khi bạn tự nhập văn bản."
+ },
+ "wait": {
+ "label": "Chờ kết quả đầu ra",
+ "help": "Tạm dừng cho đến khi máy chủ in ra đoạn văn bản bạn đã đặt tên, ví dụ như lời nhắc nhập mật khẩu."
+ },
+ "delay": {
+ "label": "Tạm dừng",
+ "help": "Chờ một khoảng thời gian cố định trước khi thực hiện bước tiếp theo."
+ },
+ "if": {
+ "label": "Nếu đầu ra chứa",
+ "help": "Kiểm tra tất cả những gì máy chủ đã in ra cho đến nay, sau đó chạy một trong hai nhánh."
+ },
+ "repeat": {
+ "label": "Lặp lại",
+ "help": "Thực thi các bước bên trong nó một số lần nhất định."
+ }
+ },
+ "send": {
+ "text": "Lệnh hoặc văn bản",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "Nhấn Enter sau khi gửi",
+ "pressEnterHelp": "Tắt tùy chọn này để nhập văn bản mà không cần gửi đi."
+ },
+ "wait": {
+ "pattern": "Chờ cho đến khi đầu ra chứa",
+ "patternPlaceholder": "mật khẩu:",
+ "timeout": "Bỏ cuộc sau",
+ "onTimeout": "Nếu nó không bao giờ xuất hiện",
+ "stop": "Dừng macro",
+ "continue": "Cứ tiếp tục đi"
+ },
+ "delay": {
+ "duration": "Chờ đợi"
+ },
+ "ifStep": {
+ "pattern": "Nếu đầu ra chứa",
+ "patternPlaceholder": "Đang hoạt động: đang hoạt động (đang chạy)",
+ "then": "Sau đó hãy làm như thế này",
+ "else": "Nếu không thì hãy làm như thế này",
+ "emptyBranch": "Không có bước nào"
+ },
+ "repeat": {
+ "count": "Số lần",
+ "times": "lần"
+ },
+ "match": {
+ "useRegex": "So khớp với biểu thức chính quy",
+ "useRegexHelp": "Bỏ phần này đi để văn bản khớp chính xác với nội dung đã viết.",
+ "invalidRegex": "Đây không phải là biểu thức chính quy hợp lệ, nó sẽ được so khớp như văn bản thuần túy."
+ },
+ "seconds": "giây",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/zh_CN.json b/plugins/ssh-terminal/locales/translated/zh_CN.json
index 40f0e367a..7a25aefab 100644
--- a/plugins/ssh-terminal/locales/translated/zh_CN.json
+++ b/plugins/ssh-terminal/locales/translated/zh_CN.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "已保存的主题",
"scrollbackBufferLabel": "回滚缓冲区",
"scrollbackMaxLines": "保留在历史记录中的最大行数",
- "startupSnippetLabel": "启动代码片段",
"sudoPasswordAutoFill": "Sudo 密码自动填充",
"sudoPasswordAutoFillDesc": "存储 sudo 密码,以便主机指标、终端提示和其他操作可以自动运行特权命令。",
"sudoPasswordAutoFillLabel": "Sudo 密码自动填充",
@@ -335,7 +334,8 @@
"history": "历史记录",
"localTerminal": "本地终端",
"sshTools": "SSH 工具",
- "terminal": "终端"
+ "terminal": "终端",
+ "macros": "宏"
},
"sshTools": {
"broadcastInputPlaceholder": "在此输入以广播按键...",
@@ -363,5 +363,112 @@
"off": "关闭",
"on": "开启",
"title": "连接默认值"
+ },
+ "keybindings": {
+ "copy": "复制选择",
+ "paste": "粘贴",
+ "sendControlCode": "发送 Ctrl+字母信号",
+ "sendText": "发送文本",
+ "controlCodeLabel": "字母",
+ "textLabel": "发送的文本",
+ "appendEnterLabel": "发送后按回车键",
+ "clipboardPermissionNote": "某些浏览器在首次使用时可能会提示授予剪贴板权限。",
+ "textRequired": "请输入要发送的文本",
+ "controlCodeRequired": "请输入单个字母作为控制码"
+ },
+ "macros": {
+ "title": "宏",
+ "description": "在终端中运行已保存的一系列命令,并对服务器返回的内容做出反应。",
+ "target": "目标",
+ "noTerminal": "没有终端打开",
+ "empty": "暂无宏",
+ "emptyHint": "创建一个宏来自动化终端流程,例如登录、重启服务或回答提示。",
+ "create": "新宏",
+ "back": "返回宏",
+ "name": "名称",
+ "namePlaceholder": "重启 Web 服务器",
+ "nameRequired": "保存之前,请给宏命名。",
+ "descriptionLabel": "描述",
+ "descriptionPlaceholder": "此宏的功能(可选)",
+ "unsaved": "未保存的更改",
+ "save": "保存",
+ "saved": "宏已保存",
+ "saveFailed": "无法保存宏",
+ "delete": "删除",
+ "deleteTitle": "删除这个宏?",
+ "deleteBody": "“{{name}}”将被永久删除。此操作无法撤销。",
+ "deleted": "宏已删除",
+ "cancel": "取消",
+ "run": "运行",
+ "stop": "停止",
+ "running": "运行中...",
+ "completed": "宏已完成",
+ "failed": "宏失败",
+ "loadFailed": "无法加载宏。",
+ "needTerminal": "首先打开并连接终端",
+ "noSteps": "此宏目前尚无步骤。请在下方添加步骤。",
+ "addStep": "添加步骤",
+ "stepNumber": "步骤 {{number}}",
+ "removeStep": "移除步骤",
+ "confirmRunTitle": "运行这个宏?",
+ "confirmRunBody": "\"{{name}}\" 将向 {{host}} 发送命令。",
+ "confirmRunAction": "运行宏",
+ "stepTypes": {
+ "send": {
+ "label": "发送命令",
+ "help": "在终端中输入文本,就像你手动输入一样。"
+ },
+ "wait": {
+ "label": "等待输出",
+ "help": "暂停,直到服务器打印出您指定的文本,例如密码提示。"
+ },
+ "delay": {
+ "label": "暂停",
+ "help": "等待一段固定时间后再执行下一步操作。"
+ },
+ "if": {
+ "label": "如果输出包含",
+ "help": "检查服务器目前为止打印的所有内容,然后运行两个分支中的一个。"
+ },
+ "repeat": {
+ "label": "重复",
+ "help": "将其中的步骤执行设定的次数。"
+ }
+ },
+ "send": {
+ "text": "命令或文本",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "发送后按回车键",
+ "pressEnterHelp": "关闭此功能即可输入文本而不提交。"
+ },
+ "wait": {
+ "pattern": "等待输出包含",
+ "patternPlaceholder": "密码:",
+ "timeout": "放弃",
+ "onTimeout": "如果它从未出现",
+ "stop": "停止宏",
+ "continue": "无论如何都继续"
+ },
+ "delay": {
+ "duration": "等待"
+ },
+ "ifStep": {
+ "pattern": "如果输出包含",
+ "patternPlaceholder": "活跃:正在运行",
+ "then": "然后这样做",
+ "else": "否则就这样做",
+ "emptyBranch": "没有步骤"
+ },
+ "repeat": {
+ "count": "次数",
+ "times": "次"
+ },
+ "match": {
+ "useRegex": "使用正则表达式进行匹配",
+ "useRegexHelp": "关闭此选项以完全按原文匹配文本。",
+ "invalidRegex": "这不是有效的正则表达式,它将被匹配为纯文本。"
+ },
+ "seconds": "秒",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/locales/translated/zh_TW.json b/plugins/ssh-terminal/locales/translated/zh_TW.json
index 55974432d..f3c8af95a 100644
--- a/plugins/ssh-terminal/locales/translated/zh_TW.json
+++ b/plugins/ssh-terminal/locales/translated/zh_TW.json
@@ -307,7 +307,6 @@
"savedThemesLabel": "已儲存的主題",
"scrollbackBufferLabel": "回滾緩衝區",
"scrollbackMaxLines": "歷史記錄保留的最大行數",
- "startupSnippetLabel": "啟動程式碼片段",
"sudoPasswordAutoFill": "Sudo 密碼自動填寫",
"sudoPasswordAutoFillDesc": "儲存 sudo 密碼,以便主機指標、終端機提示及其他功能能自動執行特權命令。",
"sudoPasswordAutoFillLabel": "Sudo 密碼自動填入",
@@ -335,7 +334,8 @@
"history": "歷史記錄",
"localTerminal": "本地端",
"sshTools": "SSH 工具",
- "terminal": "終端機"
+ "terminal": "終端機",
+ "macros": "宏"
},
"sshTools": {
"broadcastInputPlaceholder": "在此輸入以廣播按鍵輸入...",
@@ -363,5 +363,112 @@
"off": "離開",
"on": "在",
"title": "連接預設值"
+ },
+ "keybindings": {
+ "copy": "複製選擇",
+ "paste": "貼上",
+ "sendControlCode": "發送 Ctrl+字母訊號",
+ "sendText": "傳簡訊",
+ "controlCodeLabel": "信",
+ "textLabel": "發送的文字",
+ "appendEnterLabel": "發送後按回車鍵",
+ "clipboardPermissionNote": "某些瀏覽器在首次使用時可能會提示授予剪貼簿權限。",
+ "textRequired": "請輸入要發送的文字",
+ "controlCodeRequired": "請輸入一個字母作為控制碼"
+ },
+ "macros": {
+ "title": "宏",
+ "description": "在終端機中執行已儲存的一系列命令,並對伺服器傳回的內容做出反應。",
+ "target": "目標",
+ "noTerminal": "沒有終端機打開",
+ "empty": "暫無宏",
+ "emptyHint": "建立一個巨集來自動化終端流程,例如登入、重新啟動服務或回答提示。",
+ "create": "新宏",
+ "back": "回傳宏",
+ "name": "姓名",
+ "namePlaceholder": "重啟Web伺服器",
+ "nameRequired": "儲存之前,請先為巨集命名。",
+ "descriptionLabel": "描述",
+ "descriptionPlaceholder": "此巨集的功能(選用)",
+ "unsaved": "未儲存的更改",
+ "save": "節省",
+ "saved": "巨集已儲存",
+ "saveFailed": "無法保存巨集",
+ "delete": "刪除",
+ "deleteTitle": "刪除這個巨集?",
+ "deleteBody": "“{{name}}”將永久刪除。此操作無法撤銷。",
+ "deleted": "巨集已刪除",
+ "cancel": "取消",
+ "run": "跑步",
+ "stop": "停止",
+ "running": "跑步...",
+ "completed": "巨集已完成",
+ "failed": "巨集失敗",
+ "loadFailed": "無法載入巨集。",
+ "needTerminal": "首先打開並連接終端。",
+ "noSteps": "此巨集目前尚無步驟。請在下方新增步驟。",
+ "addStep": "新增步驟",
+ "stepNumber": "步驟 {{number}}",
+ "removeStep": "移除步驟",
+ "confirmRunTitle": "運行這個巨集?",
+ "confirmRunBody": "\"{{name}}\" 將向 {{host}} 傳送指令。",
+ "confirmRunAction": "運行巨集",
+ "stepTypes": {
+ "send": {
+ "label": "發送命令",
+ "help": "在終端機中輸入文本,就像你手動輸入一樣。"
+ },
+ "wait": {
+ "label": "等待輸出",
+ "help": "暫停,直到伺服器列印您指定的文本,例如密碼提示。"
+ },
+ "delay": {
+ "label": "暫停",
+ "help": "等待一段固定時間後再執行下一步操作。"
+ },
+ "if": {
+ "label": "如果輸出包含",
+ "help": "檢查伺服器目前為止列印的所有內容,然後執行兩個分支中的一個。"
+ },
+ "repeat": {
+ "label": "重複",
+ "help": "將其中的步驟執行設定的次數。"
+ }
+ },
+ "send": {
+ "text": "命令或文字",
+ "textPlaceholder": "systemctl restart nginx",
+ "pressEnter": "發送後按回車鍵",
+ "pressEnterHelp": "關閉此功能即可輸入文字而不提交。"
+ },
+ "wait": {
+ "pattern": "等待輸出包含",
+ "patternPlaceholder": "密碼:",
+ "timeout": "放棄",
+ "onTimeout": "如果它從未出現",
+ "stop": "停止宏",
+ "continue": "無論如何,繼續前進。"
+ },
+ "delay": {
+ "duration": "等待"
+ },
+ "ifStep": {
+ "pattern": "如果輸出包含",
+ "patternPlaceholder": "活躍:正在運行",
+ "then": "然後這樣做",
+ "else": "否則就這樣做",
+ "emptyBranch": "沒有步驟"
+ },
+ "repeat": {
+ "count": "次數",
+ "times": "時代"
+ },
+ "match": {
+ "useRegex": "使用正規表示式進行匹配",
+ "useRegexHelp": "請刪除此行以完全按照原文填寫。",
+ "invalidRegex": "這不是有效的正規表示式,它將被匹配為純文字。"
+ },
+ "seconds": "秒",
+ "secondsShort": "{{count}}s"
}
}
diff --git a/plugins/ssh-terminal/manifest.json b/plugins/ssh-terminal/manifest.json
index 8093822e2..d66b2dbb0 100644
--- a/plugins/ssh-terminal/manifest.json
+++ b/plugins/ssh-terminal/manifest.json
@@ -79,6 +79,11 @@
"id": "ssh-tools",
"titleKey": "nav.sshTools",
"icon": "Hammer"
+ },
+ {
+ "id": "macros",
+ "titleKey": "nav.macros",
+ "icon": "Braces"
}
],
"actionSlots": [
@@ -752,9 +757,47 @@
"labelKey": "hosts.linkClickBehaviorDirect"
}
]
+ },
+ {
+ "key": "macros",
+ "type": "json",
+ "labelKey": "macros.title",
+ "hidden": true,
+ "default": []
}
]
- }
+ },
+ "keybindingActions": [
+ {
+ "id": "copy"
+ },
+ {
+ "id": "paste"
+ },
+ {
+ "id": "sendControlCode",
+ "params": {
+ "controlCode": {
+ "type": "string",
+ "required": true,
+ "pattern": "^[a-zA-Z]$"
+ }
+ }
+ },
+ {
+ "id": "sendText",
+ "params": {
+ "text": {
+ "type": "string",
+ "maxLength": 65536,
+ "required": true
+ },
+ "appendEnter": {
+ "type": "boolean"
+ }
+ }
+ }
+ ]
},
"locales": "locales",
"platforms": ["linux", "win32", "darwin"]
diff --git a/plugins/ssh-terminal/src/backend/index.ts b/plugins/ssh-terminal/src/backend/index.ts
index 9d1c12392..18aa7f48b 100644
--- a/plugins/ssh-terminal/src/backend/index.ts
+++ b/plugins/ssh-terminal/src/backend/index.ts
@@ -22,7 +22,10 @@ import {
import { ADMIN_KEYS } from "./settings.js";
import { commandHistory } from "./tables.js";
import { createTerminalSocket } from "./terminal-socket.js";
-import { validateAdminSettings } from "./settings-validation.js";
+import {
+ validateAdminSettings,
+ validateUserSettings,
+} from "./settings-validation.js";
function toInfo(session: TerminalSession): LiveSessionInfo {
return {
@@ -57,6 +60,7 @@ function optionalService(
export async function activate(ctx: PluginContext) {
const log = createTerminalLogger(ctx.log);
ctx.settings.onValidate("admin", validateAdminSettings);
+ ctx.settings.onValidate("user", validateUserSettings);
const table = await ctx.db.define(commandHistory);
const history = createHistoryRepository(ctx.db, table);
diff --git a/plugins/ssh-terminal/src/backend/settings-validation.ts b/plugins/ssh-terminal/src/backend/settings-validation.ts
index 7a591baa0..65363b77c 100644
--- a/plugins/ssh-terminal/src/backend/settings-validation.ts
+++ b/plugins/ssh-terminal/src/backend/settings-validation.ts
@@ -30,3 +30,38 @@ export function validateAdminSettings(
}
return errors;
}
+
+const MAX_MACROS = 100;
+const MAX_MACROS_BYTES = 512 * 1024;
+
+/** Save-time checks for the user settings: macros stay bounded. */
+export function validateUserSettings(
+ values: Record,
+): Record {
+ const errors: Record = {};
+ if (values.macros === undefined || values.macros === null) return errors;
+ let macros = values.macros;
+ if (typeof macros === "string") {
+ try {
+ macros = JSON.parse(macros);
+ } catch {
+ macros = null;
+ }
+ }
+ const valid =
+ Array.isArray(macros) &&
+ macros.length <= MAX_MACROS &&
+ JSON.stringify(macros).length <= MAX_MACROS_BYTES &&
+ macros.every(
+ (macro) =>
+ !!macro &&
+ typeof macro === "object" &&
+ typeof (macro as { id?: unknown }).id === "string" &&
+ typeof (macro as { name?: unknown }).name === "string" &&
+ Array.isArray((macro as { steps?: unknown }).steps),
+ );
+ if (!valid) {
+ errors.macros = `Macros must be a list of at most ${MAX_MACROS} valid macros`;
+ }
+ return errors;
+}
diff --git a/plugins/ssh-terminal/src/frontend/index.tsx b/plugins/ssh-terminal/src/frontend/index.tsx
index 75ba3e419..1ef546537 100644
--- a/plugins/ssh-terminal/src/frontend/index.tsx
+++ b/plugins/ssh-terminal/src/frontend/index.tsx
@@ -1,5 +1,6 @@
import { lazy, Suspense, type ComponentType } from "react";
import {
+ Braces,
Hammer,
History,
Laptop,
@@ -33,6 +34,17 @@ import { HostTerminalSection } from "./settings/HostTerminalSection";
import { resetTouchInputSettingsCache } from "./terminal/touch-input-settings-store";
import { hostSetting } from "./terminal-api";
import { SshToolsPanel } from "./ssh-tools/SshToolsPanel";
+import { MacrosPanel } from "./macros/MacrosPanel";
+import {
+ TERMINAL_KEYBINDING_DEFAULTS,
+ validateSendControlCode,
+ validateSendText,
+} from "./lib/keybinding-dispatch";
+import {
+ PasteNote,
+ SendControlCodeEditor,
+ SendTextEditor,
+} from "./lib/keybinding-editors";
import { TerminalDefaultsSettings } from "./settings/TerminalDefaultsSettings";
import {
NewHostFontSetting,
@@ -191,6 +203,45 @@ export function activate(app: TermixApp): void {
separatorAfter: true,
});
+ app.registerPanel(
+ "macros",
+ MacrosPanel as unknown as ComponentType,
+ );
+ app.registerRailItem({
+ id: "macros",
+ icon: Braces,
+ titleKey: "nav.macros",
+ after: "ssh-tools",
+ hideable: true,
+ promotable: true,
+ rightDockable: true,
+ separatorAfter: true,
+ });
+
+ // The keys a user can bind to the terminal's own actions, and its
+ // built-in keys they can rebind. The terminal runs these itself.
+ app.registerKeybindingAction({ id: "copy", titleKey: "keybindings.copy" });
+ app.registerKeybindingAction({
+ id: "paste",
+ titleKey: "keybindings.paste",
+ editor: PasteNote,
+ });
+ app.registerKeybindingAction({
+ id: "sendControlCode",
+ titleKey: "keybindings.sendControlCode",
+ editor: SendControlCodeEditor,
+ validate: validateSendControlCode,
+ });
+ app.registerKeybindingAction({
+ id: "sendText",
+ titleKey: "keybindings.sendText",
+ editor: SendTextEditor,
+ validate: validateSendText,
+ });
+ for (const binding of TERMINAL_KEYBINDING_DEFAULTS) {
+ app.registerKeybindingDefault(binding);
+ }
+
// Places other plugins can fill: toolbar buttons and readouts, a side
// panel and overlays that follow the session's connection flow.
app.declareActionSlot({ id: TERMINAL_TOOLBAR_SLOT, accepts: ["button"] });
diff --git a/plugins/ssh-terminal/src/frontend/lib/keybinding-dispatch.ts b/plugins/ssh-terminal/src/frontend/lib/keybinding-dispatch.ts
index 794a14361..7e9aa79cb 100644
--- a/plugins/ssh-terminal/src/frontend/lib/keybinding-dispatch.ts
+++ b/plugins/ssh-terminal/src/frontend/lib/keybinding-dispatch.ts
@@ -1,27 +1,26 @@
import type { Terminal } from "@xterm/xterm";
-import { type KeybindingAction } from "@termix/plugin-sdk/ui";
-import { invokeAction } from "@termix/plugin-sdk/frontend";
-
-export interface SnippetHostContext {
- ip?: string;
- username?: string;
- port?: number | string;
- name?: string;
-}
+import type {
+ KeybindingAction,
+ KeybindingDefaultContribution,
+} from "@termix/plugin-sdk/frontend";
export interface KeybindingDispatchContext {
terminal: Terminal;
webSocketRef: React.MutableRefObject;
writeTextToClipboard: (text: string) => Promise;
readTextFromClipboard: () => Promise;
- /** Host context used to resolve $HOST/$USER/$PORT/$NAME in runSnippet actions. */
- hostContext?: SnippetHostContext | null;
- /**
- * Called instead of sending the snippet directly when its content still has
- * unresolved $INPUT_n placeholders after host-variable substitution -- the
- * caller is expected to collect values (e.g. via a dialog) and send itself.
- */
- onSnippetNeedsInputs?: (snippet: { id: string; content: string }) => void;
+}
+
+/** The keybinding actions the terminal runs itself. */
+export const TERMINAL_KEYBINDING_ACTIONS = [
+ "copy",
+ "paste",
+ "sendControlCode",
+ "sendText",
+] as const;
+
+export function isTerminalKeybindingAction(type: string): boolean {
+ return (TERMINAL_KEYBINDING_ACTIONS as readonly string[]).includes(type);
}
export function sendRawToSocket(
@@ -33,10 +32,19 @@ export function sendRawToSocket(
}
}
+function text(action: KeybindingAction, key: string): string {
+ const value = action[key];
+ return typeof value === "string" ? value : "";
+}
+
+/**
+ * Runs one of the terminal's own bound actions. False for any other type,
+ * which the terminal hands on to whoever registered it.
+ */
export function dispatchKeybindingAction(
action: KeybindingAction,
ctx: KeybindingDispatchContext,
-): void {
+): boolean {
const sendRaw = (data: string) => sendRawToSocket(ctx.webSocketRef, data);
switch (action.type) {
@@ -46,58 +54,96 @@ export function dispatchKeybindingAction(
ctx.writeTextToClipboard(selection);
ctx.terminal.clearSelection();
}
- return;
+ return true;
}
case "paste": {
- ctx.readTextFromClipboard().then((text) => {
- if (text) ctx.terminal.paste(text);
+ ctx.readTextFromClipboard().then((value) => {
+ if (value) ctx.terminal.paste(value);
});
- return;
+ return true;
}
case "sendControlCode": {
- if (!action.controlCode) return;
- const code = action.controlCode.toLowerCase().charCodeAt(0) - 96;
+ const letter = text(action, "controlCode");
+ if (!letter) return true;
+ const code = letter.toLowerCase().charCodeAt(0) - 96;
if (code >= 1 && code <= 26) sendRaw(String.fromCharCode(code));
- return;
+ return true;
}
case "sendText": {
- sendRaw((action.text ?? "") + (action.appendEnter ? "\r" : ""));
- return;
- }
- case "runSnippet": {
- if (!action.snippetId) return;
- const snippetId = Number(action.snippetId);
- if (!Number.isFinite(snippetId)) return;
- // Resolving with no inputValues first tells us whether the snippet
- // still needs them; the plugin decides and reports back.
- void invokeAction(
- "snippets.resolveForTerminal",
- snippetId,
- ctx.hostContext ?? null,
- ).then((result) => {
- const resolved = result as
- { needsInputs: boolean; content: string } | null | undefined;
- if (!resolved) return;
- if (resolved.needsInputs) {
- ctx.onSnippetNeedsInputs?.({
- id: action.snippetId!,
- content: resolved.content,
- });
- return;
- }
- sendRaw(resolved.content + (action.appendEnter !== false ? "\r" : ""));
- });
- return;
- }
- case "nextTab":
- case "previousTab":
- case "openCommandPalette": {
- window.dispatchEvent(
- new CustomEvent("termix:global-keybinding", {
- detail: { type: action.type },
- }),
- );
- return;
+ sendRaw(text(action, "text") + (action.appendEnter === true ? "\r" : ""));
+ return true;
}
+ default:
+ return false;
}
}
+
+export function validateSendText(action: KeybindingAction): string | null {
+ return text(action, "text").trim() ? null : "keybindings.textRequired";
+}
+
+export function validateSendControlCode(
+ action: KeybindingAction,
+): string | null {
+ return /^[a-zA-Z]$/.test(text(action, "controlCode"))
+ ? null
+ : "keybindings.controlCodeRequired";
+}
+
+function combo(
+ key: string,
+ modifiers: { ctrl?: boolean; alt?: boolean; shift?: boolean; meta?: boolean },
+) {
+ return {
+ key,
+ isCode: false,
+ ctrl: !!modifiers.ctrl,
+ alt: !!modifiers.alt,
+ shift: !!modifiers.shift,
+ meta: !!modifiers.meta,
+ };
+}
+
+/** The terminal's built-in keys, listed so a user can rebind them. */
+export const TERMINAL_KEYBINDING_DEFAULTS: KeybindingDefaultContribution[] = [
+ {
+ id: "default-copy-ctrlc",
+ combo: combo("c", { ctrl: true }),
+ descriptionKey: "keybindings.builtIn.copyCtrlC",
+ },
+ {
+ id: "default-copy-ctrlshiftc",
+ combo: combo("c", { ctrl: true, shift: true }),
+ descriptionKey: "keybindings.builtIn.copyCtrlShiftC",
+ },
+ {
+ id: "default-copy-cmdc",
+ combo: combo("c", { meta: true }),
+ descriptionKey: "keybindings.builtIn.copyCmdC",
+ },
+ {
+ id: "default-paste-ctrlshiftv",
+ combo: combo("v", { ctrl: true, shift: true }),
+ descriptionKey: "keybindings.builtIn.pasteCtrlShiftV",
+ },
+ {
+ id: "default-ctrlaltw",
+ combo: combo("w", { ctrl: true, alt: true }),
+ descriptionKey: "keybindings.builtIn.ctrlW",
+ },
+ {
+ id: "default-ctrlaltt",
+ combo: combo("t", { ctrl: true, alt: true }),
+ descriptionKey: "keybindings.builtIn.ctrlT",
+ },
+ {
+ id: "default-ctrlaltn",
+ combo: combo("n", { ctrl: true, alt: true }),
+ descriptionKey: "keybindings.builtIn.ctrlN",
+ },
+ {
+ id: "default-ctrlaltq",
+ combo: combo("q", { ctrl: true, alt: true }),
+ descriptionKey: "keybindings.builtIn.ctrlQ",
+ },
+];
diff --git a/plugins/ssh-terminal/src/frontend/lib/keybinding-editors.tsx b/plugins/ssh-terminal/src/frontend/lib/keybinding-editors.tsx
new file mode 100644
index 000000000..52d614176
--- /dev/null
+++ b/plugins/ssh-terminal/src/frontend/lib/keybinding-editors.tsx
@@ -0,0 +1,81 @@
+import {
+ useTranslation,
+ type KeybindingActionEditorProps,
+} from "@termix/plugin-sdk/frontend";
+import { Input } from "@termix/plugin-sdk/ui";
+
+export function PasteNote() {
+ const { t } = useTranslation();
+ return (
+
+ {t("keybindings.clipboardPermissionNote")}
+
+ );
+}
+
+export function SendControlCodeEditor({
+ action,
+ onChange,
+}: KeybindingActionEditorProps) {
+ const { t } = useTranslation();
+ const value =
+ typeof action.controlCode === "string" ? action.controlCode : "";
+ return (
+
+
+ {t("keybindings.controlCodeLabel")}
+
+
+ onChange({
+ type: action.type,
+ controlCode: e.target.value.toLowerCase() || undefined,
+ })
+ }
+ placeholder="w"
+ className="w-16 font-mono"
+ />
+
+ );
+}
+
+export function SendTextEditor({
+ action,
+ onChange,
+}: KeybindingActionEditorProps) {
+ const { t } = useTranslation();
+ const text = typeof action.text === "string" ? action.text : "";
+ const appendEnter = action.appendEnter === true;
+ return (
+
+
+ {t("keybindings.textLabel")}
+
+
+ );
+}
diff --git a/src/ui/sidebar/MacrosPanel.tsx b/plugins/ssh-terminal/src/frontend/macros/MacrosPanel.tsx
similarity index 92%
rename from src/ui/sidebar/MacrosPanel.tsx
rename to plugins/ssh-terminal/src/frontend/macros/MacrosPanel.tsx
index 18f4fdc18..3be0fb144 100644
--- a/src/ui/sidebar/MacrosPanel.tsx
+++ b/plugins/ssh-terminal/src/frontend/macros/MacrosPanel.tsx
@@ -1,5 +1,16 @@
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import { useTranslation } from "react-i18next";
+import {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+ useSyncExternalStore,
+} from "react";
+import {
+ useSettings,
+ useTranslation,
+ type PanelProps,
+} from "@termix/plugin-sdk/frontend";
import {
ArrowLeft,
Braces,
@@ -16,12 +27,12 @@ import {
Play,
} from "lucide-react";
import { toast } from "sonner";
-import { Button } from "@/components/button";
-import { Input } from "@/components/input";
-import { Textarea } from "@/components/textarea";
-import { Checkbox } from "@/components/checkbox";
-import { EmptyState } from "@/components/empty-state";
import {
+ Button,
+ Input,
+ Textarea,
+ Checkbox,
+ EmptyState,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
@@ -30,22 +41,27 @@ import {
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
-} from "@/components/alert-dialog";
-import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from "@/components/dropdown-menu";
-import { getUserPreferences, saveUserPreferences } from "@/api/open-tabs-api";
-import { getErrorMessage } from "../lib/error-message.js";
+} from "@termix/plugin-sdk/ui";
import {
- parseTerminalMacros,
+ mergeLocalMacros,
+ sanitizeTerminalMacros,
runTerminalMacro,
type MacroStep,
type TerminalMacro,
-} from "@/lib/terminal-macros";
-import type { Tab } from "@/types/ui-types";
+} from "./terminal-macros";
+import {
+ getSessionHandle,
+ sessionsSnapshot,
+ subscribeSessions,
+} from "../session-registry";
+
+function getErrorMessage(error: unknown): string {
+ return error instanceof Error ? error.message : "";
+}
const STEP_TYPES = ["send", "wait", "delay", "if", "repeat"] as const;
@@ -429,17 +445,14 @@ function StepList({
);
}
-export function MacrosPanel({
- terminalTabs,
- activeTabId,
- storageMode,
-}: {
- terminalTabs: Tab[];
- activeTabId: string;
- storageMode: "local" | "cloud";
-}) {
+/**
+ * Macros: a saved series of steps typed into the terminal the user is
+ * working in, waiting for and branching on what the server sends back.
+ * They are the user's own setting, so they follow them to every browser.
+ */
+export function MacrosPanel({ targetTab }: PanelProps) {
const { t } = useTranslation();
- const [macros, setMacros] = useState([]);
+ const settings = useSettings("user");
const [draft, setDraft] = useState(null);
const [dirty, setDirty] = useState(false);
const [runningId, setRunningId] = useState(null);
@@ -449,33 +462,32 @@ export function MacrosPanel({
const [pendingRun, setPendingRun] = useState(null);
const abortRef = useRef(null);
const confirmedRef = useRef>(new Set());
+ const movedLocalRef = useRef(false);
+ const sessions = useSyncExternalStore(subscribeSessions, sessionsSnapshot);
+ const macros = useMemo(
+ () => sanitizeTerminalMacros(settings.values.macros),
+ [settings.values.macros],
+ );
+ const { save: saveSettings } = settings;
+
+ useEffect(() => () => abortRef.current?.abort(), []);
+
+ // Macros a browser kept before 2.9.0 move into the setting once.
useEffect(() => {
- let cancelled = false;
- const load =
- storageMode === "local"
- ? Promise.resolve(
- parseTerminalMacros(localStorage.getItem("terminalMacros")),
- )
- : getUserPreferences().then((preferences) =>
- parseTerminalMacros(preferences.terminalMacros),
- );
- load
- .then((loaded) => {
- if (!cancelled) setMacros(loaded);
- })
- .catch(() => {
- if (!cancelled) toast.error(t("macros.loadFailed"));
- });
- return () => {
- cancelled = true;
- abortRef.current?.abort();
- };
- }, [storageMode, t]);
+ if (!settings.loaded || movedLocalRef.current) return;
+ movedLocalRef.current = true;
+ const merged = mergeLocalMacros(macros);
+ if (!merged) return;
+ saveSettings({ macros: merged.macros })
+ .then(merged.clear)
+ .catch(() => toast.error(t("macros.saveFailed")));
+ }, [settings.loaded, macros, saveSettings, t]);
const target = useMemo(
- () => terminalTabs.find((tab) => tab.id === activeTabId) ?? terminalTabs[0],
- [activeTabId, terminalTabs],
+ () =>
+ sessions.find((session) => session.id === targetTab?.id) ?? sessions[0],
+ [sessions, targetTab?.id],
);
const updateDraft = useCallback((next: TerminalMacro) => {
@@ -497,11 +509,7 @@ export function MacrosPanel({
};
const persist = async (next: TerminalMacro[]) => {
- const serialized = JSON.stringify(next);
- if (storageMode === "local")
- localStorage.setItem("terminalMacros", serialized);
- else await saveUserPreferences({ terminalMacros: serialized });
- setMacros(next);
+ await saveSettings({ macros: next });
};
const save = async () => {
@@ -548,7 +556,7 @@ export function MacrosPanel({
const execute = useCallback(
async (macro: TerminalMacro) => {
- const terminal = target?.terminalRef?.current;
+ const terminal = target ? getSessionHandle(target.id) : null;
if (!terminal?.sendInput || !terminal.subscribeOutput) {
toast.error(t("macros.needTerminal"));
return;
@@ -576,7 +584,7 @@ export function MacrosPanel({
);
const requestRun = (macro: TerminalMacro) => {
- if (!target?.terminalRef?.current?.sendInput) {
+ if (!target || !getSessionHandle(target.id)?.sendInput) {
toast.error(t("macros.needTerminal"));
return;
}
diff --git a/src/ui/lib/terminal-macros.ts b/plugins/ssh-terminal/src/frontend/macros/terminal-macros.ts
similarity index 84%
rename from src/ui/lib/terminal-macros.ts
rename to plugins/ssh-terminal/src/frontend/macros/terminal-macros.ts
index 9d3ccd95c..a99c5a393 100644
--- a/src/ui/lib/terminal-macros.ts
+++ b/plugins/ssh-terminal/src/frontend/macros/terminal-macros.ts
@@ -179,10 +179,11 @@ export async function runTerminalMacro(
}
}
-export function parseTerminalMacros(value?: string | null): TerminalMacro[] {
- if (!value) return [];
+/** Stored macros, bounded and cleaned; accepts the array or its JSON. */
+export function sanitizeTerminalMacros(value: unknown): TerminalMacro[] {
+ if (value === null || value === undefined || value === "") return [];
try {
- const parsed = JSON.parse(value);
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
if (!Array.isArray(parsed)) return [];
let remainingSteps = 500;
const sanitizeSteps = (input: unknown, depth: number): MacroStep[] => {
@@ -289,3 +290,52 @@ export function parseTerminalMacros(value?: string | null): TerminalMacro[] {
return [];
}
}
+
+export function parseTerminalMacros(value?: string | null): TerminalMacro[] {
+ return sanitizeTerminalMacros(value);
+}
+
+/** Where 2.8 kept macros for a user whose storage mode was local. */
+export const LOCAL_MACROS_KEY = "terminalMacros";
+
+/**
+ * The saved macros plus any this browser kept on its own before 2.9.0 that
+ * the saved list does not already have, or null when there is nothing to
+ * move. `clear` removes the browser copy once the merged list is saved.
+ */
+export function mergeLocalMacros(
+ saved: TerminalMacro[],
+ storage: Pick | null = safeStorage(),
+): { macros: TerminalMacro[]; clear: () => void } | null {
+ let raw: string | null = null;
+ try {
+ raw = storage?.getItem(LOCAL_MACROS_KEY) ?? null;
+ } catch {
+ return null;
+ }
+ if (raw === null) return null;
+ const clear = () => {
+ try {
+ storage?.removeItem(LOCAL_MACROS_KEY);
+ } catch {
+ // storage unavailable
+ }
+ };
+ const known = new Set(saved.map((macro) => macro.id));
+ const extra = sanitizeTerminalMacros(raw).filter(
+ (macro) => !known.has(macro.id),
+ );
+ if (extra.length === 0) {
+ clear();
+ return null;
+ }
+ return { macros: [...saved, ...extra].slice(0, 100), clear };
+}
+
+function safeStorage(): Storage | null {
+ try {
+ return typeof localStorage === "undefined" ? null : localStorage;
+ } catch {
+ return null;
+ }
+}
diff --git a/plugins/ssh-terminal/src/frontend/settings/HostTerminalSection.tsx b/plugins/ssh-terminal/src/frontend/settings/HostTerminalSection.tsx
index 67fe37994..87d1dfd08 100644
--- a/plugins/ssh-terminal/src/frontend/settings/HostTerminalSection.tsx
+++ b/plugins/ssh-terminal/src/frontend/settings/HostTerminalSection.tsx
@@ -8,7 +8,6 @@ export function HostTerminalSection({
setField,
updateForm,
host,
- snippets,
}: HostEditorSectionProps) {
return (
<>
@@ -18,7 +17,6 @@ export function HostTerminalSection({
setField={setField}
updateForm={updateForm}
host={host}
- snippets={snippets}
/>
>
);
diff --git a/plugins/ssh-terminal/src/frontend/settings/HostTerminalSettings.tsx b/plugins/ssh-terminal/src/frontend/settings/HostTerminalSettings.tsx
index da7142c4e..6f5a20b11 100644
--- a/plugins/ssh-terminal/src/frontend/settings/HostTerminalSettings.tsx
+++ b/plugins/ssh-terminal/src/frontend/settings/HostTerminalSettings.tsx
@@ -59,22 +59,16 @@ type HostPluginSettings = Record>;
/**
* The host editor's terminal appearance and behavior cards. The values are
* this plugin's host settings, kept on the editor form under pluginSettings
- * and saved with the host; the startup snippet is still a core host field.
+ * and saved with the host.
*/
export function HostTerminalSettings({
form: editorForm,
- setField: setCoreField,
updateForm,
host,
- snippets: rawSnippets,
-}: Pick<
- HostEditorSectionProps,
- "form" | "setField" | "updateForm" | "host" | "snippets"
->) {
+}: Pick) {
const { t } = useTranslation();
const client = useTerminalClientSettings();
const userSettings = useSettings("user");
- const snippets = (rawSnippets ?? []) as { id: number; name: string }[];
const { setPreviewTerminalTheme } = useTabsSafe();
const stored = ((editorForm?.pluginSettings as HostPluginSettings)?.[
@@ -91,8 +85,7 @@ export function HostTerminalSettings({
const form = {
...own,
...(own.inheritAppearance ? userDefaults : {}),
- startupSnippetId: (editorForm?.startupSnippetId ?? null) as number | null,
- } as HostTerminalValues & { startupSnippetId: number | null };
+ } as HostTerminalValues;
const writeValues = (values: Record) =>
updateForm((current) => {
@@ -883,30 +876,6 @@ export function HostTerminalSettings({
/>
)}
-
-
-
- {t("hosts.startupSnippetLabel")}
-
-
- setCoreField(
- "startupSnippetId",
- e.target.value ? Number(e.target.value) : null,
- )
- }
- className="flex h-9 w-full border border-border bg-background px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
- >
- {t("hosts.none")}
- {snippets.map((s) => (
-
- {s.name}
-
- ))}
-
-
-
>
diff --git a/plugins/ssh-terminal/src/frontend/terminal/Terminal.tsx b/plugins/ssh-terminal/src/frontend/terminal/Terminal.tsx
index b68748f7d..f1f1e3efb 100644
--- a/plugins/ssh-terminal/src/frontend/terminal/Terminal.tsx
+++ b/plugins/ssh-terminal/src/frontend/terminal/Terminal.tsx
@@ -74,7 +74,7 @@ import { toast } from "sonner";
import { Save } from "lucide-react";
import { TerminalToolbar } from "./TerminalToolbar.tsx";
import type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts";
-import type { Host, Snippet, TabType } from "../types";
+import type { Host, TabType } from "../types";
import { isTabKeyEvent } from "./terminal-key-event.ts";
import { installTouchWheelCoordinator } from "./touch-wheel-coordinator.ts";
import { loadTouchInputSettings } from "./touch-input-settings-store.ts";
@@ -85,6 +85,7 @@ import {
import { quoteTerminalImagePath } from "./terminal-image-path.ts";
import {
dispatchKeybindingAction,
+ isTerminalKeybindingAction,
sendRawToSocket,
} from "../lib/keybinding-dispatch";
import {
@@ -114,7 +115,6 @@ import {
Button,
hydrateLocalSharedHostAuth,
findMatchingKeybinding,
- SnippetVariablesDialog,
type CustomKeybinding,
isElectron,
} from "@termix/plugin-sdk/ui";
@@ -124,6 +124,7 @@ import {
usePluginApi,
useSlotContributions,
getCustomKeybindings,
+ runKeybindingAction,
getClientPreference,
logActivity,
getHostPassword,
@@ -164,17 +165,6 @@ interface SSHTerminalProps {
onSessionReady?: (sessionId: string) => void;
}
-/**
- * The host's startup snippet. Core still keeps it in the host's terminal
- * config until snippets owns it.
- */
-function startupSnippetOf(host: object): number | null {
- const config = (host as { terminalConfig?: { startupSnippetId?: unknown } })
- .terminalConfig;
- const id = Number(config?.startupSnippetId);
- return Number.isInteger(id) && id > 0 ? id : null;
-}
-
const ALTERNATE_SCREEN_SEQUENCE = /\x1b\[\?(47|1047|1049)([hl])/g;
function updateAlternateScreenMode(output: string, currentMode: boolean) {
@@ -291,11 +281,6 @@ const TerminalInner = forwardRef(
const terminalInputDisposableRef = useRef<{ dispose(): void } | null>(null);
const localEchoRef = useRef(null);
const customKeybindingsRef = useRef([]);
- const [pendingKeybindingSnippet, setPendingKeybindingSnippet] = useState<{
- id: string;
- content: string;
- appendEnter: boolean;
- } | null>(null);
const resizeTimeout = useRef(null);
const wasDisconnectedBySSH = useRef(false);
const pingIntervalRef = useRef(null);
@@ -1966,7 +1951,6 @@ const TerminalInner = forwardRef(
| null
| undefined
)?.environmentVariables ?? [];
- const startupSnippetId = startupSnippetOf(settingsHost);
if (environmentVariables.length > 0) {
for (const envVar of environmentVariables) {
@@ -1981,36 +1965,26 @@ const TerminalInner = forwardRef(
}
}
- if (startupSnippetId) {
- try {
- const resolved = (await invokeAction(
- "snippets.resolveForTerminal",
- startupSnippetId,
- {
- ip: hostConfig.ip,
- username: hostConfig.username,
- port: hostConfig.port,
- name: hostConfig.name,
- },
- )) as
- | { needsInputs: boolean; content: string }
- | null
- | undefined;
- if (
- resolved &&
- !resolved.needsInputs &&
- ws.readyState === 1
- ) {
- ws.send(
- JSON.stringify({
- type: "input",
- data: resolved.content + "\n",
- }),
- );
- }
- } catch (err) {
- console.warn("Failed to execute startup snippet:", err);
+ // The snippets plugin owns a host's startup command; while it
+ // is off nothing runs.
+ try {
+ const startup = await invokeAction(
+ "snippets.startupCommand",
+ settingsHost,
+ {
+ ip: hostConfig.ip,
+ username: hostConfig.username,
+ port: hostConfig.port,
+ name: hostConfig.name,
+ },
+ );
+ if (typeof startup === "string" && ws.readyState === 1) {
+ ws.send(
+ JSON.stringify({ type: "input", data: startup + "\n" }),
+ );
}
+ } catch (err) {
+ console.warn("Failed to run the startup command:", err);
}
if (settings.autoMosh && ws.readyState === 1) {
@@ -2988,26 +2962,30 @@ const TerminalInner = forwardRef(
e,
customKeybindingsRef.current,
);
- if (matched) {
+ // The terminal's own actions run here; anything else goes to
+ // whoever registered it (the shell, or another plugin), and the key
+ // passes through while nothing does.
+ const handled =
+ !!matched &&
+ (isTerminalKeybindingAction(matched.action.type)
+ ? dispatchKeybindingAction(matched.action, {
+ terminal,
+ webSocketRef,
+ writeTextToClipboard,
+ readTextFromClipboard,
+ })
+ : runKeybindingAction(matched.action, {
+ host: {
+ ip: hostConfig.ip,
+ username: hostConfig.username,
+ port: hostConfig.port,
+ name: hostConfig.name,
+ },
+ send: (data) => sendRawToSocket(webSocketRef, data),
+ }));
+ if (handled) {
e.preventDefault();
e.stopPropagation();
- dispatchKeybindingAction(matched.action, {
- terminal,
- webSocketRef,
- writeTextToClipboard,
- readTextFromClipboard,
- hostContext: {
- ip: hostConfig.ip,
- username: hostConfig.username,
- port: hostConfig.port,
- name: hostConfig.name,
- },
- onSnippetNeedsInputs: (snippet) =>
- setPendingKeybindingSnippet({
- ...snippet,
- appendEnter: matched.action.appendEnter !== false,
- }),
- });
return false;
}
}
@@ -3834,51 +3812,6 @@ const TerminalInner = forwardRef(
/>
)}
- {pendingKeybindingSnippet && (
- setPendingKeybindingSnippet(null)}
- onConfirm={(resolvedContent) => {
- const appendEnter = pendingKeybindingSnippet.appendEnter;
- setPendingKeybindingSnippet(null);
- const send = () =>
- sendRawToSocket(
- webSocketRef,
- resolvedContent + (appendEnter ? "\r" : ""),
- );
- const shouldConfirm =
- localStorage.getItem("confirmSnippetExecution") === "true";
- if (shouldConfirm) {
- confirmWithToast(
- t("newUi.sidebar.snippets.confirmRunMessage", {
- name: t("newUi.sidebar.keybindings.actionRunSnippet"),
- }),
- send,
- t("newUi.sidebar.snippets.confirmRunButton"),
- t("newUi.sidebar.snippets.cancel"),
- { confirmOnEnter: true, duration: 6000 },
- );
- } else {
- send();
- }
- }}
- />
- )}
-
{tmuxSessionPicker && (
{
it("rejects relative image paths", () => {
@@ -19,3 +22,28 @@ describe("validateAdminSettings", () => {
).toEqual({});
});
});
+
+describe("validateUserSettings", () => {
+ const macro = { id: "m", name: "M", steps: [] };
+
+ it("accepts bounded macros and a save without them", () => {
+ expect(validateUserSettings({ macros: [macro] })).toEqual({});
+ expect(validateUserSettings({ macros: JSON.stringify([macro]) })).toEqual(
+ {},
+ );
+ expect(validateUserSettings({ localEcho: "on" })).toEqual({});
+ });
+
+ it("refuses malformed or too many macros", () => {
+ expect(validateUserSettings({ macros: [{ id: 1 }] }).macros).toBeTruthy();
+ expect(validateUserSettings({ macros: "{" }).macros).toBeTruthy();
+ expect(
+ validateUserSettings({
+ macros: Array.from({ length: 101 }, (_, i) => ({
+ ...macro,
+ id: `${i}`,
+ })),
+ }).macros,
+ ).toBeTruthy();
+ });
+});
diff --git a/plugins/ssh-terminal/tests/frontend/lib/keybinding-dispatch.test.ts b/plugins/ssh-terminal/tests/frontend/lib/keybinding-dispatch.test.ts
index 80ef2b075..b348ca0ce 100644
--- a/plugins/ssh-terminal/tests/frontend/lib/keybinding-dispatch.test.ts
+++ b/plugins/ssh-terminal/tests/frontend/lib/keybinding-dispatch.test.ts
@@ -1,10 +1,12 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
-import { dispatchKeybindingAction } from "../../../src/frontend/lib/keybinding-dispatch";
+import {
+ dispatchKeybindingAction,
+ isTerminalKeybindingAction,
+ validateSendControlCode,
+ validateSendText,
+} from "../../../src/frontend/lib/keybinding-dispatch";
import type { Terminal } from "@xterm/xterm";
import type { KeybindingDispatchContext } from "../../../src/frontend/lib/keybinding-dispatch";
-import * as sdkFrontend from "@termix/plugin-sdk/frontend";
-
-vi.mock("@termix/plugin-sdk/frontend", () => ({ invokeAction: vi.fn() }));
function makeContext(
overrides: Partial = {},
@@ -90,65 +92,36 @@ describe("dispatchKeybindingAction", () => {
expect(ctx.sentData).toEqual(["ls -la\r"]);
});
- it("runSnippet resolves the snippet through the snippets.resolveForTerminal action and sends its content with a trailing return", async () => {
- vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
- needsInputs: false,
- content: "uptime",
- });
+ it("leaves any other action to whoever registered it", () => {
const ctx = makeContext();
- dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
- await Promise.resolve();
- await Promise.resolve();
- expect(sdkFrontend.invokeAction).toHaveBeenCalledWith(
- "snippets.resolveForTerminal",
- 1,
- null,
- );
- expect(ctx.sentData).toEqual(["uptime\r"]);
- });
-
- it("runSnippet does nothing when the snippet no longer exists", async () => {
- vi.mocked(sdkFrontend.invokeAction).mockResolvedValue(undefined);
- const ctx = makeContext();
- dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
- await Promise.resolve();
- await Promise.resolve();
+ expect(
+ dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx),
+ ).toBe(false);
+ expect(dispatchKeybindingAction({ type: "nextTab" }, ctx)).toBe(false);
+ expect(dispatchKeybindingAction({ type: "copy" }, ctx)).toBe(true);
expect(ctx.sentData).toEqual([]);
});
- it("runSnippet passes hostContext through to the resolve action", async () => {
- vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
- needsInputs: false,
- content: "ping 10.0.0.5 -p 22",
- });
- const ctx = makeContext({
- hostContext: { ip: "10.0.0.5", username: "root", port: 22 },
- });
- dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
- await Promise.resolve();
- await Promise.resolve();
- expect(sdkFrontend.invokeAction).toHaveBeenCalledWith(
- "snippets.resolveForTerminal",
- 1,
- { ip: "10.0.0.5", username: "root", port: 22 },
+ it("knows which actions are its own", () => {
+ expect(isTerminalKeybindingAction("sendText")).toBe(true);
+ expect(isTerminalKeybindingAction("runSnippet")).toBe(false);
+ });
+});
+
+describe("keybinding validators", () => {
+ it("needs text to send", () => {
+ expect(validateSendText({ type: "sendText", text: " " })).toBe(
+ "keybindings.textRequired",
);
- expect(ctx.sentData).toEqual(["ping 10.0.0.5 -p 22\r"]);
+ expect(validateSendText({ type: "sendText", text: "ls" })).toBeNull();
});
- it("runSnippet defers to onSnippetNeedsInputs instead of sending when the action reports needsInputs", async () => {
- vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
- needsInputs: true,
- content: "echo $INPUT_1",
- });
- const onSnippetNeedsInputs = vi.fn();
- const ctx = makeContext({ onSnippetNeedsInputs });
- dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
- await Promise.resolve();
- await Promise.resolve();
- expect(ctx.sentData).toEqual([]);
- expect(onSnippetNeedsInputs).toHaveBeenCalledWith({
- id: "1",
- content: "echo $INPUT_1",
- });
+ it("needs a single letter for a control code", () => {
+ expect(
+ validateSendControlCode({ type: "sendControlCode", controlCode: "ab" }),
+ ).toBe("keybindings.controlCodeRequired");
+ expect(
+ validateSendControlCode({ type: "sendControlCode", controlCode: "w" }),
+ ).toBeNull();
});
});
diff --git a/src/ui/tests/lib/terminal-macros.test.ts b/plugins/ssh-terminal/tests/frontend/macros/terminal-macros.test.ts
similarity index 74%
rename from src/ui/tests/lib/terminal-macros.test.ts
rename to plugins/ssh-terminal/tests/frontend/macros/terminal-macros.test.ts
index 21bead6c7..ecff473af 100644
--- a/src/ui/tests/lib/terminal-macros.test.ts
+++ b/plugins/ssh-terminal/tests/frontend/macros/terminal-macros.test.ts
@@ -1,10 +1,12 @@
import { describe, expect, it, vi } from "vitest";
import {
+ mergeLocalMacros,
parseTerminalMacros,
+ sanitizeTerminalMacros,
runTerminalMacro,
type MacroStep,
type TerminalMacro,
-} from "@/lib/terminal-macros";
+} from "../../../src/frontend/macros/terminal-macros";
function macro(steps: MacroStep[]): TerminalMacro {
return {
@@ -183,3 +185,48 @@ describe("terminal macro runner", () => {
expect(sent).toEqual(["no"]);
});
});
+
+describe("mergeLocalMacros", () => {
+ function storage(value: string | null) {
+ const store = new Map();
+ if (value !== null) store.set("terminalMacros", value);
+ return {
+ store,
+ getItem: (key: string) => store.get(key) ?? null,
+ removeItem: (key: string) => void store.delete(key),
+ };
+ }
+ const saved = (id: string): TerminalMacro => ({ ...macro([]), id });
+
+ it("returns null when the browser kept nothing", () => {
+ expect(mergeLocalMacros([saved("a")], storage(null))).toBeNull();
+ });
+
+ it("adds the browser's macros the saved list does not have", () => {
+ const local = storage(
+ JSON.stringify([
+ { id: "a", name: "Dupe", steps: [] },
+ { id: "b", name: "Local", steps: [] },
+ ]),
+ );
+ const merged = mergeLocalMacros([saved("a")], local);
+ expect(merged?.macros.map((m) => m.id)).toEqual(["a", "b"]);
+ expect(local.store.has("terminalMacros")).toBe(true);
+ merged?.clear();
+ expect(local.store.has("terminalMacros")).toBe(false);
+ });
+
+ it("clears the key when everything is already saved", () => {
+ const local = storage(JSON.stringify([{ id: "a", name: "A", steps: [] }]));
+ expect(mergeLocalMacros([saved("a")], local)).toBeNull();
+ expect(local.store.has("terminalMacros")).toBe(false);
+ });
+
+ it("reads a stored array as well as its JSON", () => {
+ expect(
+ sanitizeTerminalMacros([{ id: "x", name: "X", steps: [] }]).map(
+ (m) => m.id,
+ ),
+ ).toEqual(["x"]);
+ });
+});
diff --git a/plugins/ssh-terminal/tests/frontend/settings/HostTerminalSettings.test.tsx b/plugins/ssh-terminal/tests/frontend/settings/HostTerminalSettings.test.tsx
index 683b03c5f..7133ea78f 100644
--- a/plugins/ssh-terminal/tests/frontend/settings/HostTerminalSettings.test.tsx
+++ b/plugins/ssh-terminal/tests/frontend/settings/HostTerminalSettings.test.tsx
@@ -30,7 +30,6 @@ function renderSettings(form: Form = { pluginSettings: {} }) {
setField={setField}
updateForm={updateForm}
host={{ id: "1", name: "box", ip: "10.0.0.1", port: 22 }}
- snippets={[{ id: 7, name: "deploy" }]}
/>,
);
return { setField, updateForm, current: () => current };
@@ -56,13 +55,9 @@ describe("HostTerminalSettings", () => {
expect(screen.getByText("hosts.sudoPasswordAutoFillLabel")).toBeTruthy();
});
- it("sets the startup snippet, a core host field, from the offered snippets", () => {
- const { setField } = renderSettings();
- const select = screen
- .getByRole("option", { name: "deploy" })
- .closest("select")!;
- fireEvent.change(select, { target: { value: "7" } });
- expect(setField).toHaveBeenCalledWith("startupSnippetId", 7);
+ it("no longer draws the startup snippet, which is the snippets plugin's", () => {
+ renderSettings();
+ expect(screen.queryByText("hosts.startupSnippetLabel")).toBeNull();
});
it("writes its values into the host's ssh-terminal settings", () => {
diff --git a/src/backend/database/routes/host-bulk-routes.ts b/src/backend/database/routes/host-bulk-routes.ts
index 13555133f..40f961502 100644
--- a/src/backend/database/routes/host-bulk-routes.ts
+++ b/src/backend/database/routes/host-bulk-routes.ts
@@ -668,9 +668,6 @@ export function registerHostBulkRoutes(
pin: hostData.pin || false,
sudoPassword: hostData.sudoPassword || null,
jumpHosts: jumpHosts ? JSON.stringify(jumpHosts) : null,
- quickActions: hostData.quickActions
- ? JSON.stringify(hostData.quickActions)
- : null,
...importedStatusCheck(hostData as Record),
terminalConfig: hostData.terminalConfig
? JSON.stringify(hostData.terminalConfig)
@@ -921,7 +918,6 @@ export function registerHostBulkRoutes(
jumpHosts: hostData.jumpHosts
? JSON.stringify(hostData.jumpHosts)
: null,
- quickActions: null,
statusCheckEnabled: true,
statusCheckInterval: null,
terminalConfig: null,
diff --git a/src/backend/database/routes/host-normalizers.ts b/src/backend/database/routes/host-normalizers.ts
index 5dd119bfd..a03600824 100644
--- a/src/backend/database/routes/host-normalizers.ts
+++ b/src/backend/database/routes/host-normalizers.ts
@@ -179,7 +179,6 @@ export type NormalizedImportedHost = Record & {
pin?: unknown;
sudoPassword?: unknown;
jumpHosts?: unknown;
- quickActions?: unknown;
statusCheckEnabled?: unknown;
statusCheckInterval?: unknown;
terminalConfig?: unknown;
@@ -418,11 +417,11 @@ function parseJsonObject(value: unknown): Record | null {
}
/**
- * sshOptions, and terminalConfig in the shape 2.8 clients and exports read: core's own
- * keys only (the SSH options and the startup snippet). The terminal's look
- * and behavior are the ssh-terminal plugin's host settings, which it puts
- * back through its hostPayloadLegacy. A sudo password 2.8 kept in
- * terminal_config comes out as sudoPassword, for the sanitizers to handle.
+ * sshOptions, and terminalConfig in the shape 2.8 clients and exports read:
+ * core's own keys only, the SSH options. Everything else 2.8 kept there now
+ * belongs to plugins' host settings, which they put back through their
+ * hostPayloadLegacy. A sudo password 2.8 kept in terminal_config comes out as
+ * sudoPassword, for the sanitizers to handle.
*/
export function hostTerminalExport(host: Record): {
sshOptions: HostSshOptions;
@@ -434,9 +433,6 @@ export function hostTerminalExport(host: Record): {
host.sshOptions != null ? host.sshOptions : raw,
);
const terminalConfig: Record = { ...sshOptions };
- if (raw && raw.startupSnippetId !== undefined) {
- terminalConfig.startupSnippetId = raw.startupSnippetId;
- }
const legacySudo = !host.sudoPassword ? raw?.sudoPassword : undefined;
return {
sshOptions,
@@ -477,9 +473,9 @@ export function transformHostResponse(
vncUser: host.vncUser || undefined,
telnetUser: host.telnetUser || undefined,
jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts as string) : [],
- quickActions: host.quickActions
- ? JSON.parse(host.quickActions as string)
- : [],
+ // Moved into a plugin's host settings; its hostPayloadLegacy puts it
+ // back for 2.8 clients.
+ quickActions: undefined,
statusCheckEnabled:
host.statusCheckEnabled !== false && host.statusCheckEnabled !== 0,
statusCheckInterval:
diff --git a/src/backend/database/routes/host.ts b/src/backend/database/routes/host.ts
index 6617496be..1c47462b5 100644
--- a/src/backend/database/routes/host.ts
+++ b/src/backend/database/routes/host.ts
@@ -194,7 +194,6 @@ router.post(
sudoPassword,
pin,
jumpHosts,
- quickActions,
statusCheckEnabled,
statusCheckInterval,
terminalConfig,
@@ -296,9 +295,6 @@ router.post(
overrideCredentialUsername: overrideCredentialUsername ? 1 : 0,
pin: pin ? 1 : 0,
jumpHosts: Array.isArray(jumpHosts) ? JSON.stringify(jumpHosts) : null,
- quickActions: Array.isArray(quickActions)
- ? JSON.stringify(quickActions)
- : null,
statusCheckEnabled: statusCheckEnabled === false ? 0 : 1,
statusCheckInterval: normalizeStatusInterval(statusCheckInterval),
terminalConfig: terminalConfig
@@ -682,7 +678,6 @@ router.post(
keyPassword: resolvedKeyPassword,
keyType: resolvedKeyType,
jumpHosts: [],
- quickActions: [],
statusCheckEnabled: true,
statusCheckInterval: null,
notes: "",
@@ -794,7 +789,6 @@ router.put(
sudoPassword,
pin,
jumpHosts,
- quickActions,
statusCheckEnabled,
statusCheckInterval,
terminalConfig,
@@ -897,9 +891,6 @@ router.put(
overrideCredentialUsername: overrideCredentialUsername ? 1 : 0,
pin: pin ? 1 : 0,
jumpHosts: Array.isArray(jumpHosts) ? JSON.stringify(jumpHosts) : null,
- quickActions: Array.isArray(quickActions)
- ? JSON.stringify(quickActions)
- : null,
statusCheckEnabled: statusCheckEnabled === false ? 0 : 1,
statusCheckInterval: normalizeStatusInterval(statusCheckInterval),
terminalConfig: terminalConfig
@@ -1768,9 +1759,6 @@ router.get(
jumpHosts: resolvedHost.jumpHosts
? JSON.parse(resolvedHost.jumpHosts as string)
: null,
- quickActions: resolvedHost.quickActions
- ? JSON.parse(resolvedHost.quickActions as string)
- : null,
terminalConfig:
hostTerminalExport(resolvedHost).terminalConfig ?? null,
sshOptions: hostTerminalExport(resolvedHost).sshOptions,
@@ -1903,9 +1891,6 @@ router.get(
jumpHosts: resolvedHost.jumpHosts
? JSON.parse(resolvedHost.jumpHosts as string)
: null,
- quickActions: resolvedHost.quickActions
- ? JSON.parse(resolvedHost.quickActions as string)
- : null,
terminalConfig:
hostTerminalExport(resolvedHost).terminalConfig ?? null,
sshOptions: hostTerminalExport(resolvedHost).sshOptions,
diff --git a/src/backend/database/routes/keybinding-validation.ts b/src/backend/database/routes/keybinding-validation.ts
index 11911049f..f616e706d 100644
--- a/src/backend/database/routes/keybinding-validation.ts
+++ b/src/backend/database/routes/keybinding-validation.ts
@@ -1,13 +1,31 @@
-const VALID_ACTION_TYPES = [
- "copy",
- "paste",
- "sendControlCode",
- "sendText",
- "runSnippet",
- "nextTab",
- "previousTab",
- "openCommandPalette",
-];
+import {
+ CORE_KEYBINDING_ACTIONS,
+ validateKeybindingActionParams,
+ type PluginKeybindingActionContribution,
+} from "@termix/plugin-sdk/manifest";
+
+export interface DeclaredKeybindingAction extends PluginKeybindingActionContribution {
+ pluginId: string;
+}
+
+let declaredActions: () => DeclaredKeybindingAction[] = () => [];
+
+/** Set by the plugin runtime: every installed plugin's declared actions. */
+export function setKeybindingActionSource(
+ source: () => DeclaredKeybindingAction[],
+): void {
+ declaredActions = source;
+}
+
+/** The declaration a saved action type answers to, if a plugin made one. */
+export function findKeybindingAction(
+ type: string,
+): DeclaredKeybindingAction | undefined {
+ return declaredActions().find((action) => action.id === type);
+}
+
+const ACTION_TYPE_PATTERN = /^[a-zA-Z][a-zA-Z0-9.-]{0,63}$/;
+const MAX_LOOSE_VALUE = 65_536;
export function isValidKeyCombo(combo: unknown): boolean {
return (
@@ -22,22 +40,32 @@ export function isValidKeyCombo(combo: unknown): boolean {
);
}
+/**
+ * A saved action. The shell's own types take no parameters; a type a plugin
+ * declared is checked against its declaration, whether or not the plugin is
+ * running. A type nobody declares (its plugin was removed) is only checked
+ * loosely, so the rest of the list can still be saved, and whichever plugin
+ * claims it later ignores what it cannot use.
+ */
export function isValidKeybindingAction(action: unknown): boolean {
- if (!action || typeof action !== "object") return false;
- const type = (action as { type?: unknown }).type;
- if (typeof type !== "string" || !VALID_ACTION_TYPES.includes(type))
+ if (!action || typeof action !== "object" || Array.isArray(action)) {
return false;
- if (type === "sendText") {
- return typeof (action as { text?: unknown }).text === "string";
}
- if (type === "sendControlCode") {
- const code = (action as { controlCode?: unknown }).controlCode;
- return typeof code === "string" && /^[a-zA-Z]$/.test(code);
+ const record = action as Record;
+ const type = record.type;
+ if (typeof type !== "string" || !ACTION_TYPE_PATTERN.test(type)) {
+ return false;
}
- if (type === "runSnippet") {
- return typeof (action as { snippetId?: unknown }).snippetId === "string";
- }
- return true;
+ if (CORE_KEYBINDING_ACTIONS.includes(type)) return true;
+ const declared = findKeybindingAction(type);
+ if (declared) return validateKeybindingActionParams(record, declared);
+ return Object.entries(record).every(
+ ([, value]) =>
+ value === null ||
+ typeof value === "boolean" ||
+ typeof value === "number" ||
+ (typeof value === "string" && value.length <= MAX_LOOSE_VALUE),
+ );
}
export function isValidKeybinding(entry: unknown): boolean {
diff --git a/src/backend/database/routes/user-admin-routes.ts b/src/backend/database/routes/user-admin-routes.ts
index 33d176bdd..cf723d4a4 100644
--- a/src/backend/database/routes/user-admin-routes.ts
+++ b/src/backend/database/routes/user-admin-routes.ts
@@ -647,7 +647,7 @@ export function registerUserAdminRoutes(
if (outcome.status === "wipe_confirmation_required") {
return res.status(409).json({
error:
- "This user has not logged in since the encryption upgrade, so their data cannot be recovered. Set confirmDataWipe to reset anyway and delete their hosts, credentials and snippets.",
+ "This user has not logged in since the encryption upgrade, so their data cannot be recovered. Set confirmDataWipe to reset anyway and delete their hosts, credentials and other saved data.",
code: "DATA_WIPE_REQUIRED",
});
}
diff --git a/src/backend/database/routes/user-password-reset-routes.ts b/src/backend/database/routes/user-password-reset-routes.ts
index 70fed4fbb..ef5b57a87 100644
--- a/src/backend/database/routes/user-password-reset-routes.ts
+++ b/src/backend/database/routes/user-password-reset-routes.ts
@@ -71,7 +71,7 @@ export async function resetUserPassword(
await createCurrentRecentActivityRepository().deleteByUserId(userId);
await createCurrentHostRepository().deleteByUserId(userId);
await createCurrentCredentialRepository().deleteByUserId(userId);
- // A plugin holding this user's data (snippets and anything else keyed by
+ // A plugin holding this user's data (anything keyed by
// refUser()) wipes it on this topic, since the user row itself survives.
pluginEvents.emit(TOPICS.userDataWiped, { userId });
@@ -416,7 +416,7 @@ export function registerUserPasswordResetRoutes(
if (outcome.status === "wipe_confirmation_required") {
return res.status(409).json({
error:
- "This account has not logged in since the encryption upgrade, so its stored data cannot be recovered without the old password. Resetting will permanently delete its hosts, credentials and snippets.",
+ "This account has not logged in since the encryption upgrade, so its stored data cannot be recovered without the old password. Resetting will permanently delete its hosts, credentials and other saved data.",
code: "DATA_WIPE_REQUIRED",
});
}
diff --git a/src/backend/database/routes/user-preferences.ts b/src/backend/database/routes/user-preferences.ts
index 8e564f9df..e79cd90ac 100644
--- a/src/backend/database/routes/user-preferences.ts
+++ b/src/backend/database/routes/user-preferences.ts
@@ -27,8 +27,6 @@ const pickPreferences = (row?: UserPreferenceRecord | null) => ({
pinAppRail: row?.pinAppRail ?? null,
expandAppRailOnHover: row?.expandAppRailOnHover ?? null,
showPinAppRailButton: row?.showPinAppRailButton ?? null,
- foldersCollapsed: row?.foldersCollapsed ?? null,
- confirmSnippetExecution: row?.confirmSnippetExecution ?? null,
disableUpdateCheck: row?.disableUpdateCheck ?? null,
confirmTabClose: row?.confirmTabClose ?? null,
hiddenRailTabs: row?.hiddenRailTabs ?? null,
@@ -37,7 +35,6 @@ const pickPreferences = (row?: UserPreferenceRecord | null) => ({
customThemes: row?.customThemes ?? null,
customKeybindings: row?.customKeybindings ?? null,
terminalDefaults: row?.terminalDefaults ?? null,
- terminalMacros: row?.terminalMacros ?? null,
});
const connectionDefaultFields = ["terminalDefaults"] as const;
@@ -57,7 +54,7 @@ export function validateDefaultsJson(value: string): boolean {
* /user-preferences:
* get:
* summary: Get preferences for the current user
- * description: showHostTags, hostTrayOnClick, compactHostView and statusColorScheme are legacy fields, kept here read-only for backward compatibility; the authoritative copy is GET /host-sidebar/preferences. foldersCollapsed is likewise legacy and read-only; its authoritative copy is the snippets plugin's own user settings.
+ * description: showHostTags, hostTrayOnClick, compactHostView and statusColorScheme are legacy fields, kept here read-only for backward compatibility; the authoritative copy is GET /host-sidebar/preferences.
* tags:
* - User Preferences
* responses:
@@ -106,12 +103,6 @@ export function validateDefaultsJson(value: string): boolean {
* showPinAppRailButton:
* type: boolean
* nullable: true
- * foldersCollapsed:
- * type: boolean
- * nullable: true
- * confirmSnippetExecution:
- * type: boolean
- * nullable: true
* disableUpdateCheck:
* type: boolean
* nullable: true
@@ -134,7 +125,7 @@ export function validateDefaultsJson(value: string): boolean {
* customKeybindings:
* type: string
* nullable: true
- * description: JSON-encoded array of the user's custom terminal keybindings.
+ * description: JSON-encoded array of the user's custom keybindings. An action's type is one of the shell's own or one a plugin declares in contributes.keybindingActions.
*/
router.get("/", authenticateJWT, async (req: Request, res: Response) => {
const userId = (req as AuthenticatedRequest).userId;
@@ -157,7 +148,7 @@ router.get("/", authenticateJWT, async (req: Request, res: Response) => {
* /user-preferences:
* put:
* summary: Update preferences for the current user
- * description: showHostTags, hostTrayOnClick, compactHostView and statusColorScheme are no longer accepted here -- they moved to PUT /host-sidebar/preferences as part of the sidebar redesign. foldersCollapsed is no longer accepted either, now that it is a snippets plugin user setting.
+ * description: showHostTags, hostTrayOnClick, compactHostView and statusColorScheme are no longer accepted here -- they moved to PUT /host-sidebar/preferences as part of the sidebar redesign. Values that moved into a feature's own user settings are ignored too.
* tags:
* - User Preferences
* requestBody:
@@ -189,8 +180,6 @@ router.get("/", authenticateJWT, async (req: Request, res: Response) => {
* type: boolean
* showPinAppRailButton:
* type: boolean
- * confirmSnippetExecution:
- * type: boolean
* disableUpdateCheck:
* type: boolean
* confirmTabClose:
@@ -202,7 +191,7 @@ router.get("/", authenticateJWT, async (req: Request, res: Response) => {
* description: JSON-encoded array of the user's saved global custom terminal themes.
* customKeybindings:
* type: string
- * description: JSON-encoded array of the user's custom terminal keybindings.
+ * description: JSON-encoded array of the user's custom keybindings. Each action is checked against the shell's own types and the parameters a plugin declares for its type.
* responses:
* 200:
* description: Preferences updated successfully.
@@ -221,14 +210,12 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
pinAppRail,
expandAppRailOnHover,
showPinAppRailButton,
- confirmSnippetExecution,
disableUpdateCheck,
confirmTabClose,
hiddenRailTabs,
customThemes,
customKeybindings,
terminalDefaults,
- terminalMacros,
} = req.body as {
reopenTabsOnLogin?: boolean;
theme?: string | null;
@@ -241,21 +228,19 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
pinAppRail?: boolean | null;
expandAppRailOnHover?: boolean | null;
showPinAppRailButton?: boolean | null;
- confirmSnippetExecution?: boolean | null;
disableUpdateCheck?: boolean | null;
confirmTabClose?: boolean | null;
hiddenRailTabs?: string | null;
customThemes?: string | null;
customKeybindings?: string | null;
terminalDefaults?: string | null;
- terminalMacros?: string | null;
};
// showHostTags, hostTrayOnClick, compactHostView, statusColorScheme are no
// longer writable here -- they moved to /host-sidebar/preferences as of the
// sidebar redesign. The columns stay in the table (read once as a
// migration seed by that route) but this endpoint silently ignores them if
- // a stale client still sends them. foldersCollapsed similarly moved out,
- // to the snippets plugin's own user settings.
+ // a stale client still sends them. The same goes for the preferences that
+ // moved into a plugin's own user settings.
const updates: UserPreferenceUpdate = {
updatedAt: new Date().toISOString(),
@@ -280,7 +265,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
customThemes,
customKeybindings,
terminalDefaults,
- terminalMacros,
})) {
if (value !== undefined && value !== null && typeof value !== "string") {
return res.status(400).json({ error: `${key} must be a string` });
@@ -349,41 +333,12 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
}
}
- if (terminalMacros !== undefined && terminalMacros !== null) {
- let parsedMacros: unknown;
- try {
- parsedMacros = JSON.parse(terminalMacros);
- } catch {
- return res
- .status(400)
- .json({ error: "terminalMacros must be a JSON-encoded array" });
- }
- if (
- terminalMacros.length > 512 * 1024 ||
- !Array.isArray(parsedMacros) ||
- parsedMacros.length > 100 ||
- !parsedMacros.every(
- (macro) =>
- !!macro &&
- typeof macro === "object" &&
- typeof (macro as { id?: unknown }).id === "string" &&
- typeof (macro as { name?: unknown }).name === "string" &&
- Array.isArray((macro as { steps?: unknown }).steps),
- )
- ) {
- return res.status(400).json({
- error: "terminalMacros must contain at most 100 valid macros",
- });
- }
- }
-
const boolFields: Record = {
commandAutocomplete,
commandPaletteEnabled,
pinAppRail,
expandAppRailOnHover,
showPinAppRailButton,
- confirmSnippetExecution,
disableUpdateCheck,
confirmTabClose,
};
@@ -408,8 +363,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
updates.expandAppRailOnHover = expandAppRailOnHover;
if (showPinAppRailButton !== undefined)
updates.showPinAppRailButton = showPinAppRailButton;
- if (confirmSnippetExecution !== undefined)
- updates.confirmSnippetExecution = confirmSnippetExecution;
if (disableUpdateCheck !== undefined)
updates.disableUpdateCheck = disableUpdateCheck;
if (confirmTabClose !== undefined) updates.confirmTabClose = confirmTabClose;
@@ -418,7 +371,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
updates.customKeybindings = customKeybindings;
if (terminalDefaults !== undefined)
updates.terminalDefaults = terminalDefaults;
- if (terminalMacros !== undefined) updates.terminalMacros = terminalMacros;
if (Object.keys(updates).length === 1) {
return res.status(400).json({ error: "No preferences provided" });
diff --git a/src/backend/hosts/host-resolver.ts b/src/backend/hosts/host-resolver.ts
index 0f6d8a512..5a4317b97 100644
--- a/src/backend/hosts/host-resolver.ts
+++ b/src/backend/hosts/host-resolver.ts
@@ -143,13 +143,6 @@ export async function resolveHostById(
host.socks5ProxyChain = [];
}
}
- if (typeof host.quickActions === "string" && host.quickActions) {
- try {
- host.quickActions = JSON.parse(host.quickActions as string);
- } catch {
- host.quickActions = [];
- }
- }
if (typeof host.portKnockSequence === "string" && host.portKnockSequence) {
try {
host.portKnockSequence = JSON.parse(host.portKnockSequence as string);
diff --git a/src/backend/plugins/index.ts b/src/backend/plugins/index.ts
index 27a3afcbd..e6b0d5288 100644
--- a/src/backend/plugins/index.ts
+++ b/src/backend/plugins/index.ts
@@ -21,6 +21,8 @@ import { invalidatePluginPermissionCache } from "./permissions.js";
import { setSshAuthTypeOwnerSource } from "../hosts/connect/auth-provider-registry.js";
import { setSecretResolverOwnerSource } from "../hosts/connect/secret-resolver-registry.js";
import { recordConflict } from "./conflicts.js";
+import { setPluginImpersonationCheck } from "../utils/auth-manager.js";
+import { setKeybindingActionSource } from "../database/routes/keybinding-validation.js";
let loader: PluginLoader | null = null;
@@ -34,6 +36,23 @@ export function getPluginRuntime(): { loader: PluginLoader } {
(pluginId) => loader?.get(pluginId)?.state === "active",
);
setPluginInstalledCheck((pluginId) => !!loader?.get(pluginId));
+ // An admin acting for another user reaches only the plugins that opted in.
+ setPluginImpersonationCheck((pluginId) => {
+ const plugin = loader?.get(pluginId);
+ return (
+ plugin?.state === "active" &&
+ plugin.manifest.contributes?.http?.adminImpersonation === true
+ );
+ });
+ // Saved keybindings are checked against every installed plugin's
+ // declarations, so a binding keeps validating while its plugin is off.
+ setKeybindingActionSource(() =>
+ (loader?.list() ?? []).flatMap((plugin) =>
+ (plugin.manifest.contributes?.keybindingActions ?? []).map(
+ (action) => ({ ...action, pluginId: plugin.id }),
+ ),
+ ),
+ );
// Lets a host whose auth type belongs to a disabled plugin name it.
setSshAuthTypeOwnerSource(() =>
(loader?.list() ?? []).flatMap((plugin) =>
diff --git a/src/backend/sync/entities.ts b/src/backend/sync/entities.ts
index a70b2ba4e..b2e677765 100644
--- a/src/backend/sync/entities.ts
+++ b/src/backend/sync/entities.ts
@@ -57,8 +57,6 @@ const HOST_REFERENCES = [
entityType: "hosts",
},
{ field: "jumpHosts[].hostId", entityType: "hosts" },
- { field: "quickActions[].snippetId", entityType: "commandSnippet" },
- { field: "terminalConfig.startupSnippetId", entityType: "commandSnippet" },
] as const;
/** Columns that only mean something on the device that holds them. */
@@ -69,6 +67,13 @@ const HOST_LOCAL_FIELDS = [
"hostKeyLastVerified",
];
+/**
+ * Columns kept until 3.0.0 that nothing reads any more: their values moved
+ * into plugin host settings, which sync with the host's pluginSettings. They
+ * stay off the wire so a stale local id never reaches the other side.
+ */
+const HOST_RETIRED_FIELDS = ["quickActions"];
+
let registered = false;
/** Registers core's entities. Idempotent. */
@@ -148,7 +153,7 @@ export function registerCoreSyncEntities(): void {
table: hosts,
order: 50,
encryptedFields: FieldCrypto.fieldsFor("ssh_data"),
- readOnlyFields: HOST_LOCAL_FIELDS,
+ readOnlyFields: [...HOST_LOCAL_FIELDS, ...HOST_RETIRED_FIELDS],
references: HOST_REFERENCES,
shouldSync: (row) => !row.localOnly && !row.sharedSource,
permissions: {
@@ -198,13 +203,18 @@ export function registerCoreSyncEntities(): void {
order: 90,
singleton: true,
readOnlyFields: ["storageMode"],
- references: [
- {
- field: "customKeybindings[].action.snippetId",
- entityType: "commandSnippet",
- idType: "string",
- },
- ],
+ // A keybinding parameter a plugin declares with a syncEntity holds a
+ // local row id of that entity; the wire carries its syncId.
+ serialize: (row, resolveSyncId) =>
+ mapKeybindingReferences(row, async (entityType, value) => {
+ const id = Number(value);
+ return Number.isInteger(id) ? resolveSyncId(entityType, id) : null;
+ }),
+ deserialize: (row, resolveId) =>
+ mapKeybindingReferences(row, async (entityType, value) => {
+ const id = await resolveId(entityType, value);
+ return id === null ? null : String(id);
+ }),
});
registerEntity(
@@ -253,6 +263,43 @@ export function registerCoreSyncEntities(): void {
);
}
+/**
+ * Rewrites every keybinding parameter whose declaration names a sync entity,
+ * keeping the column's storage form. A value that does not map becomes null.
+ */
+export async function mapKeybindingReferences(
+ row: SyncRow,
+ map: (entityType: string, value: string) => Promise,
+): Promise {
+ const stored = row.customKeybindings;
+ if (typeof stored !== "string" || !stored) return row;
+ let bindings: unknown;
+ try {
+ bindings = JSON.parse(stored);
+ } catch {
+ return row;
+ }
+ if (!Array.isArray(bindings)) return row;
+ const { findKeybindingAction } =
+ await import("../database/routes/keybinding-validation.js");
+ let changed = false;
+ for (const binding of bindings) {
+ const action = (binding as { action?: Record } | null)
+ ?.action;
+ if (!action || typeof action.type !== "string") continue;
+ const params = findKeybindingAction(action.type)?.params ?? {};
+ for (const [name, param] of Object.entries(params)) {
+ const value = action[name];
+ if (!param.syncEntity || typeof value !== "string" || !value) continue;
+ action[name] = await map(param.syncEntity, value);
+ changed = true;
+ }
+ }
+ return changed
+ ? { ...row, customKeybindings: JSON.stringify(bindings) }
+ : row;
+}
+
/** A synced parent link that would make a cycle here is dropped. */
async function dropInvalidParent(userId: string, hostId: number) {
const { createCurrentRepositoryContext } =
diff --git a/src/backend/tests/database/routes/host-normalizers.test.ts b/src/backend/tests/database/routes/host-normalizers.test.ts
index d35b70966..89ddd5a0c 100644
--- a/src/backend/tests/database/routes/host-normalizers.test.ts
+++ b/src/backend/tests/database/routes/host-normalizers.test.ts
@@ -305,10 +305,17 @@ describe("transformHostResponse", () => {
it("parses JSON array fields and defaults them to []", () => {
const result = transformHostResponse({
jumpHosts: '[{"hostId":8}]',
- quickActions: null,
+ portKnockSequence: null,
});
expect(result.jumpHosts).toEqual([{ hostId: 8 }]);
- expect(result.quickActions).toEqual([]);
+ expect(result.portKnockSequence).toEqual([]);
+ });
+
+ it("leaves the 2.8 quick_actions column out, for its plugin to put back", () => {
+ const result = transformHostResponse({
+ quickActions: '[{"name":"x","snippetId":1}]',
+ });
+ expect(result.quickActions).toBeUndefined();
});
it("passes the stored SSH switch through", () => {
@@ -440,10 +447,7 @@ describe("transformHostResponse terminal fields", () => {
}),
});
expect(host.sshOptions).toEqual({ keepaliveInterval: 20 });
- expect(host.terminalConfig).toEqual({
- keepaliveInterval: 20,
- startupSnippetId: 3,
- });
+ expect(host.terminalConfig).toEqual({ keepaliveInterval: 20 });
});
it("reads the options out of terminal_config before the boot copy", () => {
diff --git a/src/backend/tests/database/routes/keybinding-validation.test.ts b/src/backend/tests/database/routes/keybinding-validation.test.ts
index b25ae37ba..2953e4d66 100644
--- a/src/backend/tests/database/routes/keybinding-validation.test.ts
+++ b/src/backend/tests/database/routes/keybinding-validation.test.ts
@@ -1,10 +1,44 @@
-import { describe, it, expect } from "vitest";
+import { afterEach, beforeEach, describe, it, expect } from "vitest";
import {
isValidKeyCombo,
isValidKeybindingAction,
isValidKeybinding,
+ setKeybindingActionSource,
} from "../../../database/routes/keybinding-validation.js";
+// What installed plugins declare in contributes.keybindingActions.
+beforeEach(() => {
+ setKeybindingActionSource(() => [
+ { pluginId: "terminal-fixture", id: "copy" },
+ { pluginId: "terminal-fixture", id: "paste" },
+ {
+ pluginId: "terminal-fixture",
+ id: "sendControlCode",
+ params: {
+ controlCode: { type: "string", required: true, pattern: "^[a-zA-Z]$" },
+ },
+ },
+ {
+ pluginId: "terminal-fixture",
+ id: "sendText",
+ params: {
+ text: { type: "string", required: true },
+ appendEnter: { type: "boolean" },
+ },
+ },
+ {
+ pluginId: "commands-fixture",
+ id: "runSnippet",
+ params: {
+ snippetId: { type: "string", required: true, pattern: "^[0-9]+$" },
+ appendEnter: { type: "boolean" },
+ },
+ },
+ ]);
+});
+
+afterEach(() => setKeybindingActionSource(() => []));
+
const validCombo = {
key: "c",
isCode: false,
@@ -36,15 +70,26 @@ describe("isValidKeybindingAction", () => {
expect(isValidKeybindingAction({ type: "paste" })).toBe(true);
});
- it.each(["nextTab", "previousTab", "openCommandPalette"])(
+ it.each(["nextTab", "previousTab", "openCommandPalette", "reconnectSession"])(
"accepts the global %s action",
(type) => {
expect(isValidKeybindingAction({ type })).toBe(true);
},
);
- it("rejects an unknown action type", () => {
- expect(isValidKeybindingAction({ type: "explode" })).toBe(false);
+ it("keeps a type no installed plugin declares, if it looks sane", () => {
+ expect(isValidKeybindingAction({ type: "gone.action", arg: "x" })).toBe(
+ true,
+ );
+ expect(isValidKeybindingAction({ type: "gone", nested: { a: 1 } })).toBe(
+ false,
+ );
+ expect(isValidKeybindingAction({ type: "bad type!" })).toBe(false);
+ expect(isValidKeybindingAction({ type: 5 })).toBe(false);
+ });
+
+ it("refuses a parameter the declaration does not list", () => {
+ expect(isValidKeybindingAction({ type: "copy", text: "x" })).toBe(false);
});
it("requires text for sendText", () => {
@@ -72,6 +117,9 @@ describe("isValidKeybindingAction", () => {
isValidKeybindingAction({ type: "runSnippet", snippetId: "42" }),
).toBe(true);
expect(isValidKeybindingAction({ type: "runSnippet" })).toBe(false);
+ expect(
+ isValidKeybindingAction({ type: "runSnippet", snippetId: "abc" }),
+ ).toBe(false);
});
});
diff --git a/src/backend/tests/fixtures/upgrade/db.sqlite b/src/backend/tests/fixtures/upgrade/db.sqlite
index e4b84c716..8ae7b84c7 100644
Binary files a/src/backend/tests/fixtures/upgrade/db.sqlite and b/src/backend/tests/fixtures/upgrade/db.sqlite differ
diff --git a/src/backend/tests/fixtures/upgrade/rows.ts b/src/backend/tests/fixtures/upgrade/rows.ts
index abd939b4d..156c66f45 100644
--- a/src/backend/tests/fixtures/upgrade/rows.ts
+++ b/src/backend/tests/fixtures/upgrade/rows.ts
@@ -199,7 +199,9 @@ const HOST_ROWS: Row[] = [
allowLegacyAlgorithms: false,
agentForwarding: true,
environmentVariables: [{ key: "D4", value: "yes" }],
+ startupSnippetId: 1,
}),
+ quick_actions: JSON.stringify([{ name: "d4-uptime", snippetId: 1 }]),
mac_address: "AA:BB:CC:DD:EE:04",
wol_broadcast_address: "10.4.0.255",
stats_config: JSON.stringify({
@@ -387,6 +389,32 @@ export const ROWS: Record = {
},
]),
command_autocomplete: 1,
+ confirm_snippet_execution: 1,
+ folders_collapsed: 0,
+ terminal_macros: JSON.stringify([
+ {
+ id: "d4-macro",
+ name: "d4 macro",
+ steps: [{ id: "s1", type: "send", text: "uptime", pressEnter: true }],
+ },
+ ]),
+ custom_keybindings: JSON.stringify([
+ {
+ id: "d4-kb",
+ combo: {
+ key: "u",
+ isCode: false,
+ ctrl: true,
+ alt: true,
+ shift: false,
+ meta: false,
+ },
+ action: { type: "runSnippet", snippetId: "1", appendEnter: true },
+ enabled: true,
+ createdAt: "2026-01-01T00:00:00.000Z",
+ updatedAt: "2026-01-01T00:00:00.000Z",
+ },
+ ]),
},
],
sso_providers: [
diff --git a/src/backend/tests/plugins/manifest.test.ts b/src/backend/tests/plugins/manifest.test.ts
index 0fb4b9304..3a93d37d1 100644
--- a/src/backend/tests/plugins/manifest.test.ts
+++ b/src/backend/tests/plugins/manifest.test.ts
@@ -686,3 +686,76 @@ describe("contributes.http.legacyRedirects", () => {
expect(errors).toMatch(/extra/);
});
});
+
+describe("contributes.http.adminImpersonation", () => {
+ it("accepts a boolean and refuses anything else", () => {
+ expect(
+ validateManifest(
+ base({ contributes: { http: { adminImpersonation: true } } }),
+ ),
+ ).toEqual([]);
+ expect(
+ validateManifest(
+ base({ contributes: { http: { adminImpersonation: "yes" } } }),
+ ).join(),
+ ).toMatch(/adminImpersonation" must be a boolean/);
+ });
+});
+
+describe("contributes.keybindingActions", () => {
+ const withActions = (keybindingActions: unknown) =>
+ validateManifest(base({ contributes: { keybindingActions } }));
+
+ it("accepts actions with typed parameters", () => {
+ expect(
+ withActions([
+ { id: "sample.run" },
+ {
+ id: "sample.send",
+ params: {
+ itemId: {
+ type: "string",
+ required: true,
+ pattern: "^[0-9]+$",
+ maxLength: 20,
+ syncEntity: "items",
+ },
+ enter: { type: "boolean" },
+ },
+ },
+ ]),
+ ).toEqual([]);
+ });
+
+ it("refuses the shell's own actions, duplicates and bad ids", () => {
+ const errors = withActions([
+ { id: "nextTab" },
+ { id: "sample.run" },
+ { id: "sample.run" },
+ { id: "bad id" },
+ ]).join();
+ expect(errors).toMatch(/one of the shell's own actions/);
+ expect(errors).toMatch(/duplicates "sample.run"/);
+ expect(errors).toMatch(/letters, digits, dots and dashes/);
+ });
+
+ it("refuses bad parameters", () => {
+ const errors = withActions([
+ {
+ id: "sample.run",
+ params: {
+ type: { type: "string" },
+ a: { type: "number" },
+ b: { type: "string", pattern: "(" },
+ c: { type: "boolean", syncEntity: "items" },
+ d: { type: "string", extra: 1 },
+ },
+ },
+ ]).join();
+ expect(errors).toMatch(/params.type is not a valid parameter name/);
+ expect(errors).toMatch(/a.type must be "string" or "boolean"/);
+ expect(errors).toMatch(/b.pattern must be a regular expression/);
+ expect(errors).toMatch(/c.syncEntity needs a string parameter/);
+ expect(errors).toMatch(/d/);
+ });
+});
diff --git a/src/backend/tests/plugins/sync-registry.test.ts b/src/backend/tests/plugins/sync-registry.test.ts
index f8e722397..3ffaa8cd0 100644
--- a/src/backend/tests/plugins/sync-registry.test.ts
+++ b/src/backend/tests/plugins/sync-registry.test.ts
@@ -9,7 +9,11 @@ import {
resetSyncRegistry,
unregisterByOwner,
} from "../../plugins/sync-registry.js";
-import { registerCoreSyncEntities } from "../../sync/entities.js";
+import {
+ mapKeybindingReferences,
+ registerCoreSyncEntities,
+} from "../../sync/entities.js";
+import { setKeybindingActionSource } from "../../database/routes/keybinding-validation.js";
beforeEach(() => {
resetSyncRegistry();
@@ -197,14 +201,51 @@ describe("core sync entities", () => {
);
});
- it("translates every snippet a host points at", () => {
- const fields = (getEntity("hosts")?.references ?? [])
- .filter((reference) => reference.entityType === "commandSnippet")
- .map((reference) => reference.field);
- expect(fields).toEqual([
- "quickActions[].snippetId",
- "terminalConfig.startupSnippetId",
+ it("leaves plugin data out of the host row, which travels in pluginSettings", () => {
+ const hosts = getEntity("hosts");
+ expect(
+ (hosts?.references ?? []).map((reference) => reference.entityType),
+ ).not.toContain("commandSnippet");
+ expect(hosts?.readOnlyFields).toContain("quickActions");
+ });
+
+ it("translates a keybinding parameter that names a sync entity", async () => {
+ setKeybindingActionSource(() => [
+ {
+ pluginId: "fixture",
+ id: "runThing",
+ params: {
+ thingId: { type: "string", syncEntity: "things" },
+ label: { type: "string" },
+ },
+ },
]);
+ try {
+ const row = {
+ customKeybindings: JSON.stringify([
+ { id: "a", action: { type: "runThing", thingId: "4", label: "4" } },
+ { id: "b", action: { type: "nextTab" } },
+ ]),
+ };
+ const out = await mapKeybindingReferences(row, async (entity, value) =>
+ entity === "things" && value === "4" ? "sync-4" : null,
+ );
+ const bindings = JSON.parse(out.customKeybindings as string);
+ expect(bindings[0].action).toEqual({
+ type: "runThing",
+ thingId: "sync-4",
+ label: "4",
+ });
+ expect(bindings[1].action).toEqual({ type: "nextTab" });
+ expect(
+ await mapKeybindingReferences(
+ { customKeybindings: null },
+ async () => "x",
+ ),
+ ).toEqual({ customKeybindings: null });
+ } finally {
+ setKeybindingActionSource(() => []);
+ }
});
it("encrypts host and credential secrets", () => {
diff --git a/src/backend/tests/plugins/upgrade-fixture.test.ts b/src/backend/tests/plugins/upgrade-fixture.test.ts
index dad2c6294..cdac16bab 100644
--- a/src/backend/tests/plugins/upgrade-fixture.test.ts
+++ b/src/backend/tests/plugins/upgrade-fixture.test.ts
@@ -326,6 +326,10 @@ function upgradeChecks(current: () => Booted, bootIndex: number) {
containerRuntime: "podman",
});
expect(await host("ai")).toMatchObject({ enableAiAssistant: true });
+ expect(await host("snippets")).toMatchObject({
+ startupSnippetId: 1,
+ quickActions: [{ name: "d4-uptime", snippetId: 1 }],
+ });
expect(await host("wake-on-lan")).toMatchObject({
macAddress: "AA:BB:CC:DD:EE:04",
broadcastAddress: "10.4.0.255",
@@ -473,6 +477,11 @@ function upgradeChecks(current: () => Booted, bootIndex: number) {
terminalDefaults: { fontSize: 18, cursorBlink: false },
customThemes: [expect.objectContaining({ id: "d4-theme" })],
commandAutocomplete: true,
+ macros: [expect.objectContaining({ id: "d4-macro", name: "d4 macro" })],
+ });
+ expect(await settings(booted, "snippets", "user")).toMatchObject({
+ confirmExecution: true,
+ foldersCollapsed: false,
});
expect(await settings(booted, "step-ca", "admin")).toMatchObject({
caUrl: "https://ca.d4.example",
diff --git a/src/backend/tests/upgrade/snippets-settings-migration.test.ts b/src/backend/tests/upgrade/snippets-settings-migration.test.ts
new file mode 100644
index 000000000..37d4cda68
--- /dev/null
+++ b/src/backend/tests/upgrade/snippets-settings-migration.test.ts
@@ -0,0 +1,174 @@
+/**
+ * The snippets settings migration: host quick actions, the startup snippet
+ * and the two snippet user preferences must come through into the snippets
+ * plugin's settings, and running it twice must change nothing.
+ */
+
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+interface Row {
+ pluginId: string;
+ scope: string;
+ scopeId: string | null;
+ key: string;
+ value: string | null;
+}
+
+const pluginRows: Row[] = [];
+const hostRows: Array> = [];
+const preferenceRows: Array> = [];
+let pluginInstalled = true;
+
+const find = (scope: string, scopeId: string | null, key: string) =>
+ pluginRows.find(
+ (row) =>
+ row.pluginId === "snippets" &&
+ row.scope === scope &&
+ row.scopeId === scopeId &&
+ row.key === key,
+ );
+
+vi.mock("../../database/repositories/factory.js", () => ({
+ createCurrentPluginRepository: () => ({
+ findById: async (id: string) =>
+ pluginInstalled && id === "snippets" ? { id } : null,
+ }),
+ createCurrentPluginSettingsRepository: () => ({
+ get: async (
+ _pluginId: string,
+ scope: string,
+ scopeId: string | null,
+ key: string,
+ ) => find(scope, scopeId, key) ?? null,
+ set: async (
+ pluginId: string,
+ scope: string,
+ scopeId: string | null,
+ key: string,
+ value: string | null,
+ ) => {
+ const existing = find(scope, scopeId, key);
+ if (existing) existing.value = value;
+ else pluginRows.push({ pluginId, scope, scopeId, key, value });
+ },
+ }),
+}));
+
+vi.mock("../../database/db/index.js", () => ({
+ getDb: () => ({
+ all: async (query: unknown) =>
+ JSON.stringify(query).includes("user_preferences")
+ ? preferenceRows
+ : hostRows,
+ }),
+}));
+
+const { runSnippetsSettingsMigration, snippetHostSettingsFromRow } =
+ await import("../../upgrade/snippets-settings-migration.js");
+
+function value(scope: string, scopeId: string, key: string): unknown {
+ const row = find(scope, scopeId, key);
+ return row?.value == null ? undefined : JSON.parse(row.value);
+}
+
+beforeEach(() => {
+ pluginRows.length = 0;
+ hostRows.length = 0;
+ preferenceRows.length = 0;
+ pluginInstalled = true;
+});
+
+describe("snippetHostSettingsFromRow", () => {
+ it("reads quick actions and the startup snippet, dropping broken rows", () => {
+ expect(
+ snippetHostSettingsFromRow({
+ quick_actions: JSON.stringify([
+ { name: "Restart", snippetId: 3 },
+ { name: "Broken", snippetId: "x" },
+ { name: "Str", snippetId: "4" },
+ ]),
+ terminal_config: JSON.stringify({ fontSize: 14, startupSnippetId: 9 }),
+ }),
+ ).toEqual({
+ quickActions: [
+ { name: "Restart", snippetId: 3 },
+ { name: "Str", snippetId: 4 },
+ ],
+ startupSnippetId: 9,
+ });
+ });
+
+ it("reads nothing from a row without either", () => {
+ expect(
+ snippetHostSettingsFromRow({
+ quick_actions: "[]",
+ terminal_config: JSON.stringify({ fontSize: 14 }),
+ }),
+ ).toEqual({});
+ });
+});
+
+describe("runSnippetsSettingsMigration", () => {
+ it("moves host and user values once", async () => {
+ hostRows.push(
+ {
+ id: 1,
+ quick_actions: JSON.stringify([{ name: "Up", snippetId: 2 }]),
+ terminal_config: JSON.stringify({ startupSnippetId: 5 }),
+ },
+ { id: 2, quick_actions: null, terminal_config: "{}" },
+ );
+ preferenceRows.push(
+ { user_id: "u1", confirm_snippet_execution: 1, folders_collapsed: 0 },
+ {
+ user_id: "u2",
+ confirm_snippet_execution: null,
+ folders_collapsed: null,
+ },
+ );
+
+ const first = await runSnippetsSettingsMigration();
+ expect(first).toEqual({ hostsMoved: 1, hostsSkipped: 0, usersMoved: 1 });
+ expect(value("host", "1", "quickActions")).toEqual([
+ { name: "Up", snippetId: 2 },
+ ]);
+ expect(value("host", "1", "startupSnippetId")).toBe(5);
+ expect(value("user", "u1", "confirmExecution")).toBe(true);
+ expect(value("user", "u1", "foldersCollapsed")).toBe(false);
+ expect(find("user", "u2", "confirmExecution")).toBeUndefined();
+
+ const snapshot = JSON.stringify(pluginRows);
+ const second = await runSnippetsSettingsMigration();
+ expect(second).toEqual({ hostsMoved: 0, hostsSkipped: 1, usersMoved: 0 });
+ expect(JSON.stringify(pluginRows)).toBe(snapshot);
+ });
+
+ it("never overwrites what the user already changed in the plugin", async () => {
+ pluginRows.push({
+ pluginId: "snippets",
+ scope: "host",
+ scopeId: "1",
+ key: "startupSnippetId",
+ value: "7",
+ });
+ hostRows.push({
+ id: 1,
+ quick_actions: JSON.stringify([{ name: "Up", snippetId: 2 }]),
+ terminal_config: JSON.stringify({ startupSnippetId: 5 }),
+ });
+ await runSnippetsSettingsMigration();
+ expect(value("host", "1", "startupSnippetId")).toBe(7);
+ expect(find("host", "1", "quickActions")).toBeUndefined();
+ });
+
+ it("does nothing while the plugin is not installed", async () => {
+ pluginInstalled = false;
+ hostRows.push({
+ id: 1,
+ quick_actions: JSON.stringify([{ name: "Up", snippetId: 2 }]),
+ terminal_config: null,
+ });
+ await runSnippetsSettingsMigration();
+ expect(pluginRows).toHaveLength(0);
+ });
+});
diff --git a/src/backend/tests/upgrade/ssh-terminal-macros-migration.test.ts b/src/backend/tests/upgrade/ssh-terminal-macros-migration.test.ts
new file mode 100644
index 000000000..6fe0c6f24
--- /dev/null
+++ b/src/backend/tests/upgrade/ssh-terminal-macros-migration.test.ts
@@ -0,0 +1,104 @@
+/**
+ * The terminal macros migration: a user's saved macros must come through
+ * into the ssh-terminal plugin's macros setting, and running it twice must
+ * change nothing.
+ */
+
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+interface Row {
+ pluginId: string;
+ scope: string;
+ scopeId: string | null;
+ key: string;
+ value: string | null;
+}
+
+const pluginRows: Row[] = [];
+const preferenceRows: Array> = [];
+
+const find = (scopeId: string, key: string) =>
+ pluginRows.find(
+ (row) =>
+ row.pluginId === "ssh-terminal" &&
+ row.scope === "user" &&
+ row.scopeId === scopeId &&
+ row.key === key,
+ );
+
+vi.mock("../../database/repositories/factory.js", () => ({
+ createCurrentPluginRepository: () => ({
+ findById: async (id: string) => (id === "ssh-terminal" ? { id } : null),
+ }),
+ createCurrentPluginSettingsRepository: () => ({
+ get: async (_p: string, _s: string, scopeId: string, key: string) =>
+ find(scopeId, key) ?? null,
+ set: async (
+ pluginId: string,
+ scope: string,
+ scopeId: string,
+ key: string,
+ value: string | null,
+ ) => {
+ const existing = find(scopeId, key);
+ if (existing) existing.value = value;
+ else pluginRows.push({ pluginId, scope, scopeId, key, value });
+ },
+ }),
+}));
+
+vi.mock("../../database/db/index.js", () => ({
+ getDb: () => ({ all: async () => preferenceRows }),
+}));
+
+const { runSshTerminalMacrosMigration, macrosFromPreferences } =
+ await import("../../upgrade/ssh-terminal-macros-migration.js");
+
+const macro = { id: "m1", name: "Deploy", steps: [{ id: "s", type: "send" }] };
+
+beforeEach(() => {
+ pluginRows.length = 0;
+ preferenceRows.length = 0;
+});
+
+describe("macrosFromPreferences", () => {
+ it("keeps valid macros and drops the rest", () => {
+ expect(
+ macrosFromPreferences(JSON.stringify([macro, { id: 1 }, "x"])),
+ ).toEqual([macro]);
+ expect(macrosFromPreferences("[]")).toBeNull();
+ expect(macrosFromPreferences("{bad")).toBeNull();
+ });
+});
+
+describe("runSshTerminalMacrosMigration", () => {
+ it("moves each user's macros once", async () => {
+ preferenceRows.push(
+ { user_id: "u1", terminal_macros: JSON.stringify([macro]) },
+ { user_id: "u2", terminal_macros: "[]" },
+ );
+ expect(await runSshTerminalMacrosMigration()).toEqual({ usersMoved: 1 });
+ expect(JSON.parse(find("u1", "macros")!.value!)).toEqual([macro]);
+ expect(find("u2", "macros")).toBeUndefined();
+
+ const snapshot = JSON.stringify(pluginRows);
+ expect(await runSshTerminalMacrosMigration()).toEqual({ usersMoved: 0 });
+ expect(JSON.stringify(pluginRows)).toBe(snapshot);
+ });
+
+ it("leaves a user who already has macros in the plugin alone", async () => {
+ pluginRows.push({
+ pluginId: "ssh-terminal",
+ scope: "user",
+ scopeId: "u1",
+ key: "macros",
+ value: "[]",
+ });
+ preferenceRows.push({
+ user_id: "u1",
+ terminal_macros: JSON.stringify([macro]),
+ });
+ await runSshTerminalMacrosMigration();
+ expect(find("u1", "macros")!.value).toBe("[]");
+ });
+});
diff --git a/src/backend/tests/utils/auth-manager-impersonation.test.ts b/src/backend/tests/utils/auth-manager-impersonation.test.ts
index a18bb1a6b..e3a59d1a3 100644
--- a/src/backend/tests/utils/auth-manager-impersonation.test.ts
+++ b/src/backend/tests/utils/auth-manager-impersonation.test.ts
@@ -66,7 +66,8 @@ vi.mock("../../utils/audit-logger.js", () => ({
getRequestMeta: () => ({ ipAddress: "", userAgent: "" }),
}));
-const { AuthManager } = await import("../../utils/auth-manager.js");
+const { AuthManager, setPluginImpersonationCheck, allowsAdminImpersonation } =
+ await import("../../utils/auth-manager.js");
const authManager = AuthManager.getInstance();
const middleware = authManager.createAuthMiddleware();
@@ -216,4 +217,48 @@ describe("AuthManager admin impersonation", () => {
expect(nexted).toBe(true);
expect((req as unknown as { userId: string }).userId).toBe("regular1");
});
+
+ it("rejects the header on a plugin route whose manifest did not opt in", async () => {
+ setPluginImpersonationCheck((pluginId) => pluginId === "opted-in");
+ try {
+ const { res, nexted } = await runMiddleware(token("admin1"), {
+ "x-admin-target-user": "target1",
+ __url: "/plugin-api/other/items",
+ });
+ expect(nexted).toBe(false);
+ expect(res.statusCode).toBe(403);
+ expect((res.body as { code?: string }).code).toBe(
+ "IMPERSONATION_NOT_ALLOWED",
+ );
+ } finally {
+ setPluginImpersonationCheck(() => false);
+ }
+ });
+
+ it("lets an admin act for a user on a plugin that opted in", async () => {
+ setPluginImpersonationCheck((pluginId) => pluginId === "opted-in");
+ try {
+ const { req, nexted } = await runMiddleware(token("admin1"), {
+ "x-admin-target-user": "target1",
+ __url: "/plugin-api/opted-in/items?x=1",
+ });
+ expect(nexted).toBe(true);
+ expect((req as unknown as { userId: string }).userId).toBe("target1");
+ } finally {
+ setPluginImpersonationCheck(() => false);
+ }
+ });
+
+ it("matches the plugin id exactly, not as a prefix", () => {
+ setPluginImpersonationCheck((pluginId) => pluginId === "opted-in");
+ try {
+ expect(allowsAdminImpersonation("/plugin-api/opted-in")).toBe(true);
+ expect(allowsAdminImpersonation("/plugin-api/opted-in-too/x")).toBe(
+ false,
+ );
+ expect(allowsAdminImpersonation("/users/sessions")).toBe(false);
+ } finally {
+ setPluginImpersonationCheck(() => false);
+ }
+ });
});
diff --git a/src/backend/upgrade/boot-migrations.ts b/src/backend/upgrade/boot-migrations.ts
index 6248c5ecd..a762a5a80 100644
--- a/src/backend/upgrade/boot-migrations.ts
+++ b/src/backend/upgrade/boot-migrations.ts
@@ -16,6 +16,8 @@ import { runTunnelsSettingsMigration } from "./tunnels-settings-migration.js";
import { runWebEndpointSettingsMigration } from "./web-endpoint-settings-migration.js";
import { runSshTerminalSettingsMigration } from "./ssh-terminal-settings-migration.js";
import { runSshTerminalLookMigration } from "./ssh-terminal-look-migration.js";
+import { runSshTerminalMacrosMigration } from "./ssh-terminal-macros-migration.js";
+import { runSnippetsSettingsMigration } from "./snippets-settings-migration.js";
import { runTmuxMonitorSettingsMigration } from "./tmux-monitor-settings-migration.js";
import { runSessionSharingSettingsMigration } from "./session-sharing-settings-migration.js";
import { runSessionRecordingSettingsMigration } from "./session-recording-settings-migration.js";
@@ -41,6 +43,8 @@ const MIGRATIONS: Array<[string, () => Promise]> = [
["runWebEndpointSettingsMigration", runWebEndpointSettingsMigration],
["runSshTerminalSettingsMigration", runSshTerminalSettingsMigration],
["runSshTerminalLookMigration", runSshTerminalLookMigration],
+ ["runSshTerminalMacrosMigration", runSshTerminalMacrosMigration],
+ ["runSnippetsSettingsMigration", runSnippetsSettingsMigration],
["runTmuxMonitorSettingsMigration", runTmuxMonitorSettingsMigration],
["runSessionSharingSettingsMigration", runSessionSharingSettingsMigration],
[
diff --git a/src/backend/upgrade/snippets-settings-migration.ts b/src/backend/upgrade/snippets-settings-migration.ts
new file mode 100644
index 000000000..9de681745
--- /dev/null
+++ b/src/backend/upgrade/snippets-settings-migration.ts
@@ -0,0 +1,185 @@
+/**
+ * Moves what core kept for snippets into the snippets plugin's settings.
+ *
+ * Per host: ssh_data.quick_actions becomes the quickActions host setting,
+ * and terminal_config.startupSnippetId the startupSnippetId one. Per user:
+ * user_preferences.confirm_snippet_execution and folders_collapsed become the
+ * confirmExecution and foldersCollapsed user settings.
+ *
+ * The columns keep their values until 3.0.0 drops them.
+ *
+ * Idempotent: a host that already has either host setting, and a user key
+ * already set, are skipped, so this is safe on every boot.
+ */
+
+import { sql } from "drizzle-orm";
+import { databaseLogger } from "../utils/logger.js";
+import {
+ legacyFlag,
+ selectLegacyRows,
+} from "../utils/crypto-migration/raw-rows.js";
+import {
+ createCurrentPluginRepository,
+ createCurrentPluginSettingsRepository,
+} from "../database/repositories/factory.js";
+
+const PLUGIN_ID = "snippets";
+const HOST_KEYS = ["quickActions", "startupSnippetId"];
+
+function parseJson(value: unknown): unknown {
+ if (typeof value !== "string" || !value) return value ?? undefined;
+ try {
+ let parsed = JSON.parse(value);
+ if (typeof parsed === "string") parsed = JSON.parse(parsed);
+ return parsed;
+ } catch {
+ return undefined;
+ }
+}
+
+function snippetId(value: unknown): number | null {
+ const id = typeof value === "string" ? Number(value) : value;
+ return typeof id === "number" && Number.isInteger(id) && id > 0 ? id : null;
+}
+
+/** The snippets host settings one 2.8 ssh_data row carried. */
+export function snippetHostSettingsFromRow(row: {
+ quick_actions?: unknown;
+ terminal_config?: unknown;
+}): Record {
+ const values: Record = {};
+ const actions = parseJson(row.quick_actions);
+ if (Array.isArray(actions)) {
+ const quickActions = actions.flatMap((entry) => {
+ const id = snippetId((entry as { snippetId?: unknown })?.snippetId);
+ if (id === null) return [];
+ const name = (entry as { name?: unknown }).name;
+ return [{ name: typeof name === "string" ? name : "", snippetId: id }];
+ });
+ if (quickActions.length > 0) values.quickActions = quickActions;
+ }
+ const config = parseJson(row.terminal_config) as
+ { startupSnippetId?: unknown } | undefined;
+ const startup = snippetId(config?.startupSnippetId);
+ if (startup !== null) values.startupSnippetId = startup;
+ return values;
+}
+
+export interface SnippetsSettingsMigrationResult {
+ hostsMoved: number;
+ hostsSkipped: number;
+ usersMoved: number;
+}
+
+export async function runSnippetsSettingsMigration(): Promise {
+ const result: SnippetsSettingsMigrationResult = {
+ hostsMoved: 0,
+ hostsSkipped: 0,
+ usersMoved: 0,
+ };
+
+ try {
+ const plugin = await createCurrentPluginRepository().findById(PLUGIN_ID);
+ if (!plugin) return result;
+ } catch {
+ return result;
+ }
+
+ const pluginSettings = createCurrentPluginSettingsRepository();
+
+ try {
+ const rows = await selectLegacyRows<{
+ id: number;
+ quick_actions: unknown;
+ terminal_config: unknown;
+ }>(sql`
+ SELECT id, quick_actions, terminal_config FROM ssh_data
+ WHERE quick_actions IS NOT NULL OR terminal_config IS NOT NULL
+ `);
+ for (const row of rows) {
+ const values = snippetHostSettingsFromRow(row);
+ if (Object.keys(values).length === 0) continue;
+ const scopeId = String(row.id);
+ let done = false;
+ for (const key of HOST_KEYS) {
+ if (await pluginSettings.get(PLUGIN_ID, "host", scopeId, key)) {
+ done = true;
+ }
+ }
+ if (done) {
+ result.hostsSkipped++;
+ continue;
+ }
+ for (const [key, value] of Object.entries(values)) {
+ await pluginSettings.set(
+ PLUGIN_ID,
+ "host",
+ scopeId,
+ key,
+ JSON.stringify(value),
+ );
+ }
+ result.hostsMoved++;
+ }
+ } catch (error) {
+ databaseLogger.warn("Snippets host settings migration failed", {
+ operation: "snippets_settings_migration",
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+
+ try {
+ const rows = await selectLegacyRows<{
+ user_id: string;
+ confirm_snippet_execution: unknown;
+ folders_collapsed: unknown;
+ }>(sql`
+ SELECT user_id, confirm_snippet_execution, folders_collapsed
+ FROM user_preferences
+ `);
+ for (const row of rows) {
+ const values: Record = {};
+ if (row.confirm_snippet_execution != null) {
+ values.confirmExecution = legacyFlag(
+ row.confirm_snippet_execution,
+ false,
+ );
+ }
+ if (row.folders_collapsed != null) {
+ values.foldersCollapsed = legacyFlag(row.folders_collapsed, true);
+ }
+ let moved = false;
+ for (const [key, value] of Object.entries(values)) {
+ const existing = await pluginSettings.get(
+ PLUGIN_ID,
+ "user",
+ row.user_id,
+ key,
+ );
+ if (existing && existing.value !== null) continue;
+ await pluginSettings.set(
+ PLUGIN_ID,
+ "user",
+ row.user_id,
+ key,
+ JSON.stringify(value),
+ );
+ moved = true;
+ }
+ if (moved) result.usersMoved++;
+ }
+ } catch (error) {
+ databaseLogger.warn("Snippets user settings migration failed", {
+ operation: "snippets_settings_migration",
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+
+ if (result.hostsMoved > 0 || result.usersMoved > 0) {
+ databaseLogger.info(
+ `Moved the snippet settings of ${result.hostsMoved} host(s) and ${result.usersMoved} user(s) into plugin settings`,
+ { operation: "snippets_settings_migration" },
+ );
+ }
+ return result;
+}
diff --git a/src/backend/upgrade/ssh-terminal-macros-migration.ts b/src/backend/upgrade/ssh-terminal-macros-migration.ts
new file mode 100644
index 000000000..4f59b1ca7
--- /dev/null
+++ b/src/backend/upgrade/ssh-terminal-macros-migration.ts
@@ -0,0 +1,98 @@
+/**
+ * Moves user_preferences.terminal_macros into the ssh-terminal plugin's
+ * macros user setting. Macros a browser kept on its own (storage mode
+ * "local") are moved by the plugin's frontend the first time the panel opens.
+ *
+ * The column keeps its value until 3.0.0 drops it.
+ *
+ * Idempotent: a user whose macros setting is already set is skipped.
+ */
+
+import { sql } from "drizzle-orm";
+import { databaseLogger } from "../utils/logger.js";
+import { selectLegacyRows } from "../utils/crypto-migration/raw-rows.js";
+import {
+ createCurrentPluginRepository,
+ createCurrentPluginSettingsRepository,
+} from "../database/repositories/factory.js";
+
+const PLUGIN_ID = "ssh-terminal";
+const MAX_MACROS = 100;
+
+/** The saved macros in a 2.8 terminal_macros value, or null for none. */
+export function macrosFromPreferences(value: unknown): unknown[] | null {
+ if (typeof value !== "string" || !value) return null;
+ let parsed: unknown;
+ try {
+ parsed = JSON.parse(value);
+ } catch {
+ return null;
+ }
+ if (!Array.isArray(parsed)) return null;
+ const macros = parsed
+ .filter(
+ (macro) =>
+ !!macro &&
+ typeof macro === "object" &&
+ typeof (macro as { id?: unknown }).id === "string" &&
+ typeof (macro as { name?: unknown }).name === "string" &&
+ Array.isArray((macro as { steps?: unknown }).steps),
+ )
+ .slice(0, MAX_MACROS);
+ return macros.length > 0 ? macros : null;
+}
+
+export async function runSshTerminalMacrosMigration(): Promise<{
+ usersMoved: number;
+}> {
+ const result = { usersMoved: 0 };
+ try {
+ const plugin = await createCurrentPluginRepository().findById(PLUGIN_ID);
+ if (!plugin) return result;
+ } catch {
+ return result;
+ }
+
+ const pluginSettings = createCurrentPluginSettingsRepository();
+ try {
+ const rows = await selectLegacyRows<{
+ user_id: string;
+ terminal_macros: unknown;
+ }>(sql`
+ SELECT user_id, terminal_macros FROM user_preferences
+ WHERE terminal_macros IS NOT NULL
+ `);
+ for (const row of rows) {
+ const macros = macrosFromPreferences(row.terminal_macros);
+ if (!macros) continue;
+ const existing = await pluginSettings.get(
+ PLUGIN_ID,
+ "user",
+ row.user_id,
+ "macros",
+ );
+ if (existing && existing.value !== null) continue;
+ await pluginSettings.set(
+ PLUGIN_ID,
+ "user",
+ row.user_id,
+ "macros",
+ JSON.stringify(macros),
+ );
+ result.usersMoved++;
+ }
+ } catch (error) {
+ databaseLogger.warn("Terminal macros migration failed", {
+ operation: "ssh_terminal_macros_migration",
+ error: error instanceof Error ? error.message : String(error),
+ });
+ }
+
+ if (result.usersMoved > 0) {
+ databaseLogger.info(
+ `Moved the terminal macros of ${result.usersMoved} user(s) into plugin settings`,
+ { operation: "ssh_terminal_macros_migration" },
+ );
+ }
+ return result;
+}
diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts
index 147aaae97..b1ec5f2b5 100644
--- a/src/backend/utils/auth-manager.ts
+++ b/src/backend/utils/auth-manager.ts
@@ -70,13 +70,30 @@ interface RequestWithHeaders extends Request {
const ADMIN_TARGET_USER_HEADER = "x-admin-target-user";
-// Data-plane routes an admin may hit on behalf of another user. Everything
-// else (TOTP, sessions, tunnels, file manager, ...) rejects the header.
-const IMPERSONATION_PATH_ALLOWLIST = [
- /^\/host\/db\//,
- /^\/credentials(\/|$)/,
- /^\/plugin-api\/snippets(\/|$)/,
-];
+// Core data-plane routes an admin may hit on behalf of another user.
+// Everything else (TOTP, sessions, ...) rejects the header, and so does every
+// plugin route unless its manifest sets contributes.http.adminImpersonation.
+const IMPERSONATION_PATH_ALLOWLIST = [/^\/host\/db\//, /^\/credentials(\/|$)/];
+
+const PLUGIN_API_PATH = /^\/plugin-api\/([a-z][a-z0-9-]*)(\/|$)/;
+
+let pluginAllowsImpersonation: (pluginId: string) => boolean = () => false;
+
+/** Set by the plugin runtime: whether a running plugin opted in. */
+export function setPluginImpersonationCheck(
+ check: (pluginId: string) => boolean,
+): void {
+ pluginAllowsImpersonation = check;
+}
+
+/** Whether an admin may send X-Admin-Target-User to this path. */
+export function allowsAdminImpersonation(path: string): boolean {
+ if (IMPERSONATION_PATH_ALLOWLIST.some((pattern) => pattern.test(path))) {
+ return true;
+ }
+ const plugin = PLUGIN_API_PATH.exec(path);
+ return !!plugin && pluginAllowsImpersonation(plugin[1]);
+}
class AuthManager {
private static instance: AuthManager;
@@ -836,10 +853,7 @@ class AuthManager {
}
const path = (req.originalUrl || req.url || "").split("?")[0];
- const allowed = IMPERSONATION_PATH_ALLOWLIST.some((pattern) =>
- pattern.test(path),
- );
- if (!allowed) {
+ if (!allowsAdminImpersonation(path)) {
res.status(403).json({
error: "Impersonation is not allowed for this route",
code: "IMPERSONATION_NOT_ALLOWED",
diff --git a/src/backend/utils/swagger.ts b/src/backend/utils/swagger.ts
index b951e6043..13b26576a 100644
--- a/src/backend/utils/swagger.ts
+++ b/src/backend/utils/swagger.ts
@@ -67,10 +67,6 @@ const swaggerOptions: SwaggerJSDocOptions = {
name: "RBAC",
description: "Role-based access control for host sharing",
},
- {
- name: "Snippets",
- description: "Command snippet management",
- },
{
name: "Terminal",
description: "Terminal command history",
diff --git a/src/types/index.ts b/src/types/index.ts
index e0142080b..a8bd66a08 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -61,11 +61,6 @@ export interface JumpHost {
hostId: number;
}
-export interface QuickAction {
- name: string;
- snippetId: number;
-}
-
export type Host = {
id: number;
name: string;
@@ -92,7 +87,6 @@ export type Host = {
overrideCredentialUsername?: boolean;
userId?: string;
jumpHosts?: JumpHost[];
- quickActions?: QuickAction[];
statusCheckEnabled?: boolean;
/** Seconds between status checks; null follows the global setting. */
statusCheckInterval?: number | null;
@@ -176,11 +170,6 @@ export interface JumpHostData {
hostId: number;
}
-export interface QuickActionData {
- name: string;
- snippetId: number;
-}
-
export interface ProxyNode {
host: string;
port: number;
@@ -217,7 +206,6 @@ export interface HostData {
overrideCredentialUsername?: boolean;
forceKeyboardInteractive?: boolean;
jumpHosts?: JumpHostData[];
- quickActions?: QuickActionData[];
statusCheckEnabled?: boolean;
/** Seconds between status checks; null follows the global setting. */
statusCheckInterval?: number | null;
@@ -357,13 +345,11 @@ export interface Tab {
// ============================================================================
/**
- * What core still keeps in ssh_data.terminal_config. The terminal's look and
- * behavior are the ssh-terminal plugin's host settings, and the connection
- * options have their own column (sshOptions).
+ * What 2.8 kept in ssh_data.terminal_config. Core reads none of it any more:
+ * the terminal's look and behavior and the startup command are plugins' host
+ * settings, and the connection options have their own column (sshOptions).
*/
-export interface HostTerminalConfig {
- startupSnippetId?: number | null;
-}
+export type HostTerminalConfig = Record;
export type { HostSshOptions } from "@termix/plugin-sdk/frontend";
diff --git a/src/types/keybindings.ts b/src/types/keybindings.ts
index 1c06d5c86..75e55b10c 100644
--- a/src/types/keybindings.ts
+++ b/src/types/keybindings.ts
@@ -7,23 +7,16 @@ export interface KeyCombo {
meta: boolean;
}
-export type KeybindingActionType =
- | "copy"
- | "paste"
- | "sendControlCode"
- | "sendText"
- | "runSnippet"
- | "nextTab"
- | "previousTab"
- | "openCommandPalette"
- | "reconnectSession";
+/**
+ * The shell's own actions (nextTab, previousTab, openCommandPalette,
+ * reconnectSession) or one a plugin declared in contributes.keybindingActions.
+ */
+export type KeybindingActionType = string;
+/** A bound action: its type plus the parameters its declaration lists. */
export interface KeybindingAction {
type: KeybindingActionType;
- text?: string;
- controlCode?: string;
- snippetId?: string;
- appendEnter?: boolean;
+ [param: string]: unknown;
}
export interface CustomKeybinding {
diff --git a/src/types/ui-preferences.ts b/src/types/ui-preferences.ts
index 4060bb027..bf0373bd8 100644
--- a/src/types/ui-preferences.ts
+++ b/src/types/ui-preferences.ts
@@ -135,7 +135,6 @@ const CORE_HIDEABLE_RAIL_VIEWS = [
"credentials",
"connections",
"quick-connect",
- "macros",
];
const SIMPLE_HIDDEN_RAIL_TABS = CORE_HIDEABLE_RAIL_VIEWS.filter(
diff --git a/src/types/ui-types.ts b/src/types/ui-types.ts
index 152e4371d..b56ebc11b 100644
--- a/src/types/ui-types.ts
+++ b/src/types/ui-types.ts
@@ -75,7 +75,6 @@ export type Host = {
statusCheckEnabled?: boolean;
/** Seconds between status checks; null follows the global setting. */
statusCheckInterval?: number | null;
- quickActions: { name: string; snippetId: string }[];
enableSsh: boolean;
@@ -158,8 +157,6 @@ type KnownTabType =
| "host-manager"
| "user-profile"
| "admin-settings"
- // Rail panels that can also open full-width in the main area.
- | "macros"
| "split-screen";
/**
@@ -255,8 +252,8 @@ export type UiFontId =
| "source-code-pro"
| "caskaydia-cove";
-/** A tools panel view: core's own, or a rail panel a plugin registered. */
-export type ToolsTab = "macros" | (string & {});
+/** A tools panel view: a rail panel a plugin registered. */
+export type ToolsTab = string & {};
/** "row" lays children side by side, "column" stacks them. */
export type SplitDirection = "row" | "column";
@@ -339,17 +336,6 @@ export type WorkspacePayload = {
};
};
-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",
diff --git a/src/ui/AppShell.tsx b/src/ui/AppShell.tsx
index 5c1c0dae1..657d1ed3d 100644
--- a/src/ui/AppShell.tsx
+++ b/src/ui/AppShell.tsx
@@ -60,6 +60,11 @@ import type {
CustomKeybinding,
KeybindingActionType,
} from "@/types/keybindings";
+import {
+ GLOBAL_KEYBINDING_EVENT,
+ getKeybindingAction,
+ runKeybindingAction,
+} from "@/shell/keybinding-registry";
// Shell surfaces that are not needed for first paint.
const CommandPalette = lazy(() =>
@@ -76,11 +81,6 @@ const QuickConnectPanel = lazy(() =>
})),
);
-// Secondary rail panels — load on first open, not with the shell critical path.
-const MacrosPanel = lazy(() =>
- import("@/sidebar/MacrosPanel").then((m) => ({ default: m.MacrosPanel })),
-);
-
const UserProfilePanel = lazy(() =>
import("@/sidebar/UserProfilePanel").then((m) => ({
default: m.UserProfilePanel,
@@ -483,16 +483,7 @@ export function AppShell({
if (cancelled) return;
globalKeybindingsRef.current = parseCustomKeybindings(
prefs.customKeybindings,
- ).filter(
- (binding) =>
- binding.enabled &&
- [
- "nextTab",
- "previousTab",
- "openCommandPalette",
- "reconnectSession",
- ].includes(binding.action.type),
- );
+ ).filter((binding) => binding.enabled);
})
.catch(() => {});
};
@@ -535,14 +526,17 @@ export function AppShell({
event.target.closest("[data-keybinding-recorder]")
)
return;
+ // Only actions that run anywhere; a terminal's own are its to handle.
const binding = findMatchingKeybinding(
event,
- globalKeybindingsRef.current,
+ globalKeybindingsRef.current.filter(
+ (entry) => getKeybindingAction(entry.action.type)?.scope === "global",
+ ),
);
if (!binding) return;
event.preventDefault();
event.stopPropagation();
- runAction(binding.action.type);
+ runKeybindingAction(binding.action);
};
const handleAction = (event: Event) =>
runAction(
@@ -550,10 +544,10 @@ export function AppShell({
);
window.addEventListener("keydown", handleKeyDown, true);
- window.addEventListener("termix:global-keybinding", handleAction);
+ window.addEventListener(GLOBAL_KEYBINDING_EVENT, handleAction);
return () => {
window.removeEventListener("keydown", handleKeyDown, true);
- window.removeEventListener("termix:global-keybinding", handleAction);
+ window.removeEventListener(GLOBAL_KEYBINDING_EVENT, handleAction);
};
}, []);
useEffect(() => {
@@ -648,7 +642,7 @@ export function AppShell({
setActiveTabId(splitTabId);
};
- // Panels like history and snippets act on "the terminal you're working in".
+ // Panels that type into a terminal act on "the terminal you're working in".
// Once those panels can themselves be the active tab, activeTabId points at
// the panel and the lookup misses, so remember the last terminal instead.
// In a split, the tab being worked in is the focused pane's.
@@ -990,7 +984,6 @@ export function AppShell({
"hostTrayOnClick",
"pinAppRail",
"expandAppRailOnHover",
- "confirmSnippetExecution",
"disableUpdateCheck",
"confirmTabClose",
"hiddenRailTabs",
@@ -1049,14 +1042,6 @@ export function AppShell({
);
window.dispatchEvent(new Event("expandAppRailOnHoverChanged"));
}
- if (
- prefs.confirmSnippetExecution !== null &&
- prefs.confirmSnippetExecution !== undefined
- )
- localStorage.setItem(
- "confirmSnippetExecution",
- String(prefs.confirmSnippetExecution),
- );
if (
prefs.disableUpdateCheck !== null &&
prefs.disableUpdateCheck !== undefined
@@ -2518,7 +2503,7 @@ export function AppShell({
]);
}
- // What history, snippets and macros should act on. Falls back to the remembered
+ // What command-target panels act on. Falls back to the remembered
// terminal when the active tab isn't one, and drops it once it's closed.
const targetTerminalTabId = terminalTabs.some((t) => t.id === workingTabId)
? workingTabId
@@ -2641,18 +2626,6 @@ export function AppShell({
/>
)}
- {railView === "macros" && (
-
-
-
- )}
-
{registeredPanels.map((panel) => {
const shown = railView === panel.id;
// A kept-mounted panel lives in the owning dock only, so two live
@@ -2743,7 +2716,6 @@ export function AppShell({
authType: "none",
enableSsh: false,
sshPort: 22,
- quickActions: [],
};
void invokeAction("terminal.open", host, {
joinSharedSessionId: session.sessionId,
@@ -3123,14 +3095,6 @@ export function AppShell({
isVisible: inPane || activeInline,
isFocusedPane,
inSplit: inPane,
- panelProps: {
- terminalTabs,
- targetTerminalTabId,
- storageMode:
- userPrefs.storageMode === "cloud"
- ? "cloud"
- : "local",
- },
}),
tabNode,
tab.id,
diff --git a/src/ui/api/open-tabs-api.ts b/src/ui/api/open-tabs-api.ts
index 07e4ff0d0..ddc8b7ef8 100644
--- a/src/ui/api/open-tabs-api.ts
+++ b/src/ui/api/open-tabs-api.ts
@@ -127,14 +127,12 @@ export interface UserPreferences {
pinAppRail?: boolean | null;
expandAppRailOnHover?: boolean | null;
showPinAppRailButton?: boolean | null;
- confirmSnippetExecution?: boolean | null;
disableUpdateCheck?: boolean | null;
confirmTabClose?: boolean | null;
hiddenRailTabs?: string | null;
compactHostView?: boolean | null;
statusColorScheme?: string | null;
customKeybindings?: string | null;
- terminalMacros?: string | null;
}
export function parseCustomKeybindings(
diff --git a/src/ui/hooks/use-snippet-runner.tsx b/src/ui/hooks/use-snippet-runner.tsx
deleted file mode 100644
index 2a5c7ffcb..000000000
--- a/src/ui/hooks/use-snippet-runner.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import { useCallback, useState } from "react";
-import { useTranslation } from "react-i18next";
-import { toast } from "sonner";
-import { useConfirmation } from "@/hooks/use-confirmation.ts";
-import { invokeAction } from "@/shell/action-registry";
-import { SnippetVariablesDialog } from "@/components/SnippetVariablesDialog";
-import type { Snippet, Tab } from "@/types/ui-types";
-
-interface ResolvedForTerminal {
- needsInputs: boolean;
- content: string;
- isNote: boolean;
-}
-
-/**
- * Shared "run this snippet against these terminal tabs" flow, for the few
- * core surfaces (the command palette) that still reach for a snippet
- * directly rather than through the snippets plugin's own panel. Variable
- * resolution runs through the snippets plugin's "snippet.resolveForTerminal"
- * action, and sending runs through ssh-terminal's "terminal.sendToSession",
- * since core no longer owns snippet content or a terminal ref.
- */
-export function useSnippetRunner() {
- const { t } = useTranslation();
- const { confirmWithToast } = useConfirmation();
- const [runningSnippet, setRunningSnippet] = useState<{
- snippet: Snippet;
- host: Tab["host"] | null;
- onConfirm: (
- resolvedContent: string,
- inputValues: Record,
- ) => void;
- } | null>(null);
-
- const handleConfirmRun = useCallback(
- (snippet: Snippet, execute: () => void) => {
- const shouldConfirm =
- localStorage.getItem("confirmSnippetExecution") === "true";
- if (!shouldConfirm) {
- execute();
- return;
- }
- confirmWithToast(
- t("newUi.sidebar.snippets.confirmRunMessage", { name: snippet.name }),
- execute,
- t("newUi.sidebar.snippets.confirmRunButton"),
- t("newUi.sidebar.snippets.cancel"),
- { confirmOnEnter: true, duration: 6000 },
- );
- },
- [confirmWithToast, t],
- );
-
- async function sendResolvedToTerminal(
- tab: Tab,
- snippet: Snippet,
- inputValues: Record,
- ) {
- const resolved = (await invokeAction(
- "snippet.resolveForTerminal",
- snippet.id,
- tab.host ?? null,
- inputValues,
- )) as ResolvedForTerminal | null | undefined;
- if (!resolved || resolved.needsInputs) return;
- await invokeAction("terminal.sendToSession", tab.id, resolved.content, {
- run: !resolved.isNote,
- });
- }
-
- const runSnippet = useCallback(
- (snippet: Snippet, targets: Tab[]) => {
- const runWithInputs = (inputValues: Record) => {
- const doSend = () => {
- void Promise.all(
- targets.map((tab) =>
- sendResolvedToTerminal(tab, snippet, inputValues),
- ),
- ).then(() => {
- toast.success(
- t(
- snippet.isNote
- ? "newUi.sidebar.snippets.pasteSuccess"
- : "newUi.sidebar.snippets.runSuccess",
- { name: snippet.name, count: targets.length },
- ),
- );
- });
- };
- if (snippet.isNote) {
- doSend();
- } else {
- handleConfirmRun(snippet, doSend);
- }
- };
-
- void invokeAction(
- "snippet.resolveForTerminal",
- snippet.id,
- targets[0]?.host ?? null,
- ).then((result) => {
- const resolved = result as ResolvedForTerminal | null | undefined;
- if (resolved?.needsInputs) {
- setRunningSnippet({
- snippet,
- host: targets[0]?.host ?? null,
- onConfirm: (_resolvedContent, inputValues) => {
- setRunningSnippet(null);
- runWithInputs(inputValues);
- },
- });
- return;
- }
- runWithInputs({});
- });
- },
- [handleConfirmRun, t],
- );
-
- const dialog = runningSnippet ? (
- setRunningSnippet(null)}
- onConfirm={runningSnippet.onConfirm}
- />
- ) : null;
-
- return { runSnippet, handleConfirmRun, dialog };
-}
diff --git a/src/ui/lib/default-keybindings.ts b/src/ui/lib/default-keybindings.ts
deleted file mode 100644
index d40e80dae..000000000
--- a/src/ui/lib/default-keybindings.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import type { KeyCombo } from "@/types/keybindings";
-
-export interface DefaultKeybindingInfo {
- id: string;
- combo: KeyCombo;
- descriptionKey: string;
-}
-
-export const BUILT_IN_DEFAULTS: DefaultKeybindingInfo[] = [
- {
- id: "default-copy-ctrlc",
- combo: {
- key: "c",
- isCode: false,
- ctrl: true,
- alt: false,
- shift: false,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.copyCtrlC",
- },
- {
- id: "default-copy-ctrlshiftc",
- combo: {
- key: "c",
- isCode: false,
- ctrl: true,
- alt: false,
- shift: true,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.copyCtrlShiftC",
- },
- {
- id: "default-copy-cmdc",
- combo: {
- key: "c",
- isCode: false,
- ctrl: false,
- alt: false,
- shift: false,
- meta: true,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.copyCmdC",
- },
- {
- id: "default-paste-ctrlshiftv",
- combo: {
- key: "v",
- isCode: false,
- ctrl: true,
- alt: false,
- shift: true,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.pasteCtrlShiftV",
- },
- {
- id: "default-ctrlaltw",
- combo: {
- key: "w",
- isCode: false,
- ctrl: true,
- alt: true,
- shift: false,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.ctrlW",
- },
- {
- id: "default-ctrlaltt",
- combo: {
- key: "t",
- isCode: false,
- ctrl: true,
- alt: true,
- shift: false,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.ctrlT",
- },
- {
- id: "default-ctrlaltn",
- combo: {
- key: "n",
- isCode: false,
- ctrl: true,
- alt: true,
- shift: false,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.ctrlN",
- },
- {
- id: "default-ctrlaltq",
- combo: {
- key: "q",
- isCode: false,
- ctrl: true,
- alt: true,
- shift: false,
- meta: false,
- },
- descriptionKey: "newUi.sidebar.keybindings.builtIn.ctrlQ",
- },
-];
diff --git a/src/ui/lib/snippet-provider.ts b/src/ui/lib/snippet-provider.ts
deleted file mode 100644
index 38c569f86..000000000
--- a/src/ui/lib/snippet-provider.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { invokeAction } from "@/shell/action-registry";
-
-/** A snippet as core's pickers show it. */
-export interface SnippetRow {
- id: number;
- name: string;
- content: string;
- folder?: string | null;
- isNote?: boolean;
-}
-
-/**
- * The user's snippets, from whichever plugin provides them through the
- * "snippet.list" action. Empty while no plugin does. An admin editing
- * another user's host passes that user's id.
- */
-export async function listSnippets(options?: {
- targetUserId?: string;
-}): Promise {
- const rows = await invokeAction("snippet.list", options);
- return Array.isArray(rows) ? (rows as SnippetRow[]) : [];
-}
diff --git a/src/ui/lib/snippet-variables.ts b/src/ui/lib/snippet-variables.ts
deleted file mode 100644
index 47cf6eaf5..000000000
--- a/src/ui/lib/snippet-variables.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-export interface SnippetInput {
- key: string;
- label: string;
-}
-
-export interface SnippetHostContext {
- ip?: string;
- username?: string;
- port?: number | string;
- name?: string;
-}
-
-const INPUT_PATTERN =
- /\$\{INPUT_(\d+)(?::([^}$]+))?\}|\$INPUT_(\d+)(?![a-zA-Z0-9_])/g;
-
-export function extractSnippetInputs(content: string): SnippetInput[] {
- const seen = new Map();
- let match: RegExpExecArray | null;
- INPUT_PATTERN.lastIndex = 0;
- while ((match = INPUT_PATTERN.exec(content)) !== null) {
- const digits = match[1] ?? match[3];
- const key = `INPUT_${digits}`;
- if (!seen.has(key)) {
- seen.set(key, { key, label: match[2]?.trim() || `Input ${digits}` });
- }
- }
- return Array.from(seen.values());
-}
-
-function replaceVar(
- content: string,
- name: string,
- value: string | undefined,
-): string {
- if (value === undefined) return content;
- const pattern = new RegExp(`\\$\\{?${name}\\}?`, "g");
- return content.replace(pattern, value);
-}
-
-export function resolveSnippetContent(
- content: string,
- host: SnippetHostContext | null,
- inputValues: Record = {},
-): string {
- let resolved = content;
-
- resolved = replaceVar(resolved, "HOST", host?.ip);
- resolved = replaceVar(resolved, "USER", host?.username);
- resolved = replaceVar(
- resolved,
- "PORT",
- host?.port !== undefined ? String(host.port) : undefined,
- );
- resolved = replaceVar(resolved, "NAME", host?.name);
-
- resolved = resolved.replace(
- INPUT_PATTERN,
- (
- fullMatch,
- braceDigits: string | undefined,
- _label,
- plainDigits: string | undefined,
- ) => {
- const key = `INPUT_${braceDigits ?? plainDigits}`;
- return key in inputValues ? inputValues[key] : fullMatch;
- },
- );
-
- return resolved;
-}
diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json
index 63c7c5a54..b43be04ba 100644
--- a/src/ui/locales/en.json
+++ b/src/ui/locales/en.json
@@ -294,7 +294,6 @@
"multiPanelHintDockHow": "Right-click its rail icon, or use the panel icon in the tab bar.",
"sidebarSettings": "Sidebar Settings...",
"hosts": "Hosts",
- "macros": "Macros",
"hostManager": "Host Manager",
"credentials": "Credentials",
"connections": "Connections",
@@ -711,7 +710,6 @@
"groupTags": "Tags & pin",
"groupProxy": "Proxy",
"groupJumpHosts": "Jump hosts",
- "groupQuickActions": "Quick actions",
"groupFeatureFlags": "Feature flags",
"groupAdvanced": "Advanced config",
"preview": "Preview",
@@ -916,9 +914,6 @@
"userProfileDesc": "Manage your account and preferences",
"addCredential": "Add Credential",
"addCredentialDesc": "Store SSH keys or passwords",
- "snippets": "Snippets",
- "runSnippetDesc": "Run in the active terminal tab",
- "noSnippetsFound": "No snippets found matching \"{{search}}\"",
"recentActivity": "Recent Activity",
"serversAndHosts": "Servers & Hosts",
"noHostsFound": "No hosts found matching \"{{search}}\"",
@@ -926,7 +921,8 @@
"navigate": "Navigate",
"select": "Select",
"toggleWith": "Toggle with",
- "pluginActions": "More actions"
+ "pluginActions": "More actions",
+ "noTargetTab": "No terminal tab is open"
},
"splitScreen": {
"title": "Split screen",
@@ -1723,122 +1719,9 @@
"sshProtocol": "SSH",
"noTarget": "Nothing installed can connect to this host type"
},
- "snippets": {
- "title": "Snippets",
- "createSnippetTitle": "Create Snippet",
- "createSnippetDescription": "Create a new command snippet for quick execution",
- "nameLabel": "Name",
- "namePlaceholder": "e.g., Restart Nginx",
- "descriptionLabel": "Description",
- "descriptionPlaceholder": "Optional description",
- "optional": "Optional",
- "folderLabel": "Folder",
- "noFolder": "No folder (Uncategorized)",
- "commandLabel": "Command",
- "commandPlaceholder": "e.g., sudo systemctl restart nginx",
- "typeLabel": "Type",
- "typeCommand": "Command",
- "typeNote": "Note",
- "noteLabel": "Note",
- "notePlaceholder": "e.g., a reusable prompt or reminder text",
- "cancel": "Cancel",
- "createSnippetButton": "Create Snippet",
- "createFolderTitle": "Create Folder",
- "createFolderDescription": "Organize your snippets into folders",
- "folderNameLabel": "Folder Name",
- "folderNamePlaceholder": "e.g., System Commands, Docker Scripts",
- "folderColorLabel": "Folder Color",
- "folderIconLabel": "Folder Icon",
- "previewLabel": "Preview",
- "folderNameFallback": "Folder Name",
- "createFolderButton": "Create Folder",
- "targetTerminals": "Target Terminals",
- "selectAll": "All",
- "selectNone": "None",
- "noTerminalTabsOpen": "No terminal tabs open",
- "searchPlaceholder": "Search snippets...",
- "newSnippet": "New Snippet",
- "newFolder": "New Folder",
- "run": "Run",
- "noSnippetsInFolder": "No snippets in this folder",
- "uncategorized": "Uncategorized",
- "editSnippetTitle": "Edit Snippet",
- "editSnippetDescription": "Update this command snippet",
- "saveSnippetButton": "Save Changes",
- "createSuccess": "Snippet created successfully",
- "createFailed": "Failed to create snippet",
- "updateSuccess": "Snippet updated successfully",
- "updateFailed": "Failed to update snippet",
- "deleteFailed": "Failed to delete snippet",
- "folderCreateSuccess": "Folder created successfully",
- "folderCreateFailed": "Failed to create folder",
- "editFolderTitle": "Edit Folder",
- "editFolderDescription": "Rename or change the appearance of this folder",
- "saveFolderButton": "Save Changes",
- "editFolder": "Edit folder",
- "deleteFolder": "Delete folder",
- "folderDeleteSuccess": "Folder \"{{name}}\" deleted",
- "folderDeleteFailed": "Failed to delete folder",
- "folderEditSuccess": "Folder updated successfully",
- "folderEditFailed": "Failed to update folder",
- "confirmRunMessage": "Run \"{{name}}\"?",
- "confirmRunButton": "Run",
- "runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)",
- "pasteSuccess": "Pasted \"{{name}}\" into {{count}} terminal(s)",
- "pasteToTerminal": "Paste",
- "copySuccess": "Copied \"{{name}}\" to clipboard",
- "shareTitle": "Share Snippet",
- "shareUser": "User",
- "shareRole": "Role",
- "selectUser": "Select a user...",
- "selectRole": "Select a role...",
- "shareSuccess": "Snippet shared successfully",
- "shareFailed": "Failed to share snippet",
- "revokeSuccess": "Access revoked",
- "revokeFailed": "Failed to revoke access",
- "currentAccess": "Current Access",
- "shareLoadError": "Failed to load share data",
- "loading": "Loading...",
- "close": "Close",
- "reorderFailed": "Failed to save snippet order",
- "importExport": "Import / Export",
- "exportBtn": "Export JSON",
- "importBtn": "Import JSON",
- "exportSuccess": "Snippets exported successfully",
- "exportFailed": "Failed to export snippets",
- "importTitle": "Import Snippets",
- "importDescription": "Import snippets and folders from a JSON file exported by Termix",
- "importDropOrClick": "Drop a JSON file here or click to browse",
- "importSelectedFile": "Selected: {{name}}",
- "importOverwrite": "Overwrite existing snippets with the same name and folder",
- "importStartBtn": "Import",
- "importSuccess": "Import complete: {{snippets}} snippet(s) added, {{updated}} updated, {{skipped}} skipped, {{folders}} folder(s) added",
- "importFailed": "Failed to import snippets",
- "importInvalidFile": "Invalid file: expected a JSON object with snippets or folders arrays",
- "targetHostsLabel": "Target Hosts",
- "targetHostsHint": "Assign hosts to run this snippet directly without an open terminal.",
- "noHostsAvailable": "No hosts configured",
- "clearTargetHosts": "Clear all",
- "hasTargetHosts": "Has target hosts",
- "runOnTargets": "Run on Targets",
- "directRunSuccess": "Ran \"{{name}}\" on {{count}} host(s)",
- "directRunPartialFail": "\"{{name}}\" failed on one or more hosts",
- "executionResultTitle": "Execution Results: {{name}}",
- "executionResultDescription": "Output from running the snippet on each target host.",
- "executionSuccess": "Success",
- "executionFailed": "Failed",
- "expandAll": "Expand all folders",
- "collapseAll": "Collapse all folders",
- "settings": "Snippet settings",
- "variablesHint": "Variables: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Run \"{{name}}\"",
- "variablesDialogDescription": "Fill in the values this snippet needs before running it.",
- "variablesPreviewLabel": "Preview",
- "variablesConfirmButton": "Run"
- },
"keybindings": {
"title": "Keyboard Shortcuts",
- "description": "Customize terminal copy, paste, and control shortcuts, or bind keys to send text or run a snippet.",
+ "description": "Bind keys to actions in the terminal and the app, or change the built-in shortcuts.",
"defaultsHeading": "Built-in Shortcuts",
"customHeading": "Custom Shortcuts",
"addBinding": "Add Shortcut",
@@ -1859,37 +1742,14 @@
"pressKeysPlaceholder": "Click and press keys",
"recording": "Press keys...",
"comboRequiredError": "Press a key combination first",
- "textRequiredError": "Enter the text to send",
- "controlCodeRequiredError": "Enter a single letter for the control code",
- "snippetRequiredError": "Select a snippet",
"conflictWarning": "This combination is already bound to {{combo}}. Saving will make both fire.",
"actionLabel": "Action",
- "actionCopy": "Copy selection",
- "actionPaste": "Paste",
- "actionSendControlCode": "Send Ctrl+letter signal",
- "actionSendText": "Send text",
- "actionRunSnippet": "Run existing snippet",
"actionNextTab": "Switch to next tab",
"actionPreviousTab": "Switch to previous tab",
"actionOpenCommandPalette": "Open quick search",
"actionReconnectSession": "Reconnect current session",
- "controlCodeLabel": "Letter",
- "textLabel": "Text to send",
- "appendEnterLabel": "Press Enter after sending",
- "snippetLabel": "Snippet",
- "selectSnippetPlaceholder": "Select a snippet",
- "orphanedSnippetWarning": "snippet not found",
- "clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers.",
- "builtIn": {
- "copyCtrlC": "Copy selection (Ctrl+C, only when text is selected)",
- "copyCtrlShiftC": "Copy selection (Ctrl+Shift+C)",
- "copyCmdC": "Copy selection (Cmd+C)",
- "pasteCtrlShiftV": "Paste from clipboard (Ctrl+Shift+V)",
- "ctrlW": "Send Ctrl+W to shell (blocked from closing browser tab)",
- "ctrlT": "Send Ctrl+T to shell (blocked from opening browser tab)",
- "ctrlN": "Send Ctrl+N to shell (blocked from opening browser window)",
- "ctrlQ": "Send Ctrl+Q to shell (blocked from quitting browser)"
- }
+ "unavailableAction": "{{type}} (not available)",
+ "actionRequiredError": "Pick an action first"
},
"userProfile": {
"donateTitle": "Keep Termix alive",
@@ -2085,101 +1945,6 @@
}
},
"terminalToolbar": {},
- "macros": {
- "title": "Macros",
- "description": "Run a saved series of commands in a terminal, and react to what the server sends back.",
- "target": "Target",
- "noTerminal": "No terminal open",
- "empty": "No macros yet",
- "emptyHint": "Create a macro to automate a terminal flow, like logging in, restarting a service, or answering prompts.",
- "create": "New macro",
- "back": "Back to macros",
- "name": "Name",
- "namePlaceholder": "Restart web server",
- "nameRequired": "Give the macro a name before saving",
- "descriptionLabel": "Description",
- "descriptionPlaceholder": "What this macro does (optional)",
- "unsaved": "Unsaved changes",
- "save": "Save",
- "saved": "Macro saved",
- "saveFailed": "Could not save the macro",
- "delete": "Delete",
- "deleteTitle": "Delete this macro?",
- "deleteBody": "\"{{name}}\" will be permanently deleted. This cannot be undone.",
- "deleted": "Macro deleted",
- "cancel": "Cancel",
- "run": "Run",
- "stop": "Stop",
- "running": "Running...",
- "completed": "Macro finished",
- "failed": "Macro failed",
- "loadFailed": "Could not load macros",
- "needTerminal": "Open and connect a terminal first",
- "noSteps": "This macro has no steps yet. Add one below.",
- "addStep": "Add step",
- "stepNumber": "Step {{number}}",
- "removeStep": "Remove step",
- "confirmRunTitle": "Run this macro?",
- "confirmRunBody": "\"{{name}}\" will send commands to {{host}}.",
- "confirmRunAction": "Run macro",
- "stepTypes": {
- "send": {
- "label": "Send command",
- "help": "Types text into the terminal, the same as if you typed it."
- },
- "wait": {
- "label": "Wait for output",
- "help": "Pauses until the server prints the text you name, like a password prompt."
- },
- "delay": {
- "label": "Pause",
- "help": "Waits a fixed amount of time before the next step."
- },
- "if": {
- "label": "If output contains",
- "help": "Checks everything the server has printed so far, then runs one of two branches."
- },
- "repeat": {
- "label": "Repeat",
- "help": "Runs the steps inside it a set number of times."
- }
- },
- "send": {
- "text": "Command or text",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Press Enter after sending",
- "pressEnterHelp": "Turn this off to type text without submitting it."
- },
- "wait": {
- "pattern": "Wait until the output contains",
- "patternPlaceholder": "password:",
- "timeout": "Give up after",
- "onTimeout": "If it never appears",
- "stop": "Stop the macro",
- "continue": "Keep going anyway"
- },
- "delay": {
- "duration": "Wait for"
- },
- "ifStep": {
- "pattern": "If the output contains",
- "patternPlaceholder": "Active: active (running)",
- "then": "Then do this",
- "else": "Otherwise do this",
- "emptyBranch": "No steps"
- },
- "repeat": {
- "count": "Number of times",
- "times": "times"
- },
- "match": {
- "useRegex": "Match with a regular expression",
- "useRegexHelp": "Leave this off to match the text exactly as written.",
- "invalidRegex": "Not a valid regular expression, it will be matched as plain text"
- },
- "seconds": "seconds",
- "secondsShort": "{{count}}s"
- },
"plugins": {
"capabilities": {
"credentials:read": {
diff --git a/src/ui/locales/translated/af_ZA.json b/src/ui/locales/translated/af_ZA.json
index 2e213e576..76217844c 100644
--- a/src/ui/locales/translated/af_ZA.json
+++ b/src/ui/locales/translated/af_ZA.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Sybalkinstellings...",
"hosts": "Gashere",
"snippets": "Brokkies",
- "macros": "Makro's",
"hostManager": "Gasheerbestuurder",
"credentials": "Geloofsbriewe",
"connections": "Verbindings",
@@ -869,7 +868,6 @@
"groupTags": "Etikette en pen",
"groupTunnels": "Tonnels",
"groupJumpHosts": "Spring-gashere",
- "groupQuickActions": "Vinnige aksies",
"groupFeatureFlags": "Kenmerkvlae",
"groupAdvanced": "Gevorderde konfigurasie",
"preview": "Voorskou",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Stoor SSH-sleutels of wagwoorde",
"tmuxMonitor": "Tmux-monitor",
"tmuxMonitorDesc": "Monitor tmux-sessies oor jou gashere",
- "snippets": "Brokkies",
- "runSnippetDesc": "Begin in die aktiewe terminaal-oortjie",
- "noSnippetsFound": "Geen brokkies gevind wat ooreenstem met \"{{search}}\"",
"recentActivity": "Onlangse Aktiwiteit",
"serversAndHosts": "Bedieners en gashere",
"noHostsFound": "Geen gashere gevind wat ooreenstem met \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Vorige oortjie",
"hotkeysJumpTab": "Spring na oortjie"
},
- "snippets": {
- "title": "Brokkies",
- "createSnippetTitle": "Skep brokkie",
- "createSnippetDescription": "Skep 'n nuwe opdragbrokkie vir vinnige uitvoering",
- "nameLabel": "Naam",
- "namePlaceholder": "bv., Herbegin Nginx",
- "descriptionLabel": "Beskrywing",
- "descriptionPlaceholder": "Opsionele beskrywing",
- "optional": "Opsioneel",
- "folderLabel": "Vouer",
- "noFolder": "Geen vouer nie (Ongekategoriseerd)",
- "commandLabel": "Bevel",
- "commandPlaceholder": "bv. sudo systemctl herbegin nginx",
- "typeLabel": "Tipe",
- "typeCommand": "Bevel",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "bv. 'n herbruikbare aanwysings- of herinneringteks",
- "cancel": "Kanselleer",
- "createSnippetButton": "Skep brokkie",
- "createFolderTitle": "Skep vouer",
- "createFolderDescription": "Organiseer jou brokkies in dopgehou",
- "folderNameLabel": "Vouernaam",
- "folderNamePlaceholder": "bv. Stelselopdragte, Docker-skripte",
- "folderColorLabel": "Vouerkleur",
- "folderIconLabel": "Vouer-ikoon",
- "previewLabel": "Voorskou",
- "folderNameFallback": "Vouernaam",
- "createFolderButton": "Skep vouer",
- "targetTerminals": "Teikenterminale",
- "selectAll": "Alles",
- "selectNone": "Geen",
- "noTerminalTabsOpen": "Geen terminaal-oortjies oop nie",
- "searchPlaceholder": "Soek brokkies...",
- "newSnippet": "Nuwe brokkie",
- "newFolder": "Nuwe vouer",
- "run": "Hardloop",
- "noSnippetsInFolder": "Geen brokkies in hierdie vouer nie",
- "uncategorized": "Ongekategoriseerd",
- "editSnippetTitle": "Wysig brokkie",
- "editSnippetDescription": "Dateer hierdie opdragbrokkie op",
- "saveSnippetButton": "Stoor veranderinge",
- "createSuccess": "Brokkie suksesvol geskep",
- "createFailed": "Kon nie brokkie skep nie",
- "updateSuccess": "Brokkie suksesvol opgedateer",
- "updateFailed": "Kon nie die brokkie opdateer nie",
- "deleteFailed": "Kon nie fragment uitvee nie",
- "folderCreateSuccess": "Vouer suksesvol geskep",
- "folderCreateFailed": "Kon nie vouer skep nie",
- "editFolderTitle": "Wysig vouer",
- "editFolderDescription": "Hernoem of verander die voorkoms van hierdie vouer",
- "saveFolderButton": "Stoor veranderinge",
- "editFolder": "Wysig vouer",
- "deleteFolder": "Vee vouer uit",
- "folderDeleteSuccess": "Lêer \"{{name}}\" is uitgevee",
- "folderDeleteFailed": "Kon nie vouer uitvee nie",
- "folderEditSuccess": "Vouer suksesvol opgedateer",
- "folderEditFailed": "Kon nie vouer opdateer nie",
- "confirmRunMessage": "Begin \"{{name}}\"?",
- "confirmRunButton": "Hardloop",
- "runSuccess": "Het \"{{name}}\" in {{count}} terminaal(e) uitgevoer",
- "pasteSuccess": "Het \"{{name}}\" in {{count}} terminaal(e) geplak",
- "pasteToTerminal": "Plak",
- "copySuccess": "\"{{name}}\" na knipbord gekopieer",
- "shareTitle": "Deel brokkie",
- "shareUser": "Gebruiker",
- "shareRole": "Rol",
- "selectUser": "Kies 'n gebruiker...",
- "selectRole": "Kies 'n rol...",
- "shareSuccess": "Brokkie suksesvol gedeel",
- "shareFailed": "Kon nie fragment deel nie",
- "revokeSuccess": "Toegang herroep",
- "revokeFailed": "Kon nie toegang herroep nie",
- "currentAccess": "Huidige Toegang",
- "shareLoadError": "Kon nie deeldata laai nie",
- "loading": "Laai tans...",
- "close": "Maak toe",
- "reorderFailed": "Kon nie die volgorde van die brokkie stoor nie",
- "importExport": "Invoer / Uitvoer",
- "exportBtn": "Voer JSON uit",
- "importBtn": "Voer JSON in",
- "exportSuccess": "Brokkies suksesvol uitgevoer",
- "exportFailed": "Kon nie brokkies uitvoer nie",
- "importTitle": "Voer brokkies in",
- "importDescription": "Voer brokkies en lêers in vanaf 'n JSON-lêer wat deur Termix uitgevoer is",
- "importDropOrClick": "Plaas 'n JSON-lêer hier of klik om te blaai",
- "importSelectedFile": "Gekies: {{name}}",
- "importOverwrite": "Oorskryf bestaande brokkies met dieselfde naam en vouer",
- "importStartBtn": "Invoer",
- "importSuccess": "Invoer voltooi: {{snippets}} brokkie(s) bygevoeg, {{updated}} opgedateer, {{skipped}} oorgeslaan, {{folders}} vouer(s) bygevoeg",
- "importFailed": "Kon nie brokkies invoer nie",
- "importInvalidFile": "Ongeldige lêer: 'n JSON-objek met brokkies of vouerskikkings verwag",
- "targetHostsLabel": "Teikengashere",
- "targetHostsHint": "Ken gashere toe om hierdie brokkie direk uit te voer sonder 'n oop terminaal.",
- "noHostsAvailable": "Geen gashere gekonfigureer nie",
- "clearTargetHosts": "Vee alles uit",
- "hasTargetHosts": "Het teikengashere",
- "runOnTargets": "Hardloop op teikens",
- "directRunSuccess": "Het \"{{name}}\" op {{count}} gasheer(s) uitgevoer",
- "directRunPartialFail": "\"{{name}}\" het op een of meer gashere misluk",
- "executionResultTitle": "Uitvoeringsresultate: {{name}}",
- "executionResultDescription": "Uitvoer van die uitvoer van die brokkie op elke teikengasheer.",
- "executionSuccess": "Sukses",
- "executionFailed": "Misluk",
- "expandAll": "Vou alle vouers uit",
- "collapseAll": "Vou alle vouers in",
- "settings": "Brokkie-instellings",
- "variablesHint": "Veranderlikes: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etiket}",
- "variablesDialogTitle": "Begin \"{{name}}\"",
- "variablesDialogDescription": "Vul die waardes in wat hierdie brokkie benodig voordat dit uitgevoer word.",
- "variablesPreviewLabel": "Voorskou",
- "variablesConfirmButton": "Hardloop"
- },
"keybindings": {
"title": "Sleutelbordkortpaaie",
"description": "Pasmaak terminaalkopieer-, plak- en beheerkortpaaie, of bind sleutels om teks te stuur of 'n brokkie uit te voer.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klik en druk sleutels",
"recording": "Druk sleutels...",
"comboRequiredError": "Druk eers 'n sleutelkombinasie",
- "textRequiredError": "Voer die teks in om te stuur",
- "controlCodeRequiredError": "Voer 'n enkele letter vir die beheerkode in",
- "snippetRequiredError": "Kies 'n uittreksel",
"conflictWarning": "Hierdie kombinasie is reeds gebind aan {{combo}}. As jy dit stoor, sal albei aktiveer.",
"actionLabel": "Aksie",
- "actionCopy": "Kopieer seleksie",
- "actionPaste": "Plak",
- "actionSendControlCode": "Stuur Ctrl+letter sein",
- "actionSendText": "Stuur SMS",
- "actionRunSnippet": "Voer bestaande brokkie uit",
"actionNextTab": "Skakel oor na die volgende oortjie",
"actionPreviousTab": "Skakel oor na vorige oortjie",
- "actionOpenCommandPalette": "Maak vinnige soektog oop",
- "controlCodeLabel": "Brief",
- "textLabel": "SMS om te stuur",
- "appendEnterLabel": "Druk Enter na stuur",
- "snippetLabel": "Brokkie",
- "selectSnippetPlaceholder": "Kies 'n uittreksel",
- "orphanedSnippetWarning": "brokkie nie gevind nie",
- "clipboardPermissionNote": "Kan in sommige blaaiers met die eerste gebruik vir knipbordtoestemming vra."
+ "actionOpenCommandPalette": "Maak vinnige soektog oop"
},
"userProfile": {
"donateTitle": "Hou Termix lewendig",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Onderwerp is vereis",
"ntfyToken": "Toegangstoken (opsioneel)",
"channelSaveFailed": "Kon nie kanaal stoor nie"
- },
- "macros": {
- "title": "Makro's",
- "description": "Voer 'n gestoorde reeks opdragte in 'n terminaal uit en reageer op wat die bediener terugstuur.",
- "target": "Teiken",
- "noTerminal": "Geen terminaal oop nie",
- "empty": "Geen makro's nog nie",
- "emptyHint": "Skep 'n makro om 'n terminaalvloei te outomatiseer, soos om aan te meld, 'n diens te herbegin of aanwysings te beantwoord.",
- "create": "Nuwe makro",
- "back": "Terug na makro's",
- "name": "Naam",
- "namePlaceholder": "Herbegin die webbediener",
- "nameRequired": "Gee die makro 'n naam voordat jy dit stoor",
- "descriptionLabel": "Beskrywing",
- "descriptionPlaceholder": "Wat hierdie makro doen (opsioneel)",
- "unsaved": "Ongestoorde veranderinge",
- "save": "Stoor",
- "saved": "Makro gestoor",
- "saveFailed": "Kon nie die makro stoor nie",
- "delete": "Vee uit",
- "deleteTitle": "Vee hierdie makro uit?",
- "deleteBody": "\"{{name}}\" sal permanent verwyder word. Dit kan nie ongedaan gemaak word nie.",
- "deleted": "Makro uitgevee",
- "cancel": "Kanselleer",
- "run": "Hardloop",
- "stop": "Stop",
- "running": "Hardloop...",
- "completed": "Makro klaar",
- "failed": "Makro het misluk",
- "loadFailed": "Kon nie makro's laai nie",
- "needTerminal": "Maak eers 'n terminaal oop en koppel dit",
- "noSteps": "Hierdie makro het nog geen stappe nie. Voeg een hieronder by.",
- "addStep": "Voeg stap by",
- "stepNumber": "Stap {{number}}",
- "removeStep": "Verwyder stap",
- "confirmRunTitle": "Voer hierdie makro uit?",
- "confirmRunBody": "\"{{name}}\" sal bevele stuur na {{host}}.",
- "confirmRunAction": "Begin makro",
- "stepTypes": {
- "send": {
- "label": "Stuur opdrag",
- "help": "Tik teks in die terminaal, dieselfde asof jy dit getik het."
- },
- "wait": {
- "label": "Wag vir uitvoer",
- "help": "Pouseer totdat die bediener die teks wat jy noem, druk, soos 'n wagwoordaanwysing."
- },
- "delay": {
- "label": "Pouseer",
- "help": "Wag 'n vaste hoeveelheid tyd voor die volgende stap."
- },
- "if": {
- "label": "Indien die uitvoer bevat",
- "help": "Kontroleer alles wat die bediener tot dusver gedruk het, en voer dan een van twee takke uit."
- },
- "repeat": {
- "label": "Herhaal",
- "help": "Voer die stappe daarin 'n vasgestelde aantal kere uit."
- }
- },
- "send": {
- "text": "Opdrag of teks",
- "textPlaceholder": "systemctl herbegin nginx",
- "pressEnter": "Druk Enter na stuur",
- "pressEnterHelp": "Skakel dit af om teks te tik sonder om dit in te dien."
- },
- "wait": {
- "pattern": "Wag totdat die uitvoer bevat",
- "patternPlaceholder": "wagwoord:",
- "timeout": "Gee op daarna",
- "onTimeout": "As dit nooit verskyn nie",
- "stop": "Stop die makro",
- "continue": "Gaan in elk geval voort"
- },
- "delay": {
- "duration": "Wag vir"
- },
- "ifStep": {
- "pattern": "Indien die uitvoer bevat",
- "patternPlaceholder": "Aktief: aktief (hardloop)",
- "then": "Doen dan hierdie",
- "else": "Andersins doen dit",
- "emptyBranch": "Geen stappe nie"
- },
- "repeat": {
- "count": "Aantal kere",
- "times": "tye"
- },
- "match": {
- "useRegex": "Stem ooreen met 'n gereelde uitdrukking",
- "useRegexHelp": "Los dit af om die teks presies soos geskryf te laat ooreenstem.",
- "invalidRegex": "Nie 'n geldige gereelde uitdrukking nie, dit sal as gewone teks ooreenstem"
- },
- "seconds": "sekondes",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ar_SA.json b/src/ui/locales/translated/ar_SA.json
index 270cf55e4..ad47b940b 100644
--- a/src/ui/locales/translated/ar_SA.json
+++ b/src/ui/locales/translated/ar_SA.json
@@ -409,7 +409,6 @@
"sidebarSettings": "إعدادات الشريط الجانبي...",
"hosts": "المضيفون",
"snippets": "المقتطفات",
- "macros": "وحدات الماكرو",
"hostManager": "مدير المضيفين",
"credentials": "بيانات الاعتماد",
"connections": "الاتصالات",
@@ -869,7 +868,6 @@
"groupTags": "العلامات والدبابيس",
"groupTunnels": "الأنفاق",
"groupJumpHosts": "مضيفو القفز",
- "groupQuickActions": "إجراءات سريعة",
"groupFeatureFlags": "ميزات مميزة",
"groupAdvanced": "الإعدادات المتقدمة",
"preview": "معاينة",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "تخزين مفاتيح SSH أو كلمات المرور",
"tmuxMonitor": "مراقب Tmux",
"tmuxMonitorDesc": "مراقبة جلسات tmux عبر مضيفيك",
- "snippets": "مقتطفات",
- "runSnippetDesc": "قم بتشغيله في علامة تبويب الطرفية النشطة",
- "noSnippetsFound": "لم يتم العثور على أي مقتطفات مطابقة لـ \"{{search}}\"",
"recentActivity": "النشاط الأخير",
"serversAndHosts": "الخوادم والمضيفون",
"noHostsFound": "لم يتم العثور على مضيفين يطابقون \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "التبويب السابق",
"hotkeysJumpTab": "انتقل إلى علامة التبويب"
},
- "snippets": {
- "title": "المقتطفات",
- "createSnippetTitle": "إنشاء مقتطفة",
- "createSnippetDescription": "إنشاء مقتطفة أمر جديدة للتنفيذ السريع",
- "nameLabel": "الاسم",
- "namePlaceholder": "مثال: إعادة تشغيل Nginx",
- "descriptionLabel": "الوصف",
- "descriptionPlaceholder": "وصف اختياري",
- "optional": "اختياري",
- "folderLabel": "مجلد",
- "noFolder": "بدون مجلد (غير مصنف)",
- "commandLabel": "الأمر",
- "commandPlaceholder": "مثلاً: sudo systemctl restart nginx",
- "typeLabel": "يكتب",
- "typeCommand": "يأمر",
- "typeNote": "ملحوظة",
- "noteLabel": "ملحوظة",
- "notePlaceholder": "على سبيل المثال، نص تذكير أو مطالبة قابلة لإعادة الاستخدام",
- "cancel": "إلغاء",
- "createSnippetButton": "إنشاء مقتطفة",
- "createFolderTitle": "إنشاء مجلد",
- "createFolderDescription": "تنظيم المقتطفات في مجلدات",
- "folderNameLabel": "اسم المجلد",
- "folderNamePlaceholder": "مثلاً: أوامر النظام، نصوص Docker",
- "folderColorLabel": "لون المجلد",
- "folderIconLabel": "أيقونة المجلد",
- "previewLabel": "معاينة",
- "folderNameFallback": "اسم المجلد",
- "createFolderButton": "إنشاء مجلد",
- "targetTerminals": "الطرفيات المستهدفة",
- "selectAll": "الكل",
- "selectNone": "لا شيء",
- "noTerminalTabsOpen": "لا توجد علامات تبويب طرفية مفتوحة",
- "searchPlaceholder": "ابحث في المقتطفات...",
- "newSnippet": "مقتطف جديد",
- "newFolder": "مجلد جديد",
- "run": "تشغيل",
- "noSnippetsInFolder": "لا توجد مقتطفات في هذا المجلد",
- "uncategorized": "غير مصنف",
- "editSnippetTitle": "تحرير المقتطف",
- "editSnippetDescription": "تحديث مقتطف الأمر هذا",
- "saveSnippetButton": "حفظ التغييرات",
- "createSuccess": "تم إنشاء المقتطف بنجاح",
- "createFailed": "فشل إنشاء المقتطف",
- "updateSuccess": "تم تحديث المقتطف بنجاح",
- "updateFailed": "فشل تحديث المقتطف",
- "deleteFailed": "فشل حذف المقتطف",
- "folderCreateSuccess": "تم إنشاء المجلد بنجاح",
- "folderCreateFailed": "فشل إنشاء المجلد",
- "editFolderTitle": "تحرير المجلد",
- "editFolderDescription": "إعادة تسمية أو تغيير مظهر هذا المجلد",
- "saveFolderButton": "حفظ التغييرات",
- "editFolder": "تحرير المجلد",
- "deleteFolder": "حذف المجلد",
- "folderDeleteSuccess": "تم حذف المجلد \"{{name}}\"",
- "folderDeleteFailed": "فشل حذف المجلد",
- "folderEditSuccess": "تم تحديث المجلد بنجاح",
- "folderEditFailed": "فشل تحديث المجلد",
- "confirmRunMessage": "تشغيل \"{{name}}\"؟",
- "confirmRunButton": "تشغيل",
- "runSuccess": "تم تشغيل \"{{name}}\" في {{count}} طرفية",
- "pasteSuccess": "تم لصق \"{{name}}\" في {{count}} طرفية (أطراف)",
- "pasteToTerminal": "لصق",
- "copySuccess": "تم نسخ \"{{name}}\" إلى الحافظة",
- "shareTitle": "مشاركة المقتطف",
- "shareUser": "مستخدم",
- "shareRole": "دور",
- "selectUser": "اختر مستخدمًا...",
- "selectRole": "اختر دورًا...",
- "shareSuccess": "تمت مشاركة الجزء البرمجي بنجاح",
- "shareFailed": "فشلت مشاركة الجزء البرمجي",
- "revokeSuccess": "تم إبطال الوصول",
- "revokeFailed": "فشل إبطال الوصول",
- "currentAccess": "الوصول الحالي",
- "shareLoadError": "فشل تحميل بيانات المشاركة",
- "loading": "جارٍ التحميل...",
- "close": "إغلاق",
- "reorderFailed": "فشل حفظ ترتيب الأجزاء البرمجية",
- "importExport": "استيراد / تصدير",
- "exportBtn": "تصدير JSON",
- "importBtn": "استيراد JSON",
- "exportSuccess": "تم تصدير الأجزاء البرمجية بنجاح",
- "exportFailed": "فشل تصدير الأجزاء البرمجية",
- "importTitle": "استيراد الأجزاء البرمجية",
- "importDescription": "استيراد الأجزاء البرمجية والمجلدات من ملف JSON تم تصديره بواسطة Termix",
- "importDropOrClick": "أفلت ملف JSON هنا أو انقر للاستعراض",
- "importSelectedFile": "المحدد: {{name}}",
- "importOverwrite": "الكتابة فوق الأجزاء البرمجية الموجودة بنفس الاسم والمجلد",
- "importStartBtn": "استيراد",
- "importSuccess": "اكتمل الاستيراد: تمت إضافة {{snippets}} جزء برمجي، وتحديث {{updated}}، وتخطي {{skipped}}، وإضافة {{folders}} مجلد",
- "importFailed": "فشل استيراد الأجزاء البرمجية",
- "importInvalidFile": "ملف غير صالح: يُتوقع كائن JSON يحتوي على مصفوفات من الأجزاء البرمجية أو المجلدات",
- "targetHostsLabel": "المضيفات المستهدفة",
- "targetHostsHint": "تعيين مضيفات لتشغيل هذا الجزء البرمجي مباشرة بدون فتح طرفية.",
- "noHostsAvailable": "لم يتم إعداد مضيفات",
- "clearTargetHosts": "مسح الكل",
- "hasTargetHosts": "لديه مضيفات مستهدفة",
- "runOnTargets": "تشغيل على المضيفات",
- "directRunSuccess": "تم تشغيل \"{{name}}\" على {{count}} مضيف",
- "directRunPartialFail": "فشل \"{{name}}\" على واحد أو أكثر من المضيفات",
- "executionResultTitle": "نتائج التنفيذ: {{name}}",
- "executionResultDescription": "المخرجات الناتجة عن تشغيل المقتطف على كل مضيف مستهدف.",
- "executionSuccess": "نجاح",
- "executionFailed": "فشل",
- "expandAll": "قم بتوسيع جميع المجلدات",
- "collapseAll": "طي جميع المجلدات",
- "settings": "إعدادات المقتطف",
- "variablesHint": "المتغيرات: $HOST، $USER، $PORT، $NAME، ${INPUT_1:Label}",
- "variablesDialogTitle": "تشغيل \"{{name}}\"",
- "variablesDialogDescription": "أدخل القيم التي يحتاجها هذا المقتطف البرمجي قبل تشغيله.",
- "variablesPreviewLabel": "معاينة",
- "variablesConfirmButton": "يجري"
- },
"keybindings": {
"title": "اختصارات لوحة المفاتيح",
"description": "قم بتخصيص اختصارات النسخ واللصق والتحكم في الطرفية، أو اربط المفاتيح لإرسال نص أو تشغيل جزء من التعليمات البرمجية.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "انقر واضغط على المفاتيح",
"recording": "اضغط على المفاتيح...",
"comboRequiredError": "اضغط على مجموعة مفاتيح أولاً",
- "textRequiredError": "أدخل النص المراد إرساله",
- "controlCodeRequiredError": "أدخل حرفًا واحدًا لرمز التحكم",
- "snippetRequiredError": "حدد جزءًا من التعليمات البرمجية",
"conflictWarning": "هذا المزيج مرتبط بالفعل بـ {{combo}}. سيؤدي الحفظ إلى إطلاق النار من كليهما.",
"actionLabel": "فعل",
- "actionCopy": "تحديد النص",
- "actionPaste": "لصق",
- "actionSendControlCode": "إرسال إشارة Ctrl+حرف",
- "actionSendText": "أرسل رسالة نصية",
- "actionRunSnippet": "تشغيل المقتطف البرمجي الحالي",
"actionNextTab": "انتقل إلى علامة التبويب التالية",
"actionPreviousTab": "انتقل إلى علامة التبويب السابقة",
- "actionOpenCommandPalette": "افتح البحث السريع",
- "controlCodeLabel": "خطاب",
- "textLabel": "نص لإرساله",
- "appendEnterLabel": "اضغط على زر الإدخال بعد الإرسال",
- "snippetLabel": "مقتطف",
- "selectSnippetPlaceholder": "حدد جزءًا من التعليمات البرمجية",
- "orphanedSnippetWarning": "لم يتم العثور على المقتطف",
- "clipboardPermissionNote": "قد يطلب منك بعض المتصفحات منح إذن الوصول إلى الحافظة عند الاستخدام الأول."
+ "actionOpenCommandPalette": "افتح البحث السريع"
},
"userProfile": {
"donateTitle": "ضمان استمرارية Termix",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "الموضوع مطلوب",
"ntfyToken": "رمز الوصول (اختياري)",
"channelSaveFailed": "فشل حفظ القناة"
- },
- "macros": {
- "title": "وحدات الماكرو",
- "description": "قم بتشغيل سلسلة من الأوامر المحفوظة في طرفية، وتفاعل مع ما يرسله الخادم.",
- "target": "هدف",
- "noTerminal": "لا يوجد طرفية مفتوحة",
- "empty": "لا توجد وحدات ماكرو حتى الآن",
- "emptyHint": "أنشئ ماكرو لأتمتة تدفق طرفية، مثل تسجيل الدخول أو إعادة تشغيل خدمة أو الإجابة على المطالبات.",
- "create": "ماكرو جديد",
- "back": "العودة إلى وحدات الماكرو",
- "name": "اسم",
- "namePlaceholder": "أعد تشغيل خادم الويب",
- "nameRequired": "قم بتسمية الماكرو قبل حفظه",
- "descriptionLabel": "وصف",
- "descriptionPlaceholder": "ما يفعله هذا الماكرو (اختياري)",
- "unsaved": "التغييرات غير المحفوظة",
- "save": "يحفظ",
- "saved": "تم حفظ الماكرو",
- "saveFailed": "تعذر حفظ الماكرو",
- "delete": "يمسح",
- "deleteTitle": "هل تريد حذف هذا الماكرو؟",
- "deleteBody": "سيتم حذف \"{{name}}\" نهائياً. لا يمكن التراجع عن هذا الإجراء.",
- "deleted": "تم حذف الماكرو",
- "cancel": "يلغي",
- "run": "يجري",
- "stop": "قف",
- "running": "جري...",
- "completed": "اللمسة النهائية الدقيقة",
- "failed": "فشل الماكرو",
- "loadFailed": "تعذر تحميل وحدات الماكرو",
- "needTerminal": "افتح وقم بتوصيل طرفية أولاً",
- "noSteps": "لا يحتوي هذا الماكرو على أي خطوات حتى الآن. أضف خطوة واحدة أدناه.",
- "addStep": "خطوات إضافية",
- "stepNumber": "الخطوة {{number}}",
- "removeStep": "خطوة الإزالة",
- "confirmRunTitle": "هل تريد تشغيل هذا الماكرو؟",
- "confirmRunBody": "\"{{name}}\" سيرسل أوامر إلى {{host}}.",
- "confirmRunAction": "تشغيل الماكرو",
- "stepTypes": {
- "send": {
- "label": "إرسال الأمر",
- "help": "يكتب النص في الطرفية، تمامًا كما لو كنت أنت من كتبته."
- },
- "wait": {
- "label": "انتظر حتى يتم إخراج البيانات",
- "help": "يتوقف مؤقتًا حتى يقوم الخادم بطباعة النص الذي تحدده، مثل مطالبة كلمة المرور."
- },
- "delay": {
- "label": "يوقف",
- "help": "ينتظر فترة زمنية محددة قبل الخطوة التالية."
- },
- "if": {
- "label": "إذا احتوى الناتج",
- "help": "يقوم بفحص كل ما طبعه الخادم حتى الآن، ثم يقوم بتشغيل أحد الفرعين."
- },
- "repeat": {
- "label": "يكرر",
- "help": "يقوم بتنفيذ الخطوات الموجودة بداخله لعدد محدد من المرات."
- }
- },
- "send": {
- "text": "أمر أو نص",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "اضغط على زر الإدخال بعد الإرسال",
- "pressEnterHelp": "قم بإيقاف تشغيل هذا الخيار لكتابة نص دون إرساله."
- },
- "wait": {
- "pattern": "انتظر حتى يحتوي الناتج على",
- "patternPlaceholder": "كلمة المرور:",
- "timeout": "الاستسلام بعد",
- "onTimeout": "إذا لم يظهر أبداً",
- "stop": "أوقف الماكرو",
- "continue": "استمر على أي حال"
- },
- "delay": {
- "duration": "انتظر"
- },
- "ifStep": {
- "pattern": "إذا احتوى الناتج",
- "patternPlaceholder": "نشط: نشط (قيد التشغيل)",
- "then": "ثم افعل هذا",
- "else": "وإلا فافعل هذا",
- "emptyBranch": "لا توجد"
- },
- "repeat": {
- "count": "عدد المرات",
- "times": "أوقات"
- },
- "match": {
- "useRegex": "مطابقة مع تعبير نمطي",
- "useRegexHelp": "اترك هذا الخيار فارغًا لمطابقة النص تمامًا كما هو مكتوب.",
- "invalidRegex": "هذا ليس تعبيرًا نمطيًا صالحًا، وسيتم مطابقته كنص عادي."
- },
- "seconds": "ثوانٍ",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/bg_BG.json b/src/ui/locales/translated/bg_BG.json
index d030a3961..2d5ce268a 100644
--- a/src/ui/locales/translated/bg_BG.json
+++ b/src/ui/locales/translated/bg_BG.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Настройки на страничната лента...",
"hosts": "Домакини",
"snippets": "Откъси",
- "macros": "Макроси",
"hostManager": "Мениджър на домакини",
"credentials": "Пълномощия",
"connections": "Връзки",
@@ -869,7 +868,6 @@
"groupTags": "Етикети и закачане",
"groupTunnels": "Тунели",
"groupJumpHosts": "Домакини за преходи",
- "groupQuickActions": "Бързи действия",
"groupFeatureFlags": "Флагове на функциите",
"groupAdvanced": "Разширена конфигурация",
"preview": "Преглед",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Съхранявайте SSH ключове или пароли",
"tmuxMonitor": "Tmux монитор",
"tmuxMonitorDesc": "Следете tmux сесиите на вашите хостове",
- "snippets": "Откъси",
- "runSnippetDesc": "Изпълняване в активния раздел на терминала",
- "noSnippetsFound": "Не са намерени фрагменти, съответстващи на „{{search}}“",
"recentActivity": "Последна активност",
"serversAndHosts": "Сървъри и хостове",
"noHostsFound": "Не са намерени хостове, съответстващи на „{{search}}“",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Предишен раздел",
"hotkeysJumpTab": "Преход към раздел"
},
- "snippets": {
- "title": "Откъси",
- "createSnippetTitle": "Създаване на фрагмент",
- "createSnippetDescription": "Създайте нов фрагмент от команда за бързо изпълнение",
- "nameLabel": "Име",
- "namePlaceholder": "например, рестартирайте Nginx",
- "descriptionLabel": "Описание",
- "descriptionPlaceholder": "Незадължително описание",
- "optional": "По избор",
- "folderLabel": "Папка",
- "noFolder": "Няма папка (Без категория)",
- "commandLabel": "Команда",
- "commandPlaceholder": "например, sudo systemctl рестартиране на nginx",
- "typeLabel": "Тип",
- "typeCommand": "Команда",
- "typeNote": "Забележка",
- "noteLabel": "Забележка",
- "notePlaceholder": "напр. текст за многократна употреба или напомняне",
- "cancel": "Отказ",
- "createSnippetButton": "Създаване на фрагмент",
- "createFolderTitle": "Създаване на папка",
- "createFolderDescription": "Организирайте фрагментите си в папки",
- "folderNameLabel": "Име на папката",
- "folderNamePlaceholder": "напр. системни команди, Docker скриптове",
- "folderColorLabel": "Цвят на папката",
- "folderIconLabel": "Икона на папка",
- "previewLabel": "Преглед",
- "folderNameFallback": "Име на папката",
- "createFolderButton": "Създаване на папка",
- "targetTerminals": "Целеви терминали",
- "selectAll": "Всички",
- "selectNone": "Няма",
- "noTerminalTabsOpen": "Няма отворени раздели на терминала",
- "searchPlaceholder": "Търсене на фрагменти...",
- "newSnippet": "Нов фрагмент",
- "newFolder": "Нова папка",
- "run": "Бягане",
- "noSnippetsInFolder": "Няма фрагменти в тази папка",
- "uncategorized": "Некатегоризирано",
- "editSnippetTitle": "Редактиране на фрагмент",
- "editSnippetDescription": "Актуализирайте този фрагмент от командата",
- "saveSnippetButton": "Запазване на промените",
- "createSuccess": "Фрагментът е създаден успешно",
- "createFailed": "Създаването на фрагмент не бе успешно",
- "updateSuccess": "Фрагментът е актуализиран успешно",
- "updateFailed": "Актуализирането на фрагмента не бе успешно",
- "deleteFailed": "Изтриването на фрагмента не бе успешно",
- "folderCreateSuccess": "Папката е създадена успешно",
- "folderCreateFailed": "Създаването на папка не бе успешно",
- "editFolderTitle": "Редактиране на папка",
- "editFolderDescription": "Преименувайте или променете външния вид на тази папка",
- "saveFolderButton": "Запазване на промените",
- "editFolder": "Редактиране на папка",
- "deleteFolder": "Изтриване на папка",
- "folderDeleteSuccess": "Папка „{{name}}“ е изтрита",
- "folderDeleteFailed": "Изтриването на папката не бе успешно",
- "folderEditSuccess": "Папката е актуализирана успешно",
- "folderEditFailed": "Актуализирането на папката не бе успешно",
- "confirmRunMessage": "Изпълни „{{name}}“?",
- "confirmRunButton": "Бягане",
- "runSuccess": "Изпълни „{{name}}“ в {{count}} терминал(и)",
- "pasteSuccess": "Поставено е „{{name}}“ в {{count}} терминал(и)",
- "pasteToTerminal": "Паста",
- "copySuccess": "Копирано „{{name}}“ в клипборда",
- "shareTitle": "Споделяне на фрагмент",
- "shareUser": "Потребител",
- "shareRole": "Роля",
- "selectUser": "Изберете потребител...",
- "selectRole": "Изберете роля...",
- "shareSuccess": "Фрагментът е споделен успешно",
- "shareFailed": "Споделянето на фрагмента не бе успешно",
- "revokeSuccess": "Достъпът е отменен",
- "revokeFailed": "Неуспешно отменяне на достъпа",
- "currentAccess": "Текущ достъп",
- "shareLoadError": "Зареждането на данните за споделяне не бе успешно",
- "loading": "Зареждане...",
- "close": "Затвори",
- "reorderFailed": "Запазването на реда на фрагмента не бе успешно",
- "importExport": "Внос / Износ",
- "exportBtn": "Експортиране на JSON",
- "importBtn": "Импортиране на JSON",
- "exportSuccess": "Фрагментите са експортирани успешно",
- "exportFailed": "Експортирането на фрагменти не бе успешно",
- "importTitle": "Импортиране на фрагменти",
- "importDescription": "Импортиране на фрагменти и папки от JSON файл, експортиран от Termix",
- "importDropOrClick": "Пуснете JSON файл тук или кликнете, за да разгледате",
- "importSelectedFile": "Избрано: {{name}}",
- "importOverwrite": "Презапишете съществуващите фрагменти със същото име и папка",
- "importStartBtn": "Внос",
- "importSuccess": "Импортирането е завършено: {{snippets}} добавени фрагмента, {{updated}} актуализирани, {{skipped}} пропуснати, {{folders}} добавени папки",
- "importFailed": "Импортирането на фрагменти не бе успешно",
- "importInvalidFile": "Невалиден файл: очаква се JSON обект с фрагменти или масиви от папки",
- "targetHostsLabel": "Целеви хостове",
- "targetHostsHint": "Задайте хостове, които да изпълняват този фрагмент директно, без да е отворен терминал.",
- "noHostsAvailable": "Няма конфигурирани хостове",
- "clearTargetHosts": "Изчисти всички",
- "hasTargetHosts": "Има целеви хостове",
- "runOnTargets": "Бягане по цели",
- "directRunSuccess": "Изпълни „{{name}}“ на {{count}} хост(ове)",
- "directRunPartialFail": "„{{name}}“ неуспешно на един или повече хостове",
- "executionResultTitle": "Резултати от изпълнението: {{name}}",
- "executionResultDescription": "Изход от изпълнението на фрагмента на всеки целеви хост.",
- "executionSuccess": "Успех",
- "executionFailed": "Неуспешно",
- "expandAll": "Разгъване на всички папки",
- "collapseAll": "Свиване на всички папки",
- "settings": "Настройки на фрагмента",
- "variablesHint": "Променливи: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Етикет}",
- "variablesDialogTitle": "Изпълни „{{name}}“",
- "variablesDialogDescription": "Попълнете стойностите, необходими на този фрагмент, преди да го изпълните.",
- "variablesPreviewLabel": "Преглед",
- "variablesConfirmButton": "Бягане"
- },
"keybindings": {
"title": "Клавишни комбинации",
"description": "Персонализирайте клавишните комбинации за копиране, поставяне и управление на терминала или обвържете клавишите за изпращане на текст или изпълнение на фрагмент.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Щракнете и натиснете клавиши",
"recording": "Натиснете клавишите...",
"comboRequiredError": "Първо натиснете клавишна комбинация",
- "textRequiredError": "Въведете текста за изпращане",
- "controlCodeRequiredError": "Въведете една буква за контролния код",
- "snippetRequiredError": "Изберете фрагмент",
"conflictWarning": "Тази комбинация вече е свързана с {{combo}}. Запазването ще доведе до задействане и на двете.",
"actionLabel": "Действие",
- "actionCopy": "Копиране на селекцията",
- "actionPaste": "Паста",
- "actionSendControlCode": "Изпрати сигнал Ctrl+буква",
- "actionSendText": "Изпращане на текст",
- "actionRunSnippet": "Изпълнете съществуващия фрагмент",
"actionNextTab": "Превключване към следващия раздел",
"actionPreviousTab": "Превключване към предишния раздел",
- "actionOpenCommandPalette": "Отваряне на бързо търсене",
- "controlCodeLabel": "Писмо",
- "textLabel": "Текст за изпращане",
- "appendEnterLabel": "Натиснете Enter след изпращане",
- "snippetLabel": "Фрагмент",
- "selectSnippetPlaceholder": "Изберете фрагмент",
- "orphanedSnippetWarning": "фрагмент не е намерен",
- "clipboardPermissionNote": "Може да поиска разрешение за достъп до клипборда при първото използване в някои браузъри."
+ "actionOpenCommandPalette": "Отваряне на бързо търсене"
},
"userProfile": {
"donateTitle": "Пази Термикс жив",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Темата е задължителна",
"ntfyToken": "Токен за достъп (по избор)",
"channelSaveFailed": "Запазването на канала не бе успешно"
- },
- "macros": {
- "title": "Макроси",
- "description": "Изпълнете запазена серия от команди в терминал и реагирайте на това, което сървърът изпраща обратно.",
- "target": "Цел",
- "noTerminal": "Няма отворен терминал",
- "empty": "Все още няма макроси",
- "emptyHint": "Създайте макрос за автоматизиране на процес в терминал, като например влизане в системата, рестартиране на услуга или отговаряне на подкани.",
- "create": "Нов макрос",
- "back": "Обратно към макросите",
- "name": "Име",
- "namePlaceholder": "Рестартирайте уеб сървъра",
- "nameRequired": "Дайте име на макроса, преди да го запазите",
- "descriptionLabel": "Описание",
- "descriptionPlaceholder": "Какво прави този макрос (по избор)",
- "unsaved": "Незапазени промени",
- "save": "Запазване",
- "saved": "Макросът е запазен",
- "saveFailed": "Макросът не можа да се запази",
- "delete": "Изтриване",
- "deleteTitle": "Да се изтрие ли този макрос?",
- "deleteBody": "„{{name}}“ ще бъде изтрито за постоянно. Това не може да бъде отменено.",
- "deleted": "Макросът е изтрит",
- "cancel": "Отказ",
- "run": "Бягане",
- "stop": "Спри",
- "running": "Бягане...",
- "completed": "Макрото е завършено",
- "failed": "Макросът не беше успешен",
- "loadFailed": "Не можаха да се заредят макроси",
- "needTerminal": "Първо отворете и свържете терминал",
- "noSteps": "Този макрос все още няма стъпки. Добавете една по-долу.",
- "addStep": "Добавяне на стъпка",
- "stepNumber": "Стъпка {{number}}",
- "removeStep": "Премахване на стъпка",
- "confirmRunTitle": "Да се изпълни ли този макрос?",
- "confirmRunBody": "„{{name}}“ ще изпраща команди до {{host}}.",
- "confirmRunAction": "Изпълнете макрос",
- "stepTypes": {
- "send": {
- "label": "Изпрати команда",
- "help": "Въвежда текст в терминала, по същия начин, както ако сте го написали."
- },
- "wait": {
- "label": "Изчакайте изхода",
- "help": "Пауза, докато сървърът отпечата текста, който посочите, като например подкана за парола."
- },
- "delay": {
- "label": "Пауза",
- "help": "Изчаква фиксирано време преди следващата стъпка."
- },
- "if": {
- "label": "Ако изходът съдържа",
- "help": "Проверява всичко, което сървърът е отпечатал досега, след което изпълнява един от двата клона."
- },
- "repeat": {
- "label": "Повторете",
- "help": "Изпълнява стъпките вътре в него определен брой пъти."
- }
- },
- "send": {
- "text": "Команда или текст",
- "textPlaceholder": "systemctl рестартиране на nginx",
- "pressEnter": "Натиснете Enter след изпращане",
- "pressEnterHelp": "Изключете това, за да пишете текст, без да го изпращате."
- },
- "wait": {
- "pattern": "Изчакайте, докато изходът съдържа",
- "patternPlaceholder": "парола:",
- "timeout": "Откажи се след",
- "onTimeout": "Ако никога не се появи",
- "stop": "Спиране на макроса",
- "continue": "Продължавай все пак"
- },
- "delay": {
- "duration": "Изчакайте"
- },
- "ifStep": {
- "pattern": "Ако изходът съдържа",
- "patternPlaceholder": "Активен: активен (работи)",
- "then": "Тогава направете това",
- "else": "В противен случай направете това",
- "emptyBranch": "Без стъпала"
- },
- "repeat": {
- "count": "Брой пъти",
- "times": "пъти"
- },
- "match": {
- "useRegex": "Съвпадение с регулярен израз",
- "useRegexHelp": "Не включвайте това, за да съвпада текстът точно както е написан.",
- "invalidRegex": "Не е валиден регулярен израз, ще бъде съпоставен като обикновен текст"
- },
- "seconds": "секунди",
- "secondsShort": "{{count}}с"
}
}
diff --git a/src/ui/locales/translated/bn_BD.json b/src/ui/locales/translated/bn_BD.json
index 3fbc91643..bd66e5978 100644
--- a/src/ui/locales/translated/bn_BD.json
+++ b/src/ui/locales/translated/bn_BD.json
@@ -409,7 +409,6 @@
"sidebarSettings": "সাইডবার সেটিংস...",
"hosts": "হোস্টরা",
"snippets": "খণ্ডাংশ",
- "macros": "ম্যাক্রো",
"hostManager": "হোস্ট ম্যানেজার",
"credentials": "যোগ্যতা",
"connections": "সংযোগ",
@@ -869,7 +868,6 @@
"groupTags": "ট্যাগ ও পিন",
"groupTunnels": "টানেল",
"groupJumpHosts": "জাম্প হোস্ট",
- "groupQuickActions": "দ্রুত পদক্ষেপ",
"groupFeatureFlags": "বৈশিষ্ট্য পতাকা",
"groupAdvanced": "উন্নত কনফিগারেশন",
"preview": "প্রিভিউ",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH কী বা পাসওয়ার্ড সংরক্ষণ করুন",
"tmuxMonitor": "Tmux মনিটর",
"tmuxMonitorDesc": "আপনার হোস্ট জুড়ে tmux সেশনগুলি নিরীক্ষণ করুন",
- "snippets": "খণ্ডাংশ",
- "runSnippetDesc": "সক্রিয় টার্মিনাল ট্যাবে চালান",
- "noSnippetsFound": "\"{{search}} \" এর সাথে মেলে এমন কোনো স্নিপেট খুঁজে পাওয়া যায়নি।",
"recentActivity": "সাম্প্রতিক কার্যকলাপ",
"serversAndHosts": "সার্ভার এবং হোস্ট",
"noHostsFound": "\"{{search}} \" এর সাথে মেলে এমন কোনো হোস্ট খুঁজে পাওয়া যায়নি।",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "পূর্ববর্তী ট্যাব",
"hotkeysJumpTab": "ট্যাবে যান"
},
- "snippets": {
- "title": "খণ্ডাংশ",
- "createSnippetTitle": "স্নিপেট তৈরি করুন",
- "createSnippetDescription": "দ্রুত কার্যকর করার জন্য একটি নতুন কমান্ড স্নিপেট তৈরি করুন",
- "nameLabel": "নাম",
- "namePlaceholder": "যেমন, Nginx রিস্টার্ট করুন",
- "descriptionLabel": "বর্ণনা",
- "descriptionPlaceholder": "ঐচ্ছিক বিবরণ",
- "optional": "ঐচ্ছিক",
- "folderLabel": "ফোল্ডার",
- "noFolder": "কোন ফোল্ডার নেই (শ্রেণীবিহীন)",
- "commandLabel": "আদেশ",
- "commandPlaceholder": "যেমন, sudo systemctl restart nginx",
- "typeLabel": "প্রকার",
- "typeCommand": "আদেশ",
- "typeNote": "দ্রষ্টব্য",
- "noteLabel": "দ্রষ্টব্য",
- "notePlaceholder": "যেমন, একটি পুনঃব্যবহারযোগ্য প্রম্পট বা রিমাইন্ডার টেক্সট",
- "cancel": "বাতিল করুন",
- "createSnippetButton": "স্নিপেট তৈরি করুন",
- "createFolderTitle": "ফোল্ডার তৈরি করুন",
- "createFolderDescription": "আপনার স্নিপেটগুলো ফোল্ডারে সাজিয়ে নিন।",
- "folderNameLabel": "ফোল্ডারের নাম",
- "folderNamePlaceholder": "যেমন, সিস্টেম কমান্ড, ডকার স্ক্রিপ্ট",
- "folderColorLabel": "ফোল্ডারের রঙ",
- "folderIconLabel": "ফোল্ডার আইকন",
- "previewLabel": "প্রিভিউ",
- "folderNameFallback": "ফোল্ডারের নাম",
- "createFolderButton": "ফোল্ডার তৈরি করুন",
- "targetTerminals": "টার্গেট টার্মিনাল",
- "selectAll": "সব",
- "selectNone": "কোনোটিই না",
- "noTerminalTabsOpen": "কোন টার্মিনাল ট্যাব খোলা নেই",
- "searchPlaceholder": "অনুসন্ধানের অংশবিশেষ...",
- "newSnippet": "নতুন স্নিপেট",
- "newFolder": "নতুন ফোল্ডার",
- "run": "দৌড়",
- "noSnippetsInFolder": "এই ফোল্ডারে কোনো স্নিপেট নেই",
- "uncategorized": "অশ্রেণীবদ্ধ",
- "editSnippetTitle": "সম্পাদনা স্নিপেট",
- "editSnippetDescription": "এই কমান্ড স্নিপেটটি আপডেট করুন",
- "saveSnippetButton": "পরিবর্তনগুলি সংরক্ষণ করুন",
- "createSuccess": "স্নিপেট সফলভাবে তৈরি করা হয়েছে",
- "createFailed": "স্নিপেট তৈরি করতে ব্যর্থ হয়েছে",
- "updateSuccess": "স্নিপেট সফলভাবে আপডেট করা হয়েছে",
- "updateFailed": "স্নিপেট আপডেট করতে ব্যর্থ হয়েছে",
- "deleteFailed": "স্নিপেট মুছে ফেলা ব্যর্থ হয়েছে",
- "folderCreateSuccess": "ফোল্ডারটি সফলভাবে তৈরি করা হয়েছে।",
- "folderCreateFailed": "ফোল্ডার তৈরি করতে ব্যর্থ হয়েছে",
- "editFolderTitle": "ফোল্ডার সম্পাদনা করুন",
- "editFolderDescription": "এই ফোল্ডারটির নাম পরিবর্তন করুন বা এর চেহারা বদলে দিন।",
- "saveFolderButton": "পরিবর্তনগুলি সংরক্ষণ করুন",
- "editFolder": "ফোল্ডার সম্পাদনা করুন",
- "deleteFolder": "ফোল্ডারটি মুছে ফেলুন",
- "folderDeleteSuccess": "ফোল্ডার \"{{name}}\" মুছে ফেলা হয়েছে",
- "folderDeleteFailed": "ফোল্ডারটি মুছে ফেলা ব্যর্থ হয়েছে",
- "folderEditSuccess": "ফোল্ডারটি সফলভাবে আপডেট করা হয়েছে",
- "folderEditFailed": "ফোল্ডার আপডেট করতে ব্যর্থ হয়েছে",
- "confirmRunMessage": "\"{{name}} \" চালান?",
- "confirmRunButton": "দৌড়",
- "runSuccess": "{{count}} টার্মিনালে \"{{name}}\" চালানো হয়েছে",
- "pasteSuccess": "{{name}}\" {{count}} টার্মিনালে পেস্ট করা হয়েছে",
- "pasteToTerminal": "পেস্ট",
- "copySuccess": "\"{{name}}\" ক্লিপবোর্ডে কপি করা হয়েছে",
- "shareTitle": "স্নিপেট শেয়ার করুন",
- "shareUser": "ব্যবহারকারী",
- "shareRole": "ভূমিকা",
- "selectUser": "একজন ব্যবহারকারী নির্বাচন করুন...",
- "selectRole": "একটি ভূমিকা নির্বাচন করুন...",
- "shareSuccess": "স্নিপেট সফলভাবে শেয়ার করা হয়েছে",
- "shareFailed": "স্নিপেট শেয়ার করতে ব্যর্থ হয়েছে",
- "revokeSuccess": "প্রবেশাধিকার বাতিল করা হয়েছে",
- "revokeFailed": "অ্যাক্সেস প্রত্যাহার করতে ব্যর্থ হয়েছে",
- "currentAccess": "বর্তমান অ্যাক্সেস",
- "shareLoadError": "শেয়ার ডেটা লোড করতে ব্যর্থ হয়েছে",
- "loading": "লোড হচ্ছে...",
- "close": "বন্ধ করুন",
- "reorderFailed": "স্নিপেটের ক্রম সংরক্ষণ করতে ব্যর্থ হয়েছে",
- "importExport": "আমদানি / রপ্তানি",
- "exportBtn": "JSON রপ্তানি করুন",
- "importBtn": "JSON আমদানি করুন",
- "exportSuccess": "স্নিপেটগুলি সফলভাবে রপ্তানি করা হয়েছে",
- "exportFailed": "স্নিপেট রপ্তানি করতে ব্যর্থ হয়েছে",
- "importTitle": "আমদানি স্নিপেট",
- "importDescription": "Termix দ্বারা এক্সপোর্ট করা একটি JSON ফাইল থেকে স্নিপেট এবং ফোল্ডার ইম্পোর্ট করুন।",
- "importDropOrClick": "এখানে একটি JSON ফাইল ড্রপ করুন অথবা ব্রাউজ করতে ক্লিক করুন।",
- "importSelectedFile": "নির্বাচিত: {{name}}",
- "importOverwrite": "একই নাম এবং ফোল্ডার ব্যবহার করে বিদ্যমান স্নিপেটগুলো ওভাররাইট করুন।",
- "importStartBtn": "আমদানি",
- "importSuccess": "আমদানি সম্পন্ন: {{snippets}} টি স্নিপেট যোগ করা হয়েছে, {{updated}} টি আপডেট করা হয়েছে, {{skipped}} টি বাদ দেওয়া হয়েছে, {{folders}} টি ফোল্ডার যোগ করা হয়েছে",
- "importFailed": "স্নিপেট আমদানি করতে ব্যর্থ হয়েছে",
- "importInvalidFile": "অবৈধ ফাইল: স্নিপেট বা ফোল্ডার অ্যারে সহ একটি JSON অবজেক্ট প্রত্যাশিত ছিল",
- "targetHostsLabel": "লক্ষ্য হোস্ট",
- "targetHostsHint": "টার্মিনাল খোলা ছাড়াই সরাসরি এই কোডটুকু চালানোর জন্য হোস্ট নির্ধারণ করুন।",
- "noHostsAvailable": "কোন হোস্ট কনফিগার করা নেই",
- "clearTargetHosts": "সব পরিষ্কার করুন",
- "hasTargetHosts": "লক্ষ্য হোস্ট আছে",
- "runOnTargets": "লক্ষ্যমাত্রায় দৌড়",
- "directRunSuccess": "{{count}} হোস্টে \"{{name}}\" চালানো হয়েছে",
- "directRunPartialFail": "এক বা একাধিক হোস্টে \"{{name}}\" ব্যর্থ হয়েছে",
- "executionResultTitle": "কার্য সম্পাদনের ফলাফল: {{name}}",
- "executionResultDescription": "প্রতিটি টার্গেট হোস্টে কোড স্নিপেটটি চালানোর আউটপুট।",
- "executionSuccess": "সাফল্য",
- "executionFailed": "ব্যর্থ",
- "expandAll": "সমস্ত ফোল্ডার প্রসারিত করুন",
- "collapseAll": "সমস্ত ফোল্ডার সংকুচিত করুন",
- "settings": "স্নিপেট সেটিংস",
- "variablesHint": "ভেরিয়েবল: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "দৌড়াও \"{{name}}\"",
- "variablesDialogDescription": "এই কোড স্নিপেটটি চালানোর আগে প্রয়োজনীয় মানগুলি পূরণ করুন।",
- "variablesPreviewLabel": "প্রিভিউ",
- "variablesConfirmButton": "দৌড়"
- },
"keybindings": {
"title": "কীবোর্ড শর্টকাট",
"description": "টার্মিনালের কপি, পেস্ট ও কন্ট্রোল শর্টকাটগুলো নিজের মতো করে সাজিয়ে নিন, অথবা টেক্সট পাঠাতে বা কোনো স্নিপেট চালাতে কী-গুলো বাইন্ড করুন।",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "ক্লিক করুন এবং কী টিপুন",
"recording": "কীগুলো চাপুন...",
"comboRequiredError": "প্রথমে একটি কী-কম্বিনেশন চাপুন",
- "textRequiredError": "পাঠানোর জন্য টেক্সটটি লিখুন।",
- "controlCodeRequiredError": "কন্ট্রোল কোডের জন্য একটি অক্ষর লিখুন।",
- "snippetRequiredError": "একটি স্নিপেট নির্বাচন করুন",
"conflictWarning": "এই সংমিশ্রণটি ইতিমধ্যেই {{combo}}এর সাথে আবদ্ধ। সংরক্ষণ করলে উভয়ই সক্রিয় হবে।",
"actionLabel": "পদক্ষেপ",
- "actionCopy": "কপি নির্বাচন",
- "actionPaste": "পেস্ট",
- "actionSendControlCode": "Ctrl+অক্ষর সংকেত পাঠান",
- "actionSendText": "টেক্সট পাঠান",
- "actionRunSnippet": "বিদ্যমান কোড চালান",
"actionNextTab": "পরবর্তী ট্যাবে যান",
"actionPreviousTab": "পূর্ববর্তী ট্যাবে যান",
- "actionOpenCommandPalette": "দ্রুত অনুসন্ধান খুলুন",
- "controlCodeLabel": "চিঠি",
- "textLabel": "পাঠানোর জন্য টেক্সট",
- "appendEnterLabel": "পাঠানোর পর এন্টার চাপুন",
- "snippetLabel": "স্নিপেট",
- "selectSnippetPlaceholder": "একটি স্নিপেট নির্বাচন করুন",
- "orphanedSnippetWarning": "স্নিপেটটি খুঁজে পাওয়া যায়নি",
- "clipboardPermissionNote": "কিছু ব্রাউজারে প্রথমবার ব্যবহারের সময় ক্লিপবোর্ডের অনুমতি চাইতে পারে।"
+ "actionOpenCommandPalette": "দ্রুত অনুসন্ধান খুলুন"
},
"userProfile": {
"donateTitle": "টারমিক্সকে বাঁচিয়ে রাখুন",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "বিষয় আবশ্যক",
"ntfyToken": "অ্যাক্সেস টোকেন (ঐচ্ছিক)",
"channelSaveFailed": "চ্যানেল সংরক্ষণ করতে ব্যর্থ হয়েছে"
- },
- "macros": {
- "title": "ম্যাক্রো",
- "description": "টার্মিনালে সংরক্ষিত কমান্ডের একটি সিরিজ চালান এবং সার্ভার যা ফেরত পাঠায় তার উপর প্রতিক্রিয়া জানান।",
- "target": "লক্ষ্য",
- "noTerminal": "কোন টার্মিনাল খোলা নেই",
- "empty": "এখনো কোনো ম্যাক্রো নেই",
- "emptyHint": "টার্মিনালের কোনো কাজ, যেমন লগ ইন করা, কোনো সার্ভিস রিস্টার্ট করা বা প্রম্পটের উত্তর দেওয়া, স্বয়ংক্রিয় করতে একটি ম্যাক্রো তৈরি করুন।",
- "create": "নতুন ম্যাক্রো",
- "back": "ম্যাক্রোতে ফিরে যান",
- "name": "নাম",
- "namePlaceholder": "ওয়েব সার্ভার পুনরায় চালু করুন",
- "nameRequired": "সংরক্ষণ করার আগে ম্যাক্রোটির একটি নাম দিন।",
- "descriptionLabel": "বর্ণনা",
- "descriptionPlaceholder": "এই ম্যাক্রোটি কী করে (ঐচ্ছিক)",
- "unsaved": "সংরক্ষিত নয় এমন পরিবর্তন",
- "save": "সংরক্ষণ করুন",
- "saved": "ম্যাক্রো সংরক্ষিত",
- "saveFailed": "ম্যাক্রোটি সংরক্ষণ করা যায়নি।",
- "delete": "মুছে ফেলুন",
- "deleteTitle": "এই ম্যাক্রোটি মুছে ফেলবেন?",
- "deleteBody": "\"{{name}}\" স্থায়ীভাবে মুছে ফেলা হবে। এটি আর পূর্বাবস্থায় ফেরানো যাবে না।",
- "deleted": "ম্যাক্রো মুছে ফেলা হয়েছে",
- "cancel": "বাতিল করুন",
- "run": "দৌড়",
- "stop": "থামুন",
- "running": "দৌড়াচ্ছে...",
- "completed": "ম্যাক্রো শেষ হয়েছে",
- "failed": "ম্যাক্রো ব্যর্থ হয়েছে",
- "loadFailed": "ম্যাক্রো লোড করা যায়নি",
- "needTerminal": "প্রথমে একটি টার্মিনাল খুলুন এবং সংযোগ করুন।",
- "noSteps": "এই ম্যাক্রোতে এখনও কোনো ধাপ নেই। নিচে একটি যোগ করুন।",
- "addStep": "ধাপ যোগ করুন",
- "stepNumber": "ধাপ {{number}}",
- "removeStep": "ধাপটি সরান",
- "confirmRunTitle": "এই ম্যাক্রোটি চালাবেন?",
- "confirmRunBody": "\"{{name}}\" {{host}} -এ কমান্ড পাঠাবে।",
- "confirmRunAction": "ম্যাক্রো চালান",
- "stepTypes": {
- "send": {
- "label": "কমান্ড পাঠান",
- "help": "টার্মিনালে টেক্সট টাইপ করে, ঠিক যেন আপনি নিজেই টাইপ করেছেন।"
- },
- "wait": {
- "label": "আউটপুটের জন্য অপেক্ষা করুন",
- "help": "সার্ভার আপনার দেওয়া টেক্সটটি প্রিন্ট না করা পর্যন্ত এটি থেমে থাকে, অনেকটা পাসওয়ার্ড দেওয়ার সময়কার মতো।"
- },
- "delay": {
- "label": "বিরতি",
- "help": "পরবর্তী ধাপে যাওয়ার আগে একটি নির্দিষ্ট পরিমাণ সময় অপেক্ষা করে।"
- },
- "if": {
- "label": "যদি আউটপুটে থাকে",
- "help": "সার্ভার এ পর্যন্ত যা যা প্রিন্ট করেছে, তা যাচাই করে দুটি শাখার মধ্যে একটি চালায়।"
- },
- "repeat": {
- "label": "পুনরাবৃত্তি",
- "help": "এর ভেতরের ধাপগুলো একটি নির্দিষ্ট সংখ্যক বার চালায়।"
- }
- },
- "send": {
- "text": "কমান্ড বা টেক্সট",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "পাঠানোর পর এন্টার চাপুন",
- "pressEnterHelp": "সাবমিট না করে টেক্সট টাইপ করতে এটি বন্ধ করুন।"
- },
- "wait": {
- "pattern": "আউটপুটে থাকা পর্যন্ত অপেক্ষা করুন",
- "patternPlaceholder": "পাসওয়ার্ড:",
- "timeout": "হাল ছেড়ে দেওয়ার পর",
- "onTimeout": "যদি এটি কখনও প্রদর্শিত না হয়",
- "stop": "ম্যাক্রোটি বন্ধ করুন",
- "continue": "তবুও এগিয়ে যান।"
- },
- "delay": {
- "duration": "অপেক্ষা করুন"
- },
- "ifStep": {
- "pattern": "যদি আউটপুটে থাকে",
- "patternPlaceholder": "সক্রিয়: সক্রিয় (চলমান)",
- "then": "তারপর এটা করুন",
- "else": "অন্যথায় এটা করুন",
- "emptyBranch": "কোন পদক্ষেপ নেই"
- },
- "repeat": {
- "count": "সংখ্যা",
- "times": "সময়"
- },
- "match": {
- "useRegex": "রেগুলার এক্সপ্রেশনের সাথে মেলান",
- "useRegexHelp": "লেখাটির সাথে হুবহু মিল রাখতে এটি বাদ দিন।",
- "invalidRegex": "এটি একটি বৈধ রেগুলার এক্সপ্রেশন নয়, এটিকে সাধারণ টেক্সট হিসেবে মেলানো হবে।"
- },
- "seconds": "সেকেন্ড",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ca_ES.json b/src/ui/locales/translated/ca_ES.json
index ec5698baa..8c94d2cd6 100644
--- a/src/ui/locales/translated/ca_ES.json
+++ b/src/ui/locales/translated/ca_ES.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Configuració de la barra lateral...",
"hosts": "Amfitrions",
"snippets": "Fragments",
- "macros": "Macros",
"hostManager": "Gestor d'amfitrions",
"credentials": "Credencials",
"connections": "Connexions",
@@ -869,7 +868,6 @@
"groupTags": "Etiquetes i pin",
"groupTunnels": "Túnels",
"groupJumpHosts": "Amfitrions de salt",
- "groupQuickActions": "Accions ràpides",
"groupFeatureFlags": "Banderes de funcions",
"groupAdvanced": "Configuració avançada",
"preview": "Vista prèvia",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Emmagatzemar claus o contrasenyes SSH",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Supervisa les sessions tmux a tots els teus amfitrions",
- "snippets": "Fragments",
- "runSnippetDesc": "Executar a la pestanya activa del terminal",
- "noSnippetsFound": "No s'han trobat fragments que coincideixin amb \"{{search}}\"",
"recentActivity": "Activitat recent",
"serversAndHosts": "Servidors i amfitrions",
"noHostsFound": "No s'han trobat hosts que coincideixin amb \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Pestanya anterior",
"hotkeysJumpTab": "Salta a la pestanya"
},
- "snippets": {
- "title": "Fragments",
- "createSnippetTitle": "Crea un fragment",
- "createSnippetDescription": "Crea un nou fragment d'ordre per a una execució ràpida",
- "nameLabel": "Nom",
- "namePlaceholder": "p. ex., reiniciar Nginx",
- "descriptionLabel": "Descripció",
- "descriptionPlaceholder": "Descripció opcional",
- "optional": "Opcional",
- "folderLabel": "Carpeta",
- "noFolder": "Sense carpeta (Sense categoria)",
- "commandLabel": "Comandament",
- "commandPlaceholder": "p. ex., sudo systemctl reinicia nginx",
- "typeLabel": "Tipus",
- "typeCommand": "Comandament",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "p. ex., un text de recordatori o indicació reutilitzable",
- "cancel": "Cancel·la",
- "createSnippetButton": "Crea un fragment",
- "createFolderTitle": "Crea una carpeta",
- "createFolderDescription": "Organitza els teus fragments en carpetes",
- "folderNameLabel": "Nom de la carpeta",
- "folderNamePlaceholder": "p. ex., ordres del sistema, scripts de Docker",
- "folderColorLabel": "Color de la carpeta",
- "folderIconLabel": "Icona de carpeta",
- "previewLabel": "Vista prèvia",
- "folderNameFallback": "Nom de la carpeta",
- "createFolderButton": "Crea una carpeta",
- "targetTerminals": "Terminals de destinació",
- "selectAll": "Tot",
- "selectNone": "Cap",
- "noTerminalTabsOpen": "No hi ha pestanyes de terminal obertes",
- "searchPlaceholder": "Fragments de cerca...",
- "newSnippet": "Nou fragment",
- "newFolder": "Nova carpeta",
- "run": "Corre",
- "noSnippetsInFolder": "No hi ha fragments en aquesta carpeta",
- "uncategorized": "Sense categoria",
- "editSnippetTitle": "Edita el fragment",
- "editSnippetDescription": "Actualitza aquest fragment d'ordres",
- "saveSnippetButton": "Desa els canvis",
- "createSuccess": "Fragment creat correctament",
- "createFailed": "No s'ha pogut crear el fragment",
- "updateSuccess": "Fragment actualitzat correctament",
- "updateFailed": "No s'ha pogut actualitzar el fragment",
- "deleteFailed": "No s'ha pogut suprimir el fragment",
- "folderCreateSuccess": "Carpeta creada correctament",
- "folderCreateFailed": "No s'ha pogut crear la carpeta",
- "editFolderTitle": "Edita la carpeta",
- "editFolderDescription": "Canvia el nom o l'aspecte d'aquesta carpeta",
- "saveFolderButton": "Desa els canvis",
- "editFolder": "Edita la carpeta",
- "deleteFolder": "Suprimeix la carpeta",
- "folderDeleteSuccess": "S'ha suprimit la carpeta \"{{name}}\"",
- "folderDeleteFailed": "No s'ha pogut suprimir la carpeta",
- "folderEditSuccess": "La carpeta s'ha actualitzat correctament",
- "folderEditFailed": "No s'ha pogut actualitzar la carpeta",
- "confirmRunMessage": "Executar \"{{name}}\"?",
- "confirmRunButton": "Corre",
- "runSuccess": "Ha executat \"{{name}}\" en terminals {{count}}",
- "pasteSuccess": "S'ha enganxat \"{{name}}\" al(s) terminal(s) {{count}}",
- "pasteToTerminal": "Enganxa",
- "copySuccess": "S'ha copiat \"{{name}}\" al porta-retalls",
- "shareTitle": "Compartir fragment",
- "shareUser": "Usuari",
- "shareRole": "Rol",
- "selectUser": "Selecciona un usuari...",
- "selectRole": "Selecciona un rol...",
- "shareSuccess": "Fragment compartit correctament",
- "shareFailed": "No s'ha pogut compartir el fragment",
- "revokeSuccess": "Accés revocat",
- "revokeFailed": "No s'ha pogut revocar l'accés",
- "currentAccess": "Accés actual",
- "shareLoadError": "No s'han pogut carregar les dades compartides",
- "loading": "S'està carregant...",
- "close": "Tanca",
- "reorderFailed": "No s'ha pogut desar l'ordre del fragment",
- "importExport": "Importació / Exportació",
- "exportBtn": "Exporta JSON",
- "importBtn": "Importa JSON",
- "exportSuccess": "Fragments exportats correctament",
- "exportFailed": "No s'han pogut exportar els fragments",
- "importTitle": "Importa fragments",
- "importDescription": "Importa fragments i carpetes des d'un fitxer JSON exportat per Termix",
- "importDropOrClick": "Deixa anar un fitxer JSON aquí o fes clic per navegar",
- "importSelectedFile": "Seleccionat: {{name}}",
- "importOverwrite": "Sobreescriu els fragments existents amb el mateix nom i carpeta",
- "importStartBtn": "Importa",
- "importSuccess": "Importació completada: {{snippets}} fragment(s) afegit(s), {{updated}} actualitzat, {{skipped}} omès, {{folders}} carpeta(es) afegida(es)",
- "importFailed": "No s'han pogut importar els fragments",
- "importInvalidFile": "Fitxer no vàlid: s'esperava un objecte JSON amb fragments o matrius de carpetes.",
- "targetHostsLabel": "Amfitrions de destinació",
- "targetHostsHint": "Assigneu hosts per executar aquest fragment directament sense un terminal obert.",
- "noHostsAvailable": "No hi ha cap amfitrió configurat",
- "clearTargetHosts": "Esborra-ho tot",
- "hasTargetHosts": "Té amfitrions de destinació",
- "runOnTargets": "Executar en objectius",
- "directRunSuccess": "Ha executat \"{{name}}\" en {{count}} host(s)",
- "directRunPartialFail": "\"{{name}}\" ha fallat en un o més amfitrions",
- "executionResultTitle": "Resultats de l'execució: {{name}}",
- "executionResultDescription": "Sortida de l'execució del fragment a cada amfitrió de destinació.",
- "executionSuccess": "Èxit",
- "executionFailed": "Fallat",
- "expandAll": "Expandir totes les carpetes",
- "collapseAll": "Replega totes les carpetes",
- "settings": "Configuració del fragment",
- "variablesHint": "Variables: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etiqueta}",
- "variablesDialogTitle": "Executa \"{{name}}\"",
- "variablesDialogDescription": "Empleneu els valors que necessita aquest fragment abans d'executar-lo.",
- "variablesPreviewLabel": "Vista prèvia",
- "variablesConfirmButton": "Corre"
- },
"keybindings": {
"title": "Dreceres de teclat",
"description": "Personalitza les dreceres de copiar, enganxar i controlar del terminal, o vincula tecles per enviar text o executar un fragment.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Feu clic i premeu les tecles",
"recording": "Premeu les tecles...",
"comboRequiredError": "Premeu primer una combinació de tecles",
- "textRequiredError": "Introduïu el text per enviar",
- "controlCodeRequiredError": "Introduïu una sola lletra per al codi de control",
- "snippetRequiredError": "Selecciona un fragment",
"conflictWarning": "Aquesta combinació ja està vinculada a {{combo}}. Si deseu, totes dues es dispararan.",
"actionLabel": "Acció",
- "actionCopy": "Copia la selecció",
- "actionPaste": "Enganxa",
- "actionSendControlCode": "Envia el senyal Ctrl+lletra",
- "actionSendText": "Enviar text",
- "actionRunSnippet": "Executa el fragment existent",
"actionNextTab": "Canvia a la pestanya següent",
"actionPreviousTab": "Canvia a la pestanya anterior",
- "actionOpenCommandPalette": "Obre la cerca ràpida",
- "controlCodeLabel": "Carta",
- "textLabel": "Text per enviar",
- "appendEnterLabel": "Premeu Intro després d'enviar",
- "snippetLabel": "Fragment",
- "selectSnippetPlaceholder": "Selecciona un fragment",
- "orphanedSnippetWarning": "fragment no trobat",
- "clipboardPermissionNote": "Pot demanar permisos al porta-retalls en el primer ús en alguns navegadors."
+ "actionOpenCommandPalette": "Obre la cerca ràpida"
},
"userProfile": {
"donateTitle": "Mantingueu viva la Termix",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "El tema és obligatori",
"ntfyToken": "Token d'accés (opcional)",
"channelSaveFailed": "No s'ha pogut desar el canal"
- },
- "macros": {
- "title": "Macros",
- "description": "Executa una sèrie d'ordres desades en un terminal i reacciona al que el servidor envia de tornada.",
- "target": "Objectiu",
- "noTerminal": "Cap terminal obert",
- "empty": "Encara no hi ha macros",
- "emptyHint": "Crea una macro per automatitzar un flux de terminal, com ara iniciar la sessió, reiniciar un servei o respondre a les indicacions.",
- "create": "Nova macro",
- "back": "Torna a les macros",
- "name": "Nom",
- "namePlaceholder": "Reinicia el servidor web",
- "nameRequired": "Doneu un nom a la macro abans de desar-la",
- "descriptionLabel": "Descripció",
- "descriptionPlaceholder": "Què fa aquesta macro (opcional)",
- "unsaved": "Canvis no desats",
- "save": "Desa",
- "saved": "Macro desada",
- "saveFailed": "No s'ha pogut desar la macro",
- "delete": "Suprimeix",
- "deleteTitle": "Voleu suprimir aquesta macro?",
- "deleteBody": "«{{name}}» s'eliminarà permanentment. Això no es pot desfer.",
- "deleted": "Macro suprimida",
- "cancel": "Cancel·la",
- "run": "Corre",
- "stop": "Atura",
- "running": "Corrent...",
- "completed": "Macro acabada",
- "failed": "La macro ha fallat",
- "loadFailed": "No s'han pogut carregar les macros",
- "needTerminal": "Obriu i connecteu primer un terminal",
- "noSteps": "Aquesta macro encara no té cap pas. Afegeix-ne un a continuació.",
- "addStep": "Afegeix un pas",
- "stepNumber": "Pas {{number}}",
- "removeStep": "Elimina el pas",
- "confirmRunTitle": "Voleu executar aquesta macro?",
- "confirmRunBody": "\"{{name}}\" enviarà ordres a {{host}}.",
- "confirmRunAction": "Executa macro",
- "stepTypes": {
- "send": {
- "label": "Envia l'ordre",
- "help": "Escriu text al terminal, igual que si l'haguessis escrit."
- },
- "wait": {
- "label": "Espera la sortida",
- "help": "Es posa en pausa fins que el servidor imprimeix el text que anomeneu, com si fos una sol·licitud de contrasenya."
- },
- "delay": {
- "label": "Pausa",
- "help": "Espera un temps fix abans del següent pas."
- },
- "if": {
- "label": "Si la sortida conté",
- "help": "Comprova tot el que el servidor ha imprès fins ara i després executa una de les dues branques."
- },
- "repeat": {
- "label": "Repetir",
- "help": "Executa els passos que hi ha dins un nombre determinat de vegades."
- }
- },
- "send": {
- "text": "Ordre o text",
- "textPlaceholder": "reinici de systemctl a nginx",
- "pressEnter": "Premeu Intro després d'enviar",
- "pressEnterHelp": "Desactiva això per escriure text sense enviar-lo."
- },
- "wait": {
- "pattern": "Espereu fins que la sortida contingui",
- "patternPlaceholder": "contrasenya:",
- "timeout": "Renunciar després",
- "onTimeout": "Si mai apareix",
- "stop": "Atura la macro",
- "continue": "Continua igualment"
- },
- "delay": {
- "duration": "Espera"
- },
- "ifStep": {
- "pattern": "Si la sortida conté",
- "patternPlaceholder": "Actiu: actiu (en funcionament)",
- "then": "Aleshores fes això",
- "else": "Si no, fes això",
- "emptyBranch": "Sense esglaons"
- },
- "repeat": {
- "count": "Nombre de vegades",
- "times": "vegades"
- },
- "match": {
- "useRegex": "Coincideix amb una expressió regular",
- "useRegexHelp": "Deixa això desactivat perquè el text coincideixi exactament com està escrit.",
- "invalidRegex": "No és una expressió regular vàlida, es trobarà com a text sense format."
- },
- "seconds": "segons",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/cs_CZ.json b/src/ui/locales/translated/cs_CZ.json
index 39d372f5e..40603f063 100644
--- a/src/ui/locales/translated/cs_CZ.json
+++ b/src/ui/locales/translated/cs_CZ.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Nastavení postranního panelu...",
"hosts": "Hostitelé",
"snippets": "Úryvky",
- "macros": "Makra",
"hostManager": "Hostitelský manažer",
"credentials": "Pověření",
"connections": "Připojení",
@@ -869,7 +868,6 @@
"groupTags": "Štítky a pin",
"groupTunnels": "Tunely",
"groupJumpHosts": "Hostitelé skoků",
- "groupQuickActions": "Rychlé akce",
"groupFeatureFlags": "Vlajky funkcí",
"groupAdvanced": "Pokročilá konfigurace",
"preview": "Náhled",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Ukládání SSH klíčů nebo hesel",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitorujte relace tmux napříč vašimi hostiteli",
- "snippets": "Úryvky",
- "runSnippetDesc": "Spustit v aktivní kartě terminálu",
- "noSnippetsFound": "Nebyly nalezeny žádné úryvky odpovídající „{{search}}“",
"recentActivity": "Nedávná aktivita",
"serversAndHosts": "Servery a hostitelé",
"noHostsFound": "Nenalezeny žádné hostitele odpovídající \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Předchozí karta",
"hotkeysJumpTab": "Přejít na kartu"
},
- "snippets": {
- "title": "Úryvky",
- "createSnippetTitle": "Vytvořit úryvek",
- "createSnippetDescription": "Vytvořte nový úryvek příkazu pro rychlé spuštění",
- "nameLabel": "Jméno",
- "namePlaceholder": "např. restartujte Nginx",
- "descriptionLabel": "Popis",
- "descriptionPlaceholder": "Volitelný popis",
- "optional": "Volitelný",
- "folderLabel": "Složka",
- "noFolder": "Žádná složka (Nezařazené)",
- "commandLabel": "Příkaz",
- "commandPlaceholder": "např. sudo systemctl restart nginx",
- "typeLabel": "Typ",
- "typeCommand": "Příkaz",
- "typeNote": "Poznámka",
- "noteLabel": "Poznámka",
- "notePlaceholder": "např. opakovaně použitelný text s výzvou nebo připomínkou",
- "cancel": "Zrušit",
- "createSnippetButton": "Vytvořit úryvek",
- "createFolderTitle": "Vytvořit složku",
- "createFolderDescription": "Uspořádejte si úryvky do složek",
- "folderNameLabel": "Název složky",
- "folderNamePlaceholder": "např. systémové příkazy, Docker skripty",
- "folderColorLabel": "Barva složky",
- "folderIconLabel": "Ikona složky",
- "previewLabel": "Náhled",
- "folderNameFallback": "Název složky",
- "createFolderButton": "Vytvořit složku",
- "targetTerminals": "Cílové terminály",
- "selectAll": "Vše",
- "selectNone": "Žádný",
- "noTerminalTabsOpen": "Žádné otevřené karty terminálu",
- "searchPlaceholder": "Vyhledat úryvky...",
- "newSnippet": "Nový úryvek",
- "newFolder": "Nová složka",
- "run": "Běh",
- "noSnippetsInFolder": "V této složce nejsou žádné úryvky",
- "uncategorized": "Nezařazené",
- "editSnippetTitle": "Upravit úryvek",
- "editSnippetDescription": "Aktualizujte tento úryvek příkazu",
- "saveSnippetButton": "Uložit změny",
- "createSuccess": "Úryvek byl úspěšně vytvořen",
- "createFailed": "Nepodařilo se vytvořit úryvek",
- "updateSuccess": "Úryvek byl úspěšně aktualizován",
- "updateFailed": "Nepodařilo se aktualizovat úryvek",
- "deleteFailed": "Nepodařilo se smazat úryvek",
- "folderCreateSuccess": "Složka byla úspěšně vytvořena",
- "folderCreateFailed": "Nepodařilo se vytvořit složku",
- "editFolderTitle": "Upravit složku",
- "editFolderDescription": "Přejmenovat nebo změnit vzhled této složky",
- "saveFolderButton": "Uložit změny",
- "editFolder": "Upravit složku",
- "deleteFolder": "Smazat složku",
- "folderDeleteSuccess": "Složka „{{name}}“ smazána",
- "folderDeleteFailed": "Složku se nepodařilo smazat",
- "folderEditSuccess": "Složka byla úspěšně aktualizována",
- "folderEditFailed": "Nepodařilo se aktualizovat složku",
- "confirmRunMessage": "Spustit „{{name}}“?",
- "confirmRunButton": "Běh",
- "runSuccess": "Spustil „{{name}}“ v {{count}} terminálu(ů)",
- "pasteSuccess": "Vloženo „{{name}}“ do terminálu(ů) {{count}}",
- "pasteToTerminal": "Pasta",
- "copySuccess": "Zkopírováno „{{name}}“ do schránky",
- "shareTitle": "Sdílet úryvek",
- "shareUser": "Uživatel",
- "shareRole": "Role",
- "selectUser": "Vyberte uživatele...",
- "selectRole": "Vyberte roli...",
- "shareSuccess": "Úryvek byl úspěšně sdílen",
- "shareFailed": "Sdílení úryvku se nezdařilo",
- "revokeSuccess": "Přístup zrušen",
- "revokeFailed": "Zrušení přístupu se nezdařilo",
- "currentAccess": "Aktuální přístup",
- "shareLoadError": "Nepodařilo se načíst data sdílení",
- "loading": "Načítání...",
- "close": "Blízko",
- "reorderFailed": "Nepodařilo se uložit pořadí úryvků",
- "importExport": "Import / Export",
- "exportBtn": "Exportovat JSON",
- "importBtn": "Importovat JSON",
- "exportSuccess": "Úryvky byly úspěšně exportovány",
- "exportFailed": "Export úryvků se nezdařilo",
- "importTitle": "Importovat úryvky",
- "importDescription": "Import úryvků a složek ze souboru JSON exportovaného aplikací Termix",
- "importDropOrClick": "Vložte sem soubor JSON nebo klikněte pro procházení",
- "importSelectedFile": "Vybráno: {{name}}",
- "importOverwrite": "Přepsat existující úryvky stejným názvem a složkou",
- "importStartBtn": "Importovat",
- "importSuccess": "Import dokončen: {{snippets}} přidán(y), {{updated}} aktualizováno, {{skipped}} přeskočeno, {{folders}} přidáno(y) složek",
- "importFailed": "Import úryvků se nezdařilo",
- "importInvalidFile": "Neplatný soubor: očekával se objekt JSON s úryvky kódu nebo poli složek.",
- "targetHostsLabel": "Cílové hostitele",
- "targetHostsHint": "Přiřaďte hostitele, aby spustili tento úryvek přímo bez otevřeného terminálu.",
- "noHostsAvailable": "Žádní hostitelé nakonfigurováni",
- "clearTargetHosts": "Vymazat vše",
- "hasTargetHosts": "Má cílové hostitele",
- "runOnTargets": "Běh na cíle",
- "directRunSuccess": "Spuštěno „{{name}}“ na hostiteli {{count}}",
- "directRunPartialFail": "„{{name}}“ selhalo na jednom nebo více hostitelích",
- "executionResultTitle": "Výsledky provedení: {{name}}",
- "executionResultDescription": "Výstup ze spuštění úryvku kódu na každém cílovém hostiteli.",
- "executionSuccess": "Úspěch",
- "executionFailed": "Selhalo",
- "expandAll": "Rozbalit všechny složky",
- "collapseAll": "Sbalit všechny složky",
- "settings": "Nastavení úryvků",
- "variablesHint": "Proměnné: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Popis}",
- "variablesDialogTitle": "Spustit „{{name}}“",
- "variablesDialogDescription": "Před spuštěním úryvku kódu vyplňte jeho potřebné hodnoty.",
- "variablesPreviewLabel": "Náhled",
- "variablesConfirmButton": "Běh"
- },
"keybindings": {
"title": "Klávesové zkratky",
"description": "Přizpůsobte si klávesové zkratky pro kopírování, vkládání a ovládání v terminálu nebo propojte klávesy pro odeslání textu nebo spuštění úryvku kódu.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klikněte a stiskněte klávesy",
"recording": "Stiskněte klávesy...",
"comboRequiredError": "Nejprve stiskněte kombinaci kláves",
- "textRequiredError": "Zadejte text k odeslání",
- "controlCodeRequiredError": "Zadejte jedno písmeno pro kontrolní kód",
- "snippetRequiredError": "Vyberte úryvek",
"conflictWarning": "Tato kombinace je již vázána na {{combo}}. Uložením se obě spustí.",
"actionLabel": "Akce",
- "actionCopy": "Kopírovat výběr",
- "actionPaste": "Pasta",
- "actionSendControlCode": "Odeslat signál Ctrl+písmeno",
- "actionSendText": "Odeslat text",
- "actionRunSnippet": "Spustit existující úryvek kódu",
"actionNextTab": "Přepnout na další kartu",
"actionPreviousTab": "Přepnout na předchozí kartu",
- "actionOpenCommandPalette": "Otevřít rychlé vyhledávání",
- "controlCodeLabel": "Dopis",
- "textLabel": "Text k odeslání",
- "appendEnterLabel": "Po odeslání stiskněte Enter",
- "snippetLabel": "Úryvek",
- "selectSnippetPlaceholder": "Vyberte úryvek",
- "orphanedSnippetWarning": "úryvek nenalezen",
- "clipboardPermissionNote": "V některých prohlížečích se může při prvním použití zobrazit výzva k zadání povolení ke schránce."
+ "actionOpenCommandPalette": "Otevřít rychlé vyhledávání"
},
"userProfile": {
"donateTitle": "Udrž Termixe naživu",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Téma je povinné",
"ntfyToken": "Přístupový token (volitelné)",
"channelSaveFailed": "Uložení kanálu se nezdařilo"
- },
- "macros": {
- "title": "Makra",
- "description": "Spusťte v terminálu uloženou sérii příkazů a reagujte na to, co server odešle zpět.",
- "target": "Cíl",
- "noTerminal": "Žádný otevřený terminál",
- "empty": "Zatím žádná makra",
- "emptyHint": "Vytvořte makro pro automatizaci procesů v terminálu, jako je přihlašování, restartování služby nebo odpovídání na výzvy.",
- "create": "Nové makro",
- "back": "Zpět k makrům",
- "name": "Jméno",
- "namePlaceholder": "Restartujte webový server",
- "nameRequired": "Před uložením zadejte makro název",
- "descriptionLabel": "Popis",
- "descriptionPlaceholder": "Co toto makro dělá (volitelné)",
- "unsaved": "Neuložené změny",
- "save": "Uložit",
- "saved": "Makro uloženo",
- "saveFailed": "Makro se nepodařilo uložit.",
- "delete": "Vymazat",
- "deleteTitle": "Smazat toto makro?",
- "deleteBody": "Soubor „{{name}}“ bude trvale smazán. Tuto akci nelze vrátit zpět.",
- "deleted": "Makro smazáno",
- "cancel": "Zrušit",
- "run": "Běh",
- "stop": "Zastávka",
- "running": "Běh...",
- "completed": "Makro dokončeno",
- "failed": "Makro selhalo",
- "loadFailed": "Nepodařilo se načíst makra",
- "needTerminal": "Nejprve otevřete a připojte terminál",
- "noSteps": "Toto makro zatím nemá žádné kroky. Přidejte jeden níže.",
- "addStep": "Přidat krok",
- "stepNumber": "Krok {{number}}",
- "removeStep": "Odebrat krok",
- "confirmRunTitle": "Spustit toto makro?",
- "confirmRunBody": "„{{name}}“ odešle příkazy pro {{host}}.",
- "confirmRunAction": "Spustit makro",
- "stepTypes": {
- "send": {
- "label": "Odeslat příkaz",
- "help": "Zadá text do terminálu, stejně jako byste ho napsali."
- },
- "wait": {
- "label": "Počkejte na výstup",
- "help": "Pozastaví se, dokud server nevypíše text, který pojmenujete, například výzvu k zadání hesla."
- },
- "delay": {
- "label": "Pauza",
- "help": "Čeká pevně stanovenou dobu před dalším krokem."
- },
- "if": {
- "label": "Pokud výstup obsahuje",
- "help": "Zkontroluje vše, co server dosud vytiskl, a poté spustí jednu ze dvou větví."
- },
- "repeat": {
- "label": "Opakovat",
- "help": "Spustí kroky uvnitř něj tolikrát, kolikrát je nastaveno."
- }
- },
- "send": {
- "text": "Příkaz nebo text",
- "textPlaceholder": "restartování systemctl v Nginxu",
- "pressEnter": "Po odeslání stiskněte Enter",
- "pressEnterHelp": "Vypněte tuto funkci, chcete-li psát text bez jeho odesílání."
- },
- "wait": {
- "pattern": "Počkejte, dokud výstup neobsahuje",
- "patternPlaceholder": "heslo:",
- "timeout": "Vzdej se po",
- "onTimeout": "Pokud se to nikdy neobjeví",
- "stop": "Zastavit makro",
- "continue": "Stejně pokračuj"
- },
- "delay": {
- "duration": "Čekat na"
- },
- "ifStep": {
- "pattern": "Pokud výstup obsahuje",
- "patternPlaceholder": "Aktivní: aktivní (běží)",
- "then": "Pak udělej tohle",
- "else": "Jinak to udělej",
- "emptyBranch": "Žádné schody"
- },
- "repeat": {
- "count": "Počet opakování",
- "times": "krát"
- },
- "match": {
- "useRegex": "Porovnání s regulárním výrazem",
- "useRegexHelp": "Nechte toto vypnuté, aby text přesně odpovídal napsanému textu.",
- "invalidRegex": "Není to platný regulární výraz, bude nalezen jako prostý text."
- },
- "seconds": "sekundy",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/da_DK.json b/src/ui/locales/translated/da_DK.json
index ae3169930..8d4b1174a 100644
--- a/src/ui/locales/translated/da_DK.json
+++ b/src/ui/locales/translated/da_DK.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Indstillinger for sidebjælke...",
"hosts": "Værter",
"snippets": "Uddrag",
- "macros": "Makroer",
"hostManager": "Værtsadministrator",
"credentials": "Akkreditiver",
"connections": "Forbindelser",
@@ -869,7 +868,6 @@
"groupTags": "Tags og pin",
"groupTunnels": "Tunneler",
"groupJumpHosts": "Spring værter",
- "groupQuickActions": "Hurtige handlinger",
"groupFeatureFlags": "Funktionsflag",
"groupAdvanced": "Avanceret konfiguration",
"preview": "Forhåndsvisning",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Gem SSH-nøgler eller adgangskoder",
"tmuxMonitor": "Tmux-skærm",
"tmuxMonitorDesc": "Overvåg tmux-sessioner på tværs af dine værter",
- "snippets": "Uddrag",
- "runSnippetDesc": "Kør i den aktive terminalfane",
- "noSnippetsFound": "Ingen uddrag fundet, der matcher \"{{search}}\"",
"recentActivity": "Seneste aktivitet",
"serversAndHosts": "Servere og værter",
"noHostsFound": "Ingen værter fundet, der matcher \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Forrige fane",
"hotkeysJumpTab": "Gå til faneblad"
},
- "snippets": {
- "title": "Uddrag",
- "createSnippetTitle": "Opret uddrag",
- "createSnippetDescription": "Opret et nyt kommandosnip til hurtig udførelse",
- "nameLabel": "Navn",
- "namePlaceholder": "f.eks. Genstart Nginx",
- "descriptionLabel": "Beskrivelse",
- "descriptionPlaceholder": "Valgfri beskrivelse",
- "optional": "Valgfri",
- "folderLabel": "Folder",
- "noFolder": "Ingen mappe (Ukategoriseret)",
- "commandLabel": "Kommando",
- "commandPlaceholder": "f.eks. sudo systemctl genstart nginx",
- "typeLabel": "Type",
- "typeCommand": "Kommando",
- "typeNote": "Note",
- "noteLabel": "Note",
- "notePlaceholder": "f.eks. en genanvendelig prompt eller påmindelsestekst",
- "cancel": "Ophæve",
- "createSnippetButton": "Opret uddrag",
- "createFolderTitle": "Opret mappe",
- "createFolderDescription": "Organiser dine uddrag i mapper",
- "folderNameLabel": "Mappenavn",
- "folderNamePlaceholder": "f.eks. systemkommandoer, Docker-scripts",
- "folderColorLabel": "Mappefarve",
- "folderIconLabel": "Mappeikon",
- "previewLabel": "Forhåndsvisning",
- "folderNameFallback": "Mappenavn",
- "createFolderButton": "Opret mappe",
- "targetTerminals": "Target Terminals",
- "selectAll": "Alle",
- "selectNone": "Ingen",
- "noTerminalTabsOpen": "Ingen terminalfaner åbne",
- "searchPlaceholder": "Søg i uddrag...",
- "newSnippet": "Nyt uddrag",
- "newFolder": "Ny mappe",
- "run": "Løbe",
- "noSnippetsInFolder": "Ingen uddrag i denne mappe",
- "uncategorized": "Ukategoriseret",
- "editSnippetTitle": "Rediger uddrag",
- "editSnippetDescription": "Opdater dette kommandosnip",
- "saveSnippetButton": "Gem ændringer",
- "createSuccess": "Kodestykket er oprettet",
- "createFailed": "Kunne ikke oprette kodestykket",
- "updateSuccess": "Uddraget er opdateret",
- "updateFailed": "Kunne ikke opdatere uddraget",
- "deleteFailed": "Kunne ikke slette uddraget",
- "folderCreateSuccess": "Mappen er oprettet",
- "folderCreateFailed": "Kunne ikke oprette en mappe",
- "editFolderTitle": "Rediger mappe",
- "editFolderDescription": "Omdøb eller skift udseende af denne mappe",
- "saveFolderButton": "Gem ændringer",
- "editFolder": "Rediger mappe",
- "deleteFolder": "Slet mappe",
- "folderDeleteSuccess": "Mappen \"{{name}}\" er slettet",
- "folderDeleteFailed": "Kunne ikke slette mappen",
- "folderEditSuccess": "Mappen er blevet opdateret",
- "folderEditFailed": "Mappen kunne ikke opdateres",
- "confirmRunMessage": "Kør \"{{name}}\"?",
- "confirmRunButton": "Løbe",
- "runSuccess": "Kørte \"{{name}}\" i {{count}} terminal(er)",
- "pasteSuccess": "Indsatte \"{{name}}\" i {{count}} terminal(er)",
- "pasteToTerminal": "Indsæt",
- "copySuccess": "Kopieret \"{{name}}\" til udklipsholderen",
- "shareTitle": "Del uddrag",
- "shareUser": "Bruger",
- "shareRole": "Rolle",
- "selectUser": "Vælg en bruger...",
- "selectRole": "Vælg en rolle...",
- "shareSuccess": "Uddraget blev delt",
- "shareFailed": "Kunne ikke dele uddraget",
- "revokeSuccess": "Adgang tilbagekaldt",
- "revokeFailed": "Adgang kunne ikke tilbagekaldes",
- "currentAccess": "Nuværende adgang",
- "shareLoadError": "Kunne ikke indlæse delingsdata",
- "loading": "Indlæser...",
- "close": "Tæt",
- "reorderFailed": "Kunne ikke gemme rækkefølgen af uddrag",
- "importExport": "Import/eksport",
- "exportBtn": "Eksportér JSON",
- "importBtn": "Importér JSON",
- "exportSuccess": "Snippets eksporteret",
- "exportFailed": "Kunne ikke eksportere uddrag",
- "importTitle": "Importér uddrag",
- "importDescription": "Importer snippets og mapper fra en JSON-fil eksporteret af Termix",
- "importDropOrClick": "Slip en JSON-fil her, eller klik for at gennemse",
- "importSelectedFile": "Valgt: {{name}}",
- "importOverwrite": "Overskriv eksisterende kodestykker med samme navn og mappe",
- "importStartBtn": "Importere",
- "importSuccess": "Import fuldført: {{snippets}} kodestykke(r) tilføjet, {{updated}} opdateret, {{skipped}} sprunget over, {{folders}} mappe(r) tilføjet",
- "importFailed": "Kunne ikke importere uddrag",
- "importInvalidFile": "Ugyldig fil: forventede et JSON-objekt med snippets eller mapper",
- "targetHostsLabel": "Målværter",
- "targetHostsHint": "Tildel værter til at køre dette kodestykke direkte uden en åben terminal.",
- "noHostsAvailable": "Ingen værter konfigureret",
- "clearTargetHosts": "Ryd alt",
- "hasTargetHosts": "Har målværter",
- "runOnTargets": "Løb på mål",
- "directRunSuccess": "Kørte \"{{name}}\" på {{count}} vært(er)",
- "directRunPartialFail": "\"{{name}}\" mislykkedes på en eller flere værter",
- "executionResultTitle": "Udførelsesresultater: {{name}}",
- "executionResultDescription": "Output fra kørsel af kodestykket på hver målvært.",
- "executionSuccess": "Succes",
- "executionFailed": "Mislykkedes",
- "expandAll": "Udvid alle mapper",
- "collapseAll": "Skjul alle mapper",
- "settings": "Indstillinger for uddrag",
- "variablesHint": "Variabler: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Kør \"{{name}}\"",
- "variablesDialogDescription": "Udfyld de værdier, som dette kodestykke skal bruge, før det køres.",
- "variablesPreviewLabel": "Forhåndsvisning",
- "variablesConfirmButton": "Løbe"
- },
"keybindings": {
"title": "Tastaturgenveje",
"description": "Tilpas genveje til kopiering, indsættelse og styring af terminaler, eller bind taster for at sende tekst eller køre et kodestykke.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klik og tryk på tasterne",
"recording": "Tryk på tasterne...",
"comboRequiredError": "Tryk først på en tastekombination",
- "textRequiredError": "Indtast den tekst, der skal sendes",
- "controlCodeRequiredError": "Indtast et enkelt bogstav for kontrolkoden",
- "snippetRequiredError": "Vælg et uddrag",
"conflictWarning": "Denne kombination er allerede bundet til {{combo}}. Hvis du gemmer dem, aktiveres begge.",
"actionLabel": "Handling",
- "actionCopy": "Kopiér valg",
- "actionPaste": "Indsæt",
- "actionSendControlCode": "Send Ctrl+bogstavsignal",
- "actionSendText": "Send sms",
- "actionRunSnippet": "Kør eksisterende kodestykke",
"actionNextTab": "Skift til næste faneblad",
"actionPreviousTab": "Skift til forrige fane",
- "actionOpenCommandPalette": "Åbn hurtigsøgning",
- "controlCodeLabel": "Bogstav",
- "textLabel": "Tekst der skal sendes",
- "appendEnterLabel": "Tryk Enter efter afsendelse",
- "snippetLabel": "Uddrag",
- "selectSnippetPlaceholder": "Vælg et uddrag",
- "orphanedSnippetWarning": "uddrag ikke fundet",
- "clipboardPermissionNote": "Kan bede om tilladelse til udklipsholder ved første brug i nogle browsere."
+ "actionOpenCommandPalette": "Åbn hurtigsøgning"
},
"userProfile": {
"donateTitle": "Hold Termix i live",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Emne er påkrævet",
"ntfyToken": "Adgangstoken (valgfrit)",
"channelSaveFailed": "Kanalen kunne ikke gemmes"
- },
- "macros": {
- "title": "Makroer",
- "description": "Kør en gemt række kommandoer i en terminal, og reager på, hvad serveren sender tilbage.",
- "target": "Mål",
- "noTerminal": "Ingen terminal åben",
- "empty": "Ingen makroer endnu",
- "emptyHint": "Opret en makro for at automatisere et terminalflow, f.eks. at logge ind, genstarte en tjeneste eller besvare prompts.",
- "create": "Ny makro",
- "back": "Tilbage til makroer",
- "name": "Navn",
- "namePlaceholder": "Genstart webserveren",
- "nameRequired": "Giv makroen et navn, før du gemmer den",
- "descriptionLabel": "Beskrivelse",
- "descriptionPlaceholder": "Hvad denne makro gør (valgfrit)",
- "unsaved": "Ikke-gemte ændringer",
- "save": "Spare",
- "saved": "Makro gemt",
- "saveFailed": "Makroen kunne ikke gemmes",
- "delete": "Slet",
- "deleteTitle": "Slette denne makro?",
- "deleteBody": "\"{{name}}\" vil blive slettet permanent. Dette kan ikke fortrydes.",
- "deleted": "Makro slettet",
- "cancel": "Ophæve",
- "run": "Løbe",
- "stop": "Stop",
- "running": "Løber...",
- "completed": "Makro færdig",
- "failed": "Makro mislykkedes",
- "loadFailed": "Makroer kunne ikke indlæses",
- "needTerminal": "Åbn og tilslut først en terminal",
- "noSteps": "Denne makro har endnu ingen trin. Tilføj et nedenfor.",
- "addStep": "Tilføj trin",
- "stepNumber": "Trin {{number}}",
- "removeStep": "Fjern trin",
- "confirmRunTitle": "Køre denne makro?",
- "confirmRunBody": "\"{{name}}\" sender kommandoer til {{host}}.",
- "confirmRunAction": "Kør makro",
- "stepTypes": {
- "send": {
- "label": "Send kommando",
- "help": "Skriver tekst i terminalen, på samme måde som hvis du havde skrevet den."
- },
- "wait": {
- "label": "Vent på output",
- "help": "Holder pause, indtil serveren udskriver den tekst, du navngiver, f.eks. en adgangskodeprompt."
- },
- "delay": {
- "label": "Pause",
- "help": "Venter et fast tidsrum før næste trin."
- },
- "if": {
- "label": "Hvis outputtet indeholder",
- "help": "Kontrollerer alt, hvad serveren har udskrevet indtil videre, og kører derefter en af to grene."
- },
- "repeat": {
- "label": "Gentage",
- "help": "Kører trinnene inde i den et bestemt antal gange."
- }
- },
- "send": {
- "text": "Kommando eller tekst",
- "textPlaceholder": "systemctl genstart nginx",
- "pressEnter": "Tryk Enter efter afsendelse",
- "pressEnterHelp": "Slå dette fra for at skrive tekst uden at indsende den."
- },
- "wait": {
- "pattern": "Vent indtil outputtet indeholder",
- "patternPlaceholder": "adgangskode:",
- "timeout": "Giv op efter",
- "onTimeout": "Hvis det aldrig dukker op",
- "stop": "Stop makroen",
- "continue": "Fortsæt alligevel"
- },
- "delay": {
- "duration": "Vent på"
- },
- "ifStep": {
- "pattern": "Hvis outputtet indeholder",
- "patternPlaceholder": "Aktiv: aktiv (løber)",
- "then": "Så gør dette",
- "else": "Ellers gør dette",
- "emptyBranch": "Ingen trin"
- },
- "repeat": {
- "count": "Antal gange",
- "times": "gange"
- },
- "match": {
- "useRegex": "Match med et regulært udtryk",
- "useRegexHelp": "Slå dette fra for at teksten skal være præcis som den er skrevet.",
- "invalidRegex": "Ikke et gyldigt regulært udtryk, det vil blive matchet som almindelig tekst"
- },
- "seconds": "sekunder",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/de_DE.json b/src/ui/locales/translated/de_DE.json
index afb052f28..9b191bde7 100644
--- a/src/ui/locales/translated/de_DE.json
+++ b/src/ui/locales/translated/de_DE.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Seitenleisteneinstellungen...",
"hosts": "Hosts",
"snippets": "Snippets",
- "macros": "Makros",
"hostManager": "Host-Manager",
"credentials": "Zugangsdaten",
"connections": "Verbindungen",
@@ -869,7 +868,6 @@
"groupTags": "Etiketten & Pins",
"groupTunnels": "Tunnel",
"groupJumpHosts": "Jump-Moderatoren",
- "groupQuickActions": "Schnelle Aktionen",
"groupFeatureFlags": "Funktionsflaggen",
"groupAdvanced": "Erweiterte Konfiguration",
"preview": "Vorschau",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH-Schlüssel oder Passwörter speichern",
"tmuxMonitor": "Tmux-Monitor",
"tmuxMonitorDesc": "Tmux-Sitzungen hostübergreifend überwachen",
- "snippets": "Ausschnitte",
- "runSnippetDesc": "Im aktiven Terminal-Tab ausführen",
- "noSnippetsFound": "Es wurden keine passenden Code-Snippets für \"{{search}}\" gefunden.",
"recentActivity": "Letzte Aktivität",
"serversAndHosts": "Server & Hosts",
"noHostsFound": "Keine Hosts gefunden, die \"{{search}}\" entsprechen",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Vorheriger Tab",
"hotkeysJumpTab": "Zum Tab springen"
},
- "snippets": {
- "title": "Snippets",
- "createSnippetTitle": "Snippet erstellen",
- "createSnippetDescription": "Einen neuen Befehlsschnipsel für die schnelle Ausführung erstellen",
- "nameLabel": "Name",
- "namePlaceholder": "z. B. Nginx neustarten",
- "descriptionLabel": "Beschreibung",
- "descriptionPlaceholder": "Optionale Beschreibung",
- "optional": "Optional",
- "folderLabel": "Ordner",
- "noFolder": "Kein Ordner (Nicht kategorisiert)",
- "commandLabel": "Befehl",
- "commandPlaceholder": "z. B. sudo systemctl restart nginx",
- "typeLabel": "Typ",
- "typeCommand": "Befehl",
- "typeNote": "Notiz",
- "noteLabel": "Notiz",
- "notePlaceholder": "z. B. ein wiederverwendbarer Hinweis oder Erinnerungstext",
- "cancel": "Abbrechen",
- "createSnippetButton": "Snippet erstellen",
- "createFolderTitle": "Ordner erstellen",
- "createFolderDescription": "Organisieren Sie Ihre Snippets in Ordnern",
- "folderNameLabel": "Ordnername",
- "folderNamePlaceholder": "z. B. Systembefehle, Docker-Skripte",
- "folderColorLabel": "Ordnerfarbe",
- "folderIconLabel": "Ordnersymbol",
- "previewLabel": "Vorschau",
- "folderNameFallback": "Ordnername",
- "createFolderButton": "Ordner erstellen",
- "targetTerminals": "Zielterminale",
- "selectAll": "Alle",
- "selectNone": "Keine",
- "noTerminalTabsOpen": "Keine Terminal-Tabs geöffnet",
- "searchPlaceholder": "Snippets durchsuchen...",
- "newSnippet": "Neues Snippet",
- "newFolder": "Neuer Ordner",
- "run": "Ausführen",
- "noSnippetsInFolder": "Keine Snippets in diesem Ordner",
- "uncategorized": "Nicht kategorisiert",
- "editSnippetTitle": "Snippet bearbeiten",
- "editSnippetDescription": "Dieses Befehls-Snippet aktualisieren",
- "saveSnippetButton": "Änderungen speichern",
- "createSuccess": "Snippet erfolgreich erstellt",
- "createFailed": "Erstellen des Snippets fehlgeschlagen",
- "updateSuccess": "Snippet erfolgreich aktualisiert",
- "updateFailed": "Aktualisieren des Snippets fehlgeschlagen",
- "deleteFailed": "Löschen des Snippets fehlgeschlagen",
- "folderCreateSuccess": "Ordner erfolgreich erstellt",
- "folderCreateFailed": "Erstellen des Ordners fehlgeschlagen",
- "editFolderTitle": "Ordner bearbeiten",
- "editFolderDescription": "Diesen Ordner umbenennen oder das Aussehen ändern",
- "saveFolderButton": "Änderungen speichern",
- "editFolder": "Ordner bearbeiten",
- "deleteFolder": "Ordner löschen",
- "folderDeleteSuccess": "Ordner \"{{name}}\" gelöscht",
- "folderDeleteFailed": "Löschen des Ordners fehlgeschlagen",
- "folderEditSuccess": "Ordner erfolgreich aktualisiert",
- "folderEditFailed": "Aktualisieren des Ordners fehlgeschlagen",
- "confirmRunMessage": "„{{name}}“ ausführen?",
- "confirmRunButton": "Ausführen",
- "runSuccess": "„{{name}}“ in {{count}} Terminal(s) ausgeführt",
- "pasteSuccess": "\"{{name}}\" wurde in das/die Terminal(s) {{count}} eingefügt",
- "pasteToTerminal": "Paste",
- "copySuccess": "„{{name}}“ in die Zwischenablage kopiert",
- "shareTitle": "Snippet teilen",
- "shareUser": "Benutzer",
- "shareRole": "Rolle",
- "selectUser": "Benutzer auswählen...",
- "selectRole": "Rolle auswählen...",
- "shareSuccess": "Snippet erfolgreich geteilt",
- "shareFailed": "Snippet konnte nicht geteilt werden",
- "revokeSuccess": "Zugriff widerrufen",
- "revokeFailed": "Zugriff konnte nicht widerrufen werden",
- "currentAccess": "Aktueller Zugriff",
- "shareLoadError": "Teilungsdaten konnten nicht geladen werden",
- "loading": "Laden...",
- "close": "Schließen",
- "reorderFailed": "Reihenfolge der Snippets konnte nicht gespeichert werden",
- "importExport": "Import / Export",
- "exportBtn": "JSON exportieren",
- "importBtn": "JSON importieren",
- "exportSuccess": "Snippets erfolgreich exportiert",
- "exportFailed": "Snippets konnten nicht exportiert werden",
- "importTitle": "Snippets importieren",
- "importDescription": "Snippets und Ordner aus einer von Termix exportierten JSON-Datei importieren",
- "importDropOrClick": "JSON-Datei hier ablegen oder zum Durchsuchen klicken",
- "importSelectedFile": "Ausgewählt: {{name}}",
- "importOverwrite": "Bestehende Snippets mit gleichem Namen und Ordner überschreiben",
- "importStartBtn": "Importieren",
- "importSuccess": "Import abgeschlossen: {{snippets}} Snippet(s) hinzugefügt, {{updated}} aktualisiert, {{skipped}} übersprungen, {{folders}} Ordner hinzugefügt",
- "importFailed": "Snippets konnten nicht importiert werden",
- "importInvalidFile": "Ungültige Datei: Ein JSON-Objekt mit Snippets- oder Ordner-Arrays erwartet",
- "targetHostsLabel": "Zielhosts",
- "targetHostsHint": "Hosts zuweisen, um dieses Snippet direkt ohne offenes Terminal auszuführen.",
- "noHostsAvailable": "Keine Hosts konfiguriert",
- "clearTargetHosts": "Alle löschen",
- "hasTargetHosts": "Hat Zielhosts",
- "runOnTargets": "Auf Zielen ausführen",
- "directRunSuccess": "\"{{name}}\" auf {{count}} Host(s) ausgeführt",
- "directRunPartialFail": "\"{{name}}\" auf einem oder mehreren Hosts fehlgeschlagen",
- "executionResultTitle": "Ausführungsergebnisse: {{name}}",
- "executionResultDescription": "Ausgabe der Snippet-Ausführung auf jedem Zielhost.",
- "executionSuccess": "Erfolgreich",
- "executionFailed": "Fehlgeschlagen",
- "expandAll": "Alle Ordner erweitern",
- "collapseAll": "Alle Ordner ausblenden",
- "settings": "Snippet-Einstellungen",
- "variablesHint": "Variablen: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Run \"{{name}}\"",
- "variablesDialogDescription": "Füllen Sie die für diesen Codeabschnitt erforderlichen Werte aus, bevor Sie ihn ausführen.",
- "variablesPreviewLabel": "Vorschau",
- "variablesConfirmButton": "Laufen"
- },
"keybindings": {
"title": "Tastenkombinationen",
"description": "Passen Sie die Tastenkombinationen für Kopieren, Einfügen und Steuerung im Terminal an oder belegen Sie Tasten mit Befehlen zum Senden von Text oder zum Ausführen eines Code-Snippets.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klicken und drücken Sie die Tasten",
"recording": "Tasten drücken...",
"comboRequiredError": "Drücken Sie zuerst eine Tastenkombination",
- "textRequiredError": "Geben Sie den zu sendenden Text ein.",
- "controlCodeRequiredError": "Geben Sie einen einzelnen Buchstaben als Kontrollcode ein.",
- "snippetRequiredError": "Wählen Sie einen Ausschnitt aus",
"conflictWarning": "Diese Kombination ist bereits an {{combo}}gebunden. Durch Speichern werden beide ausgelöst.",
"actionLabel": "Aktion",
- "actionCopy": "Auswahl kopieren",
- "actionPaste": "Paste",
- "actionSendControlCode": "Sende das Signal Strg+Buchstabe",
- "actionSendText": "SMS senden",
- "actionRunSnippet": "Vorhandenen Codeausschnitt ausführen",
"actionNextTab": "Zum nächsten Tab wechseln",
"actionPreviousTab": "Zum vorherigen Tab wechseln",
- "actionOpenCommandPalette": "Schnellsuche öffnen",
- "controlCodeLabel": "Brief",
- "textLabel": "Text zum Senden",
- "appendEnterLabel": "Nach dem Absenden die Eingabetaste drücken",
- "snippetLabel": "Ausschnitt",
- "selectSnippetPlaceholder": "Wählen Sie einen Ausschnitt aus",
- "orphanedSnippetWarning": "Ausschnitt nicht gefunden",
- "clipboardPermissionNote": "Bei einigen Browsern kann es bei der ersten Verwendung zu einer Anfrage nach der Berechtigung zum Ablegen der Zwischenablage kommen."
+ "actionOpenCommandPalette": "Schnellsuche öffnen"
},
"userProfile": {
"donateTitle": "Termix unterstützen",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Topic ist erforderlich",
"ntfyToken": "Zugriffstoken (optional)",
"channelSaveFailed": "Kanal konnte nicht gespeichert werden"
- },
- "macros": {
- "title": "Makros",
- "description": "Führe eine gespeicherte Befehlsfolge in einem Terminal aus und reagiere auf die Antwort des Servers.",
- "target": "Ziel",
- "noTerminal": "Kein Terminal geöffnet",
- "empty": "Noch keine Makros",
- "emptyHint": "Erstellen Sie ein Makro, um einen Terminal-Workflow zu automatisieren, z. B. das Anmelden, das Neustarten eines Dienstes oder das Beantworten von Eingabeaufforderungen.",
- "create": "Neues Makro",
- "back": "Zurück zu den Makros",
- "name": "Name",
- "namePlaceholder": "Webserver neu starten",
- "nameRequired": "Gib dem Makro vor dem Speichern einen Namen.",
- "descriptionLabel": "Beschreibung",
- "descriptionPlaceholder": "Was dieses Makro bewirkt (optional)",
- "unsaved": "Nicht gespeicherte Änderungen",
- "save": "Speichern",
- "saved": "Makro gespeichert",
- "saveFailed": "Makro konnte nicht gespeichert werden",
- "delete": "Löschen",
- "deleteTitle": "Dieses Makro löschen?",
- "deleteBody": "\"{{name}}\" wird endgültig gelöscht. Dies kann nicht rückgängig gemacht werden.",
- "deleted": "Makro gelöscht",
- "cancel": "Stornieren",
- "run": "Laufen",
- "stop": "Stoppen",
- "running": "Läuft...",
- "completed": "Makro beendet",
- "failed": "Makro fehlgeschlagen",
- "loadFailed": "Makros konnten nicht geladen werden.",
- "needTerminal": "Öffnen und verbinden Sie zuerst ein Terminal.",
- "noSteps": "Dieses Makro hat noch keine Schritte. Füge unten einen hinzu.",
- "addStep": "Schritt hinzufügen",
- "stepNumber": "Schritt {{number}}",
- "removeStep": "Schritt entfernen",
- "confirmRunTitle": "Dieses Makro ausführen?",
- "confirmRunBody": "\"{{name}}\" sendet Befehle an {{host}}.",
- "confirmRunAction": "Makro ausführen",
- "stepTypes": {
- "send": {
- "label": "Befehl senden",
- "help": "Gibt den Text im Terminal ein, genau so, als ob Sie ihn eingetippt hätten."
- },
- "wait": {
- "label": "Auf Ausgabe warten",
- "help": "Die Ausgabe wird so lange unterbrochen, bis der Server den von Ihnen angegebenen Text ausgibt, beispielsweise eine Passwortabfrage."
- },
- "delay": {
- "label": "Pause",
- "help": "Wartet eine festgelegte Zeitspanne, bevor der nächste Schritt ausgeführt wird."
- },
- "if": {
- "label": "Wenn die Ausgabe enthält",
- "help": "Prüft alles, was der Server bisher ausgegeben hat, und führt dann einen von zwei Zweigen aus."
- },
- "repeat": {
- "label": "Wiederholen",
- "help": "Führt die darin enthaltenen Schritte eine festgelegte Anzahl von Malen aus."
- }
- },
- "send": {
- "text": "Befehl oder Text",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Nach dem Absenden die Eingabetaste drücken",
- "pressEnterHelp": "Deaktivieren Sie diese Option, um Text einzugeben, ohne ihn abzusenden."
- },
- "wait": {
- "pattern": "Warten Sie, bis die Ausgabe Folgendes enthält",
- "patternPlaceholder": "Passwort:",
- "timeout": "Aufgeben nach",
- "onTimeout": "Wenn es nie erscheint",
- "stop": "Stoppe das Makro",
- "continue": "Mach trotzdem weiter."
- },
- "delay": {
- "duration": "Warten Sie auf"
- },
- "ifStep": {
- "pattern": "Wenn die Ausgabe enthält",
- "patternPlaceholder": "Aktiv: aktiv (läuft)",
- "then": "Dann tu Folgendes:",
- "else": "Andernfalls tun Sie Folgendes:",
- "emptyBranch": "Keine Stufen"
- },
- "repeat": {
- "count": "Anzahl der Male",
- "times": "mal"
- },
- "match": {
- "useRegex": "Übereinstimmung mit einem regulären Ausdruck",
- "useRegexHelp": "Lassen Sie dies weg, damit der Text genau so bleibt, wie er geschrieben steht.",
- "invalidRegex": "Kein gültiger regulärer Ausdruck, wird als Klartext erkannt."
- },
- "seconds": "Sekunden",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/el_GR.json b/src/ui/locales/translated/el_GR.json
index 0de5b689a..9021ffc9d 100644
--- a/src/ui/locales/translated/el_GR.json
+++ b/src/ui/locales/translated/el_GR.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Ρυθμίσεις πλευρικής γραμμής...",
"hosts": "Οικοδεσπότες",
"snippets": "Αποσπάσματα",
- "macros": "Μακροεντολές",
"hostManager": "Διευθυντής Υποδοχής",
"credentials": "Διαπιστευτήρια",
"connections": "Συνδέσεις",
@@ -869,7 +868,6 @@
"groupTags": "Ετικέτες & καρφίτσα",
"groupTunnels": "Σήραγγες",
"groupJumpHosts": "Jump hosts",
- "groupQuickActions": "Γρήγορες ενέργειες",
"groupFeatureFlags": "Σημαίες χαρακτηριστικών",
"groupAdvanced": "Προηγμένη διαμόρφωση",
"preview": "Πρεμιέρα",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Αποθήκευση κλειδιών SSH ή κωδικών πρόσβασης",
"tmuxMonitor": "Παρακολούθηση Tmux",
"tmuxMonitorDesc": "Παρακολουθήστε τις συνεδρίες tmux σε όλους τους κεντρικούς υπολογιστές σας",
- "snippets": "Αποσπάσματα",
- "runSnippetDesc": "Εκτέλεση στην ενεργή καρτέλα τερματικού",
- "noSnippetsFound": "Δεν βρέθηκαν αποσπάσματα που να ταιριάζουν με το \"{{search}}\"",
"recentActivity": "Πρόσφατη δραστηριότητα",
"serversAndHosts": "Διακομιστές και κεντρικοί υπολογιστές",
"noHostsFound": "Δεν βρέθηκαν κεντρικοί υπολογιστές που να ταιριάζουν με το \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Προηγούμενη καρτέλα",
"hotkeysJumpTab": "Μετάβαση στην καρτέλα"
},
- "snippets": {
- "title": "Αποσπάσματα",
- "createSnippetTitle": "Δημιουργία αποσπάσματος",
- "createSnippetDescription": "Δημιουργήστε ένα νέο απόσπασμα εντολής για γρήγορη εκτέλεση",
- "nameLabel": "Ονομα",
- "namePlaceholder": "π.χ., Επανεκκίνηση Nginx",
- "descriptionLabel": "Περιγραφή",
- "descriptionPlaceholder": "Προαιρετική περιγραφή",
- "optional": "Προαιρετικός",
- "folderLabel": "Ντοσιέ",
- "noFolder": "Δεν υπάρχει φάκελος (Χωρίς κατηγορία)",
- "commandLabel": "Εντολή",
- "commandPlaceholder": "π.χ., sudo systemctl επανεκκίνηση nginx",
- "typeLabel": "Τύπος",
- "typeCommand": "Εντολή",
- "typeNote": "Σημείωμα",
- "noteLabel": "Σημείωμα",
- "notePlaceholder": "π.χ., ένα επαναχρησιμοποιήσιμο κείμενο προτροπής ή υπενθύμισης",
- "cancel": "Ματαίωση",
- "createSnippetButton": "Δημιουργία αποσπάσματος",
- "createFolderTitle": "Δημιουργία φακέλου",
- "createFolderDescription": "Οργανώστε τα αποσπάσματά σας σε φακέλους",
- "folderNameLabel": "Όνομα φακέλου",
- "folderNamePlaceholder": "π.χ., Εντολές συστήματος, Σενάρια Docker",
- "folderColorLabel": "Χρώμα φακέλου",
- "folderIconLabel": "Εικονίδιο φακέλου",
- "previewLabel": "Πρεμιέρα",
- "folderNameFallback": "Όνομα φακέλου",
- "createFolderButton": "Δημιουργία φακέλου",
- "targetTerminals": "Τερματικά Target",
- "selectAll": "Ολοι",
- "selectNone": "Κανένας",
- "noTerminalTabsOpen": "Δεν υπάρχουν ανοιχτές καρτέλες τερματικού",
- "searchPlaceholder": "Αναζήτηση αποσπασμάτων...",
- "newSnippet": "Νέο απόσπασμα",
- "newFolder": "Νέος φάκελος",
- "run": "Τρέξιμο",
- "noSnippetsInFolder": "Δεν υπάρχουν αποσπάσματα σε αυτόν τον φάκελο",
- "uncategorized": "Χωρίς κατηγορία",
- "editSnippetTitle": "Επεξεργασία αποσπάσματος",
- "editSnippetDescription": "Ενημέρωση αυτού του αποσπάσματος εντολής",
- "saveSnippetButton": "Αποθήκευση αλλαγών",
- "createSuccess": "Το απόσπασμα δημιουργήθηκε με επιτυχία",
- "createFailed": "Η δημιουργία αποσπάσματος απέτυχε",
- "updateSuccess": "Το απόσπασμα ενημερώθηκε με επιτυχία",
- "updateFailed": "Η ενημέρωση του αποσπάσματος απέτυχε",
- "deleteFailed": "Η διαγραφή του αποσπάσματος απέτυχε",
- "folderCreateSuccess": "Ο φάκελος δημιουργήθηκε με επιτυχία",
- "folderCreateFailed": "Η δημιουργία φακέλου απέτυχε",
- "editFolderTitle": "Επεξεργασία φακέλου",
- "editFolderDescription": "Μετονομασία ή αλλαγή της εμφάνισης αυτού του φακέλου",
- "saveFolderButton": "Αποθήκευση αλλαγών",
- "editFolder": "Επεξεργασία φακέλου",
- "deleteFolder": "Διαγραφή φακέλου",
- "folderDeleteSuccess": "Ο φάκελος \"{{name}}\" διαγράφηκε",
- "folderDeleteFailed": "Η διαγραφή του φακέλου απέτυχε",
- "folderEditSuccess": "Ο φάκελος ενημερώθηκε με επιτυχία.",
- "folderEditFailed": "Η ενημέρωση του φακέλου απέτυχε",
- "confirmRunMessage": "Εκτέλεση \"{{name}}\";",
- "confirmRunButton": "Τρέξιμο",
- "runSuccess": "Εκτέλεση \"{{name}}\" σε {{count}} τερματικό(α)",
- "pasteSuccess": "Επικολλήθηκε το \"{{name}}\" σε {{count}} τερματικό(α)",
- "pasteToTerminal": "Πάστα",
- "copySuccess": "Αντιγράφηκε το \"{{name}}\" στο πρόχειρο",
- "shareTitle": "Κοινή χρήση αποσπάσματος",
- "shareUser": "Μεταχειριζόμενος",
- "shareRole": "Ρόλος",
- "selectUser": "Επιλέξτε έναν χρήστη...",
- "selectRole": "Επιλέξτε έναν ρόλο...",
- "shareSuccess": "Το απόσπασμα κοινοποιήθηκε με επιτυχία",
- "shareFailed": "Η κοινοποίηση του αποσπάσματος απέτυχε",
- "revokeSuccess": "Η πρόσβαση ανακλήθηκε",
- "revokeFailed": "Η ανάκληση της πρόσβασης απέτυχε",
- "currentAccess": "Τρέχουσα πρόσβαση",
- "shareLoadError": "Η φόρτωση των δεδομένων κοινής χρήσης απέτυχε",
- "loading": "Φόρτωση...",
- "close": "Κοντά",
- "reorderFailed": "Η αποθήκευση της σειράς αποσπάσματος απέτυχε",
- "importExport": "Εισαγωγή / Εξαγωγή",
- "exportBtn": "Εξαγωγή JSON",
- "importBtn": "Εισαγωγή JSON",
- "exportSuccess": "Τα αποσπάσματα εξήχθησαν με επιτυχία",
- "exportFailed": "Η εξαγωγή αποσπασμάτων απέτυχε",
- "importTitle": "Εισαγωγή αποσπασμάτων",
- "importDescription": "Εισαγωγή αποσπασμάτων και φακέλων από ένα αρχείο JSON που εξάγεται από το Termix",
- "importDropOrClick": "Αποθέστε ένα αρχείο JSON εδώ ή κάντε κλικ για περιήγηση",
- "importSelectedFile": "Επιλεγμένα: {{name}}",
- "importOverwrite": "Αντικατάσταση υπαρχόντων αποσπασμάτων με το ίδιο όνομα και φάκελο",
- "importStartBtn": "Εισαγωγή",
- "importSuccess": "Η εισαγωγή ολοκληρώθηκε: Προστέθηκαν {{snippets}} αποσπάσματα, ενημερώθηκε {{updated}} , παραλείφθηκε {{skipped}} , προστέθηκαν {{folders}} φάκελοι",
- "importFailed": "Η εισαγωγή αποσπασμάτων απέτυχε",
- "importInvalidFile": "Μη έγκυρο αρχείο: αναμενόταν ένα αντικείμενο JSON με τμήματα κώδικα ή πίνακες φακέλων",
- "targetHostsLabel": "Στοχευμένοι οικοδεσπότες",
- "targetHostsHint": "Αναθέστε σε κεντρικούς υπολογιστές να εκτελούν αυτό το τμήμα κώδικα απευθείας χωρίς ανοιχτό τερματικό.",
- "noHostsAvailable": "Δεν έχουν ρυθμιστεί κεντρικοί υπολογιστές",
- "clearTargetHosts": "Διαγραφή όλων",
- "hasTargetHosts": "Έχει κεντρικούς υπολογιστές-στόχους",
- "runOnTargets": "Τρέξιμο σε στόχους",
- "directRunSuccess": "Εκτέλεση \"{{name}}\" σε {{count}} κεντρικό υπολογιστή (ή κεντρικούς υπολογιστές)",
- "directRunPartialFail": "Το \"{{name}}\" απέτυχε σε έναν ή περισσότερους κεντρικούς υπολογιστές",
- "executionResultTitle": "Αποτελέσματα εκτέλεσης: {{name}}",
- "executionResultDescription": "Έξοδος από την εκτέλεση του τμήματος κώδικα σε κάθε κεντρικό υπολογιστή προορισμού.",
- "executionSuccess": "Επιτυχία",
- "executionFailed": "Αποτυχημένος",
- "expandAll": "Ανάπτυξη όλων των φακέλων",
- "collapseAll": "Σύμπτυξη όλων των φακέλων",
- "settings": "Ρυθμίσεις αποσπάσματος",
- "variablesHint": "Μεταβλητές: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Εκτέλεση \"{{name}}\"",
- "variablesDialogDescription": "Συμπληρώστε τις τιμές που χρειάζεται αυτό το τμήμα κώδικα πριν το εκτελέσετε.",
- "variablesPreviewLabel": "Πρεμιέρα",
- "variablesConfirmButton": "Τρέξιμο"
- },
"keybindings": {
"title": "Συντομεύσεις πληκτρολογίου",
"description": "Προσαρμόστε τις συντομεύσεις αντιγραφής, επικόλλησης και ελέγχου τερματικού ή συνδέστε τα πλήκτρα για αποστολή κειμένου ή εκτέλεση ενός αποσπάσματος.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Κάντε κλικ και πατήστε τα πλήκτρα",
"recording": "Πατήστε τα πλήκτρα...",
"comboRequiredError": "Πατήστε πρώτα έναν συνδυασμό πλήκτρων",
- "textRequiredError": "Εισαγάγετε το κείμενο που θέλετε να στείλετε",
- "controlCodeRequiredError": "Εισαγάγετε ένα μόνο γράμμα για τον κωδικό ελέγχου",
- "snippetRequiredError": "Επιλέξτε ένα απόσπασμα",
"conflictWarning": "Αυτός ο συνδυασμός είναι ήδη συνδεδεμένος με {{combo}}. Η αποθήκευση θα ενεργοποιήσει και τα δύο.",
"actionLabel": "Δράση",
- "actionCopy": "Αντιγραφή επιλογής",
- "actionPaste": "Πάστα",
- "actionSendControlCode": "Αποστολή σήματος Ctrl+γράμμα",
- "actionSendText": "Αποστολή κειμένου",
- "actionRunSnippet": "Εκτέλεση υπάρχοντος αποσπάσματος",
"actionNextTab": "Μετάβαση στην επόμενη καρτέλα",
"actionPreviousTab": "Μετάβαση στην προηγούμενη καρτέλα",
- "actionOpenCommandPalette": "Άνοιγμα γρήγορης αναζήτησης",
- "controlCodeLabel": "Επιστολή",
- "textLabel": "Μήνυμα προς αποστολή",
- "appendEnterLabel": "Πατήστε Enter μετά την αποστολή",
- "snippetLabel": "Απόσπασμα",
- "selectSnippetPlaceholder": "Επιλέξτε ένα απόσπασμα",
- "orphanedSnippetWarning": "το απόσπασμα δεν βρέθηκε",
- "clipboardPermissionNote": "Ενδέχεται να ζητηθεί άδεια για το πρόχειρο κατά την πρώτη χρήση σε ορισμένα προγράμματα περιήγησης."
+ "actionOpenCommandPalette": "Άνοιγμα γρήγορης αναζήτησης"
},
"userProfile": {
"donateTitle": "Κρατήστε το Termix ζωντανό",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Το θέμα είναι υποχρεωτικό",
"ntfyToken": "Διακριτικό πρόσβασης (προαιρετικό)",
"channelSaveFailed": "Η αποθήκευση του καναλιού απέτυχε"
- },
- "macros": {
- "title": "Μακροεντολές",
- "description": "Εκτελέστε μια αποθηκευμένη σειρά εντολών σε ένα τερματικό και αντιδράστε σε ό,τι στέλνει πίσω ο διακομιστής.",
- "target": "Στόχος",
- "noTerminal": "Δεν υπάρχει ανοιχτό τερματικό",
- "empty": "Δεν υπάρχουν ακόμη μακροεντολές",
- "emptyHint": "Δημιουργήστε μια μακροεντολή για να αυτοματοποιήσετε μια ροή τερματικού, όπως σύνδεση, επανεκκίνηση μιας υπηρεσίας ή απάντηση σε μηνύματα.",
- "create": "Νέα μακροεντολή",
- "back": "Επιστροφή στις μακροεντολές",
- "name": "Ονομα",
- "namePlaceholder": "Επανεκκίνηση διακομιστή ιστού",
- "nameRequired": "Δώστε ένα όνομα στη μακροεντολή πριν από την αποθήκευση",
- "descriptionLabel": "Περιγραφή",
- "descriptionPlaceholder": "Τι κάνει αυτή η μακροεντολή (προαιρετικό)",
- "unsaved": "Μη αποθηκευμένες αλλαγές",
- "save": "Εκτός",
- "saved": "Η μακροεντολή αποθηκεύτηκε",
- "saveFailed": "Δεν ήταν δυνατή η αποθήκευση της μακροεντολής",
- "delete": "Διαγράφω",
- "deleteTitle": "Διαγραφή αυτής της μακροεντολής;",
- "deleteBody": "Το \"{{name}}\" θα διαγραφεί οριστικά. Δεν είναι δυνατή η αναίρεση αυτής της ενέργειας.",
- "deleted": "Η μακροεντολή διαγράφηκε",
- "cancel": "Ματαίωση",
- "run": "Τρέξιμο",
- "stop": "Στάση",
- "running": "Τρέξιμο...",
- "completed": "Η μακροεντολή ολοκληρώθηκε",
- "failed": "Η μακροεντολή απέτυχε.",
- "loadFailed": "Δεν ήταν δυνατή η φόρτωση μακροεντολών",
- "needTerminal": "Ανοίξτε και συνδέστε πρώτα ένα τερματικό",
- "noSteps": "Αυτή η μακροεντολή δεν έχει ακόμη βήματα. Προσθέστε ένα παρακάτω.",
- "addStep": "Προσθήκη βήματος",
- "stepNumber": "Βήμα {{number}}",
- "removeStep": "Αφαίρεση βήματος",
- "confirmRunTitle": "Εκτέλεση αυτής της μακροεντολής;",
- "confirmRunBody": "Η εντολή \"{{name}}\" θα στείλει εντολές στον χρήστη {{host}}.",
- "confirmRunAction": "Εκτέλεση μακροεντολής",
- "stepTypes": {
- "send": {
- "label": "Αποστολή εντολής",
- "help": "Πληκτρολογεί κείμενο στο τερματικό, όπως θα το γράφατε."
- },
- "wait": {
- "label": "Περιμένετε για έξοδο",
- "help": "Παύει μέχρι ο διακομιστής να εκτυπώσει το κείμενο που ονομάζετε, σαν να σας ζητείται κωδικός πρόσβασης."
- },
- "delay": {
- "label": "Παύση",
- "help": "Περιμένει ένα συγκεκριμένο χρονικό διάστημα πριν από το επόμενο βήμα."
- },
- "if": {
- "label": "Εάν η έξοδος περιέχει",
- "help": "Ελέγχει όλα όσα έχει εκτυπώσει μέχρι στιγμής ο διακομιστής και, στη συνέχεια, εκτελεί μία από τις δύο διακλαδώσεις."
- },
- "repeat": {
- "label": "Επαναλαμβάνω",
- "help": "Εκτελεί τα βήματα στο εσωτερικό του έναν καθορισμένο αριθμό φορών."
- }
- },
- "send": {
- "text": "Εντολή ή κείμενο",
- "textPlaceholder": "επανεκκίνηση systemctl nginx",
- "pressEnter": "Πατήστε Enter μετά την αποστολή",
- "pressEnterHelp": "Απενεργοποιήστε αυτήν τη λειτουργία για να πληκτρολογείτε κείμενο χωρίς να το υποβάλλετε."
- },
- "wait": {
- "pattern": "Περιμένετε μέχρι η έξοδος να περιέχει",
- "patternPlaceholder": "σύνθημα:",
- "timeout": "Παραιτήσου μετά",
- "onTimeout": "Αν δεν εμφανιστεί ποτέ",
- "stop": "Διακοπή της μακροεντολής",
- "continue": "Συνέχισε ούτως ή άλλως"
- },
- "delay": {
- "duration": "Περιμένω για"
- },
- "ifStep": {
- "pattern": "Εάν η έξοδος περιέχει",
- "patternPlaceholder": "Ενεργό: ενεργό (σε λειτουργία)",
- "then": "Στη συνέχεια, κάντε αυτό",
- "else": "Διαφορετικά, κάντε αυτό",
- "emptyBranch": "Χωρίς σκαλοπάτια"
- },
- "repeat": {
- "count": "Αριθμός φορών",
- "times": "φορές"
- },
- "match": {
- "useRegex": "Ταίριασμα με μια κανονική έκφραση",
- "useRegexHelp": "Αφήστε το απενεργοποιημένο για να ταιριάζει ακριβώς με το κείμενο όπως είναι γραμμένο.",
- "invalidRegex": "Δεν είναι έγκυρη κανονική έκφραση, θα αντιστοιχιστεί ως απλό κείμενο"
- },
- "seconds": "δευτερόλεπτα",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/es_ES.json b/src/ui/locales/translated/es_ES.json
index fff920ec3..3a196aea3 100644
--- a/src/ui/locales/translated/es_ES.json
+++ b/src/ui/locales/translated/es_ES.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Configuración de la barra lateral...",
"hosts": "Hosts",
"snippets": "Fragmentos",
- "macros": "Macros",
"hostManager": "Gestor de hosts",
"credentials": "Credenciales",
"connections": "Conexiones",
@@ -869,7 +868,6 @@
"groupTags": "Etiquetas y pines",
"groupTunnels": "Túneles",
"groupJumpHosts": "Anfitriones de salto",
- "groupQuickActions": "Acciones rápidas",
"groupFeatureFlags": "Banderas de características",
"groupAdvanced": "Configuración avanzada",
"preview": "Avance",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Almacenar claves SSH o contraseñas",
"tmuxMonitor": "Monitor de Tmux",
"tmuxMonitorDesc": "Supervisar sesiones de tmux en sus hosts",
- "snippets": "Fragmentos",
- "runSnippetDesc": "Ejecutar en la pestaña de terminal activa",
- "noSnippetsFound": "No se encontraron fragmentos que coincidieran con \"{{search}}\"",
"recentActivity": "Actividad reciente",
"serversAndHosts": "Servidores y hosts",
"noHostsFound": "No se encontraron hosts que coincidan con \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Pestaña anterior",
"hotkeysJumpTab": "Ir a la pestaña"
},
- "snippets": {
- "title": "Fragmentos",
- "createSnippetTitle": "Crear fragmento",
- "createSnippetDescription": "Crear un nuevo fragmento de comando para ejecución rápida",
- "nameLabel": "Nombre",
- "namePlaceholder": "p. ej., Reiniciar Nginx",
- "descriptionLabel": "Descripción",
- "descriptionPlaceholder": "Descripción opcional",
- "optional": "Opcional",
- "folderLabel": "Carpeta",
- "noFolder": "Sin carpeta (Sin categoría)",
- "commandLabel": "Comando",
- "commandPlaceholder": "p. ej., sudo systemctl restart nginx",
- "typeLabel": "Tipo",
- "typeCommand": "Dominio",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "Por ejemplo, un texto de recordatorio o aviso reutilizable.",
- "cancel": "Cancelar",
- "createSnippetButton": "Crear fragmento",
- "createFolderTitle": "Crear carpeta",
- "createFolderDescription": "Organiza tus fragmentos en carpetas",
- "folderNameLabel": "Nombre de la carpeta",
- "folderNamePlaceholder": "p. ej., Comandos del sistema, Scripts Docker",
- "folderColorLabel": "Color de la carpeta",
- "folderIconLabel": "Icono de la carpeta",
- "previewLabel": "Vista previa",
- "folderNameFallback": "Nombre de la carpeta",
- "createFolderButton": "Crear carpeta",
- "targetTerminals": "Terminales de destino",
- "selectAll": "Todos",
- "selectNone": "Ninguno",
- "noTerminalTabsOpen": "No hay pestañas de terminal abiertas",
- "searchPlaceholder": "Buscar fragmentos...",
- "newSnippet": "Nuevo fragmento",
- "newFolder": "Nueva carpeta",
- "run": "Ejecutar",
- "noSnippetsInFolder": "No hay fragmentos en esta carpeta",
- "uncategorized": "Sin categorizar",
- "editSnippetTitle": "Editar fragmento",
- "editSnippetDescription": "Actualizar este fragmento de comando",
- "saveSnippetButton": "Guardar cambios",
- "createSuccess": "Fragmento creado correctamente",
- "createFailed": "No se pudo crear el fragmento",
- "updateSuccess": "Fragmento actualizado correctamente",
- "updateFailed": "No se pudo actualizar el fragmento",
- "deleteFailed": "No se pudo eliminar el fragmento",
- "folderCreateSuccess": "Carpeta creada correctamente",
- "folderCreateFailed": "No se pudo crear la carpeta",
- "editFolderTitle": "Editar carpeta",
- "editFolderDescription": "Renombrar o cambiar la apariencia de esta carpeta",
- "saveFolderButton": "Guardar cambios",
- "editFolder": "Editar carpeta",
- "deleteFolder": "Eliminar carpeta",
- "folderDeleteSuccess": "Carpeta \"{{name}}\" eliminada",
- "folderDeleteFailed": "No se pudo eliminar la carpeta",
- "folderEditSuccess": "Carpeta actualizada correctamente",
- "folderEditFailed": "No se pudo actualizar la carpeta",
- "confirmRunMessage": "¿Ejecutar \"{{name}}\"?",
- "confirmRunButton": "Ejecutar",
- "runSuccess": "Se ejecutó \"{{name}}\" en {{count}} terminal(es)",
- "pasteSuccess": "Se pegó \"{{name}}\" en {{count}} terminal(es)",
- "pasteToTerminal": "Pasta",
- "copySuccess": "\"{{name}}\" copiado al portapapeles",
- "shareTitle": "Compartir fragmento",
- "shareUser": "Usuario",
- "shareRole": "Rol",
- "selectUser": "Seleccionar un usuario...",
- "selectRole": "Seleccionar un rol...",
- "shareSuccess": "Fragmento compartido correctamente",
- "shareFailed": "Error al compartir el fragmento",
- "revokeSuccess": "Acceso revocado",
- "revokeFailed": "Error al revocar el acceso",
- "currentAccess": "Acceso actual",
- "shareLoadError": "Error al cargar los datos de compartición",
- "loading": "Cargando...",
- "close": "Cerrar",
- "reorderFailed": "Error al guardar el orden de los fragmentos",
- "importExport": "Importar / Exportar",
- "exportBtn": "Exportar JSON",
- "importBtn": "Importar JSON",
- "exportSuccess": "Fragmentos exportados correctamente",
- "exportFailed": "Error al exportar los fragmentos",
- "importTitle": "Importar fragmentos",
- "importDescription": "Importar fragmentos y carpetas desde un archivo JSON exportado por Termix",
- "importDropOrClick": "Suelta un archivo JSON aquí o haz clic para explorar",
- "importSelectedFile": "Seleccionado: {{name}}",
- "importOverwrite": "Sobrescribir fragmentos existentes con el mismo nombre y carpeta",
- "importStartBtn": "Importar",
- "importSuccess": "Importación completa: {{snippets}} fragmento(s) añadido(s), {{updated}} actualizado(s), {{skipped}} omitido(s), {{folders}} carpeta(s) añadida(s)",
- "importFailed": "Error al importar los fragmentos",
- "importInvalidFile": "Archivo no válido: se esperaba un objeto JSON con arrays de fragmentos o carpetas",
- "targetHostsLabel": "Hosts de destino",
- "targetHostsHint": "Asignar hosts para ejecutar este fragmento directamente sin una terminal abierta.",
- "noHostsAvailable": "No hay hosts configurados",
- "clearTargetHosts": "Limpiar todo",
- "hasTargetHosts": "Tiene hosts de destino",
- "runOnTargets": "Ejecutar en destinos",
- "directRunSuccess": "Se ejecutó \"{{name}}\" en {{count}} host(s)",
- "directRunPartialFail": "\"{{name}}\" falló en uno o más hosts",
- "executionResultTitle": "Resultados de ejecución: {{name}}",
- "executionResultDescription": "Salida de la ejecución del snippet en cada host de destino.",
- "executionSuccess": "Éxito",
- "executionFailed": "Fallido",
- "expandAll": "Expandir todas las carpetas",
- "collapseAll": "Contraer todas las carpetas",
- "settings": "Configuración del fragmento",
- "variablesHint": "Variables: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Ejecutar \"{{name}}\"",
- "variablesDialogDescription": "Rellena los valores que necesita este fragmento de código antes de ejecutarlo.",
- "variablesPreviewLabel": "Avance",
- "variablesConfirmButton": "Correr"
- },
"keybindings": {
"title": "Atajos de teclado",
"description": "Personaliza los atajos de teclado para copiar, pegar y controlar funciones del terminal, o asigna teclas para enviar texto o ejecutar un fragmento de código.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Haz clic y pulsa las teclas",
"recording": "Pulsa las teclas...",
"comboRequiredError": "Primero, pulse una combinación de teclas.",
- "textRequiredError": "Introduce el texto que deseas enviar",
- "controlCodeRequiredError": "Introduzca una sola letra para el código de control.",
- "snippetRequiredError": "Seleccione un fragmento",
"conflictWarning": "Esta combinación ya está vinculada a {{combo}}. Guardar hará que ambos se activen.",
"actionLabel": "Acción",
- "actionCopy": "Copiar selección",
- "actionPaste": "Pasta",
- "actionSendControlCode": "Enviar señal Ctrl+letra",
- "actionSendText": "Enviar mensaje de texto",
- "actionRunSnippet": "Ejecutar el fragmento existente",
"actionNextTab": "Cambiar a la siguiente pestaña",
"actionPreviousTab": "Volver a la pestaña anterior",
- "actionOpenCommandPalette": "Abrir búsqueda rápida",
- "controlCodeLabel": "Carta",
- "textLabel": "Texto para enviar",
- "appendEnterLabel": "Presione Enter después de enviar",
- "snippetLabel": "Retazo",
- "selectSnippetPlaceholder": "Seleccione un fragmento",
- "orphanedSnippetWarning": "Fragmento no encontrado",
- "clipboardPermissionNote": "En algunos navegadores, puede solicitar permiso para copiar y pegar el contenido del portapapeles la primera vez que se utilice."
+ "actionOpenCommandPalette": "Abrir búsqueda rápida"
},
"userProfile": {
"donateTitle": "Mantener Termix vivo",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "El tema es obligatorio",
"ntfyToken": "Token de acceso (opcional)",
"channelSaveFailed": "No se pudo guardar el canal"
- },
- "macros": {
- "title": "Macros",
- "description": "Ejecuta una serie de comandos guardados en una terminal y reacciona a la respuesta que envíe el servidor.",
- "target": "Objetivo",
- "noTerminal": "No hay terminal abierta",
- "empty": "Aún no hay macros",
- "emptyHint": "Crea una macro para automatizar un flujo de terminal, como iniciar sesión, reiniciar un servicio o responder a preguntas.",
- "create": "Nueva macro",
- "back": "Volver a las macros",
- "name": "Nombre",
- "namePlaceholder": "Reiniciar el servidor web",
- "nameRequired": "Dale un nombre a la macro antes de guardarla.",
- "descriptionLabel": "Descripción",
- "descriptionPlaceholder": "Qué hace esta macro (opcional)",
- "unsaved": "Cambios no guardados",
- "save": "Ahorrar",
- "saved": "Macro guardada",
- "saveFailed": "No se pudo guardar la macro",
- "delete": "Borrar",
- "deleteTitle": "¿Eliminar esta macro?",
- "deleteBody": "\"{{name}}\" se eliminará permanentemente. Esto no se puede deshacer.",
- "deleted": "Macro eliminada",
- "cancel": "Cancelar",
- "run": "Correr",
- "stop": "Detener",
- "running": "Correr...",
- "completed": "Macro terminado",
- "failed": "La macro falló",
- "loadFailed": "No se pudieron cargar las macros",
- "needTerminal": "Primero, abre y conecta una terminal.",
- "noSteps": "Esta macro aún no tiene pasos. Añada uno a continuación.",
- "addStep": "Agregar paso",
- "stepNumber": "Paso {{number}}",
- "removeStep": "Eliminar paso",
- "confirmRunTitle": "¿Ejecutar esta macro?",
- "confirmRunBody": "\"{{name}}\" enviará comandos a {{host}}.",
- "confirmRunAction": "Ejecutar macro",
- "stepTypes": {
- "send": {
- "label": "Enviar comando",
- "help": "Escribe el texto en la terminal, igual que si lo hubieras escrito tú."
- },
- "wait": {
- "label": "Espere la salida",
- "help": "Hace una pausa hasta que el servidor imprime el texto que usted especifique, como si fuera una solicitud de contraseña."
- },
- "delay": {
- "label": "Pausa",
- "help": "Espera un tiempo fijo antes de pasar al siguiente paso."
- },
- "if": {
- "label": "Si la salida contiene",
- "help": "Comprueba todo lo que el servidor ha impreso hasta el momento y, a continuación, ejecuta una de las dos ramas."
- },
- "repeat": {
- "label": "Repetir",
- "help": "Reproduce los pasos que contiene un número determinado de veces."
- }
- },
- "send": {
- "text": "Comando o texto",
- "textPlaceholder": "systemctl reiniciar nginx",
- "pressEnter": "Presione Enter después de enviar",
- "pressEnterHelp": "Desactive esta opción para escribir texto sin enviarlo."
- },
- "wait": {
- "pattern": "Espere hasta que la salida contenga",
- "patternPlaceholder": "contraseña:",
- "timeout": "Rendirse después",
- "onTimeout": "Si nunca aparece",
- "stop": "Detén la macro",
- "continue": "Sigue adelante de todos modos."
- },
- "delay": {
- "duration": "Esperar"
- },
- "ifStep": {
- "pattern": "Si la salida contiene",
- "patternPlaceholder": "Activo: activo (corriendo)",
- "then": "Entonces haz esto",
- "else": "De lo contrario, haz esto",
- "emptyBranch": "Sin escalones"
- },
- "repeat": {
- "count": "Número de veces",
- "times": "veces"
- },
- "match": {
- "useRegex": "Coincidencia con una expresión regular",
- "useRegexHelp": "Omita esta opción para que coincida exactamente con el texto escrito.",
- "invalidRegex": "No es una expresión regular válida, se interpretará como texto plano."
- },
- "seconds": "artículos de segunda clase",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/fi_FI.json b/src/ui/locales/translated/fi_FI.json
index d2187010b..90af8afab 100644
--- a/src/ui/locales/translated/fi_FI.json
+++ b/src/ui/locales/translated/fi_FI.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Sivupalkin asetukset...",
"hosts": "Isännät",
"snippets": "Katkelmat",
- "macros": "Makrot",
"hostManager": "Isäntäpäällikkö",
"credentials": "Valtakirjat",
"connections": "Yhteydet",
@@ -869,7 +868,6 @@
"groupTags": "Tunnisteet ja kiinnitys",
"groupTunnels": "Tunnelit",
"groupJumpHosts": "Jump-isännät",
- "groupQuickActions": "Pikatoiminnot",
"groupFeatureFlags": "Ominaisuusliput",
"groupAdvanced": "Lisäasetukset",
"preview": "Esikatselu",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH-avainten tai salasanojen tallentaminen",
"tmuxMonitor": "Tmux-näyttö",
"tmuxMonitorDesc": "Valvo tmux-istuntoja eri isännilläsi",
- "snippets": "Katkelmat",
- "runSnippetDesc": "Suorita aktiivisessa päätevälilehdessä",
- "noSnippetsFound": "Ei löytynyt hakutuloksia, jotka vastaavat \"{{search}}\"",
"recentActivity": "Viimeaikaiset toiminnot",
"serversAndHosts": "Palvelimet ja isännät",
"noHostsFound": "Ei löytynyt isäntiä, jotka vastaavat \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Edellinen välilehti",
"hotkeysJumpTab": "Siirry välilehdelle"
},
- "snippets": {
- "title": "Katkelmat",
- "createSnippetTitle": "Luo katkelma",
- "createSnippetDescription": "Luo uusi komentokehotteen nopeaa suorittamista varten",
- "nameLabel": "Nimi",
- "namePlaceholder": "esim. Käynnistä Nginx uudelleen",
- "descriptionLabel": "Kuvaus",
- "descriptionPlaceholder": "Valinnainen kuvaus",
- "optional": "Valinnainen",
- "folderLabel": "Kansio",
- "noFolder": "Ei kansiota (Luokittelematon)",
- "commandLabel": "Komento",
- "commandPlaceholder": "esim. sudo systemctl käynnistä nginx uudelleen",
- "typeLabel": "Tyyppi",
- "typeCommand": "Komento",
- "typeNote": "Huomautus",
- "noteLabel": "Huomautus",
- "notePlaceholder": "esim. uudelleenkäytettävä kehote tai muistutusteksti",
- "cancel": "Peruuttaa",
- "createSnippetButton": "Luo katkelma",
- "createFolderTitle": "Luo kansio",
- "createFolderDescription": "Järjestä katkelmasi kansioihin",
- "folderNameLabel": "Kansion nimi",
- "folderNamePlaceholder": "esim. järjestelmäkomennot, Docker-skriptit",
- "folderColorLabel": "Kansion väri",
- "folderIconLabel": "Kansiokuvake",
- "previewLabel": "Esikatselu",
- "folderNameFallback": "Kansion nimi",
- "createFolderButton": "Luo kansio",
- "targetTerminals": "Kohdepäätteet",
- "selectAll": "Kaikki",
- "selectNone": "Ei mitään",
- "noTerminalTabsOpen": "Ei auki olevia päätevälilehtiä",
- "searchPlaceholder": "Haku katkelmia...",
- "newSnippet": "Uusi katkelma",
- "newFolder": "Uusi kansio",
- "run": "Juokse",
- "noSnippetsInFolder": "Tässä kansiossa ei ole katkelmia",
- "uncategorized": "Luokittelematon",
- "editSnippetTitle": "Muokkaa katkelmaa",
- "editSnippetDescription": "Päivitä tämä komentokehotteen koodi",
- "saveSnippetButton": "Tallenna muutokset",
- "createSuccess": "Katkelma luotu onnistuneesti",
- "createFailed": "Katkelman luominen epäonnistui",
- "updateSuccess": "Katkelman päivitys onnistui",
- "updateFailed": "Katkelman päivittäminen epäonnistui",
- "deleteFailed": "Katkelman poistaminen epäonnistui",
- "folderCreateSuccess": "Kansio luotu onnistuneesti",
- "folderCreateFailed": "Kansion luominen epäonnistui",
- "editFolderTitle": "Muokkaa kansiota",
- "editFolderDescription": "Nimeä tämä kansio uudelleen tai muuta sen ulkoasua",
- "saveFolderButton": "Tallenna muutokset",
- "editFolder": "Muokkaa kansiota",
- "deleteFolder": "Poista kansio",
- "folderDeleteSuccess": "Kansio \"{{name}}\" poistettu",
- "folderDeleteFailed": "Kansion poistaminen epäonnistui",
- "folderEditSuccess": "Kansio päivitetty onnistuneesti",
- "folderEditFailed": "Kansion päivittäminen epäonnistui",
- "confirmRunMessage": "Suorittaa \"{{name}}\"?",
- "confirmRunButton": "Juokse",
- "runSuccess": "Suoritti komennon \"{{name}}\" {{count}} päätteessä/päätteissä",
- "pasteSuccess": "Liitti \"{{name}}\" {{count}} terminaaliin/terminaaleihin",
- "pasteToTerminal": "Liitä",
- "copySuccess": "Kopioitu \"{{name}}\" leikepöydälle",
- "shareTitle": "Jaa katkelma",
- "shareUser": "Käyttäjä",
- "shareRole": "Rooli",
- "selectUser": "Valitse käyttäjä...",
- "selectRole": "Valitse rooli...",
- "shareSuccess": "Katkelman jakaminen onnistui",
- "shareFailed": "Katkelman jakaminen epäonnistui",
- "revokeSuccess": "Pääsy peruutettu",
- "revokeFailed": "Käyttöoikeuden peruuttaminen epäonnistui",
- "currentAccess": "Nykyinen käyttöoikeus",
- "shareLoadError": "Jaettujen tietojen lataaminen epäonnistui",
- "loading": "Ladataan...",
- "close": "Lähellä",
- "reorderFailed": "Katkelmien järjestyksen tallentaminen epäonnistui",
- "importExport": "Tuonti / vienti",
- "exportBtn": "Vie JSON",
- "importBtn": "Tuo JSON",
- "exportSuccess": "Katkelmien vienti onnistui",
- "exportFailed": "Katkelmien vienti epäonnistui",
- "importTitle": "Tuo koodikatkelmia",
- "importDescription": "Tuo koodikatkelmia ja kansioita Termixin viemästä JSON-tiedostosta",
- "importDropOrClick": "Pudota JSON-tiedosto tähän tai napsauta selataksesi",
- "importSelectedFile": "Valittu: {{name}}",
- "importOverwrite": "Korvaa olemassa olevat koodinpätkät samalla nimellä ja kansiolla",
- "importStartBtn": "Tuoda",
- "importSuccess": "Tuonti valmis: {{snippets}} katkelma(a) lisätty, {{updated}} päivitetty, {{skipped}} ohitettu, {{folders}} kansio(a) lisätty",
- "importFailed": "Katkelmien tuonti epäonnistui",
- "importInvalidFile": "Virheellinen tiedosto: odotettiin JSON-objektia, jossa on koodikatkelmia tai kansiotaulukoita",
- "targetHostsLabel": "Kohdepalvelimet",
- "targetHostsHint": "Määritä isännät suorittamaan tämä koodinpätkä suoraan ilman avointa päätettä.",
- "noHostsAvailable": "Ei määritettyjä isäntiä",
- "clearTargetHosts": "Tyhjennä kaikki",
- "hasTargetHosts": "Sisältää kohdeisäntiä",
- "runOnTargets": "Juokse tavoitteilla",
- "directRunSuccess": "Suoritti komennon \"{{name}}\" {{count}} isäntäkoneella/isännillä",
- "directRunPartialFail": "\"{{name}}\" epäonnistui yhdellä tai useammalla isännällä",
- "executionResultTitle": "Suorituksen tulokset: {{name}}",
- "executionResultDescription": "Tuloste koodinpätkän suorittamisesta kullakin kohdeisännöllä.",
- "executionSuccess": "Menestys",
- "executionFailed": "Epäonnistui",
- "expandAll": "Laajenna kaikki kansiot",
- "collapseAll": "Pienennä kaikki kansiot",
- "settings": "Katkelman asetukset",
- "variablesHint": "Muuttujat: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Suorita \"{{name}}\"",
- "variablesDialogDescription": "Täytä tämän koodinpätkän tarvitsemat arvot ennen sen suorittamista.",
- "variablesPreviewLabel": "Esikatselu",
- "variablesConfirmButton": "Juokse"
- },
"keybindings": {
"title": "Pikanäppäimet",
"description": "Mukauta päätelaitteen kopiointi-, liittämis- ja ohjauspikakuvakkeita tai sido näppäimiä tekstin lähettämiseen tai koodinpätkän suorittamiseen.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Napsauta ja paina näppäimiä",
"recording": "Paina näppäimiä...",
"comboRequiredError": "Paina ensin näppäinyhdistelmää",
- "textRequiredError": "Kirjoita lähetettävä teksti",
- "controlCodeRequiredError": "Syötä yksi kirjain ohjauskoodille",
- "snippetRequiredError": "Valitse katkelma",
"conflictWarning": "Tämä yhdistelmä on jo sidottu kohtaan {{combo}}. Tallentaminen käynnistää molemmat.",
"actionLabel": "Toiminta",
- "actionCopy": "Kopioi valinta",
- "actionPaste": "Liitä",
- "actionSendControlCode": "Lähetä Ctrl+kirjain-signaali",
- "actionSendText": "Lähetä tekstiviesti",
- "actionRunSnippet": "Suorita olemassa oleva koodinpätkä",
"actionNextTab": "Vaihda seuraavaan välilehteen",
"actionPreviousTab": "Vaihda edelliseen välilehteen",
- "actionOpenCommandPalette": "Avaa pikahaku",
- "controlCodeLabel": "Kirje",
- "textLabel": "Lähetettävä tekstiviesti",
- "appendEnterLabel": "Paina Enter lähetyksen jälkeen",
- "snippetLabel": "Katkelma",
- "selectSnippetPlaceholder": "Valitse katkelma",
- "orphanedSnippetWarning": "katkelmaa ei löytynyt",
- "clipboardPermissionNote": "Saattaa pyytää leikepöydän käyttöoikeutta ensimmäisellä käyttökerralla joissakin selaimissa."
+ "actionOpenCommandPalette": "Avaa pikahaku"
},
"userProfile": {
"donateTitle": "Pidä Termix hengissä",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Aihe on pakollinen",
"ntfyToken": "Käyttöoikeustunnus (valinnainen)",
"channelSaveFailed": "Kanavan tallentaminen epäonnistui"
- },
- "macros": {
- "title": "Makrot",
- "description": "Suorita tallennettu komentosarja terminaalissa ja reagoi palvelimen lähettämiin komentoihin.",
- "target": "Kohde",
- "noTerminal": "Ei auki olevaa terminaalia",
- "empty": "Ei vielä makroja",
- "emptyHint": "Luo makro automatisoidaksesi päätetyönkulun, kuten sisäänkirjautumisen, palvelun uudelleenkäynnistyksen tai kehotteisiin vastaamisen.",
- "create": "Uusi makro",
- "back": "Takaisin makroihin",
- "name": "Nimi",
- "namePlaceholder": "Käynnistä web-palvelin uudelleen",
- "nameRequired": "Anna makrolle nimi ennen tallentamista",
- "descriptionLabel": "Kuvaus",
- "descriptionPlaceholder": "Mitä tämä makro tekee (valinnainen)",
- "unsaved": "Tallentamattomat muutokset",
- "save": "Tallentaa",
- "saved": "Makro tallennettu",
- "saveFailed": "Makroa ei voitu tallentaa",
- "delete": "Poistaa",
- "deleteTitle": "Poistetaanko tämä makro?",
- "deleteBody": "\"{{name}}\" poistetaan pysyvästi. Tätä ei voi perua.",
- "deleted": "Makro poistettu",
- "cancel": "Peruuttaa",
- "run": "Juokse",
- "stop": "Stop",
- "running": "Juoksemassa...",
- "completed": "Makro valmis",
- "failed": "Makro epäonnistui",
- "loadFailed": "Makrojen lataaminen epäonnistui",
- "needTerminal": "Avaa ja kytke ensin päätelaite",
- "noSteps": "Tällä makrolla ei ole vielä vaiheita. Lisää yksi alle.",
- "addStep": "Lisää vaihe",
- "stepNumber": "Vaihe {{number}}",
- "removeStep": "Poista vaihe",
- "confirmRunTitle": "Suoritetaanko tämä makro?",
- "confirmRunBody": "\"{{name}}\" lähettää komentoja {{host}}:een.",
- "confirmRunAction": "Suorita makro",
- "stepTypes": {
- "send": {
- "label": "Lähetä komento",
- "help": "Kirjoittaa tekstiä terminaaliin aivan kuin olisit kirjoittanut sen."
- },
- "wait": {
- "label": "Odota tulostetta",
- "help": "Pysähtyy, kunnes palvelin tulostaa nimeämäsi tekstin, kuten salasanakehotteen."
- },
- "delay": {
- "label": "Tauko",
- "help": "Odottaa tietyn ajan ennen seuraavaa vaihetta."
- },
- "if": {
- "label": "Jos tuloste sisältää",
- "help": "Tarkistaa kaiken, mitä palvelin on tähän mennessä tulostanut, ja suorittaa sitten toisen kahdesta haarasta."
- },
- "repeat": {
- "label": "Toistaa",
- "help": "Suorittaa sen sisällä olevat vaiheet tietyn määrän kertoja."
- }
- },
- "send": {
- "text": "Komento tai teksti",
- "textPlaceholder": "systemctl käynnistä nginx uudelleen",
- "pressEnter": "Paina Enter lähetyksen jälkeen",
- "pressEnterHelp": "Poista tämä käytöstä kirjoittaaksesi tekstiä lähettämättä sitä."
- },
- "wait": {
- "pattern": "Odota, kunnes tuloste sisältää",
- "patternPlaceholder": "salasana:",
- "timeout": "Luovuta jälkeenpäin",
- "onTimeout": "Jos se ei koskaan ilmesty",
- "stop": "Pysäytä makro",
- "continue": "Jatka joka tapauksessa"
- },
- "delay": {
- "duration": "Odota"
- },
- "ifStep": {
- "pattern": "Jos tuloste sisältää",
- "patternPlaceholder": "Aktiivinen: aktiivinen (käynnissä)",
- "then": "Tee sitten tämä",
- "else": "Muuten tee näin",
- "emptyBranch": "Ei portaita"
- },
- "repeat": {
- "count": "Kuinka monta kertaa",
- "times": "kertaa"
- },
- "match": {
- "useRegex": "Säännöllisen lausekkeen mukainen osuma",
- "useRegexHelp": "Jätä tämä pois, jotta teksti vastaa täysin kirjoitettua tekstiä.",
- "invalidRegex": "Ei kelvollinen säännöllinen lauseke, se tulkitaan pelkkänä tekstinä"
- },
- "seconds": "sekuntia",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/fr_FR.json b/src/ui/locales/translated/fr_FR.json
index db8568671..39ebba9d3 100644
--- a/src/ui/locales/translated/fr_FR.json
+++ b/src/ui/locales/translated/fr_FR.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Paramètres de la barre latérale...",
"hosts": "Hôtes",
"snippets": "Extraits",
- "macros": "Macros",
"hostManager": "Gestionnaire d'hôtes",
"credentials": "Identifiants",
"connections": "Connexions",
@@ -869,7 +868,6 @@
"groupTags": "Étiquettes et épingles",
"groupTunnels": "Tunnels",
"groupJumpHosts": "Hôtes de saut",
- "groupQuickActions": "Actions rapides",
"groupFeatureFlags": "drapeaux de fonctionnalités",
"groupAdvanced": "Configuration avancée",
"preview": "Aperçu",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Stocker des clés SSH ou des mots de passe",
"tmuxMonitor": "Moniteur Tmux",
"tmuxMonitorDesc": "Surveiller les sessions tmux sur vos hôtes",
- "snippets": "Extraits",
- "runSnippetDesc": "Exécuter dans l'onglet du terminal actif",
- "noSnippetsFound": "Aucun extrait trouvé correspondant à \"{{search}}\"",
"recentActivity": "Activité récente",
"serversAndHosts": "Serveurs et hôtes",
"noHostsFound": "Aucun hôte trouvé correspondant à \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Onglet précédent",
"hotkeysJumpTab": "Aller à l'onglet"
},
- "snippets": {
- "title": "Extraits",
- "createSnippetTitle": "Créer un extrait",
- "createSnippetDescription": "Créer un nouvel extrait de commande pour une exécution rapide",
- "nameLabel": "Nom",
- "namePlaceholder": "par ex., Redémarrer Nginx",
- "descriptionLabel": "Description",
- "descriptionPlaceholder": "Description facultative",
- "optional": "Facultatif",
- "folderLabel": "Dossier",
- "noFolder": "Aucun dossier (Non classé)",
- "commandLabel": "Commande",
- "commandPlaceholder": "par ex., sudo systemctl restart nginx",
- "typeLabel": "Taper",
- "typeCommand": "Commande",
- "typeNote": "Note",
- "noteLabel": "Note",
- "notePlaceholder": "par exemple, un texte d'invite ou de rappel réutilisable",
- "cancel": "Annuler",
- "createSnippetButton": "Créer l'extrait",
- "createFolderTitle": "Créer un dossier",
- "createFolderDescription": "Organisez vos extraits dans des dossiers",
- "folderNameLabel": "Nom du dossier",
- "folderNamePlaceholder": "par ex., Commandes système, Scripts Docker",
- "folderColorLabel": "Couleur du dossier",
- "folderIconLabel": "Icône du dossier",
- "previewLabel": "Aperçu",
- "folderNameFallback": "Nom du dossier",
- "createFolderButton": "Créer un dossier",
- "targetTerminals": "Terminaux cibles",
- "selectAll": "Tous",
- "selectNone": "Aucun",
- "noTerminalTabsOpen": "Aucun onglet de terminal ouvert",
- "searchPlaceholder": "Rechercher des snippets...",
- "newSnippet": "Nouveau snippet",
- "newFolder": "Nouveau dossier",
- "run": "Exécuter",
- "noSnippetsInFolder": "Aucun snippet dans ce dossier",
- "uncategorized": "Non classé",
- "editSnippetTitle": "Modifier le snippet",
- "editSnippetDescription": "Mettre à jour ce snippet de commande",
- "saveSnippetButton": "Enregistrer les modifications",
- "createSuccess": "Snippet créé avec succès",
- "createFailed": "Échec de la création du snippet",
- "updateSuccess": "Snippet mis à jour avec succès",
- "updateFailed": "Échec de la mise à jour du snippet",
- "deleteFailed": "Échec de la suppression du snippet",
- "folderCreateSuccess": "Dossier créé avec succès",
- "folderCreateFailed": "Échec de la création du dossier",
- "editFolderTitle": "Modifier le dossier",
- "editFolderDescription": "Renommer ou modifier l'apparence de ce dossier",
- "saveFolderButton": "Enregistrer les modifications",
- "editFolder": "Modifier le dossier",
- "deleteFolder": "Supprimer le dossier",
- "folderDeleteSuccess": "Dossier « {{name}} » supprimé",
- "folderDeleteFailed": "Échec de la suppression du dossier",
- "folderEditSuccess": "Dossier mis à jour avec succès",
- "folderEditFailed": "Échec de la mise à jour du dossier",
- "confirmRunMessage": "Exécuter « {{name}} » ?",
- "confirmRunButton": "Exécuter",
- "runSuccess": "Exécuté « {{name}} » dans {{count}} terminal(s)",
- "pasteSuccess": "Collé \"{{name}}\" dans le(s) terminal(s) {{count}}",
- "pasteToTerminal": "Coller",
- "copySuccess": "Copié « {{name}} » dans le presse-papiers",
- "shareTitle": "Partager le snippet",
- "shareUser": "Utilisateur",
- "shareRole": "Rôle",
- "selectUser": "Sélectionner un utilisateur...",
- "selectRole": "Sélectionner un rôle...",
- "shareSuccess": "Snippet partagé avec succès",
- "shareFailed": "Échec du partage du snippet",
- "revokeSuccess": "Accès révoqué",
- "revokeFailed": "Échec de la révocation de l’accès",
- "currentAccess": "Accès actuel",
- "shareLoadError": "Échec du chargement des données de partage",
- "loading": "Chargement...",
- "close": "Fermer",
- "reorderFailed": "Échec de l’enregistrement de l’ordre des snippets",
- "importExport": "Importer / Exporter",
- "exportBtn": "Exporter JSON",
- "importBtn": "Importer JSON",
- "exportSuccess": "Snippets exportés avec succès",
- "exportFailed": "Échec de l’exportation des snippets",
- "importTitle": "Importer des snippets",
- "importDescription": "Importer des snippets et des dossiers depuis un fichier JSON exporté par Termix",
- "importDropOrClick": "Déposez un fichier JSON ici ou cliquez pour parcourir",
- "importSelectedFile": "Sélectionné : {{name}}",
- "importOverwrite": "Écraser les snippets existants ayant le même nom et le même dossier",
- "importStartBtn": "Importer",
- "importSuccess": "Importation terminée : {{snippets}} snippet(s) ajouté(s), {{updated}} mis à jour, {{skipped}} ignoré(s), {{folders}} dossier(s) ajouté(s)",
- "importFailed": "Échec de l’importation des snippets",
- "importInvalidFile": "Fichier non valide : un objet JSON avec des tableaux « snippets » ou « folders » est attendu",
- "targetHostsLabel": "Hôtes cibles",
- "targetHostsHint": "Attribuez des hôtes pour exécuter ce snippet directement sans terminal ouvert.",
- "noHostsAvailable": "Aucun hôte configuré",
- "clearTargetHosts": "Tout effacer",
- "hasTargetHosts": "Avec hôtes cibles",
- "runOnTargets": "Exécuter sur les cibles",
- "directRunSuccess": "« {{name}} » exécuté sur {{count}} hôte(s)",
- "directRunPartialFail": "« {{name}} » a échoué sur un ou plusieurs hôtes",
- "executionResultTitle": "Résultats d'exécution : {{name}}",
- "executionResultDescription": "Sortie de l'exécution du snippet sur chaque hôte cible.",
- "executionSuccess": "Succès",
- "executionFailed": "Échec",
- "expandAll": "Développer tous les dossiers",
- "collapseAll": "Réduire tous les dossiers",
- "settings": "Paramètres de l'extrait",
- "variablesHint": "Variables : $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Exécutez \"{{name}}\"",
- "variablesDialogDescription": "Renseignez les valeurs nécessaires à cet extrait de code avant de l'exécuter.",
- "variablesPreviewLabel": "Aperçu",
- "variablesConfirmButton": "Courir"
- },
"keybindings": {
"title": "Raccourcis clavier",
"description": "Personnalisez les raccourcis de copie, de collage et de contrôle du terminal, ou associez des touches pour envoyer du texte ou exécuter un extrait de code.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Cliquez et appuyez sur les touches",
"recording": "Appuyez sur les touches...",
"comboRequiredError": "Appuyez d'abord sur une combinaison de touches",
- "textRequiredError": "Saisissez le texte à envoyer",
- "controlCodeRequiredError": "Saisissez une seule lettre pour le code de contrôle",
- "snippetRequiredError": "Sélectionnez un extrait",
"conflictWarning": "Cette combinaison est déjà liée à {{combo}}. Sauvegarder déclenchera les deux.",
"actionLabel": "Action",
- "actionCopy": "Copier la sélection",
- "actionPaste": "Coller",
- "actionSendControlCode": "Envoyer le signal Ctrl+lettre",
- "actionSendText": "Envoyer un SMS",
- "actionRunSnippet": "Exécuter l'extrait de code existant",
"actionNextTab": "Passer à l'onglet suivant",
"actionPreviousTab": "Passer à l'onglet précédent",
- "actionOpenCommandPalette": "Ouvrir la recherche rapide",
- "controlCodeLabel": "Lettre",
- "textLabel": "Texte à envoyer",
- "appendEnterLabel": "Appuyez sur Entrée après l'envoi",
- "snippetLabel": "Fragment",
- "selectSnippetPlaceholder": "Sélectionnez un extrait",
- "orphanedSnippetWarning": "extrait introuvable",
- "clipboardPermissionNote": "Il se peut que certains navigateurs demandent l'autorisation d'accéder au presse-papiers lors de la première utilisation."
+ "actionOpenCommandPalette": "Ouvrir la recherche rapide"
},
"userProfile": {
"donateTitle": "Soutenir Termix",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Le sujet est obligatoire",
"ntfyToken": "Jeton d'accès (facultatif)",
"channelSaveFailed": "Échec de l'enregistrement du canal"
- },
- "macros": {
- "title": "Macros",
- "description": "Exécutez une série de commandes enregistrées dans un terminal et réagissez à la réponse du serveur.",
- "target": "Cible",
- "noTerminal": "Aucun terminal ouvert",
- "empty": "Pas encore de macros",
- "emptyHint": "Créez une macro pour automatiser un flux de terminal, comme la connexion, le redémarrage d'un service ou la réponse à des invites.",
- "create": "Nouvelle macro",
- "back": "Retour aux macros",
- "name": "Nom",
- "namePlaceholder": "Redémarrer le serveur web",
- "nameRequired": "Donnez un nom à la macro avant de l'enregistrer.",
- "descriptionLabel": "Description",
- "descriptionPlaceholder": "Fonctionnement de cette macro (facultatif)",
- "unsaved": "Modifications non enregistrées",
- "save": "Sauvegarder",
- "saved": "Macro enregistrée",
- "saveFailed": "Impossible d'enregistrer la macro",
- "delete": "Supprimer",
- "deleteTitle": "Supprimer cette macro ?",
- "deleteBody": "\"{{name}}\" sera définitivement supprimé. Cette action est irréversible.",
- "deleted": "Macro supprimée",
- "cancel": "Annuler",
- "run": "Courir",
- "stop": "Arrêt",
- "running": "En cours d'exécution...",
- "completed": "Macro terminé",
- "failed": "Échec de la macro",
- "loadFailed": "Impossible de charger les macros",
- "needTerminal": "Ouvrez et connectez d'abord un terminal.",
- "noSteps": "Cette macro ne comporte pas encore d'étapes. Ajoutez-en une ci-dessous.",
- "addStep": "Ajouter une étape",
- "stepNumber": "Étape {{number}}",
- "removeStep": "Supprimer l'étape",
- "confirmRunTitle": "Exécuter cette macro ?",
- "confirmRunBody": "\"{{name}}\" enverra des commandes à {{host}}.",
- "confirmRunAction": "Exécuter une macro",
- "stepTypes": {
- "send": {
- "label": "Envoyer la commande",
- "help": "Saisit du texte dans le terminal, comme si vous l'aviez saisi manuellement."
- },
- "wait": {
- "label": "Attendez la sortie",
- "help": "Il y a une pause jusqu'à ce que le serveur affiche le texte que vous avez indiqué, comme une invite de mot de passe."
- },
- "delay": {
- "label": "Pause",
- "help": "Attendre un laps de temps fixe avant de passer à l'étape suivante."
- },
- "if": {
- "label": "Si la sortie contient",
- "help": "Vérifie tout ce que le serveur a imprimé jusqu'à présent, puis exécute l'une des deux branches."
- },
- "repeat": {
- "label": "Répéter",
- "help": "Exécute les étapes qu'il contient un nombre de fois défini."
- }
- },
- "send": {
- "text": "Commande ou texte",
- "textPlaceholder": "systemctl redémarrer nginx",
- "pressEnter": "Appuyez sur Entrée après l'envoi",
- "pressEnterHelp": "Désactivez cette option pour saisir du texte sans le soumettre."
- },
- "wait": {
- "pattern": "Attendez que la sortie contienne",
- "patternPlaceholder": "mot de passe:",
- "timeout": "Abandonner après",
- "onTimeout": "S'il n'apparaît jamais",
- "stop": "Arrêtez la macro",
- "continue": "Continuez quand même"
- },
- "delay": {
- "duration": "Attendre"
- },
- "ifStep": {
- "pattern": "Si la sortie contient",
- "patternPlaceholder": "Actif : actif (en cours d'exécution)",
- "then": "Ensuite, faites ceci",
- "else": "Sinon, faites ceci.",
- "emptyBranch": "Pas d'étapes"
- },
- "repeat": {
- "count": "Nombre de fois",
- "times": "fois"
- },
- "match": {
- "useRegex": "Correspondance avec une expression régulière",
- "useRegexHelp": "Laissez cette section de côté pour que le texte corresponde exactement à ce qui est écrit.",
- "invalidRegex": "Il ne s'agit pas d'une expression régulière valide ; elle sera interprétée comme du texte brut."
- },
- "seconds": "secondes",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/he_IL.json b/src/ui/locales/translated/he_IL.json
index 66c69b7d4..3f7e781f6 100644
--- a/src/ui/locales/translated/he_IL.json
+++ b/src/ui/locales/translated/he_IL.json
@@ -409,7 +409,6 @@
"sidebarSettings": "הגדרות סרגל צד...",
"hosts": "מארחים",
"snippets": "קטעי טקסט",
- "macros": "פקודות מאקרו",
"hostManager": "מנהל מארח",
"credentials": "אישורים",
"connections": "חיבורים",
@@ -869,7 +868,6 @@
"groupTags": "תגיות וסיכה",
"groupTunnels": "מנהרות",
"groupJumpHosts": "מארחים קפיציים",
- "groupQuickActions": "פעולות מהירות",
"groupFeatureFlags": "דגלי תכונה",
"groupAdvanced": "תצורה מתקדמת",
"preview": "תצוגה מקדימה",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "אחסון מפתחות או סיסמאות SSH",
"tmuxMonitor": "צג Tmux",
"tmuxMonitorDesc": "ניטור הפעלות tmux בין המארחים שלך",
- "snippets": "קטעי טקסט",
- "runSnippetDesc": "הפעל בלשונית הטרמינל הפעילה",
- "noSnippetsFound": "לא נמצאו קטעי טקסט תואמים ל-\"{{search}}\"",
"recentActivity": "פעילות אחרונה",
"serversAndHosts": "שרתים ומארחים",
"noHostsFound": "לא נמצאו מארחים התואמים ל-\"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "הכרטיסייה הקודמת",
"hotkeysJumpTab": "קפיצה לכרטיסייה"
},
- "snippets": {
- "title": "קטעי טקסט",
- "createSnippetTitle": "צור קטע",
- "createSnippetDescription": "צור קטע פקודה חדש לביצוע מהיר",
- "nameLabel": "שֵׁם",
- "namePlaceholder": "לדוגמה, הפעל מחדש את Nginx",
- "descriptionLabel": "תֵאוּר",
- "descriptionPlaceholder": "תיאור אופציונלי",
- "optional": "אופציונלי",
- "folderLabel": "תיקייה",
- "noFolder": "אין תיקייה (ללא קטגוריה)",
- "commandLabel": "פְּקוּדָה",
- "commandPlaceholder": "לדוגמה, sudo systemctl הפעל מחדש את nginx",
- "typeLabel": "סוּג",
- "typeCommand": "פְּקוּדָה",
- "typeNote": "פֶּתֶק",
- "noteLabel": "פֶּתֶק",
- "notePlaceholder": "לדוגמה, טקסט הנחיה או תזכורת לשימוש חוזר",
- "cancel": "לְבַטֵל",
- "createSnippetButton": "צור קטע",
- "createFolderTitle": "צור תיקייה",
- "createFolderDescription": "ארגנו את הקטעים שלכם בתיקיות",
- "folderNameLabel": "שם התיקייה",
- "folderNamePlaceholder": "לדוגמה, פקודות מערכת, סקריפטים של Docker",
- "folderColorLabel": "צבע התיקייה",
- "folderIconLabel": "סמל תיקייה",
- "previewLabel": "תצוגה מקדימה",
- "folderNameFallback": "שם התיקייה",
- "createFolderButton": "צור תיקייה",
- "targetTerminals": "טרמינלים של יעד",
- "selectAll": "כֹּל",
- "selectNone": "אַף לֹא אֶחָד",
- "noTerminalTabsOpen": "אין לשוניות מסוף פתוחות",
- "searchPlaceholder": "חיפוש קטעי טקסט...",
- "newSnippet": "קטע חדש",
- "newFolder": "תיקייה חדשה",
- "run": "לָרוּץ",
- "noSnippetsInFolder": "אין קטעי טקסט בתיקייה זו",
- "uncategorized": "ללא קטגוריה",
- "editSnippetTitle": "עריכת קטע",
- "editSnippetDescription": "עדכן את קטע הפקודה הזה",
- "saveSnippetButton": "שמירת שינויים",
- "createSuccess": "קטע הקוד נוצר בהצלחה",
- "createFailed": "נכשלה יצירת קטע הקוד",
- "updateSuccess": "קטע הקוד עודכן בהצלחה",
- "updateFailed": "נכשל עדכון הקטע",
- "deleteFailed": "מחיקת קטע הקוד נכשלה",
- "folderCreateSuccess": "תיקייה נוצרה בהצלחה",
- "folderCreateFailed": "יצירת התיקייה נכשלה",
- "editFolderTitle": "עריכת תיקייה",
- "editFolderDescription": "שינוי שם או שינוי מראה של תיקייה זו",
- "saveFolderButton": "שמירת שינויים",
- "editFolder": "עריכת תיקייה",
- "deleteFolder": "מחיקת תיקייה",
- "folderDeleteSuccess": "תיקייה \"{{name}}\" נמחקה",
- "folderDeleteFailed": "מחיקת התיקייה נכשלה",
- "folderEditSuccess": "התיקייה עודכנה בהצלחה",
- "folderEditFailed": "עדכון התיקייה נכשל",
- "confirmRunMessage": "להפעיל את \"{{name}}\"?",
- "confirmRunButton": "לָרוּץ",
- "runSuccess": "הריץ את \"{{name}}\" בטרמינל/ים {{count}}",
- "pasteSuccess": "הודבק \"{{name}}\" לתוך הטרמינל/ים {{count}}",
- "pasteToTerminal": "לְהַדבִּיק",
- "copySuccess": "הועתק \"{{name}}\" ללוח",
- "shareTitle": "שתף קטע",
- "shareUser": "מִשׁתַמֵשׁ",
- "shareRole": "תַפְקִיד",
- "selectUser": "בחר משתמש...",
- "selectRole": "בחר תפקיד...",
- "shareSuccess": "קטע הטקסט שותף בהצלחה",
- "shareFailed": "נכשל שיתוף הקטע",
- "revokeSuccess": "הגישה בוטלה",
- "revokeFailed": "ביטול הגישה נכשל",
- "currentAccess": "גישה נוכחית",
- "shareLoadError": "טעינת נתוני השיתוף נכשלה",
- "loading": "טְעִינָה...",
- "close": "לִסְגוֹר",
- "reorderFailed": "שמירת סדר הקטעים נכשלה",
- "importExport": "ייבוא / ייצוא",
- "exportBtn": "ייצוא JSON",
- "importBtn": "ייבוא JSON",
- "exportSuccess": "קטעי הטקסט יוצאו בהצלחה",
- "exportFailed": "ייצוא קטעי הקוד נכשל",
- "importTitle": "ייבוא קטעי טקסט",
- "importDescription": "ייבוא קטעי טקסט ותיקיות מקובץ JSON שיוצא על ידי Termix",
- "importDropOrClick": "שחררו קובץ JSON כאן או לחצו כדי לדפדף",
- "importSelectedFile": "נבחר: {{name}}",
- "importOverwrite": "החלפת קטעי טקסט קיימים עם אותו שם ותיקייה",
- "importStartBtn": "יְבוּא",
- "importSuccess": "ייבוא הושלם: קטעי טקסט {{snippets}} נוספו, {{updated}} עודכנו, {{skipped}} דילגו, {{folders}} תיקיות נוספו",
- "importFailed": "ייבוא קטעי הקוד נכשל",
- "importInvalidFile": "קובץ לא חוקי: ציפיתי לאובייקט JSON עם קטעי טקסט או מערכי תיקיות",
- "targetHostsLabel": "מארחי יעד",
- "targetHostsHint": "הקצה מארחים להפעלת קטע זה ישירות ללא טרמינל פתוח.",
- "noHostsAvailable": "לא הוגדרו מארחים",
- "clearTargetHosts": "נקה הכל",
- "hasTargetHosts": "יש מארחים יעד",
- "runOnTargets": "רוץ על מטרות",
- "directRunSuccess": "הריץ את \"{{name}}\" על מארחים {{count}}",
- "directRunPartialFail": "\"{{name}}\" נכשל במחשב מארח אחד או יותר",
- "executionResultTitle": "תוצאות ביצוע: {{name}}",
- "executionResultDescription": "פלט מהפעלת קטע הקוד על כל מארח יעד.",
- "executionSuccess": "הַצלָחָה",
- "executionFailed": "נִכשָׁל",
- "expandAll": "הרחב את כל התיקיות",
- "collapseAll": "כווץ את כל התיקיות",
- "settings": "הגדרות קטע",
- "variablesHint": "משתנים: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "הפעל \"{{name}}\"",
- "variablesDialogDescription": "מלא את הערכים הדרושים לקטע זה לפני הפעלתו.",
- "variablesPreviewLabel": "תצוגה מקדימה",
- "variablesConfirmButton": "לָרוּץ"
- },
"keybindings": {
"title": "קיצורי מקלדת",
"description": "התאם אישית קיצורי דרך להעתקה, הדבקה ובקרה של הטרמינל, או חבר מקשים לשליחת טקסט או להפעלת קטע קוד.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "לחיצה ולחיצה על המקשים",
"recording": "לחץ על מקשים...",
"comboRequiredError": "לחץ תחילה על צירוף מקשים",
- "textRequiredError": "הזן את הטקסט לשליחה",
- "controlCodeRequiredError": "הזן אות אחת עבור קוד הבקרה",
- "snippetRequiredError": "בחר קטע",
"conflictWarning": "צירוף זה כבר קשור ל- {{combo}}. שמירה תגרום לשניהם לפעול.",
"actionLabel": "פְּעוּלָה",
- "actionCopy": "העתקת הבחירה",
- "actionPaste": "לְהַדבִּיק",
- "actionSendControlCode": "שלח אות Ctrl+אות",
- "actionSendText": "שלח טקסט",
- "actionRunSnippet": "הפעלת קטע קוד קיים",
"actionNextTab": "עבור לכרטיסייה הבאה",
"actionPreviousTab": "מעבר לכרטיסייה הקודמת",
- "actionOpenCommandPalette": "פתח חיפוש מהיר",
- "controlCodeLabel": "מִכְתָב",
- "textLabel": "טקסט לשליחה",
- "appendEnterLabel": "לחץ על Enter לאחר השליחה",
- "snippetLabel": "קטע",
- "selectSnippetPlaceholder": "בחר קטע",
- "orphanedSnippetWarning": "קטע לא נמצא",
- "clipboardPermissionNote": "ייתכן שתבקש הרשאה ללוח בעת השימוש הראשון בדפדפנים מסוימים."
+ "actionOpenCommandPalette": "פתח חיפוש מהיר"
},
"userProfile": {
"donateTitle": "לשמור על טרמיקס בחיים",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "נושא נדרש",
"ntfyToken": "אסימון גישה (אופציונלי)",
"channelSaveFailed": "שמירת הערוץ נכשלה"
- },
- "macros": {
- "title": "פקודות מאקרו",
- "description": "הפעל סדרה שמורה של פקודות בטרמינל, והגב למה שהשרת שולח בחזרה.",
- "target": "יַעַד",
- "noTerminal": "אין טרמינל פתוח",
- "empty": "עדיין אין פקודות מאקרו",
- "emptyHint": "צור מאקרו כדי להפוך זרימת מסוף לאוטומטית, כמו כניסה, הפעלה מחדש של שירות או מענה לבקשות.",
- "create": "מאקרו חדש",
- "back": "חזרה למאקרו",
- "name": "שֵׁם",
- "namePlaceholder": "הפעל מחדש את שרת האינטרנט",
- "nameRequired": "תן שם למאקרו לפני השמירה",
- "descriptionLabel": "תֵאוּר",
- "descriptionPlaceholder": "מה המאקרו הזה עושה (אופציונלי)",
- "unsaved": "שינויים שלא נשמרו",
- "save": "לְהַצִיל",
- "saved": "מאקרו נשמר",
- "saveFailed": "לא ניתן היה לשמור את המאקרו",
- "delete": "לִמְחוֹק",
- "deleteTitle": "למחוק את המאקרו הזה?",
- "deleteBody": "\"{{name}}\" יימחק לצמיתות. לא ניתן לבטל פעולה זו.",
- "deleted": "מאקרו נמחק",
- "cancel": "לְבַטֵל",
- "run": "לָרוּץ",
- "stop": "לְהַפְסִיק",
- "running": "רִיצָה...",
- "completed": "מאקרו הסתיים",
- "failed": "מאקרו נכשל",
- "loadFailed": "לא ניתן היה לטעון פקודות מאקרו",
- "needTerminal": "פתח וחבר תחילה טרמינל",
- "noSteps": "למאקרו הזה עדיין אין שלבים. הוסף אחד למטה.",
- "addStep": "הוסף שלב",
- "stepNumber": "שלב {{number}}",
- "removeStep": "הסר שלב",
- "confirmRunTitle": "להפעיל את המאקרו הזה?",
- "confirmRunBody": "\"{{name}}\" ישלח פקודות אל {{host}}.",
- "confirmRunAction": "הפעל מאקרו",
- "stepTypes": {
- "send": {
- "label": "שלח פקודה",
- "help": "מקליד טקסט לתוך הטרמינל, אותו הדבר כאילו הקלדת אותו."
- },
- "wait": {
- "label": "המתן לפלט",
- "help": "מושהה עד שהשרת ידפיס את הטקסט שציינת, כמו בקשת סיסמה."
- },
- "delay": {
- "label": "הַפסָקָה",
- "help": "ממתין פרק זמן קבוע לפני השלב הבא."
- },
- "if": {
- "label": "אם הפלט מכיל",
- "help": "בודק את כל מה שהשרת הדפיס עד כה, ולאחר מכן מפעיל אחד משני ענפים."
- },
- "repeat": {
- "label": "לַחֲזוֹר עַל",
- "help": "מריץ את השלבים בתוכו מספר קבוע של פעמים."
- }
- },
- "send": {
- "text": "פקודה או טקסט",
- "textPlaceholder": "הפעל מחדש את nginx של systemctl",
- "pressEnter": "לחץ על Enter לאחר השליחה",
- "pressEnterHelp": "כבה אפשרות זו כדי להקליד טקסט מבלי לשלוח אותו."
- },
- "wait": {
- "pattern": "המתן עד שהפלט יכיל",
- "patternPlaceholder": "סִיסמָה:",
- "timeout": "לוותר אחרי",
- "onTimeout": "אם זה אף פעם לא מופיע",
- "stop": "עצור את המאקרו",
- "continue": "המשיכו בכל מקרה"
- },
- "delay": {
- "duration": "חכה ל"
- },
- "ifStep": {
- "pattern": "אם הפלט מכיל",
- "patternPlaceholder": "פעיל: פעיל (רץ)",
- "then": "אז תעשה את זה",
- "else": "אחרת תעשו את זה",
- "emptyBranch": "אין מדרגות"
- },
- "repeat": {
- "count": "מספר פעמים",
- "times": "פִּי"
- },
- "match": {
- "useRegex": "התאמה עם ביטוי רגולרי",
- "useRegexHelp": "השאר את זה כבוי כדי שהטקסט יתאים בדיוק כפי שנכתב.",
- "invalidRegex": "ביטוי רגולרי לא תקין, הוא יתאים כטקסט רגיל"
- },
- "seconds": "שניות",
- "secondsShort": "{{count}}ים"
}
}
diff --git a/src/ui/locales/translated/hi_IN.json b/src/ui/locales/translated/hi_IN.json
index 9188c2887..9afae74db 100644
--- a/src/ui/locales/translated/hi_IN.json
+++ b/src/ui/locales/translated/hi_IN.json
@@ -409,7 +409,6 @@
"sidebarSettings": "साइडबार सेटिंग्स...",
"hosts": "होस्ट्स",
"snippets": "स्निपेट्स",
- "macros": "मैक्रो",
"hostManager": "होस्ट प्रबंधक",
"credentials": "क्रेडेंशियल्स",
"connections": "कनेक्शन्स",
@@ -869,7 +868,6 @@
"groupTags": "टैग और पिन",
"groupTunnels": "सुरंगों",
"groupJumpHosts": "जंप होस्ट",
- "groupQuickActions": "त्वरित कार्रवाइयां",
"groupFeatureFlags": "फ़ीचर फ़्लैग",
"groupAdvanced": "उन्नत कॉन्फ़िगरेशन",
"preview": "पूर्व दर्शन",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH कुंजी या पासवर्ड संग्रहीत करें",
"tmuxMonitor": "Tmux मॉनिटर",
"tmuxMonitorDesc": "अपने होस्ट में tmux सत्रों की निगरानी करें",
- "snippets": "स्निपेट्स",
- "runSnippetDesc": "सक्रिय टर्मिनल टैब में चलाएँ",
- "noSnippetsFound": "\"{{search}} \" से मेल खाने वाले कोई अंश नहीं मिले",
"recentActivity": "हाल की गतिविधि",
"serversAndHosts": "सर्वर और होस्ट",
"noHostsFound": "\"{{search}}\" से मेल खाने वाला कोई होस्ट नहीं मिला",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "पिछला टैब",
"hotkeysJumpTab": "टैब पर जाएं"
},
- "snippets": {
- "title": "स्निपेट्स",
- "createSnippetTitle": "स्निपेट बनाएं",
- "createSnippetDescription": "त्वरित निष्पादन के लिए एक नया कमांड स्निपेट बनाएं",
- "nameLabel": "नाम",
- "namePlaceholder": "जैसे, Nginx पुनरारंभ करें",
- "descriptionLabel": "विवरण",
- "descriptionPlaceholder": "वैकल्पिक विवरण",
- "optional": "वैकल्पिक",
- "folderLabel": "फ़ोल्डर",
- "noFolder": "कोई फ़ोल्डर नहीं (अवर्गीकृत)",
- "commandLabel": "कमांड",
- "commandPlaceholder": "जैसे, sudo systemctl restart nginx",
- "typeLabel": "प्रकार",
- "typeCommand": "आज्ञा",
- "typeNote": "टिप्पणी",
- "noteLabel": "टिप्पणी",
- "notePlaceholder": "उदाहरण के लिए, एक पुन: उपयोग योग्य संकेत या अनुस्मारक पाठ",
- "cancel": "रद्द करें",
- "createSnippetButton": "स्निपेट बनाएं",
- "createFolderTitle": "फ़ोल्डर बनाएं",
- "createFolderDescription": "अपने स्निपेट्स को फ़ोल्डर में व्यवस्थित करें",
- "folderNameLabel": "फ़ोल्डर का नाम",
- "folderNamePlaceholder": "जैसे, सिस्टम कमांड्स, Docker स्क्रिप्ट",
- "folderColorLabel": "फ़ोल्डर का रंग",
- "folderIconLabel": "फ़ोल्डर आइकन",
- "previewLabel": "पूर्वावलोकन",
- "folderNameFallback": "फ़ोल्डर का नाम",
- "createFolderButton": "फ़ोल्डर बनाएं",
- "targetTerminals": "लक्ष्य टर्मिनल",
- "selectAll": "सभी",
- "selectNone": "कोई नहीं",
- "noTerminalTabsOpen": "कोई टर्मिनल टैब खुला नहीं है",
- "searchPlaceholder": "स्निपेट खोजें...",
- "newSnippet": "नया स्निपेट",
- "newFolder": "नया फ़ोल्डर",
- "run": "चलाएँ",
- "noSnippetsInFolder": "इस फ़ोल्डर में कोई स्निपेट नहीं है",
- "uncategorized": "अवर्गीकृत",
- "editSnippetTitle": "स्निपेट संपादित करें",
- "editSnippetDescription": "इस कमांड स्निपेट को अपडेट करें",
- "saveSnippetButton": "परिवर्तन सहेजें",
- "createSuccess": "स्निपेट सफलतापूर्वक बनाया गया",
- "createFailed": "स्निपेट बनाने में विफल",
- "updateSuccess": "स्निपेट सफलतापूर्वक अपडेट किया गया",
- "updateFailed": "स्निपेट अपडेट करने में विफल",
- "deleteFailed": "स्निपेट हटाने में विफल",
- "folderCreateSuccess": "फ़ोल्डर सफलतापूर्वक बनाया गया",
- "folderCreateFailed": "फ़ोल्डर बनाने में विफल",
- "editFolderTitle": "फ़ोल्डर संपादित करें",
- "editFolderDescription": "इस फ़ोल्डर का नाम बदलें या उसका स्वरूप बदलें",
- "saveFolderButton": "परिवर्तन सहेजें",
- "editFolder": "फ़ोल्डर संपादित करें",
- "deleteFolder": "फ़ोल्डर हटाएं",
- "folderDeleteSuccess": "फ़ोल्डर \"{{name}}\" हटाया गया",
- "folderDeleteFailed": "फ़ोल्डर हटाने में विफल",
- "folderEditSuccess": "फ़ोल्डर सफलतापूर्वक अपडेट किया गया",
- "folderEditFailed": "फ़ोल्डर अपडेट करने में विफल",
- "confirmRunMessage": "\"{{name}}\" चलाएँ?",
- "confirmRunButton": "चलाएँ",
- "runSuccess": "\"{{name}}\" को {{count}} टर्मिनल में चलाया गया",
- "pasteSuccess": "\"{{name}}\" को {{count}} टर्मिनल(ओं) में पेस्ट किया गया",
- "pasteToTerminal": "पेस्ट करें",
- "copySuccess": "\"{{name}}\" को क्लिपबोर्ड पर कॉपी किया गया",
- "shareTitle": "स्निपेट साझा करें",
- "shareUser": "उपयोगकर्ता",
- "shareRole": "भूमिका",
- "selectUser": "एक उपयोगकर्ता चुनें...",
- "selectRole": "एक भूमिका चुनें...",
- "shareSuccess": "स्निपेट सफलतापूर्वक साझा किया गया",
- "shareFailed": "स्निपेट साझा करने में विफल",
- "revokeSuccess": "पहुँच रद्द की गई",
- "revokeFailed": "पहुँच रद्द करने में विफल",
- "currentAccess": "वर्तमान पहुँच",
- "shareLoadError": "साझा डेटा लोड करने में विफल",
- "loading": "लोड हो रहा है...",
- "close": "बंद करें",
- "reorderFailed": "स्निपेट क्रम सहेजने में विफल",
- "importExport": "आयात / निर्यात",
- "exportBtn": "JSON निर्यात करें",
- "importBtn": "JSON आयात करें",
- "exportSuccess": "स्निपेट सफलतापूर्वक निर्यात किए गए",
- "exportFailed": "स्निपेट निर्यात करने में विफल",
- "importTitle": "स्निपेट आयात करें",
- "importDescription": "Termix द्वारा निर्यात की गई JSON फ़ाइल से स्निपेट और फ़ोल्डर आयात करें",
- "importDropOrClick": "यहाँ JSON फ़ाइल ड्रॉप करें या ब्राउज़ करने के लिए क्लिक करें",
- "importSelectedFile": "चयनित: {{name}}",
- "importOverwrite": "समान नाम और फ़ोल्डर वाले मौजूदा स्निपेट को अधिलेखित करें",
- "importStartBtn": "आयात करें",
- "importSuccess": "आयात पूर्ण: {{snippets}} स्निपेट जोड़े गए, {{updated}} अपडेट किए गए, {{skipped}} छोड़े गए, {{folders}} फ़ोल्डर जोड़े गए",
- "importFailed": "स्निपेट आयात करने में विफल",
- "importInvalidFile": "अमान्य फ़ाइल: स्निपेट या फ़ोल्डर सरणियों के साथ एक JSON ऑब्जेक्ट अपेक्षित था",
- "targetHostsLabel": "लक्ष्य होस्ट",
- "targetHostsHint": "बिना खुले टर्मिनल के इस स्निपेट को सीधे चलाने के लिए होस्ट असाइन करें।",
- "noHostsAvailable": "कोई होस्ट कॉन्फ़िगर नहीं है",
- "clearTargetHosts": "सभी साफ़ करें",
- "hasTargetHosts": "लक्ष्य होस्ट मौजूद हैं",
- "runOnTargets": "लक्ष्यों पर चलाएँ",
- "directRunSuccess": "\"{{name}}\" को {{count}} होस्ट पर चलाया गया",
- "directRunPartialFail": "\"{{name}}\" एक या अधिक होस्ट पर विफल रहा",
- "executionResultTitle": "निष्पादन परिणाम: {{name}}",
- "executionResultDescription": "प्रत्येक लक्ष्य होस्ट पर स्निपेट चलाने से प्राप्त आउटपुट।",
- "executionSuccess": "सफल",
- "executionFailed": "असफल",
- "expandAll": "सभी फ़ोल्डर खोलें",
- "collapseAll": "सभी फ़ोल्डरों को बंद करें",
- "settings": "स्निपेट सेटिंग्स",
- "variablesHint": "चर: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "रन \"{{name}}\"",
- "variablesDialogDescription": "इस कोड स्निपेट को चलाने से पहले इसमें आवश्यक मान भरें।",
- "variablesPreviewLabel": "पूर्व दर्शन",
- "variablesConfirmButton": "दौड़ना"
- },
"keybindings": {
"title": "कुंजीपटल अल्प मार्ग",
"description": "टर्मिनल कॉपी, पेस्ट और कंट्रोल शॉर्टकट को कस्टमाइज़ करें, या टेक्स्ट भेजने या स्निपेट चलाने के लिए कुंजियों को बाइंड करें।",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "क्लिक करें और कुंजियाँ दबाएँ",
"recording": "बटन दबाएँ...",
"comboRequiredError": "पहले एक कुंजी संयोजन दबाएँ",
- "textRequiredError": "भेजने के लिए टेक्स्ट दर्ज करें",
- "controlCodeRequiredError": "नियंत्रण कोड के लिए एक अक्षर दर्ज करें",
- "snippetRequiredError": "एक अंश का चयन करें",
"conflictWarning": "यह संयोजन पहले से ही {{combo}}से जुड़ा हुआ है। सहेजने से दोनों आग पकड़ लेंगे।",
"actionLabel": "कार्रवाई",
- "actionCopy": "चयन की प्रतिलिपि बनाएँ",
- "actionPaste": "पेस्ट करें",
- "actionSendControlCode": "Ctrl+अक्षर सिग्नल भेजें",
- "actionSendText": "संदेश भेजें",
- "actionRunSnippet": "मौजूदा स्निपेट चलाएँ",
"actionNextTab": "अगले टैब पर जाएं",
"actionPreviousTab": "पिछले टैब पर जाएं",
- "actionOpenCommandPalette": "त्वरित खोज खोलें",
- "controlCodeLabel": "पत्र",
- "textLabel": "भेजने के लिए संदेश",
- "appendEnterLabel": "भेजने के बाद एंटर दबाएँ",
- "snippetLabel": "टुकड़ा",
- "selectSnippetPlaceholder": "एक अंश का चयन करें",
- "orphanedSnippetWarning": "अंश नहीं मिला",
- "clipboardPermissionNote": "कुछ ब्राउज़रों में पहली बार उपयोग करने पर क्लिपबोर्ड की अनुमति मांगी जा सकती है।"
+ "actionOpenCommandPalette": "त्वरित खोज खोलें"
},
"userProfile": {
"donateTitle": "Termix को जीवित रखें",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "विषय आवश्यक है",
"ntfyToken": "एक्सेस टोकन (वैकल्पिक)",
"channelSaveFailed": "चैनल सहेजने में विफल"
- },
- "macros": {
- "title": "मैक्रो",
- "description": "टर्मिनल में सहेजे गए कमांडों की एक श्रृंखला चलाएं, और सर्वर द्वारा वापस भेजे गए कमांडों पर प्रतिक्रिया दें।",
- "target": "लक्ष्य",
- "noTerminal": "कोई टर्मिनल खुला नहीं है",
- "empty": "अभी तक कोई मैक्रो नहीं है",
- "emptyHint": "लॉग इन करना, किसी सेवा को रीस्टार्ट करना या प्रॉम्प्ट का जवाब देना जैसे टर्मिनल कार्यों को स्वचालित करने के लिए एक मैक्रो बनाएं।",
- "create": "नया मैक्रो",
- "back": "मैक्रोज़ पर वापस जाएँ",
- "name": "नाम",
- "namePlaceholder": "वेब सर्वर को पुनः आरंभ करें",
- "nameRequired": "मैक्रो को सेव करने से पहले उसे एक नाम दें।",
- "descriptionLabel": "विवरण",
- "descriptionPlaceholder": "यह मैक्रो क्या करता है (वैकल्पिक)",
- "unsaved": "असहेजित परिवर्तन",
- "save": "बचाना",
- "saved": "मैक्रो सहेजा गया",
- "saveFailed": "मैक्रो को सहेजा नहीं जा सका",
- "delete": "मिटाना",
- "deleteTitle": "क्या इस मैक्रो को हटा दें?",
- "deleteBody": "\"{{name}}\" को स्थायी रूप से हटा दिया जाएगा। इसे पूर्ववत नहीं किया जा सकता है।",
- "deleted": "मैक्रो हटा दिया गया",
- "cancel": "रद्द करना",
- "run": "दौड़ना",
- "stop": "रुकना",
- "running": "दौड़ना...",
- "completed": "मैक्रो समाप्त हो गया",
- "failed": "मैक्रो विफल रहा",
- "loadFailed": "मैक्रो लोड नहीं हो सके",
- "needTerminal": "सबसे पहले एक टर्मिनल खोलें और उससे कनेक्ट करें।",
- "noSteps": "इस मैक्रो में अभी तक कोई चरण नहीं है। कृपया नीचे एक चरण जोड़ें।",
- "addStep": "चरण जोड़ें",
- "stepNumber": "चरण {{number}}",
- "removeStep": "चरण हटाएँ",
- "confirmRunTitle": "क्या आप इस मैक्रो को चलाना चाहते हैं?",
- "confirmRunBody": "\"{{name}}\" {{host}} को कमांड भेजेगा।",
- "confirmRunAction": "मैक्रो चलाएँ",
- "stepTypes": {
- "send": {
- "label": "कमांड भेजें",
- "help": "यह टर्मिनल में ठीक उसी तरह टेक्स्ट टाइप करता है जैसे आप उसे खुद टाइप करते हैं।"
- },
- "wait": {
- "label": "आउटपुट की प्रतीक्षा करें",
- "help": "यह तब तक रुका रहता है जब तक सर्वर आपके द्वारा बताए गए टेक्स्ट को प्रिंट नहीं कर देता, जैसे पासवर्ड प्रॉम्प्ट।"
- },
- "delay": {
- "label": "विराम",
- "help": "अगले चरण से पहले एक निश्चित समय तक प्रतीक्षा करता है।"
- },
- "if": {
- "label": "यदि आउटपुट में शामिल है",
- "help": "यह सर्वर द्वारा अब तक प्रिंट की गई हर चीज़ की जाँच करता है, फिर दो शाखाओं में से एक को चलाता है।"
- },
- "repeat": {
- "label": "दोहराना",
- "help": "इसके अंदर के चरणों को निर्धारित संख्या में बार-बार दोहराता है।"
- }
- },
- "send": {
- "text": "आदेश या पाठ",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "भेजने के बाद एंटर दबाएँ",
- "pressEnterHelp": "इसे बंद करने पर आपको टेक्स्ट टाइप करने के बाद उसे सबमिट करने की आवश्यकता नहीं होगी।"
- },
- "wait": {
- "pattern": "आउटपुट में यह जानकारी आने तक प्रतीक्षा करें",
- "patternPlaceholder": "पासवर्ड:",
- "timeout": "हार मान लो",
- "onTimeout": "अगर यह कभी प्रकट नहीं होता",
- "stop": "मैक्रो को रोकें",
- "continue": "फिर भी आगे बढ़ते रहो।"
- },
- "delay": {
- "duration": "के लिए प्रतीक्षा करें"
- },
- "ifStep": {
- "pattern": "यदि आउटपुट में शामिल है",
- "patternPlaceholder": "सक्रिय: सक्रिय (चल रहा)",
- "then": "फिर ऐसा करें",
- "else": "अन्यथा ऐसा करें",
- "emptyBranch": "कोई कदम नहीं"
- },
- "repeat": {
- "count": "कितनी बार",
- "times": "टाइम्स"
- },
- "match": {
- "useRegex": "नियमित अभिव्यक्ति के साथ मिलान करें",
- "useRegexHelp": "इसे हटा दें ताकि यह लिखे गए पाठ से बिल्कुल मेल खाए।",
- "invalidRegex": "यह एक मान्य रेगुलर एक्सप्रेशन नहीं है, इसे सादे टेक्स्ट के रूप में ही माना जाएगा।"
- },
- "seconds": "सेकंड",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/hu_HU.json b/src/ui/locales/translated/hu_HU.json
index 25a20d8ad..890d18396 100644
--- a/src/ui/locales/translated/hu_HU.json
+++ b/src/ui/locales/translated/hu_HU.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Oldalsáv beállításai...",
"hosts": "Házigazdák",
"snippets": "Kódrészletek",
- "macros": "Makrók",
"hostManager": "Házigazda-kezelő",
"credentials": "Hitelesítő adatok",
"connections": "Kapcsolatok",
@@ -869,7 +868,6 @@
"groupTags": "Címkék és kitűzés",
"groupTunnels": "Alagutak",
"groupJumpHosts": "Ugrásszervezők",
- "groupQuickActions": "Gyors műveletek",
"groupFeatureFlags": "Jellemzőjelzők",
"groupAdvanced": "Speciális konfiguráció",
"preview": "Előnézet",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH-kulcsok vagy jelszavak tárolása",
"tmuxMonitor": "Tmux monitor",
"tmuxMonitorDesc": "TMUX munkamenetek figyelése a hosztokon keresztül",
- "snippets": "Kódrészletek",
- "runSnippetDesc": "Futtatás az aktív terminálfülön",
- "noSnippetsFound": "Nem található a(z) \"{{search}} \" keresésnek megfelelő kódrészlet",
"recentActivity": "Legutóbbi tevékenységek",
"serversAndHosts": "Szerverek és hosztok",
"noHostsFound": "Nem található a következőnek megfelelő gazdagép: \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Előző lap",
"hotkeysJumpTab": "Ugrás a lapra"
},
- "snippets": {
- "title": "Kódrészletek",
- "createSnippetTitle": "Kódrészlet létrehozása",
- "createSnippetDescription": "Hozzon létre egy új parancsrészletet a gyors végrehajtáshoz",
- "nameLabel": "Név",
- "namePlaceholder": "pl. Indítsd újra az Nginx-et",
- "descriptionLabel": "Leírás",
- "descriptionPlaceholder": "Opcionális leírás",
- "optional": "Választható",
- "folderLabel": "Mappa",
- "noFolder": "Nincs mappa (Nincs kategorizálva)",
- "commandLabel": "Parancs",
- "commandPlaceholder": "Pl.: sudo systemctl indítsa újra az nginx-et",
- "typeLabel": "Típus",
- "typeCommand": "Parancs",
- "typeNote": "Jegyzet",
- "noteLabel": "Jegyzet",
- "notePlaceholder": "pl. egy újrafelhasználható felszólítás vagy emlékeztető szöveg",
- "cancel": "Mégsem",
- "createSnippetButton": "Kódrészlet létrehozása",
- "createFolderTitle": "Mappa létrehozása",
- "createFolderDescription": "Rendezd a kódrészleteket mappákba",
- "folderNameLabel": "Mappa neve",
- "folderNamePlaceholder": "pl. rendszerparancsok, Docker szkriptek",
- "folderColorLabel": "Mappa színe",
- "folderIconLabel": "Mappa ikon",
- "previewLabel": "Előnézet",
- "folderNameFallback": "Mappa neve",
- "createFolderButton": "Mappa létrehozása",
- "targetTerminals": "Célterminálok",
- "selectAll": "Minden",
- "selectNone": "Egyik sem",
- "noTerminalTabsOpen": "Nincsenek megnyitott terminálfülek",
- "searchPlaceholder": "Keresési kódrészletek...",
- "newSnippet": "Új részlet",
- "newFolder": "Új mappa",
- "run": "Fut",
- "noSnippetsInFolder": "Nincsenek kódrészletek ebben a mappában",
- "uncategorized": "Kategorizálatlan",
- "editSnippetTitle": "Kódrészlet szerkesztése",
- "editSnippetDescription": "Frissítse ezt a parancsrészletet",
- "saveSnippetButton": "Változtatások mentése",
- "createSuccess": "A kódrészlet sikeresen létrehozva",
- "createFailed": "Nem sikerült létrehozni a kódrészletet",
- "updateSuccess": "A részlet sikeresen frissítve",
- "updateFailed": "Nem sikerült frissíteni a kódrészletet",
- "deleteFailed": "Nem sikerült törölni a részletet",
- "folderCreateSuccess": "Mappa sikeresen létrehozva",
- "folderCreateFailed": "Nem sikerült létrehozni a mappát",
- "editFolderTitle": "Mappa szerkesztése",
- "editFolderDescription": "Nevezze át vagy módosítsa a mappa megjelenését",
- "saveFolderButton": "Változtatások mentése",
- "editFolder": "Mappa szerkesztése",
- "deleteFolder": "Mappa törlése",
- "folderDeleteSuccess": "A(z) „{{name}}” mappa törölve",
- "folderDeleteFailed": "Nem sikerült törölni a mappát",
- "folderEditSuccess": "Mappa sikeresen frissítve",
- "folderEditFailed": "Nem sikerült frissíteni a mappát",
- "confirmRunMessage": "Futtassa a \"{{name}} \" programot?",
- "confirmRunButton": "Fut",
- "runSuccess": "Futtatta a következőt: \"{{name}}\" {{count}} terminál(ok)ban",
- "pasteSuccess": "Beillesztve a(z) \"{{name}}\" elemet a(z) {{count}} terminál(ok)ba",
- "pasteToTerminal": "Paszta",
- "copySuccess": "A(z) „{{name}}” a vágólapra másolva",
- "shareTitle": "Részlet megosztása",
- "shareUser": "Felhasználó",
- "shareRole": "Szerep",
- "selectUser": "Válasszon ki egy felhasználót...",
- "selectRole": "Válasszon egy szerepet...",
- "shareSuccess": "A részlet sikeresen megosztva",
- "shareFailed": "Nem sikerült megosztani a részletet",
- "revokeSuccess": "Hozzáférés visszavonva",
- "revokeFailed": "Nem sikerült visszavonni a hozzáférést",
- "currentAccess": "Jelenlegi hozzáférés",
- "shareLoadError": "Nem sikerült betölteni a megosztott adatokat",
- "loading": "Terhelés...",
- "close": "Közeli",
- "reorderFailed": "Nem sikerült menteni a kódrészlet sorrendjét",
- "importExport": "Importálás / Exportálás",
- "exportBtn": "JSON exportálása",
- "importBtn": "JSON importálása",
- "exportSuccess": "A kódrészletek exportálása sikeresen megtörtént.",
- "exportFailed": "Nem sikerült exportálni a kódrészleteket",
- "importTitle": "Kódrészletek importálása",
- "importDescription": "Kódrészletek és mappák importálása Termix által exportált JSON-fájlból",
- "importDropOrClick": "Húzz ide egy JSON fájlt, vagy kattints a böngészéshez",
- "importSelectedFile": "Kiválasztva: {{name}}",
- "importOverwrite": "Írja felül a meglévő kódrészleteket ugyanazzal a névvel és mappával",
- "importStartBtn": "Importálás",
- "importSuccess": "Importálás befejezve: {{snippets}} kódrészlet hozzáadva, {{updated}} frissítve, {{skipped}} kihagyva, {{folders}} mappa hozzáadva",
- "importFailed": "Nem sikerült importálni a kódrészleteket",
- "importInvalidFile": "Érvénytelen fájl: a várt JSON objektum kódrészletekkel vagy mappatömbökkel",
- "targetHostsLabel": "Célállomások",
- "targetHostsHint": "Rendeljen hozzá hosztokat a kódrészlet közvetlen, nyitott terminál nélküli futtatásához.",
- "noHostsAvailable": "Nincsenek konfigurált gazdagépek",
- "clearTargetHosts": "Összes törlése",
- "hasTargetHosts": "Célzott hosztokkal rendelkezik",
- "runOnTargets": "Célokon fut",
- "directRunSuccess": "\"{{name}}\" futtatva {{count}} gazdagépen",
- "directRunPartialFail": "„{{name}}” hiba történt egy vagy több gépen",
- "executionResultTitle": "Végrehajtás eredménye: {{name}}",
- "executionResultDescription": "A kódrészlet minden célállomáson történő futtatásának kimenete.",
- "executionSuccess": "Siker",
- "executionFailed": "Sikertelen",
- "expandAll": "Az összes mappa kibontása",
- "collapseAll": "Az összes mappa összecsukása",
- "settings": "Kódrészlet beállításai",
- "variablesHint": "Változók: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Címke}",
- "variablesDialogTitle": "Futtassa a \"{{name}}\"",
- "variablesDialogDescription": "Töltse ki a kódrészlet futtatása előtt szükséges értékeket.",
- "variablesPreviewLabel": "Előnézet",
- "variablesConfirmButton": "Fut"
- },
"keybindings": {
"title": "Billentyűparancsok",
"description": "Testreszabhatja a terminál másolási, beillesztési és vezérlési billentyűparancsait, vagy billentyűket köthet szöveg küldéséhez vagy kódrészlet futtatásához.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Kattintson és nyomja meg a billentyűket",
"recording": "Nyomja meg a billentyűket...",
"comboRequiredError": "Először nyomjon meg egy billentyűkombinációt",
- "textRequiredError": "Írd be a küldendő szöveget",
- "controlCodeRequiredError": "Írjon be egyetlen betűt az ellenőrző kódhoz",
- "snippetRequiredError": "Válasszon ki egy részletet",
"conflictWarning": "Ez a kombináció már a {{combo}}-hoz van kötve. Mentés esetén mindkettő aktiválódik.",
"actionLabel": "Akció",
- "actionCopy": "Kijelölés másolása",
- "actionPaste": "Paszta",
- "actionSendControlCode": "Ctrl+betűjel küldése",
- "actionSendText": "SMS küldése",
- "actionRunSnippet": "Meglévő kódrészlet futtatása",
"actionNextTab": "Váltás a következő lapra",
"actionPreviousTab": "Váltás az előző lapra",
- "actionOpenCommandPalette": "Gyorskeresés megnyitása",
- "controlCodeLabel": "Levél",
- "textLabel": "Küldendő szöveg",
- "appendEnterLabel": "Küldés után nyomja meg az Enter billentyűt",
- "snippetLabel": "Töredék",
- "selectSnippetPlaceholder": "Válasszon ki egy részletet",
- "orphanedSnippetWarning": "részlet nem található",
- "clipboardPermissionNote": "Egyes böngészőkben első használatkor vágólap-engedélyt kérhet."
+ "actionOpenCommandPalette": "Gyorskeresés megnyitása"
},
"userProfile": {
"donateTitle": "Tartsd életben a Termixet",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "A téma megadása kötelező",
"ntfyToken": "Hozzáférési token (opcionális)",
"channelSaveFailed": "Nem sikerült menteni a csatornát"
- },
- "macros": {
- "title": "Makrók",
- "description": "Futtass egy mentett parancssorozatot egy terminálban, és reagálj a szerver által küldöttekre.",
- "target": "Cél",
- "noTerminal": "Nincs nyitva terminál",
- "empty": "Még nincsenek makrók",
- "emptyHint": "Hozz létre egy makrót egy terminálfolyamat automatizálásához, például bejelentkezéshez, szolgáltatás újraindításához vagy a kérdések megválaszolásához.",
- "create": "Új makró",
- "back": "Vissza a makrókhoz",
- "name": "Név",
- "namePlaceholder": "Indítsa újra a webszervert",
- "nameRequired": "Mentés előtt adjon nevet a makrónak",
- "descriptionLabel": "Leírás",
- "descriptionPlaceholder": "Mit csinál ez a makró (opcionális)",
- "unsaved": "Nem mentett változtatások",
- "save": "Megtakarítás",
- "saved": "Makró mentve",
- "saveFailed": "Nem sikerült menteni a makrót",
- "delete": "Töröl",
- "deleteTitle": "Törli ezt a makrót?",
- "deleteBody": "A(z) „{{name}}” véglegesen törlésre kerül. Ez a művelet nem vonható vissza.",
- "deleted": "Makró törölve",
- "cancel": "Mégsem",
- "run": "Fut",
- "stop": "Stop",
- "running": "Futás...",
- "completed": "Makró kész",
- "failed": "Makró sikertelen",
- "loadFailed": "Nem sikerült betölteni a makrókat",
- "needTerminal": "Először nyiss meg és csatlakoztass egy terminált",
- "noSteps": "Ennek a makrónak még nincsenek lépései. Adjon hozzá egyet alább.",
- "addStep": "Lépés hozzáadása",
- "stepNumber": "{{number}} lépés",
- "removeStep": "Lépés eltávolítása",
- "confirmRunTitle": "Futtassa ezt a makrót?",
- "confirmRunBody": "A „{{name}}” parancsokat küld a {{host}} címre.",
- "confirmRunAction": "Makró futtatása",
- "stepTypes": {
- "send": {
- "label": "Parancs küldése",
- "help": "Beírja a szöveget a terminálba, ugyanúgy, mintha beírta volna."
- },
- "wait": {
- "label": "Várjon a kimenetre",
- "help": "Szünetet tart, amíg a szerver ki nem írja a megnevezett szöveget, például egy jelszókérést."
- },
- "delay": {
- "label": "Szünet",
- "help": "Egy meghatározott ideig vár a következő lépés előtt."
- },
- "if": {
- "label": "Ha a kimenet tartalmaz",
- "help": "Ellenőrzi a szerver által eddig kinyomtatott összes adatot, majd lefuttatja a két ág egyikét."
- },
- "repeat": {
- "label": "Ismétlés",
- "help": "Meghatározott számú alkalommal futtatja le a benne található lépéseket."
- }
- },
- "send": {
- "text": "Parancs vagy szöveg",
- "textPlaceholder": "systemctl újraindítja az nginx-et",
- "pressEnter": "Küldés után nyomja meg az Enter billentyűt",
- "pressEnterHelp": "Kapcsold ki ezt, ha beküldés nélkül szeretnél szöveget beírni."
- },
- "wait": {
- "pattern": "Várjon, amíg a kimenet tartalmazza a",
- "patternPlaceholder": "jelszó:",
- "timeout": "Add fel utána",
- "onTimeout": "Ha soha nem jelenik meg",
- "stop": "A makró leállítása",
- "continue": "Folytasd mindenképpen"
- },
- "delay": {
- "duration": "Várj"
- },
- "ifStep": {
- "pattern": "Ha a kimenet tartalmaz",
- "patternPlaceholder": "Aktív: aktív (fut)",
- "then": "Akkor csináld ezt",
- "else": "Egyébként tedd ezt",
- "emptyBranch": "Nincsenek lépcsők"
- },
- "repeat": {
- "count": "Alkalmak száma",
- "times": "alkalommal"
- },
- "match": {
- "useRegex": "Egyezés reguláris kifejezéssel",
- "useRegexHelp": "Hagyd ezt ki, hogy a szöveg pontosan úgy illeszkedjen, ahogyan le van írva.",
- "invalidRegex": "Nem érvényes reguláris kifejezés, sima szövegként lesz egyezve."
- },
- "seconds": "másodperc",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/id_ID.json b/src/ui/locales/translated/id_ID.json
index 442b0a547..47554000e 100644
--- a/src/ui/locales/translated/id_ID.json
+++ b/src/ui/locales/translated/id_ID.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Pengaturan Sidebar...",
"hosts": "Tuan rumah",
"snippets": "Cuplikan",
- "macros": "Makro",
"hostManager": "Manajer Host",
"credentials": "Kredensial",
"connections": "Koneksi",
@@ -869,7 +868,6 @@
"groupTags": "Label & pin",
"groupTunnels": "Terowongan",
"groupJumpHosts": "Pembawa acara Jump",
- "groupQuickActions": "Tindakan cepat",
"groupFeatureFlags": "Bendera fitur",
"groupAdvanced": "Konfigurasi lanjutan",
"preview": "Pratinjau",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Simpan kunci atau kata sandi SSH",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Pantau sesi tmux di seluruh host Anda.",
- "snippets": "Cuplikan",
- "runSnippetDesc": "Jalankan di tab terminal aktif",
- "noSnippetsFound": "Tidak ditemukan cuplikan yang cocok dengan \"{{search}}\"",
"recentActivity": "Aktivitas Terkini",
"serversAndHosts": "Server & Host",
"noHostsFound": "Tidak ditemukan host yang cocok dengan \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Tab sebelumnya",
"hotkeysJumpTab": "Lompat ke tab"
},
- "snippets": {
- "title": "Cuplikan",
- "createSnippetTitle": "Buat Cuplikan",
- "createSnippetDescription": "Buat cuplikan perintah baru untuk eksekusi cepat.",
- "nameLabel": "Nama",
- "namePlaceholder": "misalnya, Mulai ulang Nginx",
- "descriptionLabel": "Keterangan",
- "descriptionPlaceholder": "Deskripsi opsional",
- "optional": "Opsional",
- "folderLabel": "Map",
- "noFolder": "Tidak ada folder (Tidak terkategorikan)",
- "commandLabel": "Memerintah",
- "commandPlaceholder": "misalnya, sudo systemctl restart nginx",
- "typeLabel": "Jenis",
- "typeCommand": "Memerintah",
- "typeNote": "Catatan",
- "noteLabel": "Catatan",
- "notePlaceholder": "misalnya, teks pengingat atau perintah yang dapat digunakan kembali",
- "cancel": "Membatalkan",
- "createSnippetButton": "Buat Cuplikan",
- "createFolderTitle": "Buat Folder",
- "createFolderDescription": "Susun cuplikan Anda ke dalam folder.",
- "folderNameLabel": "Nama Folder",
- "folderNamePlaceholder": "misalnya, Perintah Sistem, Skrip Docker",
- "folderColorLabel": "Warna Folder",
- "folderIconLabel": "Ikon Folder",
- "previewLabel": "Pratinjau",
- "folderNameFallback": "Nama Folder",
- "createFolderButton": "Buat Folder",
- "targetTerminals": "Terminal Target",
- "selectAll": "Semua",
- "selectNone": "Tidak ada",
- "noTerminalTabsOpen": "Tidak ada tab terminal yang terbuka.",
- "searchPlaceholder": "Cuplikan pencarian...",
- "newSnippet": "Cuplikan Baru",
- "newFolder": "Folder Baru",
- "run": "Berlari",
- "noSnippetsInFolder": "Tidak ada cuplikan di folder ini",
- "uncategorized": "Tidak dikategorikan",
- "editSnippetTitle": "Edit Cuplikan",
- "editSnippetDescription": "Perbarui cuplikan perintah ini",
- "saveSnippetButton": "Simpan Perubahan",
- "createSuccess": "Cuplikan berhasil dibuat.",
- "createFailed": "Gagal membuat cuplikan",
- "updateSuccess": "Cuplikan berhasil diperbarui.",
- "updateFailed": "Gagal memperbarui cuplikan",
- "deleteFailed": "Gagal menghapus cuplikan",
- "folderCreateSuccess": "Folder berhasil dibuat.",
- "folderCreateFailed": "Gagal membuat folder",
- "editFolderTitle": "Edit Folder",
- "editFolderDescription": "Ganti nama atau ubah tampilan folder ini",
- "saveFolderButton": "Simpan Perubahan",
- "editFolder": "Edit folder",
- "deleteFolder": "Hapus folder",
- "folderDeleteSuccess": "Folder \"{{name}}\" dihapus",
- "folderDeleteFailed": "Gagal menghapus folder",
- "folderEditSuccess": "Folder berhasil diperbarui.",
- "folderEditFailed": "Gagal memperbarui folder",
- "confirmRunMessage": "Jalankan \"{{name}}\"?",
- "confirmRunButton": "Berlari",
- "runSuccess": "Menjalankan \"{{name}}\" di {{count}} terminal",
- "pasteSuccess": "\"{{name}}\" ditempelkan ke terminal {{count}}",
- "pasteToTerminal": "Pasta",
- "copySuccess": "\"{{name}}\" disalin ke papan klip",
- "shareTitle": "Bagikan Cuplikan",
- "shareUser": "Pengguna",
- "shareRole": "Peran",
- "selectUser": "Pilih pengguna...",
- "selectRole": "Pilih peran...",
- "shareSuccess": "Cuplikan berhasil dibagikan",
- "shareFailed": "Cuplikan gagal dibagikan",
- "revokeSuccess": "Akses dicabut",
- "revokeFailed": "Gagal mencabut akses",
- "currentAccess": "Akses Saat Ini",
- "shareLoadError": "Gagal memuat data berbagi.",
- "loading": "Memuat...",
- "close": "Menutup",
- "reorderFailed": "Gagal menyimpan urutan cuplikan",
- "importExport": "Impor / Ekspor",
- "exportBtn": "Ekspor JSON",
- "importBtn": "Impor JSON",
- "exportSuccess": "Cuplikan berhasil diekspor.",
- "exportFailed": "Gagal mengekspor cuplikan",
- "importTitle": "Cuplikan Impor",
- "importDescription": "Impor cuplikan dan folder dari file JSON yang diekspor oleh Termix.",
- "importDropOrClick": "Seret file JSON di sini atau klik untuk menelusuri.",
- "importSelectedFile": "Dipilih: {{name}}",
- "importOverwrite": "Timpa cuplikan yang sudah ada dengan nama dan folder yang sama.",
- "importStartBtn": "Impor",
- "importSuccess": "Impor selesai: {{snippets}} cuplikan ditambahkan, {{updated}} diperbarui, {{skipped}} dilewati, {{folders}} folder ditambahkan",
- "importFailed": "Gagal mengimpor cuplikan kode.",
- "importInvalidFile": "Berkas tidak valid: diharapkan objek JSON dengan cuplikan atau array folder.",
- "targetHostsLabel": "Host Target",
- "targetHostsHint": "Tetapkan host untuk menjalankan cuplikan kode ini secara langsung tanpa terminal yang terbuka.",
- "noHostsAvailable": "Tidak ada host yang dikonfigurasi.",
- "clearTargetHosts": "Hapus semua",
- "hasTargetHosts": "Memiliki host target",
- "runOnTargets": "Lari ke Target",
- "directRunSuccess": "Menjalankan \"{{name}}\" pada {{count}} host",
- "directRunPartialFail": "\"{{name}}\" gagal pada satu atau lebih host",
- "executionResultTitle": "Hasil Eksekusi: {{name}}",
- "executionResultDescription": "Hasil dari menjalankan cuplikan kode pada setiap host target.",
- "executionSuccess": "Kesuksesan",
- "executionFailed": "Gagal",
- "expandAll": "Buka semua folder",
- "collapseAll": "Tutup semua folder",
- "settings": "Pengaturan cuplikan",
- "variablesHint": "Variabel: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Jalankan \"{{name}}\"",
- "variablesDialogDescription": "Isi nilai-nilai yang dibutuhkan cuplikan kode ini sebelum menjalankannya.",
- "variablesPreviewLabel": "Pratinjau",
- "variablesConfirmButton": "Berlari"
- },
"keybindings": {
"title": "Pintasan Keyboard",
"description": "Sesuaikan pintasan salin, tempel, dan kontrol terminal, atau tetapkan tombol untuk mengirim teks atau menjalankan cuplikan kode.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klik dan tekan tombol",
"recording": "Tekan tombol...",
"comboRequiredError": "Tekan kombinasi tombol terlebih dahulu.",
- "textRequiredError": "Masukkan teks yang ingin dikirim",
- "controlCodeRequiredError": "Masukkan satu huruf untuk kode kontrol.",
- "snippetRequiredError": "Pilih cuplikan",
"conflictWarning": "Kombinasi ini sudah terikat ke {{combo}}. Menyimpan akan membuat keduanya menyala.",
"actionLabel": "Tindakan",
- "actionCopy": "Salin pilihan",
- "actionPaste": "Pasta",
- "actionSendControlCode": "Kirim sinyal Ctrl+huruf",
- "actionSendText": "Kirim pesan teks",
- "actionRunSnippet": "Jalankan cuplikan kode yang sudah ada.",
"actionNextTab": "Beralih ke tab berikutnya",
"actionPreviousTab": "Beralih ke tab sebelumnya",
- "actionOpenCommandPalette": "Buka pencarian cepat",
- "controlCodeLabel": "Surat",
- "textLabel": "Teks untuk dikirim",
- "appendEnterLabel": "Tekan Enter setelah mengirim",
- "snippetLabel": "Potongan",
- "selectSnippetPlaceholder": "Pilih cuplikan",
- "orphanedSnippetWarning": "cuplikan tidak ditemukan",
- "clipboardPermissionNote": "Pada beberapa browser, mungkin akan meminta izin akses clipboard saat pertama kali digunakan."
+ "actionOpenCommandPalette": "Buka pencarian cepat"
},
"userProfile": {
"donateTitle": "Jaga agar Termix tetap hidup.",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Topik wajib diisi",
"ntfyToken": "Token Akses (opsional)",
"channelSaveFailed": "Gagal menyimpan saluran"
- },
- "macros": {
- "title": "Makro",
- "description": "Jalankan serangkaian perintah yang tersimpan di terminal, dan tanggapi apa yang dikirimkan kembali oleh server.",
- "target": "Target",
- "noTerminal": "Tidak ada terminal yang terbuka",
- "empty": "Belum ada makro",
- "emptyHint": "Buat makro untuk mengotomatiskan alur terminal, seperti masuk, memulai ulang layanan, atau menjawab perintah.",
- "create": "Makro baru",
- "back": "Kembali ke makro",
- "name": "Nama",
- "namePlaceholder": "Mulai ulang server web",
- "nameRequired": "Beri nama makro sebelum menyimpannya.",
- "descriptionLabel": "Keterangan",
- "descriptionPlaceholder": "Fungsi makro ini (opsional)",
- "unsaved": "Perubahan yang belum disimpan",
- "save": "Menyimpan",
- "saved": "Makro tersimpan",
- "saveFailed": "Makro tidak dapat disimpan.",
- "delete": "Menghapus",
- "deleteTitle": "Hapus makro ini?",
- "deleteBody": "\"{{name}}\" akan dihapus secara permanen. Ini tidak dapat dibatalkan.",
- "deleted": "Makro dihapus",
- "cancel": "Membatalkan",
- "run": "Berlari",
- "stop": "Berhenti",
- "running": "Berlari...",
- "completed": "Makro selesai",
- "failed": "Makro gagal",
- "loadFailed": "Makro tidak dapat dimuat",
- "needTerminal": "Buka dan sambungkan terminal terlebih dahulu.",
- "noSteps": "Makro ini belum memiliki langkah. Tambahkan langkah di bawah ini.",
- "addStep": "Tambahkan langkah",
- "stepNumber": "Langkah {{number}}",
- "removeStep": "Hapus langkah",
- "confirmRunTitle": "Jalankan makro ini?",
- "confirmRunBody": "\"{{name}}\" akan mengirimkan perintah ke {{host}}.",
- "confirmRunAction": "Jalankan makro",
- "stepTypes": {
- "send": {
- "label": "Kirim perintah",
- "help": "Mengetik teks ke terminal, sama seperti jika Anda mengetiknya."
- },
- "wait": {
- "label": "Tunggu hingga muncul output.",
- "help": "Akan berhenti sejenak hingga server menampilkan teks yang Anda sebutkan, seperti saat meminta kata sandi."
- },
- "delay": {
- "label": "Berhenti sebentar",
- "help": "Menunggu dalam jangka waktu tertentu sebelum melanjutkan ke langkah berikutnya."
- },
- "if": {
- "label": "Jika output berisi",
- "help": "Memeriksa semua yang telah dicetak server sejauh ini, lalu menjalankan salah satu dari dua cabang."
- },
- "repeat": {
- "label": "Mengulang",
- "help": "Menjalankan langkah-langkah di dalamnya sebanyak jumlah kali yang telah ditentukan."
- }
- },
- "send": {
- "text": "Perintah atau teks",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Tekan Enter setelah mengirim",
- "pressEnterHelp": "Matikan opsi ini untuk mengetik teks tanpa mengirimkannya."
- },
- "wait": {
- "pattern": "Tunggu hingga output berisi",
- "patternPlaceholder": "kata sandi:",
- "timeout": "Menyerah setelah",
- "onTimeout": "Jika itu tidak pernah muncul",
- "stop": "Hentikan makro",
- "continue": "Tetaplah melanjutkan"
- },
- "delay": {
- "duration": "Tunggu"
- },
- "ifStep": {
- "pattern": "Jika outputnya berisi",
- "patternPlaceholder": "Aktif: aktif (berjalan)",
- "then": "Kemudian lakukan ini",
- "else": "Jika tidak, lakukan ini",
- "emptyBranch": "Tidak ada tangga"
- },
- "repeat": {
- "count": "Jumlah kali",
- "times": "waktu"
- },
- "match": {
- "useRegex": "Cocokkan dengan ekspresi reguler",
- "useRegexHelp": "Hilangkan bagian ini agar sesuai persis dengan teks yang tertulis.",
- "invalidRegex": "Bukan ekspresi reguler yang valid, akan dicocokkan sebagai teks biasa."
- },
- "seconds": "detik",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/it_IT.json b/src/ui/locales/translated/it_IT.json
index d6f373b3f..6274fbaa4 100644
--- a/src/ui/locales/translated/it_IT.json
+++ b/src/ui/locales/translated/it_IT.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Impostazioni della barra laterale...",
"hosts": "Host",
"snippets": "Frammenti",
- "macros": "Macro",
"hostManager": "Gestione host",
"credentials": "Credenziali",
"connections": "Connessioni",
@@ -869,7 +868,6 @@
"groupTags": "Tag e pin",
"groupTunnels": "Gallerie",
"groupJumpHosts": "Ospiti di Jump",
- "groupQuickActions": "Azioni rapide",
"groupFeatureFlags": "Flag di funzionalità",
"groupAdvanced": "Configurazione avanzata",
"preview": "Anteprima",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Archivia chiavi SSH o password",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitora le sessioni tmux sui tuoi host",
- "snippets": "Frammenti",
- "runSnippetDesc": "Esegui nella scheda del terminale attiva",
- "noSnippetsFound": "Nessun frammento trovato corrispondente a \"{{search}}\"",
"recentActivity": "Attività recenti",
"serversAndHosts": "Server e host",
"noHostsFound": "Nessun host trovato corrispondente a \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Scheda precedente",
"hotkeysJumpTab": "Vai alla scheda"
},
- "snippets": {
- "title": "Snippet",
- "createSnippetTitle": "Crea snippet",
- "createSnippetDescription": "Crea un nuovo snippet di comando per un'esecuzione rapida",
- "nameLabel": "Nome",
- "namePlaceholder": "es. Riavvia Nginx",
- "descriptionLabel": "Descrizione",
- "descriptionPlaceholder": "Descrizione opzionale",
- "optional": "Opzionale",
- "folderLabel": "Cartella",
- "noFolder": "Nessuna cartella (Senza categoria)",
- "commandLabel": "Comando",
- "commandPlaceholder": "es. sudo systemctl restart nginx",
- "typeLabel": "Tipo",
- "typeCommand": "Comando",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "ad esempio, un messaggio di promemoria o un avviso riutilizzabile",
- "cancel": "Annulla",
- "createSnippetButton": "Crea snippet",
- "createFolderTitle": "Crea cartella",
- "createFolderDescription": "Organizza i tuoi snippet in cartelle",
- "folderNameLabel": "Nome cartella",
- "folderNamePlaceholder": "es. Comandi di sistema, Script Docker",
- "folderColorLabel": "Colore cartella",
- "folderIconLabel": "Icona cartella",
- "previewLabel": "Anteprima",
- "folderNameFallback": "Nome cartella",
- "createFolderButton": "Crea cartella",
- "targetTerminals": "Terminali di destinazione",
- "selectAll": "Tutti",
- "selectNone": "Nessuno",
- "noTerminalTabsOpen": "Nessuna scheda terminale aperta",
- "searchPlaceholder": "Cerca snippet...",
- "newSnippet": "Nuovo snippet",
- "newFolder": "Nuova cartella",
- "run": "Esegui",
- "noSnippetsInFolder": "Nessuno snippet in questa cartella",
- "uncategorized": "Senza categoria",
- "editSnippetTitle": "Modifica snippet",
- "editSnippetDescription": "Aggiorna questo snippet di comando",
- "saveSnippetButton": "Salva modifiche",
- "createSuccess": "Snippet creato con successo",
- "createFailed": "Impossibile creare lo snippet",
- "updateSuccess": "Snippet aggiornato con successo",
- "updateFailed": "Impossibile aggiornare lo snippet",
- "deleteFailed": "Impossibile eliminare lo snippet",
- "folderCreateSuccess": "Cartella creata con successo",
- "folderCreateFailed": "Impossibile creare la cartella",
- "editFolderTitle": "Modifica cartella",
- "editFolderDescription": "Rinomina o modifica l'aspetto di questa cartella",
- "saveFolderButton": "Salva modifiche",
- "editFolder": "Modifica cartella",
- "deleteFolder": "Elimina cartella",
- "folderDeleteSuccess": "Cartella \"{{name}}\" eliminata",
- "folderDeleteFailed": "Impossibile eliminare la cartella",
- "folderEditSuccess": "Cartella aggiornata con successo",
- "folderEditFailed": "Impossibile aggiornare la cartella",
- "confirmRunMessage": "Eseguire \"{{name}}\"?",
- "confirmRunButton": "Esegui",
- "runSuccess": "Eseguito \"{{name}}\" su {{count}} terminali",
- "pasteSuccess": "Incollato \"{{name}}\" nel/nei terminale/i {{count}}",
- "pasteToTerminal": "Impasto",
- "copySuccess": "Copiato \"{{name}}\" negli appunti",
- "shareTitle": "Condividi snippet",
- "shareUser": "Utente",
- "shareRole": "Ruolo",
- "selectUser": "Seleziona un utente...",
- "selectRole": "Seleziona un ruolo...",
- "shareSuccess": "Snippet condiviso con successo",
- "shareFailed": "Condivisione snippet non riuscita",
- "revokeSuccess": "Accesso revocato",
- "revokeFailed": "Revoca accesso non riuscita",
- "currentAccess": "Accesso corrente",
- "shareLoadError": "Caricamento dati di condivisione non riuscito",
- "loading": "Caricamento...",
- "close": "Chiudi",
- "reorderFailed": "Salvataggio ordine snippet non riuscito",
- "importExport": "Importa / Esporta",
- "exportBtn": "Esporta JSON",
- "importBtn": "Importa JSON",
- "exportSuccess": "Snippet esportati con successo",
- "exportFailed": "Esportazione snippet non riuscita",
- "importTitle": "Importa snippet",
- "importDescription": "Importa snippet e cartelle da un file JSON esportato da Termix",
- "importDropOrClick": "Trascina qui un file JSON o clicca per cercare",
- "importSelectedFile": "Selezionato: {{name}}",
- "importOverwrite": "Sovrascrivi snippet esistenti con stesso nome e cartella",
- "importStartBtn": "Importa",
- "importSuccess": "Importazione completata: {{snippets}} snippet aggiunti, {{updated}} aggiornati, {{skipped}} saltati, {{folders}} cartelle aggiunte",
- "importFailed": "Importazione snippet non riuscita",
- "importInvalidFile": "File non valido: previsto un oggetto JSON con array di snippet o cartelle",
- "targetHostsLabel": "Host di destinazione",
- "targetHostsHint": "Assegna host per eseguire questo snippet direttamente senza un terminale aperto.",
- "noHostsAvailable": "Nessun host configurato",
- "clearTargetHosts": "Cancella tutto",
- "hasTargetHosts": "Ha host di destinazione",
- "runOnTargets": "Esegui sugli host",
- "directRunSuccess": "Eseguito \"{{name}}\" su {{count}} host",
- "directRunPartialFail": "\"{{name}}\" non riuscito su uno o più host",
- "executionResultTitle": "Risultati esecuzione: {{name}}",
- "executionResultDescription": "Output dell'esecuzione dello snippet su ciascun host di destinazione.",
- "executionSuccess": "Successo",
- "executionFailed": "Fallito",
- "expandAll": "Espandi tutte le cartelle",
- "collapseAll": "Comprimi tutte le cartelle",
- "settings": "Impostazioni dello snippet",
- "variablesHint": "Variabili: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etichetta}",
- "variablesDialogTitle": "Esegui \"{{name}}\"",
- "variablesDialogDescription": "Inserisci i valori richiesti da questo frammento di codice prima di eseguirlo.",
- "variablesPreviewLabel": "Anteprima",
- "variablesConfirmButton": "Correre"
- },
"keybindings": {
"title": "Scorciatoie da tastiera",
"description": "Personalizza le scorciatoie da tastiera per copiare, incollare e controllare il terminale, oppure associa dei tasti per inviare testo o eseguire un frammento di codice.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Fare clic e premere i tasti",
"recording": "Premere i tasti...",
"comboRequiredError": "Premere prima una combinazione di tasti",
- "textRequiredError": "Inserisci il testo da inviare",
- "controlCodeRequiredError": "Inserisci una sola lettera come codice di controllo",
- "snippetRequiredError": "Seleziona un frammento",
"conflictWarning": "Questa combinazione è già associata a {{combo}}. Il salvataggio farà scattare entrambi.",
"actionLabel": "Azione",
- "actionCopy": "Copia selezione",
- "actionPaste": "Impasto",
- "actionSendControlCode": "Invia il segnale Ctrl+lettera",
- "actionSendText": "Invia testo",
- "actionRunSnippet": "Esegui il frammento di codice esistente",
"actionNextTab": "Passa alla scheda successiva",
"actionPreviousTab": "Passa alla scheda precedente",
- "actionOpenCommandPalette": "Apri la ricerca rapida",
- "controlCodeLabel": "Lettera",
- "textLabel": "Testo da inviare",
- "appendEnterLabel": "Premi Invio dopo aver inviato",
- "snippetLabel": "Frammento",
- "selectSnippetPlaceholder": "Seleziona un frammento",
- "orphanedSnippetWarning": "frammento non trovato",
- "clipboardPermissionNote": "In alcuni browser potrebbe essere richiesta l'autorizzazione per l'apposizione degli appunti al primo utilizzo."
+ "actionOpenCommandPalette": "Apri la ricerca rapida"
},
"userProfile": {
"donateTitle": "Mantieni Termix vivo",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Argomento obbligatorio",
"ntfyToken": "Token di accesso (facoltativo)",
"channelSaveFailed": "Salvataggio del canale non riuscito"
- },
- "macros": {
- "title": "Macro",
- "description": "Esegui una serie di comandi salvati in un terminale e reagisci a ciò che il server ti invia in risposta.",
- "target": "Bersaglio",
- "noTerminal": "Nessun terminale aperto",
- "empty": "Ancora nessuna macro",
- "emptyHint": "Crea una macro per automatizzare un flusso di lavoro da terminale, come l'accesso, il riavvio di un servizio o la risposta a richieste.",
- "create": "Nuova macro",
- "back": "Torna alle macro",
- "name": "Nome",
- "namePlaceholder": "Riavvia il server web",
- "nameRequired": "Assegna un nome alla macro prima di salvarla.",
- "descriptionLabel": "Descrizione",
- "descriptionPlaceholder": "Cosa fa questa macro (facoltativo)",
- "unsaved": "Modifiche non salvate",
- "save": "Salva",
- "saved": "Macro salvata",
- "saveFailed": "Impossibile salvare la macro",
- "delete": "Eliminare",
- "deleteTitle": "Eliminare questa macro?",
- "deleteBody": "\"{{name}}\" verrà eliminato definitivamente. Questa operazione non può essere annullata.",
- "deleted": "Macro eliminato",
- "cancel": "Cancellare",
- "run": "Correre",
- "stop": "Fermare",
- "running": "Corsa...",
- "completed": "Macro terminato",
- "failed": "Il fallimento di Macro",
- "loadFailed": "Impossibile caricare le macro",
- "needTerminal": "Aprire e collegare prima un terminale",
- "noSteps": "Questa macro non ha ancora passaggi. Aggiungine uno qui sotto.",
- "addStep": "Aggiungi passaggio",
- "stepNumber": "Passo {{number}}",
- "removeStep": "Rimuovere il passaggio",
- "confirmRunTitle": "Eseguire questa macro?",
- "confirmRunBody": "\"{{name}}\" invierà comandi a {{host}}.",
- "confirmRunAction": "Esegui macro",
- "stepTypes": {
- "send": {
- "label": "inviare comando",
- "help": "Digita il testo nel terminale, proprio come se lo avessi digitato tu."
- },
- "wait": {
- "label": "Attendi l'output",
- "help": "Si mette in pausa finché il server non stampa il testo che hai specificato, ad esempio una richiesta di password."
- },
- "delay": {
- "label": "Pausa",
- "help": "Attende un intervallo di tempo fisso prima di procedere al passo successivo."
- },
- "if": {
- "label": "Se l'output contiene",
- "help": "Controlla tutto ciò che il server ha stampato finora, quindi esegue uno dei due rami."
- },
- "repeat": {
- "label": "Ripetere",
- "help": "Esegue i passaggi al suo interno un numero prestabilito di volte."
- }
- },
- "send": {
- "text": "Comando o testo",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Premi Invio dopo aver inviato",
- "pressEnterHelp": "Disattiva questa opzione per digitare del testo senza inviarlo."
- },
- "wait": {
- "pattern": "Attendi che l'output contenga",
- "patternPlaceholder": "password:",
- "timeout": "Rinunciare dopo",
- "onTimeout": "Se non appare mai",
- "stop": "Interrompi la macro",
- "continue": "Continua comunque"
- },
- "delay": {
- "duration": "Attendi"
- },
- "ifStep": {
- "pattern": "Se l'output contiene",
- "patternPlaceholder": "Attivo: attivo (in corsa)",
- "then": "Poi fai questo",
- "else": "Altrimenti fai così",
- "emptyBranch": "Nessun gradino"
- },
- "repeat": {
- "count": "Numero di volte",
- "times": "volte"
- },
- "match": {
- "useRegex": "Corrispondenza con un'espressione regolare",
- "useRegexHelp": "Lasciare vuoto questo elemento per far sì che il testo corrisponda esattamente a come è stato scritto.",
- "invalidRegex": "Non è un'espressione regolare valida, verrà interpretata come testo semplice."
- },
- "seconds": "secondi",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ja_JP.json b/src/ui/locales/translated/ja_JP.json
index 069b23679..759ad6ad9 100644
--- a/src/ui/locales/translated/ja_JP.json
+++ b/src/ui/locales/translated/ja_JP.json
@@ -409,7 +409,6 @@
"sidebarSettings": "サイドバーの設定...",
"hosts": "ホスト",
"snippets": "スニペット",
- "macros": "マクロ",
"hostManager": "ホストマネージャー",
"credentials": "認証情報",
"connections": "接続",
@@ -869,7 +868,6 @@
"groupTags": "タグとピン",
"groupTunnels": "トンネル",
"groupJumpHosts": "ジャンプホスト",
- "groupQuickActions": "クイックアクション",
"groupFeatureFlags": "機能フラグ",
"groupAdvanced": "詳細設定",
"preview": "プレビュー",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH鍵やパスワードを保存",
"tmuxMonitor": "Tmux モニター",
"tmuxMonitorDesc": "ホスト間の tmux セッションを監視",
- "snippets": "断片",
- "runSnippetDesc": "アクティブなターミナルタブで実行します",
- "noSnippetsFound": "「{{search}}」に一致するスニペットは見つかりませんでした",
"recentActivity": "最近のアクティビティ",
"serversAndHosts": "サーバー & ホスト",
"noHostsFound": "\"{{search}}\" に一致するホストは見つかりませんでした",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "前のタブ",
"hotkeysJumpTab": "タブにジャンプ"
},
- "snippets": {
- "title": "スニペット",
- "createSnippetTitle": "スニペットを作成",
- "createSnippetDescription": "クイック実行用の新しいコマンドスニペットを作成します",
- "nameLabel": "名前",
- "namePlaceholder": "例: Nginxの再起動",
- "descriptionLabel": "説明",
- "descriptionPlaceholder": "任意の説明",
- "optional": "任意",
- "folderLabel": "フォルダ",
- "noFolder": "フォルダなし (未分類)",
- "commandLabel": "コマンド",
- "commandPlaceholder": "例: sudo systemctl restart nginx",
- "typeLabel": "タイプ",
- "typeCommand": "指示",
- "typeNote": "注記",
- "noteLabel": "注記",
- "notePlaceholder": "例えば、再利用可能なプロンプトまたはリマインダーテキスト",
- "cancel": "キャンセル",
- "createSnippetButton": "スニペットを作成",
- "createFolderTitle": "フォルダを作成",
- "createFolderDescription": "スニペットをフォルダに整理します",
- "folderNameLabel": "フォルダ名",
- "folderNamePlaceholder": "例: システムコマンド, Dockerスクリプト",
- "folderColorLabel": "フォルダの色",
- "folderIconLabel": "フォルダアイコン",
- "previewLabel": "プレビュー",
- "folderNameFallback": "フォルダ名",
- "createFolderButton": "フォルダを作成",
- "targetTerminals": "対象端末",
- "selectAll": "すべて選択",
- "selectNone": "すべて解除",
- "noTerminalTabsOpen": "開いている端末タブはありません",
- "searchPlaceholder": "スニペットを検索...",
- "newSnippet": "新規スニペット",
- "newFolder": "新規フォルダ",
- "run": "実行",
- "noSnippetsInFolder": "このフォルダにスニペットはありません",
- "uncategorized": "未分類",
- "editSnippetTitle": "スニペットの編集",
- "editSnippetDescription": "このコマンドスニペットを更新",
- "saveSnippetButton": "変更を保存",
- "createSuccess": "スニペットが正常に作成されました",
- "createFailed": "スニペットの作成に失敗しました",
- "updateSuccess": "スニペットが正常に更新されました",
- "updateFailed": "スニペットの更新に失敗しました",
- "deleteFailed": "スニペットの削除に失敗しました",
- "folderCreateSuccess": "フォルダが正常に作成されました",
- "folderCreateFailed": "フォルダの作成に失敗しました",
- "editFolderTitle": "フォルダの編集",
- "editFolderDescription": "フォルダの名前や外観を変更",
- "saveFolderButton": "変更を保存",
- "editFolder": "フォルダを編集",
- "deleteFolder": "フォルダを削除",
- "folderDeleteSuccess": "フォルダ「{{name}}」を削除しました",
- "folderDeleteFailed": "フォルダの削除に失敗しました",
- "folderEditSuccess": "フォルダが正常に更新されました",
- "folderEditFailed": "フォルダの更新に失敗しました",
- "confirmRunMessage": "「{{name}}」を実行しますか?",
- "confirmRunButton": "実行",
- "runSuccess": "{{count}}個の端末で「{{name}}」を実行しました",
- "pasteSuccess": "「{{name}}」を {{count}} 端末に貼り付けました",
- "pasteToTerminal": "ペースト",
- "copySuccess": "「{{name}}」をクリップボードにコピーしました",
- "shareTitle": "スニペットを共有",
- "shareUser": "ユーザー",
- "shareRole": "ロール",
- "selectUser": "ユーザーを選択...",
- "selectRole": "ロールを選択...",
- "shareSuccess": "スニペットが正常に共有されました",
- "shareFailed": "スニペットの共有に失敗しました",
- "revokeSuccess": "アクセスが取り消されました",
- "revokeFailed": "アクセスの取り消しに失敗しました",
- "currentAccess": "現在のアクセス権",
- "shareLoadError": "共有データの読み込みに失敗しました",
- "loading": "読み込み中...",
- "close": "閉じる",
- "reorderFailed": "スニペットの並び順の保存に失敗しました",
- "importExport": "インポート / エクスポート",
- "exportBtn": "JSONをエクスポート",
- "importBtn": "JSONをインポート",
- "exportSuccess": "スニペットが正常にエクスポートされました",
- "exportFailed": "スニペットのエクスポートに失敗しました",
- "importTitle": "スニペットのインポート",
- "importDescription": "Termix がエクスポートした JSON ファイルからスニペットとフォルダーをインポートします",
- "importDropOrClick": "JSON ファイルをここにドロップするか、クリックして参照",
- "importSelectedFile": "選択中: {{name}}",
- "importOverwrite": "同じ名前とフォルダーの既存スニペットを上書きする",
- "importStartBtn": "インポート",
- "importSuccess": "インポート完了: {{snippets}} 件のスニペット追加、{{updated}} 件更新、{{skipped}} 件スキップ、{{folders}} 件のフォルダー追加",
- "importFailed": "スニペットのインポートに失敗しました",
- "importInvalidFile": "無効なファイルです: snippets または folders 配列を含む JSON オブジェクトが必要です",
- "targetHostsLabel": "ターゲットホスト",
- "targetHostsHint": "このスニペットを端末を開かずに直接実行するホストを割り当てます。",
- "noHostsAvailable": "設定されたホストがありません",
- "clearTargetHosts": "すべてクリア",
- "hasTargetHosts": "ターゲットホストあり",
- "runOnTargets": "ターゲットで実行",
- "directRunSuccess": "\"{{name}}\" を {{count}} 台のホストで実行しました",
- "directRunPartialFail": "\"{{name}}\" が1つ以上のホストで失敗しました",
- "executionResultTitle": "実行結果: {{name}}",
- "executionResultDescription": "各ターゲットホストでスニペットを実行した結果の出力です。",
- "executionSuccess": "成功",
- "executionFailed": "失敗",
- "expandAll": "すべてのフォルダーを展開する",
- "collapseAll": "すべてのフォルダーを折りたたむ",
- "settings": "スニペット設定",
- "variablesHint": "変数: $HOST、 $USER、 $PORT、 $NAME、 ${INPUT_1:Label}",
- "variablesDialogTitle": "「{{name}} を実行」",
- "variablesDialogDescription": "このコードスニペットを実行する前に、必要な値を入力してください。",
- "variablesPreviewLabel": "プレビュー",
- "variablesConfirmButton": "走る"
- },
"keybindings": {
"title": "キーボードショートカット",
"description": "ターミナルのコピー、ペースト、コントロールのショートカットをカスタマイズしたり、テキストの送信やスニペットの実行にキーを割り当てたりできます。",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "クリックしてキーを押す",
"recording": "キーを押してください...",
"comboRequiredError": "まずキーの組み合わせを押してください",
- "textRequiredError": "送信するテキストを入力してください",
- "controlCodeRequiredError": "制御コードとして1文字を入力してください",
- "snippetRequiredError": "抜粋を選択してください",
"conflictWarning": "この組み合わせは既に {{combo}}にバインドされています。保存すると両方とも発火します。",
"actionLabel": "アクション",
- "actionCopy": "選択範囲をコピー",
- "actionPaste": "ペースト",
- "actionSendControlCode": "Ctrlキーと文字キーを同時に押して信号を送る",
- "actionSendText": "テキストを送信",
- "actionRunSnippet": "既存のスニペットを実行する",
"actionNextTab": "次のタブに切り替える",
"actionPreviousTab": "前のタブに切り替える",
- "actionOpenCommandPalette": "クイック検索を開く",
- "controlCodeLabel": "手紙",
- "textLabel": "送信するテキスト",
- "appendEnterLabel": "送信後にEnterキーを押してください。",
- "snippetLabel": "抜粋",
- "selectSnippetPlaceholder": "抜粋を選択してください",
- "orphanedSnippetWarning": "スニペットが見つかりません",
- "clipboardPermissionNote": "一部のブラウザでは、初回使用時にクリップボードのアクセス許可を求められる場合があります。"
+ "actionOpenCommandPalette": "クイック検索を開く"
},
"userProfile": {
"donateTitle": "Termixを支援する",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "トピックは必須です",
"ntfyToken": "アクセストークン(任意)",
"channelSaveFailed": "チャンネルの保存に失敗しました"
- },
- "macros": {
- "title": "マクロ",
- "description": "ターミナルで保存済みのコマンド系列を実行し、サーバーからの応答に応じて処理を行います。",
- "target": "ターゲット",
- "noTerminal": "端末が開いていません",
- "empty": "マクロはまだありません",
- "emptyHint": "ログイン、サービスの再起動、プロンプトへの応答など、ターミナル操作の流れを自動化するマクロを作成します。",
- "create": "新しいマクロ",
- "back": "マクロに戻る",
- "name": "名前",
- "namePlaceholder": "ウェブサーバーを再起動します",
- "nameRequired": "保存する前にマクロに名前を付けてください。",
- "descriptionLabel": "説明",
- "descriptionPlaceholder": "このマクロの機能(オプション)",
- "unsaved": "保存されていない変更",
- "save": "保存",
- "saved": "マクロが保存されました",
- "saveFailed": "マクロを保存できませんでした",
- "delete": "消去",
- "deleteTitle": "このマクロを削除しますか?",
- "deleteBody": "\"{{name}}\" は完全に削除されます。元に戻すことはできません。",
- "deleted": "マクロが削除されました",
- "cancel": "キャンセル",
- "run": "走る",
- "stop": "停止",
- "running": "実行中...",
- "completed": "マクロ撮影終了",
- "failed": "マクロが失敗しました",
- "loadFailed": "マクロを読み込めませんでした",
- "needTerminal": "まずターミナルを開いて接続します。",
- "noSteps": "このマクロにはまだ手順がありません。以下に手順を追加してください。",
- "addStep": "ステップを追加する",
- "stepNumber": "ステップ {{number}}",
- "removeStep": "削除手順",
- "confirmRunTitle": "このマクロを実行しますか?",
- "confirmRunBody": "\"{{name}}\" は {{host}} にコマンドを送信します。",
- "confirmRunAction": "マクロを実行する",
- "stepTypes": {
- "send": {
- "label": "コマンドを送信",
- "help": "端末にテキストを入力します。まるで手入力したかのように表示されます。"
- },
- "wait": {
- "label": "出力を待つ",
- "help": "サーバーが指定したテキスト(パスワード入力画面など)を表示するまで一時停止します。"
- },
- "delay": {
- "label": "一時停止",
- "help": "次のステップに進む前に、一定時間待機します。"
- },
- "if": {
- "label": "出力に以下が含まれる場合",
- "help": "サーバーがこれまでに出力したすべての内容を確認し、その後、2つの分岐のうちいずれか1つを実行します。"
- },
- "repeat": {
- "label": "繰り返す",
- "help": "指定された回数だけ、その中の手順を実行します。"
- }
- },
- "send": {
- "text": "コマンドまたはテキスト",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "送信後にEnterキーを押してください。",
- "pressEnterHelp": "テキストを送信せずに入力する場合は、これをオフにしてください。"
- },
- "wait": {
- "pattern": "出力に以下の内容が含まれるまで待機してください",
- "patternPlaceholder": "パスワード:",
- "timeout": "諦めた後",
- "onTimeout": "それが決して現れない場合",
- "stop": "マクロを停止する",
- "continue": "とにかく続けて"
- },
- "delay": {
- "duration": "待って"
- },
- "ifStep": {
- "pattern": "出力に以下が含まれる場合",
- "patternPlaceholder": "アクティブ:アクティブ(実行中)",
- "then": "次にこれを実行してください",
- "else": "そうでなければ、これを実行してください。",
- "emptyBranch": "階段なし"
- },
- "repeat": {
- "count": "回数",
- "times": "タイムズ"
- },
- "match": {
- "useRegex": "正規表現で一致させる",
- "useRegexHelp": "原文と完全に一致させるには、この部分を削除してください。",
- "invalidRegex": "有効な正規表現ではないため、プレーンテキストとしてマッチします。"
- },
- "seconds": "秒",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ko_KR.json b/src/ui/locales/translated/ko_KR.json
index 8bf606765..022c4ada5 100644
--- a/src/ui/locales/translated/ko_KR.json
+++ b/src/ui/locales/translated/ko_KR.json
@@ -409,7 +409,6 @@
"sidebarSettings": "사이드바 설정...",
"hosts": "호스트",
"snippets": "스니펫",
- "macros": "매크로",
"hostManager": "호스트 관리자",
"credentials": "자격 증명",
"connections": "연결",
@@ -869,7 +868,6 @@
"groupTags": "태그 및 핀",
"groupTunnels": "터널",
"groupJumpHosts": "점프 호스트",
- "groupQuickActions": "빠른 조치",
"groupFeatureFlags": "기능 플래그",
"groupAdvanced": "고급 설정",
"preview": "시사",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH 키 또는 비밀번호 저장",
"tmuxMonitor": "Tmux 모니터",
"tmuxMonitorDesc": "호스트 전반의 Tmux 세션 모니터링",
- "snippets": "짧은 발췌",
- "runSnippetDesc": "현재 활성화된 터미널 탭에서 실행하세요",
- "noSnippetsFound": "\"{{search}} \" 와 일치하는 스니펫을 찾을 수 없습니다.",
"recentActivity": "최근 활동",
"serversAndHosts": "서버 및 호스트",
"noHostsFound": "\"{{search}}\"에 일치하는 호스트를 찾을 수 없습니다.",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "이전 탭",
"hotkeysJumpTab": "탭으로 이동"
},
- "snippets": {
- "title": "스니펫",
- "createSnippetTitle": "스니펫 생성",
- "createSnippetDescription": "빠른 실행을 위한 새 명령 스니펫을 생성합니다",
- "nameLabel": "이름",
- "namePlaceholder": "예: Nginx 재시작",
- "descriptionLabel": "설명",
- "descriptionPlaceholder": "선택 사항 설명",
- "optional": "선택 사항",
- "folderLabel": "폴더",
- "noFolder": "폴더 없음 (미분류)",
- "commandLabel": "명령",
- "commandPlaceholder": "예: sudo systemctl restart nginx",
- "typeLabel": "유형",
- "typeCommand": "명령",
- "typeNote": "메모",
- "noteLabel": "메모",
- "notePlaceholder": "예: 재사용 가능한 안내 메시지 또는 알림 텍스트",
- "cancel": "취소",
- "createSnippetButton": "스니펫 생성",
- "createFolderTitle": "폴더 생성",
- "createFolderDescription": "스니펫을 폴더로 정리합니다",
- "folderNameLabel": "폴더 이름",
- "folderNamePlaceholder": "예: 시스템 명령, Docker 스크립트",
- "folderColorLabel": "폴더 색상",
- "folderIconLabel": "폴더 아이콘",
- "previewLabel": "미리보기",
- "folderNameFallback": "폴더 이름",
- "createFolderButton": "폴더 만들기",
- "targetTerminals": "대상 터미널",
- "selectAll": "전체",
- "selectNone": "없음",
- "noTerminalTabsOpen": "열린 터미널 탭 없음",
- "searchPlaceholder": "스니펫 검색...",
- "newSnippet": "새 스니펫",
- "newFolder": "새 폴더",
- "run": "실행",
- "noSnippetsInFolder": "이 폴더에 스니펫이 없습니다",
- "uncategorized": "미분류",
- "editSnippetTitle": "스니펫 편집",
- "editSnippetDescription": "이 명령 스니펫을 수정합니다",
- "saveSnippetButton": "변경사항 저장",
- "createSuccess": "스니펫이 생성되었습니다",
- "createFailed": "스니펫 생성에 실패했습니다",
- "updateSuccess": "스니펫이 업데이트되었습니다",
- "updateFailed": "스니펫 업데이트에 실패했습니다",
- "deleteFailed": "스니펫 삭제에 실패했습니다",
- "folderCreateSuccess": "폴더가 생성되었습니다",
- "folderCreateFailed": "폴더 생성에 실패했습니다",
- "editFolderTitle": "폴더 편집",
- "editFolderDescription": "이 폴더의 이름을 바꾸거나 모양을 변경합니다",
- "saveFolderButton": "변경사항 저장",
- "editFolder": "폴더 편집",
- "deleteFolder": "폴더 삭제",
- "folderDeleteSuccess": "폴더 \"{{name}}\"이(가) 삭제되었습니다",
- "folderDeleteFailed": "폴더 삭제에 실패했습니다",
- "folderEditSuccess": "폴더가 업데이트되었습니다",
- "folderEditFailed": "폴더 업데이트에 실패했습니다",
- "confirmRunMessage": "\"{{name}}\"을(를) 실행하시겠습니까?",
- "confirmRunButton": "실행",
- "runSuccess": "\"{{name}}\"을(를) {{count}}개 터미널에서 실행했습니다",
- "pasteSuccess": "\"{{name}}\"을 {{count}} 터미널에 붙여넣었습니다.",
- "pasteToTerminal": "반죽",
- "copySuccess": "\"{{name}}\"을(를) 클립보드에 복사했습니다",
- "shareTitle": "스니펫 공유",
- "shareUser": "사용자",
- "shareRole": "역할",
- "selectUser": "사용자 선택...",
- "selectRole": "역할 선택...",
- "shareSuccess": "스니펫이 성공적으로 공유되었습니다",
- "shareFailed": "스니펫 공유 실패",
- "revokeSuccess": "접근 권한이 취소되었습니다",
- "revokeFailed": "접근 권한 취소 실패",
- "currentAccess": "현재 접근 권한",
- "shareLoadError": "공유 데이터 불러오기 실패",
- "loading": "로딩 중...",
- "close": "닫기",
- "reorderFailed": "스니펫 순서 저장 실패",
- "importExport": "가져오기 / 내보내기",
- "exportBtn": "JSON 내보내기",
- "importBtn": "JSON 가져오기",
- "exportSuccess": "스니펫을 성공적으로 내보냈습니다",
- "exportFailed": "스니펫 내보내기 실패",
- "importTitle": "스니펫 가져오기",
- "importDescription": "Termix에서 내보낸 JSON 파일로부터 스니펫과 폴더를 가져옵니다",
- "importDropOrClick": "JSON 파일을 여기에 놓거나 클릭하여 찾아보기",
- "importSelectedFile": "선택됨: {{name}}",
- "importOverwrite": "같은 이름과 폴더의 기존 스니펫 덮어쓰기",
- "importStartBtn": "가져오기",
- "importSuccess": "가져오기 완료: 스니펫 {{snippets}}개 추가됨, {{updated}}개 업데이트됨, {{skipped}}개 건너뜀, 폴더 {{folders}}개 추가됨",
- "importFailed": "스니펫 가져오기 실패",
- "importInvalidFile": "잘못된 파일: 스니펫 또는 폴더 배열을 포함하는 JSON 객체가 필요합니다",
- "targetHostsLabel": "대상 호스트",
- "targetHostsHint": "열린 터미널 없이 이 스니펫을 직접 실행할 호스트를 할당하세요.",
- "noHostsAvailable": "구성된 호스트 없음",
- "clearTargetHosts": "모두 지우기",
- "hasTargetHosts": "대상 호스트 있음",
- "runOnTargets": "대상에서 실행",
- "directRunSuccess": "\"{{name}}\"을(를) {{count}}개 호스트에서 실행했습니다",
- "directRunPartialFail": "\"{{name}}\"이(가) 하나 이상의 호스트에서 실패했습니다",
- "executionResultTitle": "실행 결과: {{name}}",
- "executionResultDescription": "각 대상 호스트에서 스니펫을 실행한 출력입니다.",
- "executionSuccess": "성공",
- "executionFailed": "실패",
- "expandAll": "모든 폴더를 펼치세요",
- "collapseAll": "모든 폴더 접기",
- "settings": "스니펫 설정",
- "variablesHint": "변수: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "\"{{name}} \" 실행",
- "variablesDialogDescription": "이 코드 조각을 실행하기 전에 필요한 값을 입력하세요.",
- "variablesPreviewLabel": "시사",
- "variablesConfirmButton": "달리다"
- },
"keybindings": {
"title": "키보드 단축키",
"description": "터미널 복사, 붙여넣기 및 제어 단축키를 사용자 지정하거나, 키를 바인딩하여 텍스트를 전송하거나 코드 조각을 실행할 수 있습니다.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "클릭하고 키를 누르세요",
"recording": "키를 누르세요...",
"comboRequiredError": "먼저 키 조합을 누르세요.",
- "textRequiredError": "보낼 텍스트를 입력하세요",
- "controlCodeRequiredError": "제어 코드에 한 글자를 입력하세요.",
- "snippetRequiredError": "코드 조각을 선택하세요",
"conflictWarning": "이 조합은 이미 {{combo}}에 연결되어 있습니다. 저장하면 둘 다 발사됩니다.",
"actionLabel": "행동",
- "actionCopy": "선택 복사",
- "actionPaste": "반죽",
- "actionSendControlCode": "Ctrl+문자 신호를 보내세요",
- "actionSendText": "문자 보내기",
- "actionRunSnippet": "기존 코드 조각을 실행하세요",
"actionNextTab": "다음 탭으로 이동",
"actionPreviousTab": "이전 탭으로 이동",
- "actionOpenCommandPalette": "빠른 검색 열기",
- "controlCodeLabel": "편지",
- "textLabel": "보낼 텍스트",
- "appendEnterLabel": "전송 후 Enter 키를 누르세요.",
- "snippetLabel": "단편",
- "selectSnippetPlaceholder": "코드 조각을 선택하세요",
- "orphanedSnippetWarning": "스니펫을 찾을 수 없습니다.",
- "clipboardPermissionNote": "일부 브라우저에서는 처음 사용 시 클립보드 권한을 요청할 수 있습니다."
+ "actionOpenCommandPalette": "빠른 검색 열기"
},
"userProfile": {
"donateTitle": "Termix 유지 지원",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "토픽은 필수입니다",
"ntfyToken": "액세스 토큰 (선택 사항)",
"channelSaveFailed": "채널 저장에 실패했습니다"
- },
- "macros": {
- "title": "매크로",
- "description": "터미널에서 저장된 일련의 명령어를 실행하고, 서버가 보내는 응답에 반응합니다.",
- "target": "목표",
- "noTerminal": "터미널이 열려 있지 않습니다",
- "empty": "아직 매크로가 없습니다.",
- "emptyHint": "터미널 로그인, 서비스 재시작 또는 프롬프트 응답과 같은 터미널 작업 흐름을 자동화하는 매크로를 만드세요.",
- "create": "새로운 매크로",
- "back": "매크로로 돌아가기",
- "name": "이름",
- "namePlaceholder": "웹 서버를 재시작하세요",
- "nameRequired": "매크로를 저장하기 전에 이름을 지정하세요.",
- "descriptionLabel": "설명",
- "descriptionPlaceholder": "이 매크로의 기능 (선택 사항)",
- "unsaved": "저장되지 않은 변경 사항",
- "save": "구하다",
- "saved": "매크로 저장됨",
- "saveFailed": "매크로를 저장할 수 없습니다.",
- "delete": "삭제",
- "deleteTitle": "이 매크로를 삭제하시겠습니까?",
- "deleteBody": "\"{{name}}\"는 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다.",
- "deleted": "매크로 삭제됨",
- "cancel": "취소",
- "run": "달리다",
- "stop": "멈추다",
- "running": "달리기...",
- "completed": "매크로 완료",
- "failed": "매크로 실패",
- "loadFailed": "매크로를 불러올 수 없습니다.",
- "needTerminal": "먼저 터미널을 열고 연결하세요.",
- "noSteps": "이 매크로는 아직 단계가 없습니다. 아래에 단계를 추가하세요.",
- "addStep": "단계 추가",
- "stepNumber": "단계 {{number}}",
- "removeStep": "제거 단계",
- "confirmRunTitle": "이 매크로를 실행하시겠습니까?",
- "confirmRunBody": "\"{{name}}\"는 {{host}}로 명령을 보냅니다.",
- "confirmRunAction": "매크로 실행",
- "stepTypes": {
- "send": {
- "label": "명령을 보내세요",
- "help": "터미널에 텍스트를 입력합니다. 마치 직접 타이핑하는 것과 같습니다."
- },
- "wait": {
- "label": "출력이 나올 때까지 기다리세요",
- "help": "서버가 사용자가 입력한 텍스트(예: 비밀번호 입력창)를 출력할 때까지 일시 중지됩니다."
- },
- "delay": {
- "label": "정지시키다",
- "help": "다음 단계로 넘어가기 전에 일정 시간 동안 기다립니다."
- },
- "if": {
- "label": "출력에 다음 내용이 포함되어 있는 경우",
- "help": "서버에서 지금까지 출력한 모든 내용을 확인한 후 두 가지 분기 중 하나를 실행합니다."
- },
- "repeat": {
- "label": "반복하다",
- "help": "그 안에 있는 단계들을 정해진 횟수만큼 실행합니다."
- }
- },
- "send": {
- "text": "명령 또는 텍스트",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "전송 후 Enter 키를 누르세요.",
- "pressEnterHelp": "텍스트를 입력하되 제출하지 않으려면 이 기능을 끄세요."
- },
- "wait": {
- "pattern": "출력 내용이 포함될 때까지 기다리세요",
- "patternPlaceholder": "비밀번호:",
- "timeout": "포기한 후에",
- "onTimeout": "만약 전혀 나타나지 않는다면",
- "stop": "매크로를 중지하세요",
- "continue": "어쨌든 계속 가세요"
- },
- "delay": {
- "duration": "기다리세요"
- },
- "ifStep": {
- "pattern": "출력에 다음 내용이 포함되어 있는 경우",
- "patternPlaceholder": "활성: 활성(실행 중)",
- "then": "그럼 이렇게 하세요",
- "else": "그렇지 않으면 이렇게 하세요.",
- "emptyBranch": "단계 없음"
- },
- "repeat": {
- "count": "횟수",
- "times": "타임스"
- },
- "match": {
- "useRegex": "정규 표현식과 일치",
- "useRegexHelp": "원문과 정확히 일치하려면 이 부분을 삭제하세요.",
- "invalidRegex": "유효한 정규 표현식이 아니므로 일반 텍스트로 처리됩니다."
- },
- "seconds": "초",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/nl_NL.json b/src/ui/locales/translated/nl_NL.json
index 059caadfa..a0efbc47c 100644
--- a/src/ui/locales/translated/nl_NL.json
+++ b/src/ui/locales/translated/nl_NL.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Instellingen zijbalk...",
"hosts": "Gastheren",
"snippets": "Fragmenten",
- "macros": "Macro's",
"hostManager": "Hostmanager",
"credentials": "Referenties",
"connections": "Verbindingen",
@@ -869,7 +868,6 @@
"groupTags": "Labels en speld",
"groupTunnels": "Tunnels",
"groupJumpHosts": "Jump-hosts",
- "groupQuickActions": "Snelle acties",
"groupFeatureFlags": "Functievlaggen",
"groupAdvanced": "Geavanceerde configuratie",
"preview": "Voorbeeld",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Bewaar SSH-sleutels of -wachtwoorden",
"tmuxMonitor": "Tmux Monitor",
"tmuxMonitorDesc": "Monitor tmux-sessies op al je hosts.",
- "snippets": "Fragmenten",
- "runSnippetDesc": "Voer dit uit in het actieve terminaltabblad.",
- "noSnippetsFound": "Geen fragmenten gevonden die overeenkomen met \"{{search}}\"",
"recentActivity": "Recente activiteit",
"serversAndHosts": "Servers en hostingproviders",
"noHostsFound": "Geen hosts gevonden die overeenkomen met \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Vorig tabblad",
"hotkeysJumpTab": "Ga naar het tabblad"
},
- "snippets": {
- "title": "Fragmenten",
- "createSnippetTitle": "Maak een codefragment aan",
- "createSnippetDescription": "Maak een nieuw opdrachtfragment aan voor snelle uitvoering.",
- "nameLabel": "Naam",
- "namePlaceholder": "bijvoorbeeld: Nginx opnieuw opstarten",
- "descriptionLabel": "Beschrijving",
- "descriptionPlaceholder": "Optionele beschrijving",
- "optional": "Optioneel",
- "folderLabel": "Map",
- "noFolder": "Geen map (Niet gecategoriseerd)",
- "commandLabel": "Commando",
- "commandPlaceholder": "bijvoorbeeld: sudo systemctl restart nginx",
- "typeLabel": "Type",
- "typeCommand": "Commando",
- "typeNote": "Opmerking",
- "noteLabel": "Opmerking",
- "notePlaceholder": "bijvoorbeeld een herbruikbare prompt of herinneringstekst",
- "cancel": "Annuleren",
- "createSnippetButton": "Maak een codefragment aan",
- "createFolderTitle": "Map maken",
- "createFolderDescription": "Organiseer je codefragmenten in mappen.",
- "folderNameLabel": "Mapnaam",
- "folderNamePlaceholder": "bijvoorbeeld systeemopdrachten, Docker-scripts",
- "folderColorLabel": "Mapkleur",
- "folderIconLabel": "Mappictogram",
- "previewLabel": "Voorbeeld",
- "folderNameFallback": "Mapnaam",
- "createFolderButton": "Map maken",
- "targetTerminals": "Doelterminals",
- "selectAll": "Alle",
- "selectNone": "Geen",
- "noTerminalTabsOpen": "Geen terminaltabbladen geopend",
- "searchPlaceholder": "Zoekfragmenten...",
- "newSnippet": "Nieuw fragment",
- "newFolder": "Nieuwe map",
- "run": "Loop",
- "noSnippetsInFolder": "Geen codefragmenten in deze map",
- "uncategorized": "Niet gecategoriseerd",
- "editSnippetTitle": "Bewerk fragment",
- "editSnippetDescription": "Werk dit opdrachtfragment bij.",
- "saveSnippetButton": "Wijzigingen opslaan",
- "createSuccess": "Codefragment succesvol aangemaakt",
- "createFailed": "Het is niet gelukt om een codefragment te maken.",
- "updateSuccess": "Codefragment succesvol bijgewerkt",
- "updateFailed": "Het bijwerken van het codefragment is mislukt.",
- "deleteFailed": "Het verwijderen van het fragment is mislukt.",
- "folderCreateSuccess": "Map succesvol aangemaakt",
- "folderCreateFailed": "Map aanmaken mislukt",
- "editFolderTitle": "Map bewerken",
- "editFolderDescription": "Hernoem of wijzig het uiterlijk van deze map.",
- "saveFolderButton": "Wijzigingen opslaan",
- "editFolder": "Map bewerken",
- "deleteFolder": "Map verwijderen",
- "folderDeleteSuccess": "Map \"{{name}}\" verwijderd",
- "folderDeleteFailed": "Map verwijderen mislukt",
- "folderEditSuccess": "Map succesvol bijgewerkt",
- "folderEditFailed": "Het bijwerken van de map is mislukt.",
- "confirmRunMessage": "Run \"{{name}}\"?",
- "confirmRunButton": "Loop",
- "runSuccess": "Voerde \"{{name}}\" uit in {{count}} terminal(s)",
- "pasteSuccess": "\"{{name}}\" is geplakt in de terminal(s) {{count}}",
- "pasteToTerminal": "Plakken",
- "copySuccess": "\"{{name}}\" is naar het klembord gekopieerd",
- "shareTitle": "Fragment delen",
- "shareUser": "Gebruiker",
- "shareRole": "Rol",
- "selectUser": "Selecteer een gebruiker...",
- "selectRole": "Kies een rol...",
- "shareSuccess": "Fragment succesvol gedeeld",
- "shareFailed": "Het delen van het fragment is mislukt.",
- "revokeSuccess": "Toegang ingetrokken",
- "revokeFailed": "Toegang intrekken is mislukt",
- "currentAccess": "Huidige toegang",
- "shareLoadError": "Het laden van de gedeelde gegevens is mislukt.",
- "loading": "Laden...",
- "close": "Dichtbij",
- "reorderFailed": "Het opslaan van de volgorde van de codefragmenten is mislukt.",
- "importExport": "Import/Export",
- "exportBtn": "Exporteer JSON",
- "importBtn": "JSON importeren",
- "exportSuccess": "Codefragmenten succesvol geëxporteerd",
- "exportFailed": "Het exporteren van codefragmenten is mislukt.",
- "importTitle": "Importeer codefragmenten",
- "importDescription": "Importeer codefragmenten en mappen vanuit een JSON-bestand dat is geëxporteerd door Termix.",
- "importDropOrClick": "Sleep hier een JSON-bestand naartoe of klik om te bladeren.",
- "importSelectedFile": "Geselecteerd: {{name}}",
- "importOverwrite": "Overschrijf bestaande codefragmenten met dezelfde naam en map.",
- "importStartBtn": "Importeren",
- "importSuccess": "Import voltooid: {{snippets}} fragment(en) toegevoegd, {{updated}} bijgewerkt, {{skipped}} overgeslagen, {{folders}} map(pen) toegevoegd",
- "importFailed": "Het importeren van codefragmenten is mislukt.",
- "importInvalidFile": "Ongeldig bestand: er werd een JSON-object verwacht met snippets of mappenarrays.",
- "targetHostsLabel": "Doelhosts",
- "targetHostsHint": "Wijs hosts toe om dit codefragment direct uit te voeren zonder een geopende terminal.",
- "noHostsAvailable": "Geen hosts geconfigureerd",
- "clearTargetHosts": "Alles wissen",
- "hasTargetHosts": "Heeft doelhosts",
- "runOnTargets": "Uitvoeren op doelen",
- "directRunSuccess": "Voerde \"{{name}}\" uit op {{count}} host(s)",
- "directRunPartialFail": "\"{{name}}\" is mislukt op een of meer hosts",
- "executionResultTitle": "Uitvoeringsresultaten: {{name}}",
- "executionResultDescription": "Uitvoer van het uitvoeren van het codefragment op elke doelhost.",
- "executionSuccess": "Succes",
- "executionFailed": "Mislukt",
- "expandAll": "Vouw alle mappen uit",
- "collapseAll": "Alle mappen inklappen",
- "settings": "Snippet-instellingen",
- "variablesHint": "Variabelen: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Uitvoeren \"{{name}}\"",
- "variablesDialogDescription": "Vul de waarden in die dit codefragment nodig heeft voordat je het uitvoert.",
- "variablesPreviewLabel": "Voorbeeld",
- "variablesConfirmButton": "Loop"
- },
"keybindings": {
"title": "Toetsenbord sneltoetsen",
"description": "Pas de sneltoetsen voor kopiëren, plakken en besturing in de terminal aan, of koppel toetsen aan het verzenden van tekst of het uitvoeren van een codefragment.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klik en druk op de toetsen",
"recording": "Druk op de toetsen...",
"comboRequiredError": "Druk eerst op een toetsencombinatie.",
- "textRequiredError": "Voer de tekst in die u wilt verzenden",
- "controlCodeRequiredError": "Voer één letter in als besturingscode.",
- "snippetRequiredError": "Selecteer een fragment",
"conflictWarning": "Deze combinatie is al gekoppeld aan {{combo}}. Door op te slaan zullen beide vuren.",
"actionLabel": "Actie",
- "actionCopy": "Selectie kopiëren",
- "actionPaste": "Plakken",
- "actionSendControlCode": "Verzend het Ctrl+letter-signaal",
- "actionSendText": "Sms verzenden",
- "actionRunSnippet": "Voer het bestaande codefragment uit.",
"actionNextTab": "Ga naar het volgende tabblad",
"actionPreviousTab": "Ga terug naar het vorige tabblad",
- "actionOpenCommandPalette": "Snel zoeken openen",
- "controlCodeLabel": "Brief",
- "textLabel": "Tekst om te verzenden",
- "appendEnterLabel": "Druk op Enter na het verzenden.",
- "snippetLabel": "Fragment",
- "selectSnippetPlaceholder": "Selecteer een fragment",
- "orphanedSnippetWarning": "fragment niet gevonden",
- "clipboardPermissionNote": "In sommige browsers kan bij het eerste gebruik om toestemming voor het gebruik van het klembord worden gevraagd."
+ "actionOpenCommandPalette": "Snel zoeken openen"
},
"userProfile": {
"donateTitle": "Houd Termix in leven",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Onderwerp is verplicht",
"ntfyToken": "Toegangstoken (optioneel)",
"channelSaveFailed": "Het opslaan van het kanaal is mislukt."
- },
- "macros": {
- "title": "Macro's",
- "description": "Voer een opgeslagen reeks commando's uit in een terminal en reageer op wat de server terugstuurt.",
- "target": "Doel",
- "noTerminal": "Geen terminal geopend",
- "empty": "Nog geen macro's",
- "emptyHint": "Maak een macro om een terminalproces te automatiseren, zoals inloggen, een service herstarten of prompts beantwoorden.",
- "create": "Nieuwe macro",
- "back": "Terug naar macro's",
- "name": "Naam",
- "namePlaceholder": "Webserver opnieuw opstarten",
- "nameRequired": "Geef de macro een naam voordat je hem opslaat.",
- "descriptionLabel": "Beschrijving",
- "descriptionPlaceholder": "Wat deze macro doet (optioneel)",
- "unsaved": "Niet-opgeslagen wijzigingen",
- "save": "Redden",
- "saved": "Macro opgeslagen",
- "saveFailed": "Het lukte niet om de macro op te slaan.",
- "delete": "Verwijderen",
- "deleteTitle": "Deze macro verwijderen?",
- "deleteBody": "\"{{name}}\" wordt permanent verwijderd. Dit kan niet ongedaan gemaakt worden.",
- "deleted": "Macro verwijderd",
- "cancel": "Annuleren",
- "run": "Loop",
- "stop": "Stop",
- "running": "Rennen...",
- "completed": "Macro voltooid",
- "failed": "Macro mislukt",
- "loadFailed": "Macro's konden niet worden geladen.",
- "needTerminal": "Open en verbind eerst een terminal.",
- "noSteps": "Deze macro bevat nog geen stappen. Voeg er hieronder een toe.",
- "addStep": "Voeg stap toe",
- "stepNumber": "Stap {{number}}",
- "removeStep": "Verwijder stap",
- "confirmRunTitle": "Voer deze macro uit?",
- "confirmRunBody": "\"{{name}}\" zal commando's naar {{host}} sturen.",
- "confirmRunAction": "Macro uitvoeren",
- "stepTypes": {
- "send": {
- "label": "Verzend commando",
- "help": "Typt tekst in de terminal, net alsof je die zelf hebt ingetypt."
- },
- "wait": {
- "label": "Wacht op uitvoer",
- "help": "Het programma pauzeert totdat de server de door u opgegeven tekst afdrukt, zoals bij een wachtwoordprompt."
- },
- "delay": {
- "label": "Pauze",
- "help": "Wacht een vaste tijdsduur voordat de volgende stap wordt uitgevoerd."
- },
- "if": {
- "label": "Als de uitvoer het volgende bevat:",
- "help": "Controleert alles wat de server tot nu toe heeft afgedrukt en voert vervolgens een van de twee vertakkingen uit."
- },
- "repeat": {
- "label": "Herhalen",
- "help": "Voert de stappen binnenin een vast aantal keren uit."
- }
- },
- "send": {
- "text": "Opdracht of tekst",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Druk op Enter na het verzenden.",
- "pressEnterHelp": "Schakel deze optie uit om tekst te typen zonder deze te verzenden."
- },
- "wait": {
- "pattern": "Wacht tot de uitvoer het volgende bevat:",
- "patternPlaceholder": "wachtwoord:",
- "timeout": "Geef het op na",
- "onTimeout": "Als het nooit verschijnt",
- "stop": "Stop de macro",
- "continue": "Ga toch door."
- },
- "delay": {
- "duration": "Wachten op"
- },
- "ifStep": {
- "pattern": "Als de uitvoer het volgende bevat:",
- "patternPlaceholder": "Actief: actief (lopend)",
- "then": "Doe dan dit",
- "else": "Doe anders dit",
- "emptyBranch": "Geen treden"
- },
- "repeat": {
- "count": "Aantal keren",
- "times": "tijden"
- },
- "match": {
- "useRegex": "Overeenkomst met een reguliere expressie",
- "useRegexHelp": "Laat dit weg zodat de tekst exact overeenkomt met de geschreven tekst.",
- "invalidRegex": "Dit is geen geldige reguliere expressie; het wordt als platte tekst herkend."
- },
- "seconds": "seconden",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/no_NO.json b/src/ui/locales/translated/no_NO.json
index c49fb84e2..9788affcd 100644
--- a/src/ui/locales/translated/no_NO.json
+++ b/src/ui/locales/translated/no_NO.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Innstillinger for sidefelt...",
"hosts": "Verter",
"snippets": "Utdrag",
- "macros": "Makroer",
"hostManager": "Vertsadministrator",
"credentials": "Legitimasjon",
"connections": "Tilkoblinger",
@@ -869,7 +868,6 @@
"groupTags": "Tagger og pin",
"groupTunnels": "Tunneler",
"groupJumpHosts": "Hoppverter",
- "groupQuickActions": "Hurtighandlinger",
"groupFeatureFlags": "Funksjonsflagg",
"groupAdvanced": "Avansert konfigurasjon",
"preview": "Forhåndsvisning",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Lagre SSH-nøkler eller passord",
"tmuxMonitor": "Tmux-skjerm",
"tmuxMonitorDesc": "Overvåk tmux-økter på tvers av vertene dine",
- "snippets": "Utdrag",
- "runSnippetDesc": "Kjør i den aktive terminalfanen",
- "noSnippetsFound": "Ingen utdrag funnet som samsvarer med «{{search}}»",
"recentActivity": "Nylig aktivitet",
"serversAndHosts": "Servere og verter",
"noHostsFound": "Ingen verter funnet som samsvarer med «{{search}}»",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Forrige fane",
"hotkeysJumpTab": "Hopp til fane"
},
- "snippets": {
- "title": "Utdrag",
- "createSnippetTitle": "Opprett utdrag",
- "createSnippetDescription": "Opprett et nytt kommandosnutt for rask utførelse",
- "nameLabel": "Navn",
- "namePlaceholder": "f.eks. Start Nginx på nytt",
- "descriptionLabel": "Beskrivelse",
- "descriptionPlaceholder": "Valgfri beskrivelse",
- "optional": "Valgfri",
- "folderLabel": "Mappe",
- "noFolder": "Ingen mappe (ukategorisert)",
- "commandLabel": "Kommando",
- "commandPlaceholder": "f.eks. sudo systemctl start nginx på nytt",
- "typeLabel": "Type",
- "typeCommand": "Kommando",
- "typeNote": "Note",
- "noteLabel": "Note",
- "notePlaceholder": "f.eks. en gjenbrukbar ledetekst eller påminnelsestekst",
- "cancel": "Kansellere",
- "createSnippetButton": "Opprett utdrag",
- "createFolderTitle": "Opprett mappe",
- "createFolderDescription": "Organiser utdragene dine i mapper",
- "folderNameLabel": "Mappenavn",
- "folderNamePlaceholder": "f.eks. systemkommandoer, Docker-skript",
- "folderColorLabel": "Mappefarge",
- "folderIconLabel": "Mappeikon",
- "previewLabel": "Forhåndsvisning",
- "folderNameFallback": "Mappenavn",
- "createFolderButton": "Opprett mappe",
- "targetTerminals": "Target Terminals",
- "selectAll": "Alle",
- "selectNone": "Ingen",
- "noTerminalTabsOpen": "Ingen terminalfaner åpne",
- "searchPlaceholder": "Søk i utdrag ...",
- "newSnippet": "Nytt utdrag",
- "newFolder": "Ny mappe",
- "run": "Løp",
- "noSnippetsInFolder": "Ingen utdrag i denne mappen",
- "uncategorized": "Ukategorisert",
- "editSnippetTitle": "Rediger utdrag",
- "editSnippetDescription": "Oppdater dette kommandosnuttet",
- "saveSnippetButton": "Lagre endringer",
- "createSuccess": "Kodeutdraget er opprettet",
- "createFailed": "Kunne ikke opprette kodebiten",
- "updateSuccess": "Kodeutdraget er oppdatert",
- "updateFailed": "Kunne ikke oppdatere kodestykket",
- "deleteFailed": "Kunne ikke slette utdraget",
- "folderCreateSuccess": "Mappen er opprettet",
- "folderCreateFailed": "Kunne ikke opprette mappen",
- "editFolderTitle": "Rediger mappe",
- "editFolderDescription": "Gi denne mappen nytt navn eller endre utseendet",
- "saveFolderButton": "Lagre endringer",
- "editFolder": "Rediger mappe",
- "deleteFolder": "Slett mappe",
- "folderDeleteSuccess": "Mappen «{{name}}» er slettet",
- "folderDeleteFailed": "Kunne ikke slette mappen",
- "folderEditSuccess": "Mappen er oppdatert",
- "folderEditFailed": "Kunne ikke oppdatere mappen",
- "confirmRunMessage": "Kjør \"{{name}}\"?",
- "confirmRunButton": "Løp",
- "runSuccess": "Kjørte \"{{name}}\" i {{count}} terminal(er)",
- "pasteSuccess": "Limte inn «{{name}}» i {{count}} terminal(er)",
- "pasteToTerminal": "Lim inn",
- "copySuccess": "Kopierte «{{name}}» til utklippstavlen",
- "shareTitle": "Del utdrag",
- "shareUser": "Bruker",
- "shareRole": "Rolle",
- "selectUser": "Velg en bruker...",
- "selectRole": "Velg en rolle...",
- "shareSuccess": "Kodestykket ble delt",
- "shareFailed": "Kunne ikke dele utdraget",
- "revokeSuccess": "Tilgang tilbakekalt",
- "revokeFailed": "Kunne ikke tilbakekalle tilgang",
- "currentAccess": "Nåværende tilgang",
- "shareLoadError": "Kunne ikke laste inn delingsdata",
- "loading": "Laster inn...",
- "close": "Lukke",
- "reorderFailed": "Kunne ikke lagre rekkefølgen på kodeutdraget",
- "importExport": "Import/eksport",
- "exportBtn": "Eksporter JSON",
- "importBtn": "Importer JSON",
- "exportSuccess": "Utdragene er eksportert",
- "exportFailed": "Kunne ikke eksportere utdrag",
- "importTitle": "Importer utdrag",
- "importDescription": "Importer utdrag og mapper fra en JSON-fil eksportert av Termix",
- "importDropOrClick": "Slipp en JSON-fil her eller klikk for å bla gjennom",
- "importSelectedFile": "Valgt: {{name}}",
- "importOverwrite": "Overskriv eksisterende utdrag med samme navn og mappe",
- "importStartBtn": "Import",
- "importSuccess": "Import fullført: {{snippets}} kodebit(er) lagt til, {{updated}} oppdatert, {{skipped}} hoppet over, {{folders}} mappe(r) lagt til",
- "importFailed": "Kunne ikke importere kodebiter",
- "importInvalidFile": "Ugyldig fil: forventet et JSON-objekt med snippets eller mappearrayer",
- "targetHostsLabel": "Målverter",
- "targetHostsHint": "Tildel verter til å kjøre dette kodestykket direkte uten en åpen terminal.",
- "noHostsAvailable": "Ingen verter konfigurert",
- "clearTargetHosts": "Fjern alle",
- "hasTargetHosts": "Har målverter",
- "runOnTargets": "Løp på mål",
- "directRunSuccess": "Kjørte \"{{name}}\" på {{count}} vert(er)",
- "directRunPartialFail": "«{{name}}» mislyktes på én eller flere verter",
- "executionResultTitle": "Utførelsesresultater: {{name}}",
- "executionResultDescription": "Utdata fra kjøring av kodestykket på hver målvert.",
- "executionSuccess": "Suksess",
- "executionFailed": "Mislyktes",
- "expandAll": "Utvid alle mapper",
- "collapseAll": "Skjul alle mapper",
- "settings": "Innstillinger for utdrag",
- "variablesHint": "Variabler: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etikett}",
- "variablesDialogTitle": "Kjør «{{name}}»",
- "variablesDialogDescription": "Fyll inn verdiene dette kodestykket trenger før du kjører det.",
- "variablesPreviewLabel": "Forhåndsvisning",
- "variablesConfirmButton": "Løp"
- },
"keybindings": {
"title": "Tastatursnarveier",
"description": "Tilpass snarveier for terminalkopiering, liming og kontroll, eller bind taster for å sende tekst eller kjøre et tekstutdrag.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klikk og trykk på tastene",
"recording": "Trykk på tastene...",
"comboRequiredError": "Trykk først på en tastekombinasjon",
- "textRequiredError": "Skriv inn teksten som skal sendes",
- "controlCodeRequiredError": "Skriv inn én bokstav for kontrollkoden",
- "snippetRequiredError": "Velg et utdrag",
"conflictWarning": "Denne kombinasjonen er allerede bundet til {{combo}}. Lagring vil få begge til å aktiveres.",
"actionLabel": "Handling",
- "actionCopy": "Kopier valg",
- "actionPaste": "Lim inn",
- "actionSendControlCode": "Send Ctrl+bokstavsignal",
- "actionSendText": "Send tekstmelding",
- "actionRunSnippet": "Kjør eksisterende kodebit",
"actionNextTab": "Bytt til neste fane",
"actionPreviousTab": "Bytt til forrige fane",
- "actionOpenCommandPalette": "Åpne hurtigsøk",
- "controlCodeLabel": "Brev",
- "textLabel": "Tekst som skal sendes",
- "appendEnterLabel": "Trykk Enter etter sending",
- "snippetLabel": "Utdrag",
- "selectSnippetPlaceholder": "Velg et utdrag",
- "orphanedSnippetWarning": "utdraget ikke funnet",
- "clipboardPermissionNote": "Kan be om tillatelse til utklippstavlen ved første gangs bruk i noen nettlesere."
+ "actionOpenCommandPalette": "Åpne hurtigsøk"
},
"userProfile": {
"donateTitle": "Hold Termix i live",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Emne er obligatorisk",
"ntfyToken": "Tilgangstoken (valgfritt)",
"channelSaveFailed": "Kunne ikke lagre kanalen"
- },
- "macros": {
- "title": "Makroer",
- "description": "Kjør en lagret serie med kommandoer i en terminal, og reager på det serveren sender tilbake.",
- "target": "Mål",
- "noTerminal": "Ingen terminal åpen",
- "empty": "Ingen makroer ennå",
- "emptyHint": "Opprett en makro for å automatisere en terminalflyt, som å logge inn, starte en tjeneste på nytt eller svare på forespørsler.",
- "create": "Ny makro",
- "back": "Tilbake til makroer",
- "name": "Navn",
- "namePlaceholder": "Start nettserveren på nytt",
- "nameRequired": "Gi makroen et navn før du lagrer",
- "descriptionLabel": "Beskrivelse",
- "descriptionPlaceholder": "Hva denne makroen gjør (valgfritt)",
- "unsaved": "Ulagrede endringer",
- "save": "Spare",
- "saved": "Makro lagret",
- "saveFailed": "Kunne ikke lagre makroen",
- "delete": "Slett",
- "deleteTitle": "Slette denne makroen?",
- "deleteBody": "«{{name}}» vil bli slettet permanent. Dette kan ikke angres.",
- "deleted": "Makro slettet",
- "cancel": "Kansellere",
- "run": "Løp",
- "stop": "Stoppe",
- "running": "Løping...",
- "completed": "Makro ferdig",
- "failed": "Makroen mislyktes",
- "loadFailed": "Kunne ikke laste inn makroer",
- "needTerminal": "Åpne og koble til en terminal først",
- "noSteps": "Denne makroen har ingen trinn ennå. Legg til et nedenfor.",
- "addStep": "Legg til trinn",
- "stepNumber": "Trinn {{number}}",
- "removeStep": "Fjern trinn",
- "confirmRunTitle": "Kjøre denne makroen?",
- "confirmRunBody": "\"{{name}}\" vil sende kommandoer til {{host}}.",
- "confirmRunAction": "Kjør makro",
- "stepTypes": {
- "send": {
- "label": "Send kommando",
- "help": "Skriver tekst inn i terminalen, på samme måte som om du hadde skrevet den."
- },
- "wait": {
- "label": "Vent på utdata",
- "help": "Pauserer til serveren skriver ut teksten du oppgir, som en passordforespørsel."
- },
- "delay": {
- "label": "Pause",
- "help": "Venter en fast tid før neste trinn."
- },
- "if": {
- "label": "Hvis utdataene inneholder",
- "help": "Sjekker alt serveren har skrevet ut så langt, og kjører deretter en av to grener."
- },
- "repeat": {
- "label": "Gjenta",
- "help": "Kjører trinnene inni den et angitt antall ganger."
- }
- },
- "send": {
- "text": "Kommando eller tekst",
- "textPlaceholder": "systemctl omstart nginx",
- "pressEnter": "Trykk Enter etter sending",
- "pressEnterHelp": "Slå av dette for å skrive tekst uten å sende den inn."
- },
- "wait": {
- "pattern": "Vent til utdataene inneholder",
- "patternPlaceholder": "passord:",
- "timeout": "Gi opp etterpå",
- "onTimeout": "Hvis den aldri dukker opp",
- "stop": "Stopp makroen",
- "continue": "Fortsett uansett"
- },
- "delay": {
- "duration": "Vent på"
- },
- "ifStep": {
- "pattern": "Hvis utdataene inneholder",
- "patternPlaceholder": "Aktiv: aktiv (løper)",
- "then": "Gjør så dette",
- "else": "Ellers gjør dette",
- "emptyBranch": "Ingen trinn"
- },
- "repeat": {
- "count": "Antall ganger",
- "times": "ganger"
- },
- "match": {
- "useRegex": "Samsvar med et regulært uttrykk",
- "useRegexHelp": "La dette være av for å få teksten til å se nøyaktig slik den er skrevet.",
- "invalidRegex": "Ikke et gyldig regulært uttrykk, det vil bli matchet som ren tekst"
- },
- "seconds": "sekunder",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/pl_PL.json b/src/ui/locales/translated/pl_PL.json
index a5d0f40da..50d95b414 100644
--- a/src/ui/locales/translated/pl_PL.json
+++ b/src/ui/locales/translated/pl_PL.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Ustawienia paska bocznego...",
"hosts": "Hosty",
"snippets": "Snippety",
- "macros": "Makra",
"hostManager": "Menedżer hostów",
"credentials": "Poświadczenia",
"connections": "Połączenia",
@@ -869,7 +868,6 @@
"groupTags": "Tagi i przypinanie",
"groupTunnels": "Tunele",
"groupJumpHosts": "Hosty pośrednie",
- "groupQuickActions": "Szybkie akcje",
"groupFeatureFlags": "Flagi funkcji",
"groupAdvanced": "Zaawansowana konfiguracja",
"preview": "Podgląd",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Przechowuj klucze SSH lub hasła",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitoruj sesje tmux na swoich hostach",
- "snippets": "Snippety",
- "runSnippetDesc": "Uruchom w aktywnej karcie terminala",
- "noSnippetsFound": "Nie znaleziono snippetów pasujących do „{{search}}”",
"recentActivity": "Ostatnia aktywność",
"serversAndHosts": "Serwery i hosty",
"noHostsFound": "Nie znaleziono hostów pasujących do „{{search}}”",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Poprzednia karta",
"hotkeysJumpTab": "Przejdź do karty"
},
- "snippets": {
- "title": "Snippety",
- "createSnippetTitle": "Utwórz snippet",
- "createSnippetDescription": "Utwórz nowy snippet polecenia do szybkiego uruchamiania",
- "nameLabel": "Nazwa",
- "namePlaceholder": "np. Uruchom ponownie Nginx",
- "descriptionLabel": "Opis",
- "descriptionPlaceholder": "Opis opcjonalny",
- "optional": "Opcjonalne",
- "folderLabel": "Folder",
- "noFolder": "Brak folderu (bez kategorii)",
- "commandLabel": "Polecenie",
- "commandPlaceholder": "np. sudo systemctl restart nginx",
- "typeLabel": "Typ",
- "typeCommand": "Polecenie",
- "typeNote": "Notatka",
- "noteLabel": "Notatka",
- "notePlaceholder": "np. tekst monitu lub przypomnienia do wielokrotnego użytku",
- "cancel": "Anuluj",
- "createSnippetButton": "Utwórz snippet",
- "createFolderTitle": "Utwórz folder",
- "createFolderDescription": "Uporządkuj snippety w folderach",
- "folderNameLabel": "Nazwa folderu",
- "folderNamePlaceholder": "np. Polecenia systemowe, Skrypty Dockera",
- "folderColorLabel": "Kolor folderu",
- "folderIconLabel": "Ikona folderu",
- "previewLabel": "Podgląd",
- "folderNameFallback": "Nazwa folderu",
- "createFolderButton": "Utwórz folder",
- "targetTerminals": "Terminale docelowe",
- "selectAll": "Wszystkie",
- "selectNone": "Żaden",
- "noTerminalTabsOpen": "Brak otwartych kart terminala",
- "searchPlaceholder": "Wyszukaj snippety...",
- "newSnippet": "Nowy snippet",
- "newFolder": "Nowy folder",
- "run": "Uruchom",
- "noSnippetsInFolder": "Brak snippetów w tym folderze",
- "uncategorized": "Bez kategorii",
- "editSnippetTitle": "Edytuj snippet",
- "editSnippetDescription": "Zaktualizuj ten snippet polecenia",
- "saveSnippetButton": "Zapisz zmiany",
- "createSuccess": "Utworzono snippet",
- "createFailed": "Nie udało się utworzyć snippetu",
- "updateSuccess": "Zaktualizowano snippet",
- "updateFailed": "Nie udało się zaktualizować snippetu",
- "deleteFailed": "Nie udało się usunąć snippetu",
- "folderCreateSuccess": "Folder został pomyślnie utworzony",
- "folderCreateFailed": "Nie udało się utworzyć folderu",
- "editFolderTitle": "Edytuj folder",
- "editFolderDescription": "Zmień nazwę lub wygląd tego folderu",
- "saveFolderButton": "Zapisz zmiany",
- "editFolder": "Edytuj folder",
- "deleteFolder": "Usuń folder",
- "folderDeleteSuccess": "Folder „{{name}}” został usunięty",
- "folderDeleteFailed": "Nie udało się usunąć folderu",
- "folderEditSuccess": "Folder został pomyślnie zaktualizowany",
- "folderEditFailed": "Nie udało się zaktualizować folderu",
- "confirmRunMessage": "Uruchomić „{{name}}”?",
- "confirmRunButton": "Uruchom",
- "runSuccess": "Uruchomiono „{{name}}” w {{count}} terminalach",
- "pasteSuccess": "Wklejono „{{name}}” do {{count}} terminali",
- "pasteToTerminal": "Wklej",
- "copySuccess": "Skopiowano „{{name}}” do schowka",
- "shareTitle": "Udostępnij snippet",
- "shareUser": "Użytkownik",
- "shareRole": "Rola",
- "selectUser": "Wybierz użytkownika...",
- "selectRole": "Wybierz rolę...",
- "shareSuccess": "Udostępniono snippet",
- "shareFailed": "Nie udało się udostępnić snippetu",
- "revokeSuccess": "Unieważniono dostęp",
- "revokeFailed": "Nie udało się unieważnić dostępu",
- "currentAccess": "Aktualny dostęp",
- "shareLoadError": "Nie udało się załadować danych udostępniania",
- "loading": "Ładowanie...",
- "close": "Zamknij",
- "reorderFailed": "Nie udało się zapisać kolejności snippetów",
- "importExport": "Import/Eksport",
- "exportBtn": "Eksportuj JSON",
- "importBtn": "Importuj JSON",
- "exportSuccess": "Wyeksportowano snippety",
- "exportFailed": "Nie udało się wyeksportować snippetów",
- "importTitle": "Importuj snippety",
- "importDescription": "Importuj snippety i foldery z pliku JSON wyeksportowanego przez Termix",
- "importDropOrClick": "Upuść plik JSON tutaj lub kliknij, aby przeglądać",
- "importSelectedFile": "Wybrano: {{name}}",
- "importOverwrite": "Nadpisz istniejące snippety o tej samej nazwie i folderze",
- "importStartBtn": "Import",
- "importSuccess": "Import ukończony: dodano {{snippets}} snippetów, zaktualizowano {{updated}}, pominięto {{skipped}}, dodano {{folders}} folderów",
- "importFailed": "Nie udało się zaimportować snippetów",
- "importInvalidFile": "Nieprawidłowy plik: oczekiwano obiektu JSON z tablicami snippets lub folders",
- "targetHostsLabel": "Hosty docelowe",
- "targetHostsHint": "Przypisz hosty, aby uruchamiać ten snippet bezpośrednio, bez otwartego terminala.",
- "noHostsAvailable": "Brak skonfigurowanych hostów",
- "clearTargetHosts": "Wyczyść wszystko",
- "hasTargetHosts": "Ma hosty docelowe",
- "runOnTargets": "Uruchom na hostach docelowych",
- "directRunSuccess": "Uruchomiono „{{name}}” na {{count}} hostach",
- "directRunPartialFail": "„{{name}}” nie powiodło się na jednym lub kilku hostach",
- "executionResultTitle": "Wyniki wykonania: {{name}}",
- "executionResultDescription": "Wyjście z uruchomienia snippetu na każdym hoście docelowym.",
- "executionSuccess": "Powodzenie",
- "executionFailed": "Niepowodzenie",
- "expandAll": "Rozwiń wszystkie foldery",
- "collapseAll": "Zwiń wszystkie foldery",
- "settings": "Ustawienia snippetów",
- "variablesHint": "Zmienne: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Uruchom „{{name}}”",
- "variablesDialogDescription": "Przed uruchomieniem snippetu wprowadź wymagane wartości.",
- "variablesPreviewLabel": "Podgląd",
- "variablesConfirmButton": "Uruchom"
- },
"keybindings": {
"title": "Skróty klawiaturowe",
"description": "Dostosuj skróty kopiowania, wklejania i sterowania w terminalu albo przypisz klawiszom wysyłanie tekstu lub uruchomienie snippetu.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Kliknij i naciśnij klawisze",
"recording": "Naciśnij klawisze...",
"comboRequiredError": "Najpierw naciśnij kombinację klawiszy",
- "textRequiredError": "Wprowadź tekst do wysłania",
- "controlCodeRequiredError": "Wprowadź pojedynczą literę kodu kontrolnego",
- "snippetRequiredError": "Wybierz snippet",
"conflictWarning": "Ta kombinacja jest już przypisana do {{combo}}. Po zapisaniu zadziałają obie.",
"actionLabel": "Akcja",
- "actionCopy": "Kopiuj zaznaczenie",
- "actionPaste": "Wklej",
- "actionSendControlCode": "Wyślij sygnał Ctrl+litera",
- "actionSendText": "Wyślij tekst",
- "actionRunSnippet": "Uruchom istniejący snippet",
"actionNextTab": "Przejdź do następnej karty",
"actionPreviousTab": "Przejdź do poprzedniej karty",
- "actionOpenCommandPalette": "Otwórz szybkie wyszukiwanie",
- "controlCodeLabel": "Litera",
- "textLabel": "Tekst do wysłania",
- "appendEnterLabel": "Naciśnij Enter po wysłaniu",
- "snippetLabel": "Snippet",
- "selectSnippetPlaceholder": "Wybierz snippet",
- "orphanedSnippetWarning": "nie znaleziono snippetu",
- "clipboardPermissionNote": "Przy pierwszym użyciu w niektórych przeglądarkach może zostać wyświetlony monit o zezwolenie na dostęp do schowka."
+ "actionOpenCommandPalette": "Otwórz szybkie wyszukiwanie"
},
"userProfile": {
"donateTitle": "Utrzymajmy Termix przy życiu",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Temat jest wymagany",
"ntfyToken": "Token dostępu (opcjonalnie)",
"channelSaveFailed": "Nie udało się zapisać kanału"
- },
- "macros": {
- "title": "Makra",
- "description": "Uruchom zapisaną serię poleceń w terminalu i zareaguj na to, co odeśle serwer.",
- "target": "Cel",
- "noTerminal": "Brak otwartego terminala",
- "empty": "Brak makr",
- "emptyHint": "Utwórz makro, aby zautomatyzować pracę w terminalu: logowanie, restart usługi czy odpowiadanie na monity.",
- "create": "Nowe makro",
- "back": "Powrót do makr",
- "name": "Nazwa",
- "namePlaceholder": "Uruchom ponownie serwer WWW",
- "nameRequired": "Przed zapisaniem nadaj makrze nazwę",
- "descriptionLabel": "Opis",
- "descriptionPlaceholder": "Co robi to makro (opcjonalnie)",
- "unsaved": "Niezapisane zmiany",
- "save": "Zapisz",
- "saved": "Zapisano makro",
- "saveFailed": "Nie udało się zapisać makra",
- "delete": "Usuń",
- "deleteTitle": "Usunąć to makro?",
- "deleteBody": "„{{name}}” zostanie trwale usunięte. Nie można tego cofnąć.",
- "deleted": "Usunięto makro",
- "cancel": "Anuluj",
- "run": "Uruchom",
- "stop": "Zatrzymaj",
- "running": "Działa...",
- "completed": "Makro zakończone",
- "failed": "Makro nie powiodło się",
- "loadFailed": "Nie można załadować makr",
- "needTerminal": "Najpierw otwórz i podłącz terminal",
- "noSteps": "To makro nie ma jeszcze żadnych kroków. Dodaj krok poniżej.",
- "addStep": "Dodaj krok",
- "stepNumber": "Krok {{number}}",
- "removeStep": "Usuń krok",
- "confirmRunTitle": "Uruchomić to makro?",
- "confirmRunBody": "„{{name}}” wyśle polecenia do {{host}}.",
- "confirmRunAction": "Uruchom makro",
- "stepTypes": {
- "send": {
- "label": "Wyślij polecenie",
- "help": "Wpisuje tekst do terminala, tak samo, jakby wpisano go ręcznie."
- },
- "wait": {
- "label": "Czekaj na wyjście",
- "help": "Wstrzymuje wykonanie, aż serwer wypisze podany tekst, np. monit o hasło."
- },
- "delay": {
- "label": "Pauza",
- "help": "Czeka przez ustalony czas przed kolejnym krokiem."
- },
- "if": {
- "label": "Jeśli wyjście zawiera",
- "help": "Sprawdza wszystko, co serwer wypisał do tej pory, a potem uruchamia jedną z dwóch gałęzi."
- },
- "repeat": {
- "label": "Powtórz",
- "help": "Uruchamia zawarte w nim kroki ustaloną liczbę razy."
- }
- },
- "send": {
- "text": "Polecenie lub tekst",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Naciśnij Enter po wysłaniu",
- "pressEnterHelp": "Wyłącz tę opcję, aby wpisać tekst bez przesyłania go."
- },
- "wait": {
- "pattern": "Czekaj, aż wyjście będzie zawierać",
- "patternPlaceholder": "password:",
- "timeout": "Przerwij po",
- "onTimeout": "Jeśli nigdy się nie pojawi",
- "stop": "Zatrzymaj makro",
- "continue": "Kontynuuj mimo wszystko"
- },
- "delay": {
- "duration": "Czekaj"
- },
- "ifStep": {
- "pattern": "Jeśli wyjście zawiera",
- "patternPlaceholder": "Active: active (running)",
- "then": "Następnie zrób to",
- "else": "W przeciwnym razie zrób to",
- "emptyBranch": "Brak kroków"
- },
- "repeat": {
- "count": "Liczba razy",
- "times": "razy"
- },
- "match": {
- "useRegex": "Dopasuj do wyrażenia regularnego",
- "useRegexHelp": "Zostaw wyłączone, aby dopasować tekst dokładnie tak, jak został wpisany.",
- "invalidRegex": "Nie jest to prawidłowe wyrażenie regularne, zostanie dopasowane jako zwykły tekst"
- },
- "seconds": "sekundy",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/pt_BR.json b/src/ui/locales/translated/pt_BR.json
index b1e402b08..48fa74807 100644
--- a/src/ui/locales/translated/pt_BR.json
+++ b/src/ui/locales/translated/pt_BR.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Configurações da barra lateral...",
"hosts": "Anfitriões",
"snippets": "Trechos",
- "macros": "Macros",
"hostManager": "Gerente de Hospedagem",
"credentials": "Credenciais",
"connections": "Conexões",
@@ -869,7 +868,6 @@
"groupTags": "Etiquetas e alfinete",
"groupTunnels": "Túneis",
"groupJumpHosts": "Apresentadores do Jump",
- "groupQuickActions": "Ações rápidas",
"groupFeatureFlags": "Sinalizadores de recursos",
"groupAdvanced": "Configuração avançada",
"preview": "Pré-visualização",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Armazene chaves ou senhas SSH",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitore as sessões do tmux em seus hosts.",
- "snippets": "Trechos",
- "runSnippetDesc": "Execute na aba do terminal ativo",
- "noSnippetsFound": "Nenhum trecho encontrado que corresponda a \"{{search}}\"",
"recentActivity": "Atividade recente",
"serversAndHosts": "Servidores e Hosts",
"noHostsFound": "Nenhum host encontrado que corresponda a \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Aba anterior",
"hotkeysJumpTab": "Ir para a aba"
},
- "snippets": {
- "title": "Trechos",
- "createSnippetTitle": "Criar trecho de código",
- "createSnippetDescription": "Crie um novo trecho de comando para execução rápida.",
- "nameLabel": "Nome",
- "namePlaceholder": "Por exemplo, reinicie o Nginx.",
- "descriptionLabel": "Descrição",
- "descriptionPlaceholder": "Descrição opcional",
- "optional": "Opcional",
- "folderLabel": "Pasta",
- "noFolder": "Sem pasta (Não categorizado)",
- "commandLabel": "Comando",
- "commandPlaceholder": "Por exemplo, sudo systemctl restart nginx",
- "typeLabel": "Tipo",
- "typeCommand": "Comando",
- "typeNote": "Observação",
- "noteLabel": "Observação",
- "notePlaceholder": "Por exemplo, um texto de lembrete ou aviso reutilizável.",
- "cancel": "Cancelar",
- "createSnippetButton": "Criar trecho de código",
- "createFolderTitle": "Criar pasta",
- "createFolderDescription": "Organize seus trechos em pastas.",
- "folderNameLabel": "Nome da pasta",
- "folderNamePlaceholder": "Por exemplo, comandos do sistema, scripts do Docker.",
- "folderColorLabel": "Pasta colorida",
- "folderIconLabel": "Ícone de pasta",
- "previewLabel": "Pré-visualização",
- "folderNameFallback": "Nome da pasta",
- "createFolderButton": "Criar pasta",
- "targetTerminals": "Terminais de destino",
- "selectAll": "Todos",
- "selectNone": "Nenhum",
- "noTerminalTabsOpen": "Nenhuma aba do terminal aberta",
- "searchPlaceholder": "Trechos de pesquisa...",
- "newSnippet": "Novo trecho",
- "newFolder": "Nova pasta",
- "run": "Correr",
- "noSnippetsInFolder": "Não há trechos de código nesta pasta.",
- "uncategorized": "Sem categoria",
- "editSnippetTitle": "Editar trecho",
- "editSnippetDescription": "Atualize este trecho de comando",
- "saveSnippetButton": "Salvar alterações",
- "createSuccess": "Trecho criado com sucesso",
- "createFailed": "Falha ao criar o trecho de código",
- "updateSuccess": "Trecho de código atualizado com sucesso",
- "updateFailed": "Falha ao atualizar o trecho de código",
- "deleteFailed": "Falha ao excluir o trecho",
- "folderCreateSuccess": "Pasta criada com sucesso",
- "folderCreateFailed": "Falha ao criar a pasta",
- "editFolderTitle": "Pasta de edição",
- "editFolderDescription": "Renomeie ou altere a aparência desta pasta.",
- "saveFolderButton": "Salvar alterações",
- "editFolder": "Pasta de edição",
- "deleteFolder": "Excluir pasta",
- "folderDeleteSuccess": "Pasta \"{{name}}\" excluída",
- "folderDeleteFailed": "Falha ao excluir a pasta",
- "folderEditSuccess": "Pasta atualizada com sucesso",
- "folderEditFailed": "Falha ao atualizar a pasta",
- "confirmRunMessage": "Executar \"{{name}}\"?",
- "confirmRunButton": "Correr",
- "runSuccess": "Executado \"{{name}}\" em {{count}} terminal(is)",
- "pasteSuccess": "Colado \"{{name}}\" em {{count}} terminal(is)",
- "pasteToTerminal": "Colar",
- "copySuccess": "Copiado \"{{name}}\" para a área de transferência",
- "shareTitle": "Compartilhar trecho",
- "shareUser": "Usuário",
- "shareRole": "Papel",
- "selectUser": "Selecione um usuário...",
- "selectRole": "Selecione uma função...",
- "shareSuccess": "Trecho compartilhado com sucesso",
- "shareFailed": "Falha ao compartilhar o trecho",
- "revokeSuccess": "Acesso revogado",
- "revokeFailed": "Falha ao revogar o acesso",
- "currentAccess": "Acesso atual",
- "shareLoadError": "Falha ao carregar os dados compartilhados.",
- "loading": "Carregando...",
- "close": "Fechar",
- "reorderFailed": "Falha ao salvar a ordem do trecho de código",
- "importExport": "Importação/Exportação",
- "exportBtn": "Exportar JSON",
- "importBtn": "Importar JSON",
- "exportSuccess": "Trechos exportados com sucesso",
- "exportFailed": "Falha ao exportar trechos de código.",
- "importTitle": "Importar trechos",
- "importDescription": "Importe trechos e pastas de um arquivo JSON exportado pelo Termix.",
- "importDropOrClick": "Arraste e solte um arquivo JSON aqui ou clique para navegar.",
- "importSelectedFile": "Selecionado: {{name}}",
- "importOverwrite": "Sobrescrever trechos de código existentes com o mesmo nome e pasta.",
- "importStartBtn": "Importar",
- "importSuccess": "Importação concluída: {{snippets}} trecho(s) adicionado(s), {{updated}} atualizado(s), {{skipped}} ignorado(s), {{folders}} pasta(s) adicionada(s)",
- "importFailed": "Falha ao importar trechos de código.",
- "importInvalidFile": "Arquivo inválido: esperava-se um objeto JSON com trechos ou arrays de pastas.",
- "targetHostsLabel": "Hosts de destino",
- "targetHostsHint": "Atribua hosts para executar este trecho de código diretamente, sem a necessidade de um terminal aberto.",
- "noHostsAvailable": "Nenhum host configurado",
- "clearTargetHosts": "Limpar tudo",
- "hasTargetHosts": "Possui hosts de destino",
- "runOnTargets": "Corrida com base em metas",
- "directRunSuccess": "Executado \"{{name}}\" em {{count}} host(s)",
- "directRunPartialFail": "\"{{name}}\" falhou em um ou mais hosts",
- "executionResultTitle": "Resultados da execução: {{name}}",
- "executionResultDescription": "Resultado da execução do trecho de código em cada host de destino.",
- "executionSuccess": "Sucesso",
- "executionFailed": "Fracassado",
- "expandAll": "Expandir todas as pastas",
- "collapseAll": "Recolher todas as pastas",
- "settings": "Configurações de trechos de código",
- "variablesHint": "Variáveis: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Executar \"{{name}}\"",
- "variablesDialogDescription": "Preencha os valores necessários para este trecho de código antes de executá-lo.",
- "variablesPreviewLabel": "Pré-visualização",
- "variablesConfirmButton": "Correr"
- },
"keybindings": {
"title": "Atalhos de teclado",
"description": "Personalize os atalhos de copiar, colar e controlar o terminal, ou associe teclas para enviar texto ou executar um trecho de código.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Clique e pressione as teclas.",
"recording": "Pressione as teclas...",
"comboRequiredError": "Pressione primeiro uma combinação de teclas",
- "textRequiredError": "Digite o texto que deseja enviar.",
- "controlCodeRequiredError": "Digite uma única letra para o código de controle.",
- "snippetRequiredError": "Selecione um trecho",
"conflictWarning": "Esta combinação já está vinculada a {{combo}}. Salvar fará com que ambas disparem.",
"actionLabel": "Ação",
- "actionCopy": "Copiar seleção",
- "actionPaste": "Colar",
- "actionSendControlCode": "Enviar sinal Ctrl+letra",
- "actionSendText": "Enviar mensagem de texto",
- "actionRunSnippet": "Executar trecho de código existente",
"actionNextTab": "Mudar para a próxima aba",
"actionPreviousTab": "Alternar para a aba anterior",
- "actionOpenCommandPalette": "Abrir pesquisa rápida",
- "controlCodeLabel": "Carta",
- "textLabel": "Texto a enviar",
- "appendEnterLabel": "Pressione Enter após enviar",
- "snippetLabel": "Trecho",
- "selectSnippetPlaceholder": "Selecione um trecho",
- "orphanedSnippetWarning": "trecho não encontrado",
- "clipboardPermissionNote": "Pode solicitar permissão para acessar a área de transferência no primeiro uso em alguns navegadores."
+ "actionOpenCommandPalette": "Abrir pesquisa rápida"
},
"userProfile": {
"donateTitle": "Mantenha o Termix vivo",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "É necessário definir o tema.",
"ntfyToken": "Token de acesso (opcional)",
"channelSaveFailed": "Falha ao salvar o canal"
- },
- "macros": {
- "title": "Macros",
- "description": "Execute uma série de comandos salvos em um terminal e reaja ao que o servidor envia de volta.",
- "target": "Alvo",
- "noTerminal": "Nenhum terminal aberto",
- "empty": "Ainda não há macros.",
- "emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como fazer login, reiniciar um serviço ou responder a solicitações.",
- "create": "Nova macro",
- "back": "Voltar às macros",
- "name": "Nome",
- "namePlaceholder": "Reinicie o servidor web.",
- "nameRequired": "Dê um nome à macro antes de salvar.",
- "descriptionLabel": "Descrição",
- "descriptionPlaceholder": "O que esta macro faz (opcional)",
- "unsaved": "Alterações não salvas",
- "save": "Salvar",
- "saved": "Macro salvo",
- "saveFailed": "Não foi possível salvar a macro.",
- "delete": "Excluir",
- "deleteTitle": "Excluir esta macro?",
- "deleteBody": "\"{{name}}\" será excluído permanentemente. Esta ação não pode ser desfeita.",
- "deleted": "Macro excluída",
- "cancel": "Cancelar",
- "run": "Correr",
- "stop": "Parar",
- "running": "Correndo...",
- "completed": "Macro finalizado",
- "failed": "A macro falhou",
- "loadFailed": "Não foi possível carregar as macros",
- "needTerminal": "Primeiro, abra e conecte um terminal.",
- "noSteps": "Esta macro ainda não possui etapas. Adicione uma abaixo.",
- "addStep": "Adicionar etapa",
- "stepNumber": "Passo {{number}}",
- "removeStep": "Remover etapa",
- "confirmRunTitle": "Executar esta macro?",
- "confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
- "confirmRunAction": "Executar macro",
- "stepTypes": {
- "send": {
- "label": "Enviar comando",
- "help": "Digita o texto no terminal, da mesma forma que se você o digitasse."
- },
- "wait": {
- "label": "Aguarde a saída",
- "help": "Aguarda até que o servidor imprima o texto que você digitou, como uma solicitação de senha."
- },
- "delay": {
- "label": "Pausa",
- "help": "Aguarda um período de tempo fixo antes da próxima etapa."
- },
- "if": {
- "label": "Se a saída contiver",
- "help": "Verifica tudo o que o servidor imprimiu até o momento e, em seguida, executa uma das duas ramificações."
- },
- "repeat": {
- "label": "Repita",
- "help": "Executa os passos internos um número definido de vezes."
- }
- },
- "send": {
- "text": "Comando ou texto",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Pressione Enter após enviar",
- "pressEnterHelp": "Desative esta opção para digitar texto sem enviá-lo."
- },
- "wait": {
- "pattern": "Aguarde até que a saída contenha",
- "patternPlaceholder": "senha:",
- "timeout": "Desistir depois",
- "onTimeout": "Se nunca aparecer",
- "stop": "Pare a macro",
- "continue": "Continue mesmo assim"
- },
- "delay": {
- "duration": "Aguarde"
- },
- "ifStep": {
- "pattern": "Se a saída contiver",
- "patternPlaceholder": "Ativo: ativo (em execução)",
- "then": "Então faça isso",
- "else": "Caso contrário, faça isto",
- "emptyBranch": "Sem degraus"
- },
- "repeat": {
- "count": "Número de vezes",
- "times": "vezes"
- },
- "match": {
- "useRegex": "Combinar com uma expressão regular",
- "useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
- "invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
- },
- "seconds": "segundos",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/pt_PT.json b/src/ui/locales/translated/pt_PT.json
index d98c9537f..b4480ad98 100644
--- a/src/ui/locales/translated/pt_PT.json
+++ b/src/ui/locales/translated/pt_PT.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Definições da barra lateral...",
"hosts": "Máquinas",
"snippets": "Fragmentos",
- "macros": "Macros",
"hostManager": "Gestor de Máquinas",
"credentials": "Credenciais",
"connections": "Ligações",
@@ -869,7 +868,6 @@
"groupTags": "Etiquetas e alfinete",
"groupTunnels": "Túneis",
"groupJumpHosts": "Apresentadores do Jump",
- "groupQuickActions": "Ações rápidas",
"groupFeatureFlags": "Sinalizadores de recursos",
"groupAdvanced": "Configuração avançada",
"preview": "Pré-visualização",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Guardar chaves SSH ou palavras-passe",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitorizar sessões tmux nos seus hosts",
- "snippets": "Trechos",
- "runSnippetDesc": "Execute no separador do terminal ativo",
- "noSnippetsFound": "Não foram encontrados trechos que correspondam a \"{{search}}\"",
"recentActivity": "Atividade Recente",
"serversAndHosts": "Servidores e Hosts",
"noHostsFound": "Nenhum host encontrado que corresponda a \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Separador anterior",
"hotkeysJumpTab": "Ir para o separador"
},
- "snippets": {
- "title": "Fragmentos",
- "createSnippetTitle": "Criar Fragmento",
- "createSnippetDescription": "Criar um novo fragmento de comando para execução rápida",
- "nameLabel": "Nome",
- "namePlaceholder": "p. ex., Reiniciar Nginx",
- "descriptionLabel": "Descrição",
- "descriptionPlaceholder": "Descrição opcional",
- "optional": "Opcional",
- "folderLabel": "Pasta",
- "noFolder": "Sem pasta (Não categorizado)",
- "commandLabel": "Comando",
- "commandPlaceholder": "p. ex., sudo systemctl restart nginx",
- "typeLabel": "Tipo",
- "typeCommand": "Comando",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "Por exemplo, um texto de lembrete ou aviso reutilizável.",
- "cancel": "Cancelar",
- "createSnippetButton": "Criar Fragmento",
- "createFolderTitle": "Criar Pasta",
- "createFolderDescription": "Organize os seus fragmentos em pastas",
- "folderNameLabel": "Nome da Pasta",
- "folderNamePlaceholder": "p. ex., Comandos de Sistema, Scripts Docker",
- "folderColorLabel": "Cor da Pasta",
- "folderIconLabel": "Ícone da Pasta",
- "previewLabel": "Pré-visualização",
- "folderNameFallback": "Nome da Pasta",
- "createFolderButton": "Criar Pasta",
- "targetTerminals": "Terminais de Destino",
- "selectAll": "Todos",
- "selectNone": "Nenhum",
- "noTerminalTabsOpen": "Nenhum separador de terminal aberto",
- "searchPlaceholder": "Pesquisar snippets...",
- "newSnippet": "Novo Snippet",
- "newFolder": "Nova Pasta",
- "run": "Executar",
- "noSnippetsInFolder": "Nenhum snippet nesta pasta",
- "uncategorized": "Sem categoria",
- "editSnippetTitle": "Editar Snippet",
- "editSnippetDescription": "Atualizar este snippet de comando",
- "saveSnippetButton": "Guardar Alterações",
- "createSuccess": "Snippet criado com sucesso",
- "createFailed": "Falha ao criar snippet",
- "updateSuccess": "Snippet atualizado com sucesso",
- "updateFailed": "Falha ao atualizar snippet",
- "deleteFailed": "Falha ao eliminar snippet",
- "folderCreateSuccess": "Pasta criada com sucesso",
- "folderCreateFailed": "Falha ao criar pasta",
- "editFolderTitle": "Editar Pasta",
- "editFolderDescription": "Renomear ou alterar a aparência desta pasta",
- "saveFolderButton": "Guardar Alterações",
- "editFolder": "Editar pasta",
- "deleteFolder": "Eliminar pasta",
- "folderDeleteSuccess": "Pasta \"{{name}}\" eliminada",
- "folderDeleteFailed": "Falha ao eliminar pasta",
- "folderEditSuccess": "Pasta atualizada com sucesso",
- "folderEditFailed": "Falha ao atualizar pasta",
- "confirmRunMessage": "Executar \"{{name}}\"?",
- "confirmRunButton": "Executar",
- "runSuccess": "Executado \"{{name}}\" em {{count}} terminal(is)",
- "pasteSuccess": "Colado \"{{name}}\" em {{count}} terminal(is)",
- "pasteToTerminal": "Colar",
- "copySuccess": "Copiado \"{{name}}\" para a área de transferência",
- "shareTitle": "Partilhar Snippet",
- "shareUser": "Utilizador",
- "shareRole": "Função",
- "selectUser": "Selecionar um utilizador...",
- "selectRole": "Selecionar uma função...",
- "shareSuccess": "Fragmento partilhado com sucesso",
- "shareFailed": "Falha ao partilhar fragmento",
- "revokeSuccess": "Acesso revogado",
- "revokeFailed": "Falha ao revogar acesso",
- "currentAccess": "Acesso Atual",
- "shareLoadError": "Falha ao carregar dados de partilha",
- "loading": "A carregar...",
- "close": "Fechar",
- "reorderFailed": "Falha ao guardar ordem dos fragmentos",
- "importExport": "Importar / Exportar",
- "exportBtn": "Exportar JSON",
- "importBtn": "Importar JSON",
- "exportSuccess": "Fragmentos exportados com sucesso",
- "exportFailed": "Falha ao exportar fragmentos",
- "importTitle": "Importar Fragmentos",
- "importDescription": "Importar fragmentos e pastas de um ficheiro JSON exportado pelo Termix",
- "importDropOrClick": "Arraste um ficheiro JSON aqui ou clique para procurar",
- "importSelectedFile": "Selecionado: {{name}}",
- "importOverwrite": "Substituir fragmentos existentes com o mesmo nome e pasta",
- "importStartBtn": "Importar",
- "importSuccess": "Importação concluída: {{snippets}} fragmento(s) adicionado(s), {{updated}} atualizado(s), {{skipped}} ignorado(s), {{folders}} pasta(s) adicionada(s)",
- "importFailed": "Falha ao importar fragmentos",
- "importInvalidFile": "Ficheiro inválido: esperado um objeto JSON com arrays de fragmentos ou pastas",
- "targetHostsLabel": "Máquinas de Destino",
- "targetHostsHint": "Atribuir máquinas para executar este fragmento diretamente sem um terminal aberto.",
- "noHostsAvailable": "Nenhuma máquina configurada",
- "clearTargetHosts": "Limpar tudo",
- "hasTargetHosts": "Possui máquinas de destino",
- "runOnTargets": "Executar nos Destinos",
- "directRunSuccess": "Executou \"{{name}}\" em {{count}} máquina(s)",
- "directRunPartialFail": "\"{{name}}\" falhou numa ou mais máquinas",
- "executionResultTitle": "Resultados da Execução: {{name}}",
- "executionResultDescription": "Saída da execução do fragmento em cada host de destino.",
- "executionSuccess": "Sucesso",
- "executionFailed": "Falhou",
- "expandAll": "Expandir todas as pastas",
- "collapseAll": "Recolher todas as pastas",
- "settings": "Configurações de excertos de código",
- "variablesHint": "Variáveis: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Executar \"{{name}}\"",
- "variablesDialogDescription": "Preencha os valores necessários para este excerto de código antes de o executar.",
- "variablesPreviewLabel": "Pré-visualização",
- "variablesConfirmButton": "Correr"
- },
"keybindings": {
"title": "Atalhos de teclado",
"description": "Personalize os atalhos de copiar, colar e controlar o terminal, ou associe teclas para enviar texto ou executar um trecho de código.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Clique e pressione as teclas.",
"recording": "Pressione as teclas...",
"comboRequiredError": "Pressione primeiro uma combinação de teclas",
- "textRequiredError": "Introduza o texto que pretende enviar.",
- "controlCodeRequiredError": "Introduza uma única letra para o código de controlo.",
- "snippetRequiredError": "Selecione um excerto",
"conflictWarning": "Esta combinação já está ligada a {{combo}}. Guardar fará com que ambas disparem.",
"actionLabel": "Ação",
- "actionCopy": "Copiar seleção",
- "actionPaste": "Colar",
- "actionSendControlCode": "Enviar sinal Ctrl+letra",
- "actionSendText": "Enviar mensagem de texto",
- "actionRunSnippet": "Executar excerto de código existente",
"actionNextTab": "Mudar para o separador seguinte",
"actionPreviousTab": "Alternar para o separador anterior",
- "actionOpenCommandPalette": "Abrir pesquisa rápida",
- "controlCodeLabel": "Carta",
- "textLabel": "Texto a enviar",
- "appendEnterLabel": "Prima Enter após enviar",
- "snippetLabel": "Trecho",
- "selectSnippetPlaceholder": "Selecione um excerto",
- "orphanedSnippetWarning": "trecho não encontrado",
- "clipboardPermissionNote": "Pode solicitar permissão para aceder à área de transferência na primeira utilização em alguns navegadores."
+ "actionOpenCommandPalette": "Abrir pesquisa rápida"
},
"userProfile": {
"donateTitle": "Mantenha o Termix ativo",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "O tópico é obrigatório",
"ntfyToken": "Token de acesso (opcional)",
"channelSaveFailed": "Falha ao guardar o canal"
- },
- "macros": {
- "title": "Macros",
- "description": "Execute uma série de comandos guardados num terminal e reaja ao que o servidor envia de volta.",
- "target": "Objetivo",
- "noTerminal": "Nenhum terminal aberto",
- "empty": "Ainda não existem macros.",
- "emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como por exemplo, iniciar sessão, reiniciar um serviço ou responder a pedidos.",
- "create": "Nova macro",
- "back": "Voltar às macros",
- "name": "Nome",
- "namePlaceholder": "Reinicie o servidor web.",
- "nameRequired": "Dê um nome à macro antes de guardar.",
- "descriptionLabel": "Descrição",
- "descriptionPlaceholder": "O que faz esta macro (opcional)",
- "unsaved": "Alterações não guardadas",
- "save": "Guardar",
- "saved": "Macro salvo",
- "saveFailed": "Não foi possível guardar a macro.",
- "delete": "Eliminar",
- "deleteTitle": "Apagar esta macro?",
- "deleteBody": "\"{{name}}\" será eliminado permanentemente. Esta ação não pode ser anulada.",
- "deleted": "Macro excluída",
- "cancel": "Cancelar",
- "run": "Correr",
- "stop": "Parar",
- "running": "Corrida...",
- "completed": "Macro finalizado",
- "failed": "A macro falhou",
- "loadFailed": "Não foi possível carregar as macros",
- "needTerminal": "Primeiro, abra e ligue um terminal.",
- "noSteps": "Esta macro ainda não tem etapas. Adicione uma abaixo.",
- "addStep": "Adicionar etapa",
- "stepNumber": "Passo {{number}}",
- "removeStep": "Remover etapa",
- "confirmRunTitle": "Executar esta macro?",
- "confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
- "confirmRunAction": "Executar macro",
- "stepTypes": {
- "send": {
- "label": "Enviar comando",
- "help": "Digita o texto no terminal, da mesma forma que se o digitasse."
- },
- "wait": {
- "label": "Aguarde a saída",
- "help": "Aguarda até que o servidor imprima o texto que introduziu, como por exemplo um pedido de palavra-passe."
- },
- "delay": {
- "label": "Pausa",
- "help": "Aguarda um período de tempo fixo antes da etapa seguinte."
- },
- "if": {
- "label": "Se a saída contiver",
- "help": "Verifica tudo o que o servidor imprimiu até à data e, em seguida, executa uma das duas ramificações."
- },
- "repeat": {
- "label": "Repetir",
- "help": "Executa os passos internos um número definido de vezes."
- }
- },
- "send": {
- "text": "Comando ou texto",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Prima Enter após enviar",
- "pressEnterHelp": "Desative esta opção para digitar texto sem o enviar."
- },
- "wait": {
- "pattern": "Aguarde até que a saída contenha",
- "patternPlaceholder": "palavra-passe:",
- "timeout": "Desistir depois",
- "onTimeout": "Se nunca aparecer",
- "stop": "Pare a macro",
- "continue": "Continue mesmo assim"
- },
- "delay": {
- "duration": "Aguarde"
- },
- "ifStep": {
- "pattern": "Se a saída contiver",
- "patternPlaceholder": "Ativo: ativo (em execução)",
- "then": "Então faça isso",
- "else": "Caso contrário, faça-o",
- "emptyBranch": "Sem degraus"
- },
- "repeat": {
- "count": "Número de vezes",
- "times": "vezes"
- },
- "match": {
- "useRegex": "Combinar com uma expressão regular",
- "useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
- "invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
- },
- "seconds": "segundos",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ro_RO.json b/src/ui/locales/translated/ro_RO.json
index 39ec9495d..5a58cba3c 100644
--- a/src/ui/locales/translated/ro_RO.json
+++ b/src/ui/locales/translated/ro_RO.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Setări bară laterală...",
"hosts": "Gazde",
"snippets": "Fragmente",
- "macros": "Macrocomenzi",
"hostManager": "Manager de gazdă",
"credentials": "Acreditări",
"connections": "Conexiuni",
@@ -869,7 +868,6 @@
"groupTags": "Etichete și pinuri",
"groupTunnels": "Tuneluri",
"groupJumpHosts": "Gazde de salt",
- "groupQuickActions": "Acțiuni rapide",
"groupFeatureFlags": "Steaguri de caracteristici",
"groupAdvanced": "Configurație avansată",
"preview": "Previzualizare",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Stocarea cheilor SSH sau a parolelor",
"tmuxMonitor": "Monitor Tmux",
"tmuxMonitorDesc": "Monitorizați sesiunile tmux pe toate gazdele dvs.",
- "snippets": "Fragmente",
- "runSnippetDesc": "Execută în fila activă a terminalului",
- "noSnippetsFound": "Nu s-au găsit fragmente care să corespundă cu „{{search}}”",
"recentActivity": "Activitate recentă",
"serversAndHosts": "Servere și gazde",
"noHostsFound": "Nu s-au găsit gazde care să corespundă cu „{{search}}”",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Fila anterioară",
"hotkeysJumpTab": "Salt la filă"
},
- "snippets": {
- "title": "Fragmente",
- "createSnippetTitle": "Creați fragment",
- "createSnippetDescription": "Creați un nou fragment de comandă pentru execuție rapidă",
- "nameLabel": "Nume",
- "namePlaceholder": "De exemplu, reporniți Nginx",
- "descriptionLabel": "Descriere",
- "descriptionPlaceholder": "Descriere opțională",
- "optional": "Opțional",
- "folderLabel": "Pliant",
- "noFolder": "Niciun dosar (Necategorizat)",
- "commandLabel": "Comanda",
- "commandPlaceholder": "de exemplu, sudo systemctl restart nginx",
- "typeLabel": "Tip",
- "typeCommand": "Comanda",
- "typeNote": "Nota",
- "noteLabel": "Nota",
- "notePlaceholder": "de exemplu, un mesaj sau un text de reamintire reutilizabil",
- "cancel": "Anula",
- "createSnippetButton": "Creați fragment",
- "createFolderTitle": "Creați un folder",
- "createFolderDescription": "Organizați fragmentele în foldere",
- "folderNameLabel": "Nume folder",
- "folderNamePlaceholder": "de exemplu, comenzi de sistem, scripturi Docker",
- "folderColorLabel": "Culoarea folderului",
- "folderIconLabel": "Pictogramă folder",
- "previewLabel": "Previzualizare",
- "folderNameFallback": "Nume folder",
- "createFolderButton": "Creați folderul",
- "targetTerminals": "Terminale țintă",
- "selectAll": "Toate",
- "selectNone": "Nici unul",
- "noTerminalTabsOpen": "Nicio filă de terminal deschisă",
- "searchPlaceholder": "Fragmente de căutare...",
- "newSnippet": "Fragment nou",
- "newFolder": "Dosar nou",
- "run": "Aleargă",
- "noSnippetsInFolder": "Niciun fragment în acest dosar",
- "uncategorized": "Necategorizat",
- "editSnippetTitle": "Editați fragmentul",
- "editSnippetDescription": "Actualizați acest fragment de comandă",
- "saveSnippetButton": "Salvați modificările",
- "createSuccess": "Fragmentul a fost creat cu succes",
- "createFailed": "Nu s-a putut crea fragmentul",
- "updateSuccess": "Fragment actualizat cu succes",
- "updateFailed": "Fragmentul nu a putut fi actualizat",
- "deleteFailed": "Nu s-a putut șterge fragmentul",
- "folderCreateSuccess": "Dosarul a fost creat cu succes",
- "folderCreateFailed": "Nu s-a putut crea folderul",
- "editFolderTitle": "Editați folderul",
- "editFolderDescription": "Redenumiți sau modificați aspectul acestui folder",
- "saveFolderButton": "Salvați modificările",
- "editFolder": "Editați folderul",
- "deleteFolder": "Ștergeți folderul",
- "folderDeleteSuccess": "Dosarul „{{name}}” a fost șters",
- "folderDeleteFailed": "Ștergerea folderului a eșuat",
- "folderEditSuccess": "Dosarul a fost actualizat cu succes",
- "folderEditFailed": "Nu s-a putut actualiza folderul",
- "confirmRunMessage": "Executați „{{name}}”?",
- "confirmRunButton": "Aleargă",
- "runSuccess": "A rulat „{{name}}” în terminalul(ele) {{count}}",
- "pasteSuccess": "„{{name}}” a fost lipit în terminalul(ele) {{count}}",
- "pasteToTerminal": "Pastă",
- "copySuccess": "„{{name}}” a fost copiat în clipboard",
- "shareTitle": "Partajați fragmentul",
- "shareUser": "Utilizator",
- "shareRole": "Rol",
- "selectUser": "Selectați un utilizator...",
- "selectRole": "Selectați un rol...",
- "shareSuccess": "Fragmentul a fost partajat cu succes",
- "shareFailed": "Nu s-a putut partaja fragmentul",
- "revokeSuccess": "Acces revocat",
- "revokeFailed": "Nu s-a putut revoca accesul",
- "currentAccess": "Acces curent",
- "shareLoadError": "Nu s-au putut încărca datele de partajare",
- "loading": "Încărcare...",
- "close": "Aproape",
- "reorderFailed": "Nu s-a putut salva ordinea fragmentelor.",
- "importExport": "Import / Export",
- "exportBtn": "Export JSON",
- "importBtn": "Import JSON",
- "exportSuccess": "Fragmentele au fost exportate cu succes",
- "exportFailed": "Exportul fragmentelor a eșuat",
- "importTitle": "Importați fragmente",
- "importDescription": "Importați fragmente și foldere dintr-un fișier JSON exportat de Termix",
- "importDropOrClick": "Plasați un fișier JSON aici sau faceți clic pentru a răsfoi",
- "importSelectedFile": "Selectat: {{name}}",
- "importOverwrite": "Suprascrie fragmentele existente cu același nume și folder",
- "importStartBtn": "Import",
- "importSuccess": "Import finalizat: {{snippets}} fragment(e) adăugat(e), {{updated}} actualizat, {{skipped}} omis, {{folders}} folder(e) adăugat(e)",
- "importFailed": "Importarea fragmentelor a eșuat",
- "importInvalidFile": "Fișier nevalid: se aștepta un obiect JSON cu fragmente de date sau tablouri de foldere",
- "targetHostsLabel": "Gazde țintă",
- "targetHostsHint": "Atribuiți gazde pentru a rula acest fragment direct, fără un terminal deschis.",
- "noHostsAvailable": "Nicio gazdă configurată",
- "clearTargetHosts": "Ștergeți tot",
- "hasTargetHosts": "Are gazde țintă",
- "runOnTargets": "Executare pe ținte",
- "directRunSuccess": "A rulat „{{name}}” pe gazdă(e) {{count}}",
- "directRunPartialFail": "„{{name}}” a eșuat pe una sau mai multe gazde",
- "executionResultTitle": "Rezultatele execuției: {{name}}",
- "executionResultDescription": "Rezultatul rulării fragmentului de cod pe fiecare gazdă țintă.",
- "executionSuccess": "Succes",
- "executionFailed": "Eșuat",
- "expandAll": "Extindeți toate folderele",
- "collapseAll": "Restrângeți toate folderele",
- "settings": "Setări fragment",
- "variablesHint": "Variabile: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etichetă}",
- "variablesDialogTitle": "Executați „{{name}}”",
- "variablesDialogDescription": "Completați valorile necesare acestui fragment de cod înainte de a-l rula.",
- "variablesPreviewLabel": "Previzualizare",
- "variablesConfirmButton": "Aleargă"
- },
"keybindings": {
"title": "Comenzi rapide de la tastatură",
"description": "Personalizați comenzile rapide de copiere, lipire și control din terminal sau asociați taste pentru a trimite text sau a rula un fragment de cod.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Faceți clic și apăsați tastele",
"recording": "Apăsați tastele...",
"comboRequiredError": "Apăsați mai întâi o combinație de taste",
- "textRequiredError": "Introduceți textul de trimis",
- "controlCodeRequiredError": "Introduceți o singură literă pentru codul de control",
- "snippetRequiredError": "Selectați un fragment",
"conflictWarning": "Această combinație este deja legată de {{combo}}. Salvarea va face ca ambele să se declanșeze.",
"actionLabel": "Acţiune",
- "actionCopy": "Copiați selecția",
- "actionPaste": "Pastă",
- "actionSendControlCode": "Trimite semnal Ctrl+literă",
- "actionSendText": "Trimiteți mesaj text",
- "actionRunSnippet": "Execută fragmentul existent",
"actionNextTab": "Comutare la fila următoare",
"actionPreviousTab": "Comutare la fila anterioară",
- "actionOpenCommandPalette": "Deschide căutarea rapidă",
- "controlCodeLabel": "Scrisoare",
- "textLabel": "Text de trimis",
- "appendEnterLabel": "Apăsați Enter după trimitere",
- "snippetLabel": "Fragment",
- "selectSnippetPlaceholder": "Selectați un fragment",
- "orphanedSnippetWarning": "fragmentul nu a fost găsit",
- "clipboardPermissionNote": "În unele browsere, este posibil să solicite permisiunea pentru clipboard la prima utilizare."
+ "actionOpenCommandPalette": "Deschide căutarea rapidă"
},
"userProfile": {
"donateTitle": "Menține Termix în viață",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Subiectul este obligatoriu",
"ntfyToken": "Token de acces (opțional)",
"channelSaveFailed": "Canalul nu a putut fi salvat."
- },
- "macros": {
- "title": "Macrocomenzi",
- "description": "Executați o serie de comenzi salvate într-un terminal și reacționați la ceea ce serverul trimite înapoi.",
- "target": "Ţintă",
- "noTerminal": "Niciun terminal deschis",
- "empty": "Încă nu există macrocomenzi",
- "emptyHint": "Creați o macrocomandă pentru a automatiza un flux de lucru în terminal, cum ar fi conectarea, repornirea unui serviciu sau răspunsul la solicitări.",
- "create": "Macrocomandă nouă",
- "back": "Înapoi la macrocomenzi",
- "name": "Nume",
- "namePlaceholder": "Reporniți serverul web",
- "nameRequired": "Dați un nume macrocomenzii înainte de salvare",
- "descriptionLabel": "Descriere",
- "descriptionPlaceholder": "Ce face această macrocomandă (opțional)",
- "unsaved": "Modificări nesalvate",
- "save": "Salva",
- "saved": "Macrocomandă salvată",
- "saveFailed": "Nu s-a putut salva macrocomanda",
- "delete": "Şterge",
- "deleteTitle": "Ștergeți această macrocomandă?",
- "deleteBody": "„{{name}}” va fi șters definitiv. Această acțiune nu poate fi anulată.",
- "deleted": "Macrocomandă ștearsă",
- "cancel": "Anula",
- "run": "Aleargă",
- "stop": "Stop",
- "running": "Funcţionare...",
- "completed": "Macrocomandă finalizată",
- "failed": "Macrocomanda a eșuat",
- "loadFailed": "Nu s-au putut încărca macrocomenzile",
- "needTerminal": "Deschideți și conectați mai întâi un terminal",
- "noSteps": "Această macrocomandă nu are încă pași. Adăugați unul mai jos.",
- "addStep": "Adăugați un pas",
- "stepNumber": "Pasul {{number}}",
- "removeStep": "Eliminați pasul",
- "confirmRunTitle": "Rulați această macrocomandă?",
- "confirmRunBody": "„{{name}}” va trimite comenzi către {{host}}.",
- "confirmRunAction": "Executare macrocomandă",
- "stepTypes": {
- "send": {
- "label": "Trimiteți comanda",
- "help": "Introduce text în terminal, la fel ca și cum l-ai fi tastat."
- },
- "wait": {
- "label": "Așteptați ieșirea",
- "help": "Se întrerupe până când serverul afișează textul pe care îl denumiți, ca o solicitare de parolă."
- },
- "delay": {
- "label": "Pauză",
- "help": "Așteaptă o perioadă fixă de timp înainte de pasul următor."
- },
- "if": {
- "label": "Dacă ieșirea conține",
- "help": "Verifică tot ce a imprimat serverul până acum, apoi rulează una dintre cele două ramificații."
- },
- "repeat": {
- "label": "Repeta",
- "help": "Execută pașii din interiorul său de un număr stabilit de ori."
- }
- },
- "send": {
- "text": "Comandă sau text",
- "textPlaceholder": "repornirea systemctl a nginx",
- "pressEnter": "Apăsați Enter după trimitere",
- "pressEnterHelp": "Dezactivați această opțiune pentru a introduce text fără a-l trimite."
- },
- "wait": {
- "pattern": "Așteptați până când ieșirea conține",
- "patternPlaceholder": "parolă:",
- "timeout": "Renunță după",
- "onTimeout": "Dacă nu apare niciodată",
- "stop": "Opriți macrocomanda",
- "continue": "Continuă oricum"
- },
- "delay": {
- "duration": "Așteptați"
- },
- "ifStep": {
- "pattern": "Dacă ieșirea conține",
- "patternPlaceholder": "Activ: activ (alergând)",
- "then": "Apoi fă asta",
- "else": "Altfel, fă asta",
- "emptyBranch": "Fără trepte"
- },
- "repeat": {
- "count": "Numărul de ori",
- "times": "ori"
- },
- "match": {
- "useRegex": "Potrivire cu o expresie regulată",
- "useRegexHelp": "Omiteți această opțiune pentru a corespunde exact textului așa cum este scris.",
- "invalidRegex": "Nu este o expresie regulată validă, va fi găsită ca text simplu."
- },
- "seconds": "secunde",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/ru_RU.json b/src/ui/locales/translated/ru_RU.json
index d48b63ad1..5d6abeda1 100644
--- a/src/ui/locales/translated/ru_RU.json
+++ b/src/ui/locales/translated/ru_RU.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Настройки боковой панели...",
"hosts": "Хосты",
"snippets": "Сниппеты",
- "macros": "Макросы",
"hostManager": "Менеджер хостов",
"credentials": "Учетные данные",
"connections": "Подключения",
@@ -869,7 +868,6 @@
"groupTags": "Теги и значок",
"groupTunnels": "Туннели",
"groupJumpHosts": "Переключение хостов",
- "groupQuickActions": "Быстрые действия",
"groupFeatureFlags": "Флаги функций",
"groupAdvanced": "Расширенные настройки",
"preview": "Предварительный просмотр",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Хранение SSH-ключей или паролей",
"tmuxMonitor": "Мониторинг Tmux",
"tmuxMonitorDesc": "Мониторинг сессий tmux на ваших хостах",
- "snippets": "Фрагменты",
- "runSnippetDesc": "Запустите в активной вкладке терминала.",
- "noSnippetsFound": "Фрагменты, соответствующие \"{{search}}\", не найдены.",
"recentActivity": "Недавняя активность",
"serversAndHosts": "Серверы и хосты",
"noHostsFound": "Не найдено хостов, соответствующих \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Предыдущая вкладка",
"hotkeysJumpTab": "Перейти на вкладку"
},
- "snippets": {
- "title": "Сниппеты",
- "createSnippetTitle": "Создать сниппет",
- "createSnippetDescription": "Создать новый сниппет команды для быстрого выполнения",
- "nameLabel": "Название",
- "namePlaceholder": "например, Перезапустить Nginx",
- "descriptionLabel": "Описание",
- "descriptionPlaceholder": "Необязательное описание",
- "optional": "Необязательно",
- "folderLabel": "Папка",
- "noFolder": "Нет папки (Без категории)",
- "commandLabel": "Команда",
- "commandPlaceholder": "например, sudo systemctl restart nginx",
- "typeLabel": "Тип",
- "typeCommand": "Командование",
- "typeNote": "Примечание",
- "noteLabel": "Примечание",
- "notePlaceholder": "например, многоразовый текст-подсказка или напоминание.",
- "cancel": "Отмена",
- "createSnippetButton": "Создать сниппет",
- "createFolderTitle": "Создать папку",
- "createFolderDescription": "Организуйте сниппеты по папкам",
- "folderNameLabel": "Название папки",
- "folderNamePlaceholder": "например, Системные команды, Скрипты Docker",
- "folderColorLabel": "Цвет папки",
- "folderIconLabel": "Значок папки",
- "previewLabel": "Предпросмотр",
- "folderNameFallback": "Название папки",
- "createFolderButton": "Создать папку",
- "targetTerminals": "Целевые терминалы",
- "selectAll": "Все",
- "selectNone": "Ничего",
- "noTerminalTabsOpen": "Нет открытых вкладок терминала",
- "searchPlaceholder": "Поиск сниппетов...",
- "newSnippet": "Новый сниппет",
- "newFolder": "Новая папка",
- "run": "Запустить",
- "noSnippetsInFolder": "В этой папке нет сниппетов",
- "uncategorized": "Без категории",
- "editSnippetTitle": "Редактировать сниппет",
- "editSnippetDescription": "Обновить этот командный сниппет",
- "saveSnippetButton": "Сохранить изменения",
- "createSuccess": "Сниппет успешно создан",
- "createFailed": "Не удалось создать сниппет",
- "updateSuccess": "Сниппет успешно обновлён",
- "updateFailed": "Не удалось обновить сниппет",
- "deleteFailed": "Не удалось удалить сниппет",
- "folderCreateSuccess": "Папка успешно создана",
- "folderCreateFailed": "Не удалось создать папку",
- "editFolderTitle": "Редактировать папку",
- "editFolderDescription": "Переименовать или изменить внешний вид этой папки",
- "saveFolderButton": "Сохранить изменения",
- "editFolder": "Редактировать папку",
- "deleteFolder": "Удалить папку",
- "folderDeleteSuccess": "Папка \"{{name}}\" удалена",
- "folderDeleteFailed": "Не удалось удалить папку",
- "folderEditSuccess": "Папка успешно обновлена",
- "folderEditFailed": "Не удалось обновить папку",
- "confirmRunMessage": "Запустить \"{{name}}\"?",
- "confirmRunButton": "Запустить",
- "runSuccess": "Запущен \"{{name}}\" в {{count}} терминале(ах)",
- "pasteSuccess": "Вставлено \"{{name}}\" в {{count}} терминал(ы)",
- "pasteToTerminal": "Вставить",
- "copySuccess": "Сниппет \"{{name}}\" скопирован в буфер обмена",
- "shareTitle": "Поделиться сниппетом",
- "shareUser": "Пользователь",
- "shareRole": "Роль",
- "selectUser": "Выберите пользователя...",
- "selectRole": "Выберите роль...",
- "shareSuccess": "Сниппет успешно предоставлен в общий доступ",
- "shareFailed": "Не удалось предоставить сниппет в общий доступ",
- "revokeSuccess": "Доступ отозван",
- "revokeFailed": "Не удалось отозвать доступ",
- "currentAccess": "Текущий доступ",
- "shareLoadError": "Не удалось загрузить данные общего доступа",
- "loading": "Загрузка...",
- "close": "Закрыть",
- "reorderFailed": "Не удалось сохранить порядок сниппетов",
- "importExport": "Импорт / Экспорт",
- "exportBtn": "Экспорт JSON",
- "importBtn": "Импорт JSON",
- "exportSuccess": "Сниппеты успешно экспортированы",
- "exportFailed": "Не удалось экспортировать сниппеты",
- "importTitle": "Импорт сниппетов",
- "importDescription": "Импорт сниппетов и папок из JSON-файла, экспортированного Termix",
- "importDropOrClick": "Перетащите JSON-файл сюда или нажмите, чтобы выбрать",
- "importSelectedFile": "Выбрано: {{name}}",
- "importOverwrite": "Перезаписывать существующие сниппеты с одинаковыми именами и папками",
- "importStartBtn": "Импортировать",
- "importSuccess": "Импорт завершён: добавлено сниппетов: {{snippets}}, обновлено: {{updated}}, пропущено: {{skipped}}, добавлено папок: {{folders}}",
- "importFailed": "Не удалось импортировать сниппеты",
- "importInvalidFile": "Неверный файл: ожидается JSON-объект с массивами snippets или folders",
- "targetHostsLabel": "Целевые хосты",
- "targetHostsHint": "Назначьте хосты для запуска этого сниппета напрямую, без открытого терминала.",
- "noHostsAvailable": "Нет настроенных хостов",
- "clearTargetHosts": "Очистить всё",
- "hasTargetHosts": "Имеет целевые хосты",
- "runOnTargets": "Запустить на целевых хостах",
- "directRunSuccess": "Запущен \"{{name}}\" на {{count}} хосте(ах)",
- "directRunPartialFail": "\"{{name}}\" не удалось запустить на одном или нескольких хостах",
- "executionResultTitle": "Результаты выполнения: {{name}}",
- "executionResultDescription": "Вывод выполнения сниппета на каждом целевом хосте.",
- "executionSuccess": "Успех",
- "executionFailed": "Ошибка",
- "expandAll": "Развернуть все папки",
- "collapseAll": "Свернуть все папки",
- "settings": "Настройки фрагмента",
- "variablesHint": "Переменные: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Запуск \"{{name}}\"",
- "variablesDialogDescription": "Перед запуском заполните необходимые для этого фрагмента значения.",
- "variablesPreviewLabel": "Предварительный просмотр",
- "variablesConfirmButton": "Бегать"
- },
"keybindings": {
"title": "Клавиатурные сочетания",
"description": "Настройте сочетания клавиш для копирования, вставки и управления в терминале, а также назначьте клавиши для отправки текста или запуска фрагмента кода.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Нажмите и удерживайте клавиши.",
"recording": "Нажимайте клавиши...",
"comboRequiredError": "Сначала нажмите комбинацию клавиш.",
- "textRequiredError": "Введите текст для отправки",
- "controlCodeRequiredError": "Введите одну букву в качестве управляющего кода.",
- "snippetRequiredError": "Выберите фрагмент",
"conflictWarning": "Эта комбинация уже привязана к {{combo}}. Сохранение активирует обе комбинации.",
"actionLabel": "Действие",
- "actionCopy": "Скопировать выделенный фрагмент",
- "actionPaste": "Вставить",
- "actionSendControlCode": "Отправить сигнал Ctrl+буква",
- "actionSendText": "Отправить текст",
- "actionRunSnippet": "Запустить существующий фрагмент",
"actionNextTab": "Перейти к следующей вкладке",
"actionPreviousTab": "Перейти на предыдущую вкладку",
- "actionOpenCommandPalette": "Открыть быстрый поиск",
- "controlCodeLabel": "Письмо",
- "textLabel": "Текст для отправки",
- "appendEnterLabel": "Нажмите Enter после отправки",
- "snippetLabel": "Фрагмент",
- "selectSnippetPlaceholder": "Выберите фрагмент",
- "orphanedSnippetWarning": "фрагмент не найден",
- "clipboardPermissionNote": "В некоторых браузерах при первом использовании может запрашиваться разрешение на использование буфера обмена."
+ "actionOpenCommandPalette": "Открыть быстрый поиск"
},
"userProfile": {
"donateTitle": "Поддержите Termix",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Тема обязательна",
"ntfyToken": "Токен доступа (необязательно)",
"channelSaveFailed": "Не удалось сохранить канал"
- },
- "macros": {
- "title": "Макросы",
- "description": "Выполните сохраненную последовательность команд в терминале и отреагируйте на то, что сервер отправит вам в ответ.",
- "target": "Цель",
- "noTerminal": "Терминал не открыт",
- "empty": "Макросы пока отсутствуют",
- "emptyHint": "Создайте макрос для автоматизации действий в терминале, таких как вход в систему, перезапуск службы или ответ на запросы.",
- "create": "Новый макрос",
- "back": "Вернуться к макроэлементам",
- "name": "Имя",
- "namePlaceholder": "Перезапустите веб-сервер",
- "nameRequired": "Перед сохранением дайте макросу имя.",
- "descriptionLabel": "Описание",
- "descriptionPlaceholder": "Что делает этот макрос (необязательно)",
- "unsaved": "Несохраненные изменения",
- "save": "Сохранять",
- "saved": "Макрос сохранен",
- "saveFailed": "Не удалось сохранить макрос.",
- "delete": "Удалить",
- "deleteTitle": "Удалить этот макрос?",
- "deleteBody": "\"{{name}}\" будет удалено навсегда. Этого нельзя будет отменить.",
- "deleted": "Макрос удален",
- "cancel": "Отмена",
- "run": "Бегать",
- "stop": "Останавливаться",
- "running": "Бег...",
- "completed": "Макро завершен",
- "failed": "Макрос не сработал",
- "loadFailed": "Не удалось загрузить макросы.",
- "needTerminal": "Сначала откройте и подключите терминал.",
- "noSteps": "В этом макросе пока нет шагов. Добавьте один ниже.",
- "addStep": "Добавить шаг",
- "stepNumber": "Шаг {{number}}",
- "removeStep": "Удалить шаг",
- "confirmRunTitle": "Запустить этот макрос?",
- "confirmRunBody": "\"{{name}}\" будет отправлять команды {{host}}.",
- "confirmRunAction": "Запустить макрос",
- "stepTypes": {
- "send": {
- "label": "Отправить команду",
- "help": "Вводит текст в терминал так же, как если бы вы его ввели вручную."
- },
- "wait": {
- "label": "Дождитесь вывода",
- "help": "Приостанавливается до тех пор, пока сервер не выведет указанный вами текст, как запрос пароля."
- },
- "delay": {
- "label": "Пауза",
- "help": "Перед следующим шагом выполняется ожидание в течение заданного промежутка времени."
- },
- "if": {
- "label": "Если вывод содержит",
- "help": "Проверяет все данные, выведенные сервером на экран, а затем запускает одну из двух ветвей выполнения."
- },
- "repeat": {
- "label": "Повторить",
- "help": "Выполняет все шаги, входящие в его состав, заданное количество раз."
- }
- },
- "send": {
- "text": "Команда или текст",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Нажмите Enter после отправки",
- "pressEnterHelp": "Отключите эту функцию, чтобы вводить текст без отправки."
- },
- "wait": {
- "pattern": "Дождитесь, пока на выходе не появится",
- "patternPlaceholder": "пароль:",
- "timeout": "Сдаться после",
- "onTimeout": "Если оно никогда не появится",
- "stop": "Остановите макрос",
- "continue": "Продолжайте в любом случае."
- },
- "delay": {
- "duration": "Подождите"
- },
- "ifStep": {
- "pattern": "Если в выходных данных содержится",
- "patternPlaceholder": "Активный: активен (работает)",
- "then": "Затем сделайте это.",
- "else": "В противном случае сделайте это.",
- "emptyBranch": "Без ступенек"
- },
- "repeat": {
- "count": "Количество раз",
- "times": "времена"
- },
- "match": {
- "useRegex": "Сопоставление с использованием регулярного выражения",
- "useRegexHelp": "Оставьте этот параметр отключенным, чтобы текст точно соответствовал написанному.",
- "invalidRegex": "Не является допустимым регулярным выражением, будет сопоставлено как обычный текст."
- },
- "seconds": "секунд",
- "secondsShort": "{{count}}с"
}
}
diff --git a/src/ui/locales/translated/sr_SP.json b/src/ui/locales/translated/sr_SP.json
index 82b523665..b44cbc849 100644
--- a/src/ui/locales/translated/sr_SP.json
+++ b/src/ui/locales/translated/sr_SP.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Подешавања бочне траке...",
"hosts": "Домаћини",
"snippets": "Исечци",
- "macros": "Макрои",
"hostManager": "Менаџер домаћина",
"credentials": "Акредитиви",
"connections": "Везе",
@@ -869,7 +868,6 @@
"groupTags": "Ознаке и пин",
"groupTunnels": "Тунели",
"groupJumpHosts": "Јумп хостови",
- "groupQuickActions": "Брзе акције",
"groupFeatureFlags": "Заставице функција",
"groupAdvanced": "Напредна конфигурација",
"preview": "Преглед",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Чувајте SSH кључеве или лозинке",
"tmuxMonitor": "Tmux монитор",
"tmuxMonitorDesc": "Пратите tmux сесије на свим вашим хостовима",
- "snippets": "Исечци",
- "runSnippetDesc": "Покрени у активној картици терминала",
- "noSnippetsFound": "Није пронађен ниједан фрагмент који одговара „{{search}}“",
"recentActivity": "Недавне активности",
"serversAndHosts": "Сервери и хостови",
"noHostsFound": "Није пронађен ниједан хост који одговара „{{search}}“",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Претходна картица",
"hotkeysJumpTab": "Пређи на картицу"
},
- "snippets": {
- "title": "Исечци",
- "createSnippetTitle": "Направи исечак",
- "createSnippetDescription": "Направите нови исечак команде за брзо извршавање",
- "nameLabel": "Име",
- "namePlaceholder": "нпр. Рестартујте Nginx",
- "descriptionLabel": "Опис",
- "descriptionPlaceholder": "Опциони опис",
- "optional": "Опционо",
- "folderLabel": "Фасцикла",
- "noFolder": "Нема фасцикле (Некатегоризовано)",
- "commandLabel": "Команда",
- "commandPlaceholder": "нпр. sudo systemctl рестарт nginx",
- "typeLabel": "Тип",
- "typeCommand": "Команда",
- "typeNote": "Напомена",
- "noteLabel": "Напомена",
- "notePlaceholder": "нпр. текст подсетника или упута за вишекратну употребу",
- "cancel": "Откажи",
- "createSnippetButton": "Направи исечак",
- "createFolderTitle": "Направи фасциклу",
- "createFolderDescription": "Организујте своје фрагменте у фасцикле",
- "folderNameLabel": "Назив фасцикле",
- "folderNamePlaceholder": "нпр. системске команде, Докер скрипте",
- "folderColorLabel": "Боја фасцикле",
- "folderIconLabel": "Икона фасцикле",
- "previewLabel": "Преглед",
- "folderNameFallback": "Назив фасцикле",
- "createFolderButton": "Направи фасциклу",
- "targetTerminals": "Циљни терминали",
- "selectAll": "Сви",
- "selectNone": "Ниједан",
- "noTerminalTabsOpen": "Нема отворених картица терминала",
- "searchPlaceholder": "Претражи фрагменте...",
- "newSnippet": "Нови исечак",
- "newFolder": "Нова фасцикла",
- "run": "Трчи",
- "noSnippetsInFolder": "Нема фрагмената у овој фасцикли",
- "uncategorized": "Некатегоризовано",
- "editSnippetTitle": "Измени исечак",
- "editSnippetDescription": "Ажурирајте овај исечак команде",
- "saveSnippetButton": "Сачувај измене",
- "createSuccess": "Исечак је успешно креиран",
- "createFailed": "Није успело креирање фрагмента",
- "updateSuccess": "Исечак је успешно ажуриран",
- "updateFailed": "Ажурирање исечка није успело",
- "deleteFailed": "Брисање исечка није успело",
- "folderCreateSuccess": "Фолдер је успешно креиран",
- "folderCreateFailed": "Није успело креирање фасцикле",
- "editFolderTitle": "Уреди фасциклу",
- "editFolderDescription": "Преименујте или промените изглед ове фасцикле",
- "saveFolderButton": "Сачувај измене",
- "editFolder": "Уреди фасциклу",
- "deleteFolder": "Обриши фасциклу",
- "folderDeleteSuccess": "Фасцикла „{{name}}“ је обрисана",
- "folderDeleteFailed": "Брисање фасцикле није успело",
- "folderEditSuccess": "Фолдер је успешно ажуриран",
- "folderEditFailed": "Ажурирање фасцикле није успело",
- "confirmRunMessage": "Покрени „{{name}}“?",
- "confirmRunButton": "Трчи",
- "runSuccess": "Покренуо је „{{name}}“ у {{count}} терминалу(има)",
- "pasteSuccess": "Налепљено је „{{name}}“ у {{count}} терминал(е)",
- "pasteToTerminal": "Залепи",
- "copySuccess": "Копирано „{{name}}“ у међуспремник",
- "shareTitle": "Дели исечак",
- "shareUser": "Корисник",
- "shareRole": "Улога",
- "selectUser": "Изаберите корисника...",
- "selectRole": "Изаберите улогу...",
- "shareSuccess": "Исечак је успешно дељен",
- "shareFailed": "Дељење фрагмента није успело",
- "revokeSuccess": "Приступ је опозван",
- "revokeFailed": "Опозивање приступа није успело",
- "currentAccess": "Тренутни приступ",
- "shareLoadError": "Учитавање података о дељењу није успело",
- "loading": "Учитавање...",
- "close": "Затвори",
- "reorderFailed": "Чување редоследа исечака није успело",
- "importExport": "Увоз / Извоз",
- "exportBtn": "Извези JSON",
- "importBtn": "Увези JSON",
- "exportSuccess": "Исечци су успешно извезени",
- "exportFailed": "Извоз фрагмената није успео",
- "importTitle": "Увези фрагменте",
- "importDescription": "Увезите фрагменте и фасцикле из JSON датотеке коју је извезао Termix",
- "importDropOrClick": "Отпустите JSON датотеку овде или кликните да бисте прегледали",
- "importSelectedFile": "Изабрано: {{name}}",
- "importOverwrite": "Пребришите постојеће фрагменте истим именом и фасциклом",
- "importStartBtn": "Увоз",
- "importSuccess": "Увоз завршен: додато {{snippets}} фрагмената, ажурирано {{updated}} , прескочено {{skipped}} , додато {{folders}} фасцикли",
- "importFailed": "Увоз фрагмената није успео",
- "importInvalidFile": "Неважећа датотека: очекиван је JSON објекат са исечцима или низовима фасцикли",
- "targetHostsLabel": "Циљни хостови",
- "targetHostsHint": "Доделите хостове да покрену овај исечак директно без отвореног терминала.",
- "noHostsAvailable": "Ниједан хост није конфигурисан",
- "clearTargetHosts": "Обриши све",
- "hasTargetHosts": "Има циљне хостове",
- "runOnTargets": "Трчи ка циљевима",
- "directRunSuccess": "Покренуто „{{name}}“ на {{count}} хосту(има)",
- "directRunPartialFail": "„{{name}}“ није успело на једном или више хостова",
- "executionResultTitle": "Резултати извршења: {{name}}",
- "executionResultDescription": "Излаз из покретања фрагмента кода на сваком циљном хосту.",
- "executionSuccess": "Успех",
- "executionFailed": "Неуспешно",
- "expandAll": "Прошири све фасцикле",
- "collapseAll": "Скупи све фасцикле",
- "settings": "Подешавања исечка",
- "variablesHint": "Променљиве: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Ознака}",
- "variablesDialogTitle": "Покрени „{{name}}“",
- "variablesDialogDescription": "Унесите вредности које су потребне овом фрагменту пре него што га покренете.",
- "variablesPreviewLabel": "Преглед",
- "variablesConfirmButton": "Трчи"
- },
"keybindings": {
"title": "Пречице на тастатури",
"description": "Прилагодите пречице за копирање, лепљење и контролу терминала или повежите тастере за слање текста или покретање исечка кода.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Кликните и притисните тастере",
"recording": "Притисните тастере...",
"comboRequiredError": "Прво притисните комбинацију тастера",
- "textRequiredError": "Унесите текст за слање",
- "controlCodeRequiredError": "Унесите једно слово за контролни код",
- "snippetRequiredError": "Изаберите исечак",
"conflictWarning": "Ова комбинација је већ повезана са {{combo}}. Чување ће активирати обе.",
"actionLabel": "Акција",
- "actionCopy": "Копирај избор",
- "actionPaste": "Залепи",
- "actionSendControlCode": "Пошаљи сигнал Ctrl+слово",
- "actionSendText": "Пошаљи текст",
- "actionRunSnippet": "Покрени постојећи исечак кода",
"actionNextTab": "Пређи на следећу картицу",
"actionPreviousTab": "Пређи на претходну картицу",
- "actionOpenCommandPalette": "Отвори брзу претрагу",
- "controlCodeLabel": "Писмо",
- "textLabel": "Текст за слање",
- "appendEnterLabel": "Притисните Ентер након слања",
- "snippetLabel": "Исечак",
- "selectSnippetPlaceholder": "Изаберите исечак",
- "orphanedSnippetWarning": "фрагмент није пронађен",
- "clipboardPermissionNote": "Може затражити дозволу за међуспремник при првој употреби у неким прегледачима."
+ "actionOpenCommandPalette": "Отвори брзу претрагу"
},
"userProfile": {
"donateTitle": "Нека Термикс остане жив",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Тема је обавезна",
"ntfyToken": "Приступни токен (опционо)",
"channelSaveFailed": "Чување канала није успело"
- },
- "macros": {
- "title": "Макрои",
- "description": "Покрените сачувану серију команди у терминалу и реагујте на оно што сервер пошаље назад.",
- "target": "Циљ",
- "noTerminal": "Ниједан терминал није отворен",
- "empty": "Још нема макроа",
- "emptyHint": "Направите макро за аутоматизацију тока рада терминала, као што је пријављивање, поновно покретање сервиса или одговарање на упите.",
- "create": "Нови макро",
- "back": "Назад на макрое",
- "name": "Име",
- "namePlaceholder": "Поново покрените веб сервер",
- "nameRequired": "Дајте макроу име пре чувања",
- "descriptionLabel": "Опис",
- "descriptionPlaceholder": "Шта овај макро ради (опционо)",
- "unsaved": "Несачуване промене",
- "save": "Сачувај",
- "saved": "Макро је сачуван",
- "saveFailed": "Није могуће сачувати макро",
- "delete": "Обриши",
- "deleteTitle": "Да ли желите да обришете овај макро?",
- "deleteBody": "„{{name}}“ ће бити трајно обрисано. Ова радња се не може поништити.",
- "deleted": "Макро је обрисан",
- "cancel": "Откажи",
- "run": "Трчи",
- "stop": "Заустави",
- "running": "Трчи се...",
- "completed": "Макро је завршен",
- "failed": "Макро није успео",
- "loadFailed": "Није могуће учитати макрое",
- "needTerminal": "Прво отворите и повежите терминал",
- "noSteps": "Овај макро још нема кораке. Додајте један испод.",
- "addStep": "Додај корак",
- "stepNumber": "Корак {{number}}",
- "removeStep": "Уклони корак",
- "confirmRunTitle": "Покрени овај макро?",
- "confirmRunBody": "„{{name}}“ ће послати команде на {{host}}.",
- "confirmRunAction": "Покрени макро",
- "stepTypes": {
- "send": {
- "label": "Пошаљи команду",
- "help": "Уноси текст у терминал, исто као да сте га откуцали."
- },
- "wait": {
- "label": "Сачекајте излаз",
- "help": "Паузира док сервер не одштампа текст који наведете, као што је захтев за лозинку."
- },
- "delay": {
- "label": "Пауза",
- "help": "Чека одређено време пре следећег корака."
- },
- "if": {
- "label": "Ако излаз садржи",
- "help": "Проверава све што је сервер до сада одштампао, а затим покреће једну од две гране."
- },
- "repeat": {
- "label": "Понови",
- "help": "Покреће кораке унутар њега одређени број пута."
- }
- },
- "send": {
- "text": "Команда или текст",
- "textPlaceholder": "системцтл рестартовање нгинкса",
- "pressEnter": "Притисните Ентер након слања",
- "pressEnterHelp": "Искључите ово да бисте куцали текст без његовог слања."
- },
- "wait": {
- "pattern": "Сачекајте док излаз не садржи",
- "patternPlaceholder": "лозинка:",
- "timeout": "Одустани после",
- "onTimeout": "Ако се никада не појави",
- "stop": "Зауставите макро",
- "continue": "Настави ипак"
- },
- "delay": {
- "duration": "Сачекајте"
- },
- "ifStep": {
- "pattern": "Ако излаз садржи",
- "patternPlaceholder": "Активно: активно (покреће се)",
- "then": "Онда уради ово",
- "else": "У супротном, урадите ово",
- "emptyBranch": "Без степеница"
- },
- "repeat": {
- "count": "Број пута",
- "times": "пута"
- },
- "match": {
- "useRegex": "Упари се са регуларним изразом",
- "useRegexHelp": "Оставите ово искључено да би текст тачно одговарао написаном тексту.",
- "invalidRegex": "Није важећи регуларни израз, биће упарен као обичан текст"
- },
- "seconds": "секунде",
- "secondsShort": "{{count}}с"
}
}
diff --git a/src/ui/locales/translated/sv_SE.json b/src/ui/locales/translated/sv_SE.json
index 0278c97d5..ade4804af 100644
--- a/src/ui/locales/translated/sv_SE.json
+++ b/src/ui/locales/translated/sv_SE.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Inställningar för sidofältet...",
"hosts": "Värdar",
"snippets": "Snuttar",
- "macros": "Makron",
"hostManager": "Värdhanterare",
"credentials": "Referenser",
"connections": "Anslutningar",
@@ -869,7 +868,6 @@
"groupTags": "Taggar och pin",
"groupTunnels": "Tunnlar",
"groupJumpHosts": "Hoppa värdar",
- "groupQuickActions": "Snabbåtgärder",
"groupFeatureFlags": "Funktionsflaggor",
"groupAdvanced": "Avancerad konfiguration",
"preview": "Förhandsvisning",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Lagra SSH-nycklar eller lösenord",
"tmuxMonitor": "Tmux-monitor",
"tmuxMonitorDesc": "Övervaka tmux-sessioner över dina värdar",
- "snippets": "Snuttar",
- "runSnippetDesc": "Kör i den aktiva terminalfliken",
- "noSnippetsFound": "Inga utdrag hittades som matchar \"{{search}}\"",
"recentActivity": "Senaste aktivitet",
"serversAndHosts": "Servrar och värdar",
"noHostsFound": "Inga värdar hittades som matchar \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Föregående flik",
"hotkeysJumpTab": "Hoppa till fliken"
},
- "snippets": {
- "title": "Snuttar",
- "createSnippetTitle": "Skapa utdrag",
- "createSnippetDescription": "Skapa ett nytt kommandokodavsnitt för snabb körning",
- "nameLabel": "Namn",
- "namePlaceholder": "t.ex. starta om Nginx",
- "descriptionLabel": "Beskrivning",
- "descriptionPlaceholder": "Valfri beskrivning",
- "optional": "Frivillig",
- "folderLabel": "Mapp",
- "noFolder": "Ingen mapp (Okategoriserad)",
- "commandLabel": "Kommando",
- "commandPlaceholder": "t.ex. sudo systemctl starta om nginx",
- "typeLabel": "Typ",
- "typeCommand": "Kommando",
- "typeNote": "Notera",
- "noteLabel": "Notera",
- "notePlaceholder": "t.ex. en återanvändbar prompt eller påminnelsetext",
- "cancel": "Avboka",
- "createSnippetButton": "Skapa utdrag",
- "createFolderTitle": "Skapa mapp",
- "createFolderDescription": "Organisera dina utdrag i mappar",
- "folderNameLabel": "Mappnamn",
- "folderNamePlaceholder": "t.ex. systemkommandon, Docker-skript",
- "folderColorLabel": "Mappfärg",
- "folderIconLabel": "Mappikon",
- "previewLabel": "Förhandsvisning",
- "folderNameFallback": "Mappnamn",
- "createFolderButton": "Skapa mapp",
- "targetTerminals": "Target-terminaler",
- "selectAll": "Alla",
- "selectNone": "Ingen",
- "noTerminalTabsOpen": "Inga terminalflikar öppna",
- "searchPlaceholder": "Sök utdrag...",
- "newSnippet": "Nytt utdrag",
- "newFolder": "Ny mapp",
- "run": "Sikt",
- "noSnippetsInFolder": "Inga utdrag i den här mappen",
- "uncategorized": "Okategoriserad",
- "editSnippetTitle": "Redigera utdrag",
- "editSnippetDescription": "Uppdatera detta kommandokodavsnitt",
- "saveSnippetButton": "Spara ändringar",
- "createSuccess": "Kodavsnittet har skapats",
- "createFailed": "Misslyckades med att skapa utdrag",
- "updateSuccess": "Kodavsnittet har uppdaterats",
- "updateFailed": "Misslyckades med att uppdatera utdraget",
- "deleteFailed": "Misslyckades med att ta bort utdraget",
- "folderCreateSuccess": "Mappen skapades",
- "folderCreateFailed": "Misslyckades med att skapa mappen",
- "editFolderTitle": "Redigera mapp",
- "editFolderDescription": "Byt namn på eller ändra utseendet på den här mappen",
- "saveFolderButton": "Spara ändringar",
- "editFolder": "Redigera mapp",
- "deleteFolder": "Ta bort mapp",
- "folderDeleteSuccess": "Mappen \"{{name}}\" raderad",
- "folderDeleteFailed": "Misslyckades med att ta bort mappen",
- "folderEditSuccess": "Mappen har uppdaterats",
- "folderEditFailed": "Misslyckades med att uppdatera mappen",
- "confirmRunMessage": "Kör \"{{name}}\"?",
- "confirmRunButton": "Sikt",
- "runSuccess": "Körde \"{{name}}\" i {{count}} terminal(er)",
- "pasteSuccess": "Klistrade in \"{{name}}\" i {{count}} terminal(er)",
- "pasteToTerminal": "Klistra",
- "copySuccess": "Kopierade \"{{name}}\" till urklipp",
- "shareTitle": "Dela utdrag",
- "shareUser": "Användare",
- "shareRole": "Roll",
- "selectUser": "Välj en användare...",
- "selectRole": "Välj en roll...",
- "shareSuccess": "Kodavsnittet delades",
- "shareFailed": "Misslyckades med att dela utdraget",
- "revokeSuccess": "Åtkomst återkallad",
- "revokeFailed": "Misslyckades med att återkalla åtkomst",
- "currentAccess": "Nuvarande åtkomst",
- "shareLoadError": "Misslyckades med att ladda delningsdata",
- "loading": "Belastning...",
- "close": "Nära",
- "reorderFailed": "Det gick inte att spara kodavsnittets ordning",
- "importExport": "Importera / Exportera",
- "exportBtn": "Exportera JSON",
- "importBtn": "Importera JSON",
- "exportSuccess": "Snippet har exporterats",
- "exportFailed": "Misslyckades med att exportera utdrag",
- "importTitle": "Importera utdrag",
- "importDescription": "Importera utdrag och mappar från en JSON-fil som exporterats av Termix",
- "importDropOrClick": "Släpp en JSON-fil här eller klicka för att bläddra",
- "importSelectedFile": "Vald: {{name}}",
- "importOverwrite": "Skriv över befintliga utdrag med samma namn och mapp",
- "importStartBtn": "Importera",
- "importSuccess": "Importen är klar: {{snippets}} kodavsnitt tillagda, {{updated}} uppdaterade, {{skipped}} hoppade över, {{folders}} mapp(ar) tillagda",
- "importFailed": "Misslyckades med att importera utdrag",
- "importInvalidFile": "Ogiltig fil: förväntade ett JSON-objekt med kodavsnitt eller mappmatriser",
- "targetHostsLabel": "Målvärdar",
- "targetHostsHint": "Tilldela värdar att köra detta kodavsnitt direkt utan en öppen terminal.",
- "noHostsAvailable": "Inga värdar konfigurerade",
- "clearTargetHosts": "Rensa alla",
- "hasTargetHosts": "Har målvärdar",
- "runOnTargets": "Kör på mål",
- "directRunSuccess": "Körde \"{{name}}\" på {{count}} värd(ar)",
- "directRunPartialFail": "\"{{name}}\" misslyckades på en eller flera värdar",
- "executionResultTitle": "Exekveringsresultat: {{name}}",
- "executionResultDescription": "Utdata från att köra kodavsnittet på varje målvärd.",
- "executionSuccess": "Framgång",
- "executionFailed": "Misslyckades",
- "expandAll": "Expandera alla mappar",
- "collapseAll": "Komprimera alla mappar",
- "settings": "Inställningar för utdrag",
- "variablesHint": "Variabler: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Etikett}",
- "variablesDialogTitle": "Kör \"{{name}}\"",
- "variablesDialogDescription": "Fyll i de värden som detta kodavsnitt behöver innan du kör det.",
- "variablesPreviewLabel": "Förhandsvisning",
- "variablesConfirmButton": "Sikt"
- },
"keybindings": {
"title": "Kortkommandon",
"description": "Anpassa kortkommandon för att kopiera, klistra in och kontrollera terminalen, eller bind tangenter för att skicka text eller köra ett kodavsnitt.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Klicka och tryck på tangenterna",
"recording": "Tryck på tangenterna...",
"comboRequiredError": "Tryck först på en tangentkombination",
- "textRequiredError": "Ange texten som ska skickas",
- "controlCodeRequiredError": "Ange en enda bokstav för kontrollkoden",
- "snippetRequiredError": "Välj ett utdrag",
"conflictWarning": "Denna kombination är redan bunden till {{combo}}. Om du sparar den aktiveras båda.",
"actionLabel": "Handling",
- "actionCopy": "Kopiera val",
- "actionPaste": "Klistra",
- "actionSendControlCode": "Skicka Ctrl+bokstavssignal",
- "actionSendText": "Skicka sms",
- "actionRunSnippet": "Kör befintligt kodavsnitt",
"actionNextTab": "Växla till nästa flik",
"actionPreviousTab": "Växla till föregående flik",
- "actionOpenCommandPalette": "Öppna snabbsökning",
- "controlCodeLabel": "Brev",
- "textLabel": "SMS att skicka",
- "appendEnterLabel": "Tryck Enter efter att du skickat",
- "snippetLabel": "Utdrag",
- "selectSnippetPlaceholder": "Välj ett utdrag",
- "orphanedSnippetWarning": "utdraget hittades inte",
- "clipboardPermissionNote": "Kan be om behörighet för urklipp vid första användningen i vissa webbläsare."
+ "actionOpenCommandPalette": "Öppna snabbsökning"
},
"userProfile": {
"donateTitle": "Håll Termix vid liv",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Ämne är obligatoriskt",
"ntfyToken": "Åtkomsttoken (valfritt)",
"channelSaveFailed": "Misslyckades med att spara kanalen"
- },
- "macros": {
- "title": "Makron",
- "description": "Kör en sparad serie kommandon i en terminal och reagera på vad servern skickar tillbaka.",
- "target": "Mål",
- "noTerminal": "Ingen terminal öppen",
- "empty": "Inga makron ännu",
- "emptyHint": "Skapa ett makro för att automatisera ett terminalflöde, som att logga in, starta om en tjänst eller svara på frågor.",
- "create": "Nytt makro",
- "back": "Tillbaka till makron",
- "name": "Namn",
- "namePlaceholder": "Starta om webbservern",
- "nameRequired": "Ge makrot ett namn innan du sparar",
- "descriptionLabel": "Beskrivning",
- "descriptionPlaceholder": "Vad detta makro gör (valfritt)",
- "unsaved": "Osparade ändringar",
- "save": "Spara",
- "saved": "Makrot sparat",
- "saveFailed": "Kunde inte spara makrot",
- "delete": "Radera",
- "deleteTitle": "Ta bort det här makrot?",
- "deleteBody": "\"{{name}}\" kommer att raderas permanent. Detta kan inte ångras.",
- "deleted": "Makro borttaget",
- "cancel": "Avboka",
- "run": "Sikt",
- "stop": "Stopp",
- "running": "Spring...",
- "completed": "Makro klart",
- "failed": "Makrot misslyckades",
- "loadFailed": "Kunde inte läsa in makron",
- "needTerminal": "Öppna och anslut en terminal först",
- "noSteps": "Det här makrot har inga steg ännu. Lägg till ett nedan.",
- "addStep": "Lägg till steg",
- "stepNumber": "Steg {{number}}",
- "removeStep": "Ta bort steg",
- "confirmRunTitle": "Köra det här makrot?",
- "confirmRunBody": "\"{{name}}\" skickar kommandon till {{host}}.",
- "confirmRunAction": "Kör makro",
- "stepTypes": {
- "send": {
- "label": "Skicka kommando",
- "help": "Skriver text i terminalen, på samma sätt som om du hade skrivit den."
- },
- "wait": {
- "label": "Vänta på utdata",
- "help": "Pausar tills servern skriver ut texten du anger, som en lösenordsfråga."
- },
- "delay": {
- "label": "Paus",
- "help": "Väntar en fastställd tid innan nästa steg."
- },
- "if": {
- "label": "Om utdata innehåller",
- "help": "Kontrollerar allt som servern har skrivit ut hittills och kör sedan en av två grenar."
- },
- "repeat": {
- "label": "Upprepa",
- "help": "Kör stegen inuti ett visst antal gånger."
- }
- },
- "send": {
- "text": "Kommando eller text",
- "textPlaceholder": "systemctl starta om nginx",
- "pressEnter": "Tryck Enter efter att du skickat",
- "pressEnterHelp": "Stäng av detta för att skriva text utan att skicka in den."
- },
- "wait": {
- "pattern": "Vänta tills utdata innehåller",
- "patternPlaceholder": "lösenord:",
- "timeout": "Ge upp efteråt",
- "onTimeout": "Om det aldrig dyker upp",
- "stop": "Stoppa makrot",
- "continue": "Fortsätt ändå"
- },
- "delay": {
- "duration": "Vänta på"
- },
- "ifStep": {
- "pattern": "Om utdata innehåller",
- "patternPlaceholder": "Aktiv: aktiv (löper)",
- "then": "Gör sedan detta",
- "else": "Annars gör så här",
- "emptyBranch": "Inga steg"
- },
- "repeat": {
- "count": "Antal gånger",
- "times": "gånger"
- },
- "match": {
- "useRegex": "Matcha med ett reguljärt uttryck",
- "useRegexHelp": "Lämna detta av för att texten ska se exakt ut som den är skriven.",
- "invalidRegex": "Inte ett giltigt reguljärt uttryck, det kommer att matchas som vanlig text"
- },
- "seconds": "sekunder",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/th_TH.json b/src/ui/locales/translated/th_TH.json
index 40533a5b4..659ef3d3c 100644
--- a/src/ui/locales/translated/th_TH.json
+++ b/src/ui/locales/translated/th_TH.json
@@ -409,7 +409,6 @@
"sidebarSettings": "การตั้งค่าแถบด้านข้าง...",
"hosts": "โฮสต์",
"snippets": "เศษเสี้ยว",
- "macros": "มาโคร",
"hostManager": "ผู้จัดการโฮสต์",
"credentials": "คุณสมบัติ",
"connections": "การเชื่อมต่อ",
@@ -869,7 +868,6 @@
"groupTags": "แท็กและพิน",
"groupTunnels": "อุโมงค์",
"groupJumpHosts": "โฮสต์ Jump",
- "groupQuickActions": "การดำเนินการอย่างรวดเร็ว",
"groupFeatureFlags": "ธงคุณสมบัติ",
"groupAdvanced": "การกำหนดค่าขั้นสูง",
"preview": "ตัวอย่าง",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "จัดเก็บคีย์ SSH หรือรหัสผ่าน",
"tmuxMonitor": "มอนิเตอร์ Tmux",
"tmuxMonitorDesc": "ตรวจสอบเซสชัน tmux บนโฮสต์ต่างๆ ของคุณ",
- "snippets": "เศษเสี้ยว",
- "runSnippetDesc": "เรียกใช้ในแท็บเทอร์มินัลที่ใช้งานอยู่",
- "noSnippetsFound": "ไม่พบข้อความตัวอย่างที่ตรงกับ \"{{search}}\"",
"recentActivity": "กิจกรรมล่าสุด",
"serversAndHosts": "เซิร์ฟเวอร์และโฮสต์",
"noHostsFound": "ไม่พบโฮสต์ที่ตรงกับ \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "แท็บก่อนหน้า",
"hotkeysJumpTab": "ข้ามไปยังแท็บ"
},
- "snippets": {
- "title": "เศษเสี้ยว",
- "createSnippetTitle": "สร้างโค้ดสั้น",
- "createSnippetDescription": "สร้างส่วนคำสั่งใหม่เพื่อเรียกใช้งานอย่างรวดเร็ว",
- "nameLabel": "ชื่อ",
- "namePlaceholder": "เช่น รีสตาร์ท Nginx",
- "descriptionLabel": "คำอธิบาย",
- "descriptionPlaceholder": "คำอธิบายเพิ่มเติม (ไม่บังคับ)",
- "optional": "ไม่จำเป็น",
- "folderLabel": "โฟลเดอร์",
- "noFolder": "ไม่มีโฟลเดอร์ (ไม่ได้จัดหมวดหมู่)",
- "commandLabel": "สั่งการ",
- "commandPlaceholder": "เช่น sudo systemctl restart nginx",
- "typeLabel": "พิมพ์",
- "typeCommand": "สั่งการ",
- "typeNote": "บันทึก",
- "noteLabel": "บันทึก",
- "notePlaceholder": "เช่น ข้อความแจ้งเตือนหรือข้อความเตือนความจำที่สามารถนำกลับมาใช้ซ้ำได้",
- "cancel": "ยกเลิก",
- "createSnippetButton": "สร้างโค้ดสั้น",
- "createFolderTitle": "สร้างโฟลเดอร์",
- "createFolderDescription": "จัดระเบียบข้อความย่อของคุณลงในโฟลเดอร์",
- "folderNameLabel": "ชื่อโฟลเดอร์",
- "folderNamePlaceholder": "เช่น คำสั่งระบบ, สคริปต์ Docker",
- "folderColorLabel": "สีของโฟลเดอร์",
- "folderIconLabel": "ไอคอนโฟลเดอร์",
- "previewLabel": "ตัวอย่าง",
- "folderNameFallback": "ชื่อโฟลเดอร์",
- "createFolderButton": "สร้างโฟลเดอร์",
- "targetTerminals": "เทอร์มินัลเป้าหมาย",
- "selectAll": "ทั้งหมด",
- "selectNone": "ไม่มี",
- "noTerminalTabsOpen": "ไม่มีแท็บเทอร์มินัลเปิดอยู่",
- "searchPlaceholder": "ตัวอย่างการค้นหา...",
- "newSnippet": "โค้ดตัวอย่างใหม่",
- "newFolder": "โฟลเดอร์ใหม่",
- "run": "วิ่ง",
- "noSnippetsInFolder": "ไม่มีไฟล์ตัวอย่างในโฟลเดอร์นี้",
- "uncategorized": "ไม่มีหมวดหมู่",
- "editSnippetTitle": "แก้ไขส่วนย่อย",
- "editSnippetDescription": "อัปเดตส่วนคำสั่งนี้",
- "saveSnippetButton": "บันทึกการเปลี่ยนแปลง",
- "createSuccess": "สร้างโค้ดตัวอย่างสำเร็จแล้ว",
- "createFailed": "ไม่สามารถสร้างโค้ดตัวอย่างได้",
- "updateSuccess": "อัปเดตข้อมูลตัวอย่างสำเร็จแล้ว",
- "updateFailed": "ไม่สามารถอัปเดตโค้ดตัวอย่างได้",
- "deleteFailed": "ไม่สามารถลบส่วนย่อยได้",
- "folderCreateSuccess": "สร้างโฟลเดอร์สำเร็จแล้ว",
- "folderCreateFailed": "ไม่สามารถสร้างโฟลเดอร์ได้",
- "editFolderTitle": "แก้ไขโฟลเดอร์",
- "editFolderDescription": "เปลี่ยนชื่อหรือเปลี่ยนลักษณะของโฟลเดอร์นี้",
- "saveFolderButton": "บันทึกการเปลี่ยนแปลง",
- "editFolder": "แก้ไขโฟลเดอร์",
- "deleteFolder": "ลบโฟลเดอร์",
- "folderDeleteSuccess": "โฟลเดอร์ \"{{name}}\" ถูกลบแล้ว",
- "folderDeleteFailed": "ไม่สามารถลบโฟลเดอร์ได้",
- "folderEditSuccess": "อัปเดตโฟลเดอร์สำเร็จแล้ว",
- "folderEditFailed": "ไม่สามารถอัปเดตโฟลเดอร์ได้",
- "confirmRunMessage": "เรียกใช้ \"{{name}}\"?",
- "confirmRunButton": "วิ่ง",
- "runSuccess": "Ran \"{{name}}\" ในเทอร์มินัล {{count}} เครื่อง",
- "pasteSuccess": "วาง \"{{name}}\" ลงในเทอร์มินัล {{count}} แล้ว",
- "pasteToTerminal": "แปะ",
- "copySuccess": "คัดลอก \"{{name}}\" ไปยังคลิปบอร์ด",
- "shareTitle": "แชร์ตัวอย่าง",
- "shareUser": "ผู้ใช้",
- "shareRole": "บทบาท",
- "selectUser": "เลือกผู้ใช้...",
- "selectRole": "เลือกบทบาท...",
- "shareSuccess": "แชร์โค้ดตัวอย่างสำเร็จแล้ว",
- "shareFailed": "ไม่สามารถแชร์ส่วนย่อยได้",
- "revokeSuccess": "สิทธิ์การเข้าถึงถูกเพิกถอน",
- "revokeFailed": "ไม่สามารถเพิกถอนสิทธิ์การเข้าถึงได้",
- "currentAccess": "การเข้าถึงปัจจุบัน",
- "shareLoadError": "ไม่สามารถโหลดข้อมูลการแชร์ได้",
- "loading": "กำลังโหลด...",
- "close": "ปิด",
- "reorderFailed": "ไม่สามารถบันทึกลำดับของส่วนย่อยได้",
- "importExport": "นำเข้า/ส่งออก",
- "exportBtn": "ส่งออก JSON",
- "importBtn": "นำเข้า JSON",
- "exportSuccess": "ส่งออกข้อมูลตัวอย่างสำเร็จแล้ว",
- "exportFailed": "ไม่สามารถส่งออกส่วนย่อยได้",
- "importTitle": "นำเข้าโค้ดตัวอย่าง",
- "importDescription": "นำเข้าส่วนย่อยและโฟลเดอร์จากไฟล์ JSON ที่ส่งออกโดย Termix",
- "importDropOrClick": "วางไฟล์ JSON ที่นี่ หรือคลิกเพื่อเรียกดู",
- "importSelectedFile": "เลือกแล้ว: {{name}}",
- "importOverwrite": "เขียนทับโค้ดตัวอย่างที่มีอยู่แล้วซึ่งมีชื่อและโฟลเดอร์เดียวกัน",
- "importStartBtn": "นำเข้า",
- "importSuccess": "การนำเข้าเสร็จสมบูรณ์: เพิ่มส่วนย่อย {{snippets}} รายการ, อัปเดต {{updated}} รายการ, ข้าม {{skipped}} รายการ, เพิ่มโฟลเดอร์ {{folders}} รายการ",
- "importFailed": "ไม่สามารถนำเข้าส่วนย่อยได้",
- "importInvalidFile": "ไฟล์ไม่ถูกต้อง: คาดหวังอ็อบเจ็กต์ JSON ที่มีอาร์เรย์ของส่วนย่อยหรือโฟลเดอร์",
- "targetHostsLabel": "โฮสต์เป้าหมาย",
- "targetHostsHint": "กำหนดให้โฮสต์สามารถรันโค้ดส่วนนี้ได้โดยตรงโดยไม่ต้องเปิดเทอร์มินัล",
- "noHostsAvailable": "ไม่มีการกำหนดค่าโฮสต์",
- "clearTargetHosts": "ล้างทั้งหมด",
- "hasTargetHosts": "มีโฮสต์เป้าหมาย",
- "runOnTargets": "วิ่งตามเป้าหมาย",
- "directRunSuccess": "Ran \"{{name}}\" บนโฮสต์ {{count}} เครื่อง",
- "directRunPartialFail": "\"{{name}}\" ล้มเหลวบนโฮสต์อย่างน้อยหนึ่งเครื่อง",
- "executionResultTitle": "ผลลัพธ์การดำเนินการ: {{name}}",
- "executionResultDescription": "ผลลัพธ์จากการรันโค้ดบนโฮสต์เป้าหมายแต่ละตัว",
- "executionSuccess": "ความสำเร็จ",
- "executionFailed": "ล้มเหลว",
- "expandAll": "ขยายโฟลเดอร์ทั้งหมด",
- "collapseAll": "ยุบโฟลเดอร์ทั้งหมด",
- "settings": "การตั้งค่าสนิปเป็ต",
- "variablesHint": "ตัวแปร: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "วิ่ง \"{{name}}\"",
- "variablesDialogDescription": "กรอกค่าที่จำเป็นสำหรับโค้ดส่วนนี้ก่อนที่จะเรียกใช้งาน",
- "variablesPreviewLabel": "ตัวอย่าง",
- "variablesConfirmButton": "วิ่ง"
- },
"keybindings": {
"title": "แป้นพิมพ์ลัด",
"description": "ปรับแต่งปุ่มลัดสำหรับการคัดลอก วาง และควบคุมในเทอร์มินัล หรือกำหนดปุ่มเพื่อส่งข้อความหรือเรียกใช้โค้ดตัวอย่าง",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "คลิกและกดปุ่ม",
"recording": "กดปุ่ม...",
"comboRequiredError": "กดปุ่มคีย์ผสมก่อน",
- "textRequiredError": "ป้อนข้อความที่จะส่ง",
- "controlCodeRequiredError": "ป้อนตัวอักษรตัวเดียวสำหรับรหัสควบคุม",
- "snippetRequiredError": "เลือกส่วนย่อย",
"conflictWarning": "การรวมกันนี้ถูกผูกไว้กับ {{combo}}แล้ว การบันทึกจะทำให้ทั้งสองอย่างทำงาน",
"actionLabel": "การกระทำ",
- "actionCopy": "คัดลอกการเลือก",
- "actionPaste": "แปะ",
- "actionSendControlCode": "ส่งสัญญาณ Ctrl+ตัวอักษร",
- "actionSendText": "ส่งข้อความ",
- "actionRunSnippet": "เรียกใช้โค้ดตัวอย่างที่มีอยู่",
"actionNextTab": "สลับไปยังแท็บถัดไป",
"actionPreviousTab": "สลับไปยังแท็บก่อนหน้า",
- "actionOpenCommandPalette": "เปิดการค้นหาด่วน",
- "controlCodeLabel": "จดหมาย",
- "textLabel": "ข้อความที่จะส่ง",
- "appendEnterLabel": "กด Enter หลังจากส่งแล้ว",
- "snippetLabel": "ส่วนหนึ่ง",
- "selectSnippetPlaceholder": "เลือกส่วนย่อย",
- "orphanedSnippetWarning": "ไม่พบส่วนย่อย",
- "clipboardPermissionNote": "ในบางเบราว์เซอร์ อาจขออนุญาตคัดลอกและวางข้อมูลลงในคลิปบอร์ดเมื่อใช้งานครั้งแรก"
+ "actionOpenCommandPalette": "เปิดการค้นหาด่วน"
},
"userProfile": {
"donateTitle": "รักษา Termix ให้คงอยู่ต่อไป",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "จำเป็นต้องระบุหัวข้อ",
"ntfyToken": "โทเค็นการเข้าถึง (ไม่บังคับ)",
"channelSaveFailed": "ไม่สามารถบันทึกช่องได้"
- },
- "macros": {
- "title": "มาโคร",
- "description": "เรียกใช้ชุดคำสั่งที่บันทึกไว้ในเทอร์มินัล แล้วตอบสนองต่อสิ่งที่เซิร์ฟเวอร์ส่งกลับมา",
- "target": "เป้า",
- "noTerminal": "ไม่มีเทอร์มินัลเปิดอยู่",
- "empty": "ยังไม่มีมาโคร",
- "emptyHint": "สร้างมาโครเพื่อทำให้กระบวนการทำงานบนเทอร์มินัลเป็นไปโดยอัตโนมัติ เช่น การล็อกอิน การรีสตาร์ทบริการ หรือการตอบคำถามต่างๆ",
- "create": "มาโครใหม่",
- "back": "กลับไปที่มาโคร",
- "name": "ชื่อ",
- "namePlaceholder": "รีสตาร์ทเว็บเซิร์ฟเวอร์",
- "nameRequired": "ตั้งชื่อมาโครก่อนบันทึก",
- "descriptionLabel": "คำอธิบาย",
- "descriptionPlaceholder": "มาโครนี้ทำอะไร (ไม่บังคับ)",
- "unsaved": "การเปลี่ยนแปลงที่ยังไม่ได้บันทึก",
- "save": "บันทึก",
- "saved": "บันทึกมาโครแล้ว",
- "saveFailed": "ไม่สามารถบันทึกมาโครได้",
- "delete": "ลบ",
- "deleteTitle": "ลบมาโครนี้?",
- "deleteBody": "\"{{name}}\" จะถูกลบอย่างถาวร ไม่สามารถกู้คืนได้",
- "deleted": "มาโครถูกลบแล้ว",
- "cancel": "ยกเลิก",
- "run": "วิ่ง",
- "stop": "หยุด",
- "running": "วิ่ง...",
- "completed": "มาโครเสร็จสิ้น",
- "failed": "มาโครล้มเหลว",
- "loadFailed": "ไม่สามารถโหลดมาโครได้",
- "needTerminal": "เปิดและเชื่อมต่อเทอร์มินัลก่อน",
- "noSteps": "มาโครนี้ยังไม่มีขั้นตอนใดๆ เพิ่มขั้นตอนด้านล่าง",
- "addStep": "เพิ่มขั้นตอน",
- "stepNumber": "ขั้นตอน {{number}}",
- "removeStep": "ลบขั้นตอน",
- "confirmRunTitle": "เรียกใช้มาโครนี้หรือไม่?",
- "confirmRunBody": "\"{{name}}\" จะส่งคำสั่งไปยัง {{host}}",
- "confirmRunAction": "เรียกใช้มาโคร",
- "stepTypes": {
- "send": {
- "label": "ส่งคำสั่ง",
- "help": "พิมพ์ข้อความลงในเทอร์มินัล เหมือนกับที่คุณพิมพ์ข้อความนั้นด้วยตนเอง"
- },
- "wait": {
- "label": "รอผลลัพธ์",
- "help": "หยุดรอจนกว่าเซิร์ฟเวอร์จะแสดงข้อความที่คุณระบุ เช่นเดียวกับการขอให้ป้อนรหัสผ่าน"
- },
- "delay": {
- "label": "หยุดชั่วคราว",
- "help": "รอเป็นระยะเวลาที่กำหนดก่อนดำเนินการขั้นตอนต่อไป"
- },
- "if": {
- "label": "หากผลลัพธ์ประกอบด้วย",
- "help": "ตรวจสอบทุกอย่างที่เซิร์ฟเวอร์ได้พิมพ์ออกมาแล้ว จากนั้นจึงเลือกดำเนินการตามหนึ่งในสองทางเลือก"
- },
- "repeat": {
- "label": "ทำซ้ำ",
- "help": "ดำเนินการตามขั้นตอนภายในตามจำนวนครั้งที่กำหนด"
- }
- },
- "send": {
- "text": "คำสั่งหรือข้อความ",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "กด Enter หลังจากส่งแล้ว",
- "pressEnterHelp": "ปิดตัวเลือกนี้เพื่อพิมพ์ข้อความโดยไม่ต้องกดส่ง"
- },
- "wait": {
- "pattern": "รอจนกว่าผลลัพธ์จะประกอบด้วย",
- "patternPlaceholder": "รหัสผ่าน:",
- "timeout": "ยอมแพ้หลังจาก",
- "onTimeout": "หากมันไม่ปรากฏเลย",
- "stop": "หยุดมาโคร",
- "continue": "อย่าหยุดแค่นี้"
- },
- "delay": {
- "duration": "รอสักครู่"
- },
- "ifStep": {
- "pattern": "หากผลลัพธ์ประกอบด้วย",
- "patternPlaceholder": "สถานะ: กำลังทำกิจกรรม (วิ่ง)",
- "then": "จากนั้นให้ทำแบบนี้",
- "else": "หรือทำแบบนี้ก็ได้",
- "emptyBranch": "ไม่มีขั้นตอน"
- },
- "repeat": {
- "count": "จำนวนครั้ง",
- "times": "ครั้ง"
- },
- "match": {
- "useRegex": "จับคู่ด้วยนิพจน์ปกติ",
- "useRegexHelp": "ไม่ต้องใส่ส่วนนี้เพื่อให้ตรงกับข้อความที่เขียนไว้ทุกประการ",
- "invalidRegex": "ไม่ใช่รูปแบบนิพจน์ปกติที่ถูกต้อง ระบบจะถือว่าเป็นข้อความธรรมดา"
- },
- "seconds": "วินาที",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/tr_TR.json b/src/ui/locales/translated/tr_TR.json
index aad615b78..b18943420 100644
--- a/src/ui/locales/translated/tr_TR.json
+++ b/src/ui/locales/translated/tr_TR.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Kenar Çubuğu Ayarları...",
"hosts": "Ana Bilgisayarlar",
"snippets": "Kod Parçacıkları",
- "macros": "Makrolar",
"hostManager": "Ana Bilgisayar Yöneticisi",
"credentials": "Kimlik Bilgileri",
"connections": "Bağlantılar",
@@ -869,7 +868,6 @@
"groupTags": "Etiketler ve pinler",
"groupTunnels": "Tüneller",
"groupJumpHosts": "Sunucuları atla",
- "groupQuickActions": "Hızlı işlemler",
"groupFeatureFlags": "Özellik bayrakları",
"groupAdvanced": "Gelişmiş yapılandırma",
"preview": "Önizleme",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "SSH anahtarları veya parolaları sakla",
"tmuxMonitor": "Tmux İzleyici",
"tmuxMonitorDesc": "Ana makinelerinizdeki tmux oturumlarını izle",
- "snippets": "Kısa bölümler",
- "runSnippetDesc": "Etkin terminal sekmesinde çalıştırın.",
- "noSnippetsFound": "\"{{search}} \" ile eşleşen hiçbir kod parçacığı bulunamadı.",
"recentActivity": "Son Etkinlik",
"serversAndHosts": "Sunucular ve Ana Makineler",
"noHostsFound": "\"{{search}}\" ile eşleşen ana makineler bulunamadı",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Önceki sekme",
"hotkeysJumpTab": "Sekmeye atla"
},
- "snippets": {
- "title": "Parçacıklar",
- "createSnippetTitle": "Parçacık Oluştur",
- "createSnippetDescription": "Hızlı çalıştırma için yeni bir komut parçacığı oluşturun",
- "nameLabel": "Ad",
- "namePlaceholder": "ör. Nginx'i Yeniden Başlat",
- "descriptionLabel": "Açıklama",
- "descriptionPlaceholder": "İsteğe bağlı açıklama",
- "optional": "İsteğe bağlı",
- "folderLabel": "Klasör",
- "noFolder": "Klasör yok (Kategorisiz)",
- "commandLabel": "Komut",
- "commandPlaceholder": "ör. sudo systemctl restart nginx",
- "typeLabel": "Tip",
- "typeCommand": "Emretmek",
- "typeNote": "Not",
- "noteLabel": "Not",
- "notePlaceholder": "Örneğin, tekrar kullanılabilir bir uyarı veya hatırlatma metni.",
- "cancel": "İptal",
- "createSnippetButton": "Parçacık Oluştur",
- "createFolderTitle": "Klasör Oluştur",
- "createFolderDescription": "Parçacıklarınızı klasörlerde düzenleyin",
- "folderNameLabel": "Klasör Adı",
- "folderNamePlaceholder": "ör. Sistem Komutları, Docker Betikleri",
- "folderColorLabel": "Klasör Rengi",
- "folderIconLabel": "Klasör Simgesi",
- "previewLabel": "Önizleme",
- "folderNameFallback": "Klasör Adı",
- "createFolderButton": "Klasör Oluştur",
- "targetTerminals": "Hedef Terminaller",
- "selectAll": "Tümü",
- "selectNone": "Hiçbiri",
- "noTerminalTabsOpen": "Açık terminal sekmesi yok",
- "searchPlaceholder": "Parçacıkları ara...",
- "newSnippet": "Yeni Parçacık",
- "newFolder": "Yeni Klasör",
- "run": "Çalıştır",
- "noSnippetsInFolder": "Bu klasörde parçacık yok",
- "uncategorized": "Kategorisiz",
- "editSnippetTitle": "Parçacığı Düzenle",
- "editSnippetDescription": "Bu komut parçacığını güncelle",
- "saveSnippetButton": "Değişiklikleri Kaydet",
- "createSuccess": "Parçacık başarıyla oluşturuldu",
- "createFailed": "Parçacık oluşturulamadı",
- "updateSuccess": "Parçacık başarıyla güncellendi",
- "updateFailed": "Parçacık güncellenemedi",
- "deleteFailed": "Parçacık silinemedi",
- "folderCreateSuccess": "Klasör başarıyla oluşturuldu",
- "folderCreateFailed": "Klasör oluşturulamadı",
- "editFolderTitle": "Klasörü Düzenle",
- "editFolderDescription": "Bu klasörü yeniden adlandırın veya görünümünü değiştirin",
- "saveFolderButton": "Değişiklikleri Kaydet",
- "editFolder": "Klasörü düzenle",
- "deleteFolder": "Klasörü sil",
- "folderDeleteSuccess": "\"{{name}}\" klasörü silindi",
- "folderDeleteFailed": "Klasör silinemedi",
- "folderEditSuccess": "Klasör başarıyla güncellendi",
- "folderEditFailed": "Klasör güncellenemedi",
- "confirmRunMessage": "\"{{name}}\" çalıştırılsın mı?",
- "confirmRunButton": "Çalıştır",
- "runSuccess": "\"{{name}}\" {{count}} terminalde çalıştırıldı",
- "pasteSuccess": "\"{{name}}\" ifadesi {{count}} terminal(ler)ine yapıştırıldı",
- "pasteToTerminal": "Yapıştır",
- "copySuccess": "\"{{name}}\" panoya kopyalandı",
- "shareTitle": "Parçacığı Paylaş",
- "shareUser": "Kullanıcı",
- "shareRole": "Rol",
- "selectUser": "Kullanıcı seç...",
- "selectRole": "Rol seç...",
- "shareSuccess": "Kod parçacığı başarıyla paylaşıldı",
- "shareFailed": "Kod parçacığı paylaşılamadı",
- "revokeSuccess": "Erişim iptal edildi",
- "revokeFailed": "Erişim iptal edilemedi",
- "currentAccess": "Geçerli Erişim",
- "shareLoadError": "Paylaşım verisi yüklenemedi",
- "loading": "Yükleniyor...",
- "close": "Kapat",
- "reorderFailed": "Kod parçacığı sıralaması kaydedilemedi",
- "importExport": "İçe / Dışa Aktar",
- "exportBtn": "JSON Dışa Aktar",
- "importBtn": "JSON İçe Aktar",
- "exportSuccess": "Kod parçacıkları başarıyla dışa aktarıldı",
- "exportFailed": "Kod parçacıkları dışa aktarılamadı",
- "importTitle": "Kod Parçacıklarını İçe Aktar",
- "importDescription": "Termix tarafından dışa aktarılan bir JSON dosyasından kod parçacıklarını ve klasörleri içe aktar",
- "importDropOrClick": "Bir JSON dosyasını buraya sürükleyin veya göz atmak için tıklayın",
- "importSelectedFile": "Seçili: {{name}}",
- "importOverwrite": "Aynı ada ve klasöre sahip mevcut kod parçacıklarının üzerine yaz",
- "importStartBtn": "İçe Aktar",
- "importSuccess": "İçe aktarma tamamlandı: {{snippets}} kod parçacığı eklendi, {{updated}} güncellendi, {{skipped}} atlandı, {{folders}} klasör eklendi",
- "importFailed": "Kod parçacıkları içe aktarılamadı",
- "importInvalidFile": "Geçersiz dosya: snippets veya folders dizileri içeren bir JSON nesnesi bekleniyor",
- "targetHostsLabel": "Hedef Sunucular",
- "targetHostsHint": "Bu kod parçacığını açık bir terminal olmadan doğrudan çalıştırmak için sunucular atayın.",
- "noHostsAvailable": "Yapılandırılmış sunucu yok",
- "clearTargetHosts": "Tümünü temizle",
- "hasTargetHosts": "Hedef sunuculara sahip",
- "runOnTargets": "Hedeflerde Çalıştır",
- "directRunSuccess": "{{count}} sunucuda \"{{name}}\" çalıştırıldı",
- "directRunPartialFail": "\"{{name}}\" bir veya daha fazla sunucuda başarısız oldu",
- "executionResultTitle": "Çalıştırma Sonuçları: {{name}}",
- "executionResultDescription": "Her hedef sunucuda parçacığı çalıştırmanın çıktısı.",
- "executionSuccess": "Başarılı",
- "executionFailed": "Başarısız",
- "expandAll": "Tüm klasörleri genişlet",
- "collapseAll": "Tüm klasörleri daralt",
- "settings": "Kod parçacığı ayarları",
- "variablesHint": "Değişkenler: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Çalıştır \"{{name}}\"",
- "variablesDialogDescription": "Bu kod parçasını çalıştırmadan önce gerekli değerleri girin.",
- "variablesPreviewLabel": "Önizleme",
- "variablesConfirmButton": "Koşmak"
- },
"keybindings": {
"title": "Klavye Kısayolları",
"description": "Terminaldeki kopyalama, yapıştırma ve kontrol kısayollarını özelleştirin veya metin göndermek ya da kod parçacığı çalıştırmak için tuş atamaları yapın.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Tıklayın ve tuşlara basın",
"recording": "Tuşlara basın...",
"comboRequiredError": "Önce bir tuş kombinasyonuna basın.",
- "textRequiredError": "Göndermek istediğiniz metni girin.",
- "controlCodeRequiredError": "Kontrol kodu için tek bir harf girin.",
- "snippetRequiredError": "Bir parça seçin",
"conflictWarning": "Bu kombinasyon zaten {{combo}}ile ilişkilendirilmiş durumda. Kaydetme işlemi her ikisinin de çalışmasını sağlayacaktır.",
"actionLabel": "Aksiyon",
- "actionCopy": "Seçimi kopyala",
- "actionPaste": "Yapıştır",
- "actionSendControlCode": "Ctrl+harf sinyali gönder",
- "actionSendText": "Mesaj gönder",
- "actionRunSnippet": "Mevcut kod parçasını çalıştır",
"actionNextTab": "Sonraki sekmeye geç",
"actionPreviousTab": "Önceki sekmeye geç",
- "actionOpenCommandPalette": "Hızlı aramayı aç",
- "controlCodeLabel": "Mektup",
- "textLabel": "Gönderilecek metin",
- "appendEnterLabel": "Gönderdikten sonra Enter tuşuna basın.",
- "snippetLabel": "Alıntı",
- "selectSnippetPlaceholder": "Bir parça seçin",
- "orphanedSnippetWarning": "Kod parçası bulunamadı",
- "clipboardPermissionNote": "Bazı tarayıcılarda ilk kullanımda panoya kopyalama izni isteyebilir."
+ "actionOpenCommandPalette": "Hızlı aramayı aç"
},
"userProfile": {
"donateTitle": "Termix'i canlı tutun",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Konu zorunludur",
"ntfyToken": "Erişim Anahtarı (isteğe bağlı)",
"channelSaveFailed": "Kanal kaydedilemedi"
- },
- "macros": {
- "title": "Makrolar",
- "description": "Önceden kaydedilmiş bir komut dizisini terminalde çalıştırın ve sunucunun geri gönderdiği yanıtlara göre hareket edin.",
- "target": "Hedef",
- "noTerminal": "Açık terminal yok.",
- "empty": "Henüz makro yok",
- "emptyHint": "Giriş yapma, bir hizmeti yeniden başlatma veya komut istemlerine yanıt verme gibi terminal işlemlerini otomatikleştirmek için bir makro oluşturun.",
- "create": "Yeni makro",
- "back": "Makrolara geri dön",
- "name": "İsim",
- "namePlaceholder": "Web sunucusunu yeniden başlatın.",
- "nameRequired": "Kaydetmeden önce makroya bir ad verin.",
- "descriptionLabel": "Tanım",
- "descriptionPlaceholder": "Bu makronun ne yaptığı (isteğe bağlı)",
- "unsaved": "Kaydedilmemiş değişiklikler",
- "save": "Kaydetmek",
- "saved": "Makro kaydedildi.",
- "saveFailed": "Makro kaydedilemedi.",
- "delete": "Silmek",
- "deleteTitle": "Bu makroyu silmek ister misiniz?",
- "deleteBody": "\"{{name}}\" kalıcı olarak silinecektir. Bu işlem geri alınamaz.",
- "deleted": "Makro silindi",
- "cancel": "İptal etmek",
- "run": "Koşmak",
- "stop": "Durmak",
- "running": "Koşma...",
- "completed": "Makro tamamlandı",
- "failed": "Makro başarısız oldu",
- "loadFailed": "Makrolar yüklenemedi.",
- "needTerminal": "Öncelikle bir terminal açın ve bağlayın.",
- "noSteps": "Bu makronun henüz hiçbir adımı yok. Aşağıya bir adım ekleyin.",
- "addStep": "Adım ekle",
- "stepNumber": "Adım {{number}}",
- "removeStep": "Adımı kaldır",
- "confirmRunTitle": "Bu makroyu çalıştırayım mı?",
- "confirmRunBody": "\"{{name}}\" {{host}} adresine komutlar gönderecektir.",
- "confirmRunAction": "Makroyu çalıştır",
- "stepTypes": {
- "send": {
- "label": "Komut gönder",
- "help": "Terminale, sanki siz yazmışsınız gibi metin yazar."
- },
- "wait": {
- "label": "Çıktıyı bekleyin",
- "help": "Sunucu, belirttiğiniz metni (örneğin parola istemi gibi) yazdırana kadar bekler."
- },
- "delay": {
- "label": "Duraklat",
- "help": "Bir sonraki adıma geçmeden önce belirli bir süre bekler."
- },
- "if": {
- "label": "Çıktı içeriyorsa",
- "help": "Sunucunun şu ana kadar yazdırdığı her şeyi kontrol eder, ardından iki daldan birini çalıştırır."
- },
- "repeat": {
- "label": "Tekrarlamak",
- "help": "İçindeki adımları belirtilen sayıda tekrarlar."
- }
- },
- "send": {
- "text": "Komut veya metin",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Gönderdikten sonra Enter tuşuna basın.",
- "pressEnterHelp": "Göndermeden metin yazmak için bunu kapatın."
- },
- "wait": {
- "pattern": "Çıktıyı oluşturana kadar bekleyin.",
- "patternPlaceholder": "şifre:",
- "timeout": "Vazgeçtikten sonra",
- "onTimeout": "Eğer hiç ortaya çıkmazsa",
- "stop": "Makroyu durdur",
- "continue": "Yine de devam edin."
- },
- "delay": {
- "duration": "Bekleyin"
- },
- "ifStep": {
- "pattern": "Çıktı şunları içeriyorsa:",
- "patternPlaceholder": "Aktif: aktif (koşmak)",
- "then": "O halde şunu yapın",
- "else": "Aksi takdirde şunu yapın",
- "emptyBranch": "Hiçbir adım yok"
- },
- "repeat": {
- "count": "Sayısı",
- "times": "kez"
- },
- "match": {
- "useRegex": "Düzenli ifadeyle eşleştirin",
- "useRegexHelp": "Metnin aynen yazıldığı gibi kalması için bunu atlayın.",
- "invalidRegex": "Geçerli bir normal ifade değil, düz metin olarak eşleştirilecektir."
- },
- "seconds": "saniyeler",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/uk_UA.json b/src/ui/locales/translated/uk_UA.json
index 6a919cfa6..01cbefa30 100644
--- a/src/ui/locales/translated/uk_UA.json
+++ b/src/ui/locales/translated/uk_UA.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Налаштування бічної панелі...",
"hosts": "Хости",
"snippets": "Фрагменти",
- "macros": "Макроси",
"hostManager": "Менеджер хостингу",
"credentials": "Облікові дані",
"connections": "З'єднання",
@@ -869,7 +868,6 @@
"groupTags": "Теги та закріплення",
"groupTunnels": "Тунелі",
"groupJumpHosts": "Хости Jump",
- "groupQuickActions": "Швидкі дії",
"groupFeatureFlags": "Прапорці функцій",
"groupAdvanced": "Розширена конфігурація",
"preview": "Попередній перегляд",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Зберігайте SSH-ключі або паролі",
"tmuxMonitor": "Монітор Tmux",
"tmuxMonitorDesc": "Моніторинг сесій tmux на ваших хостах",
- "snippets": "Фрагменти",
- "runSnippetDesc": "Запустити в активній вкладці терміналу",
- "noSnippetsFound": "Не знайдено фрагментів, що відповідають \"{{search}}\"",
"recentActivity": "Нещодавня активність",
"serversAndHosts": "Сервери та хости",
"noHostsFound": "Не знайдено хостів, що відповідають \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Попередня вкладка",
"hotkeysJumpTab": "Перейти до вкладки"
},
- "snippets": {
- "title": "Фрагменти",
- "createSnippetTitle": "Створити фрагмент",
- "createSnippetDescription": "Створіть новий фрагмент команди для швидкого виконання",
- "nameLabel": "Ім'я",
- "namePlaceholder": "наприклад, перезавантажте Nginx",
- "descriptionLabel": "Опис",
- "descriptionPlaceholder": "Додатковий опис",
- "optional": "Необов'язково",
- "folderLabel": "Папка",
- "noFolder": "Немає папки (Без категорії)",
- "commandLabel": "Команда",
- "commandPlaceholder": "наприклад, sudo systemctl перезавантажити nginx",
- "typeLabel": "Тип",
- "typeCommand": "Команда",
- "typeNote": "Примітка",
- "noteLabel": "Примітка",
- "notePlaceholder": "наприклад, текст нагадування або підказки для повторного використання",
- "cancel": "Скасувати",
- "createSnippetButton": "Створити фрагмент",
- "createFolderTitle": "Створити папку",
- "createFolderDescription": "Упорядкуйте свої фрагменти коду в папки",
- "folderNameLabel": "Назва папки",
- "folderNamePlaceholder": "наприклад, системні команди, скрипти Docker",
- "folderColorLabel": "Колір папки",
- "folderIconLabel": "Піктограма папки",
- "previewLabel": "Попередній перегляд",
- "folderNameFallback": "Назва папки",
- "createFolderButton": "Створити папку",
- "targetTerminals": "Цільові термінали",
- "selectAll": "Усі",
- "selectNone": "Жоден",
- "noTerminalTabsOpen": "Немає відкритих вкладок терміналу",
- "searchPlaceholder": "Пошук фрагментів...",
- "newSnippet": "Новий фрагмент",
- "newFolder": "Нова папка",
- "run": "Бігти",
- "noSnippetsInFolder": "У цій папці немає фрагментів",
- "uncategorized": "Без категорії",
- "editSnippetTitle": "Редагувати фрагмент",
- "editSnippetDescription": "Оновіть цей фрагмент команди",
- "saveSnippetButton": "Зберегти зміни",
- "createSuccess": "Фрагмент успішно створено",
- "createFailed": "Не вдалося створити фрагмент",
- "updateSuccess": "Фрагмент успішно оновлено",
- "updateFailed": "Не вдалося оновити фрагмент",
- "deleteFailed": "Не вдалося видалити фрагмент",
- "folderCreateSuccess": "Папку успішно створено",
- "folderCreateFailed": "Не вдалося створити папку",
- "editFolderTitle": "Редагувати папку",
- "editFolderDescription": "Перейменуйте або змініть зовнішній вигляд цієї папки",
- "saveFolderButton": "Зберегти зміни",
- "editFolder": "Редагувати папку",
- "deleteFolder": "Видалити папку",
- "folderDeleteSuccess": "Папку «{{name}}» видалено",
- "folderDeleteFailed": "Не вдалося видалити папку",
- "folderEditSuccess": "Папку успішно оновлено",
- "folderEditFailed": "Не вдалося оновити папку",
- "confirmRunMessage": "Запустити \"{{name}}\"?",
- "confirmRunButton": "Бігти",
- "runSuccess": "Виконано \"{{name}}\" у {{count}} терміналі(ах)",
- "pasteSuccess": "Вставлено \"{{name}}\" у термінал(и) {{count}}",
- "pasteToTerminal": "Вставити",
- "copySuccess": "Скопійовано \"{{name}}\" у буфер обміну",
- "shareTitle": "Поділитися фрагментом",
- "shareUser": "Користувач",
- "shareRole": "Роль",
- "selectUser": "Виберіть користувача...",
- "selectRole": "Виберіть роль...",
- "shareSuccess": "Фрагмент успішно поділився",
- "shareFailed": "Не вдалося поділитися фрагментом",
- "revokeSuccess": "Доступ скасовано",
- "revokeFailed": "Не вдалося скасувати доступ",
- "currentAccess": "Поточний доступ",
- "shareLoadError": "Не вдалося завантажити дані спільного доступу",
- "loading": "Завантаження...",
- "close": "Закрити",
- "reorderFailed": "Не вдалося зберегти порядок фрагментів",
- "importExport": "Імпорт / Експорт",
- "exportBtn": "Експорт JSON",
- "importBtn": "Імпорт JSON",
- "exportSuccess": "Фрагменти успішно експортовано",
- "exportFailed": "Не вдалося експортувати фрагменти",
- "importTitle": "Імпорт фрагментів",
- "importDescription": "Імпорт фрагментів та папок з JSON-файлу, експортованого Termix",
- "importDropOrClick": "Перетягніть сюди JSON-файл або натисніть, щоб переглянути",
- "importSelectedFile": "Вибрано: {{name}}",
- "importOverwrite": "Перезаписати існуючі фрагменти з тією ж назвою та папкою",
- "importStartBtn": "Імпорт",
- "importSuccess": "Імпорт завершено: додано {{snippets}} фрагментів, оновлено {{updated}} , пропущено {{skipped}} , додано папок {{folders}}",
- "importFailed": "Не вдалося імпортувати фрагменти",
- "importInvalidFile": "Недійсний файл: очікується об'єкт JSON з фрагментами або масивами папок",
- "targetHostsLabel": "Цільові хости",
- "targetHostsHint": "Призначте хости для запуску цього фрагмента коду безпосередньо без відкритого терміналу.",
- "noHostsAvailable": "Немає налаштованих хостів",
- "clearTargetHosts": "Очистити все",
- "hasTargetHosts": "Має цільові хости",
- "runOnTargets": "Біг за цілями",
- "directRunSuccess": "Виконано \"{{name}}\" на хості(ах) {{count}}",
- "directRunPartialFail": "«{{name}}» не вдалося виконати на одному або кількох хостах",
- "executionResultTitle": "Результати виконання: {{name}}",
- "executionResultDescription": "Вивідні дані запуску фрагмента коду на кожному цільовому хості.",
- "executionSuccess": "Успіх",
- "executionFailed": "Не вдалося",
- "expandAll": "Розгорнути всі папки",
- "collapseAll": "Згорнути всі папки",
- "settings": "Налаштування фрагмента",
- "variablesHint": "Змінні: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Мітка}",
- "variablesDialogTitle": "Виконати \"{{name}}\"",
- "variablesDialogDescription": "Заповніть значення, необхідні цьому фрагменту, перш ніж запускати його.",
- "variablesPreviewLabel": "Попередній перегляд",
- "variablesConfirmButton": "Бігти"
- },
"keybindings": {
"title": "Комбінації клавіш",
"description": "Налаштуйте комбінації клавіш для копіювання, вставки та керування терміналом або прив’яжіть клавіші для надсилання тексту чи запуску фрагмента коду.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Клацайте та натискайте клавіші",
"recording": "Натискайте клавіші...",
"comboRequiredError": "Спочатку натисніть комбінацію клавіш",
- "textRequiredError": "Введіть текст для надсилання",
- "controlCodeRequiredError": "Введіть одну літеру для контрольного коду",
- "snippetRequiredError": "Виберіть фрагмент",
"conflictWarning": "Ця комбінація вже пов'язана з {{combo}}. Збереження призведе до спрацювання обох.",
"actionLabel": "Дія",
- "actionCopy": "Копіювати виділення",
- "actionPaste": "Вставити",
- "actionSendControlCode": "Надіслати сигнал Ctrl+літера",
- "actionSendText": "Надіслати текст",
- "actionRunSnippet": "Запустити існуючий фрагмент коду",
"actionNextTab": "Перейти до наступної вкладки",
"actionPreviousTab": "Перейти до попередньої вкладки",
- "actionOpenCommandPalette": "Відкрити швидкий пошук",
- "controlCodeLabel": "Лист",
- "textLabel": "Текст для надсилання",
- "appendEnterLabel": "Натисніть Enter після відправлення",
- "snippetLabel": "Фрагмент",
- "selectSnippetPlaceholder": "Виберіть фрагмент",
- "orphanedSnippetWarning": "фрагмент не знайдено",
- "clipboardPermissionNote": "У деяких браузерах може запитуватися дозвіл на використання буфера обміну під час першого використання."
+ "actionOpenCommandPalette": "Відкрити швидкий пошук"
},
"userProfile": {
"donateTitle": "Збережи Термікс живим",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Тема обов'язкова",
"ntfyToken": "Маркер доступу (необов'язково)",
"channelSaveFailed": "Не вдалося зберегти канал"
- },
- "macros": {
- "title": "Макроси",
- "description": "Виконайте збережену серію команд у терміналі та відреагуйте на те, що надсилає сервер.",
- "target": "Ціль",
- "noTerminal": "Термінал не відкритий",
- "empty": "Поки що немає макросів",
- "emptyHint": "Створіть макрос для автоматизації процесу роботи термінала, такого як вхід у систему, перезапуск служби або відповідь на запити.",
- "create": "Новий макрос",
- "back": "Повернутися до макросів",
- "name": "Ім'я",
- "namePlaceholder": "Перезапустіть веб-сервер",
- "nameRequired": "Введіть ім'я макросу перед збереженням",
- "descriptionLabel": "Опис",
- "descriptionPlaceholder": "Що робить цей макрос (необов'язково)",
- "unsaved": "Незбережені зміни",
- "save": "Зберегти",
- "saved": "Макрос збережено",
- "saveFailed": "Не вдалося зберегти макрос",
- "delete": "Видалити",
- "deleteTitle": "Видалити цей макрос?",
- "deleteBody": "«{{name}}» буде видалено назавжди. Цю дію неможливо скасувати.",
- "deleted": "Макрос видалено",
- "cancel": "Скасувати",
- "run": "Бігти",
- "stop": "СТІЙ",
- "running": "Біг...",
- "completed": "Макрос завершено",
- "failed": "Помилка макросу",
- "loadFailed": "Не вдалося завантажити макроси",
- "needTerminal": "Спочатку відкрийте та підключіть термінал",
- "noSteps": "Цей макрос ще не має кроків. Додайте один нижче.",
- "addStep": "Додати крок",
- "stepNumber": "Крок {{number}}",
- "removeStep": "Видалити крок",
- "confirmRunTitle": "Запустити цей макрос?",
- "confirmRunBody": "\"{{name}}\" надсилатиме команди до {{host}}.",
- "confirmRunAction": "Запустити макрос",
- "stepTypes": {
- "send": {
- "label": "Надіслати команду",
- "help": "Вводить текст у термінал так само, як і надруковано."
- },
- "wait": {
- "label": "Зачекайте на вивід",
- "help": "Призупиняється, доки сервер не надрукує вказаний вами текст, наприклад, запит на пароль."
- },
- "delay": {
- "label": "Пауза",
- "help": "Очікує фіксований проміжок часу перед наступним кроком."
- },
- "if": {
- "label": "Якщо вихід містить",
- "help": "Перевіряє все, що сервер надрукував до цього часу, а потім запускає одну з двох гілок."
- },
- "repeat": {
- "label": "Повторити",
- "help": "Виконує кроки всередині нього задану кількість разів."
- }
- },
- "send": {
- "text": "Команда або текст",
- "textPlaceholder": "перезапуск systemctl nginx",
- "pressEnter": "Натисніть Enter після відправлення",
- "pressEnterHelp": "Вимкніть цю функцію, щоб вводити текст, не надсилаючи його."
- },
- "wait": {
- "pattern": "Зачекайте, поки вихідні дані міститимуть",
- "patternPlaceholder": "пароль:",
- "timeout": "Здайся після",
- "onTimeout": "Якщо воно ніколи не з'явиться",
- "stop": "Зупинити макрос",
- "continue": "Продовжуй у будь-якому разі"
- },
- "delay": {
- "duration": "Зачекайте"
- },
- "ifStep": {
- "pattern": "Якщо вихід містить",
- "patternPlaceholder": "Активний: активний (працює)",
- "then": "Тоді зробіть це",
- "else": "Інакше зробіть це",
- "emptyBranch": "Без сходинок"
- },
- "repeat": {
- "count": "Кількість разів",
- "times": "рази"
- },
- "match": {
- "useRegex": "Зіставлення з регулярним виразом",
- "useRegexHelp": "Не виключайте це, щоб текст точно відповідав написаному.",
- "invalidRegex": "Недійсний регулярний вираз, буде знайдено звичайний текст"
- },
- "seconds": "секунди",
- "secondsShort": "{{count}}с"
}
}
diff --git a/src/ui/locales/translated/vi_VN.json b/src/ui/locales/translated/vi_VN.json
index 591989ac4..65059cf74 100644
--- a/src/ui/locales/translated/vi_VN.json
+++ b/src/ui/locales/translated/vi_VN.json
@@ -409,7 +409,6 @@
"sidebarSettings": "Cài đặt thanh bên...",
"hosts": "Máy chủ",
"snippets": "Đoạn mã",
- "macros": "Macro",
"hostManager": "Quản lý máy chủ",
"credentials": "Thông tin đăng nhập",
"connections": "Kết nối",
@@ -869,7 +868,6 @@
"groupTags": "Thẻ & ghim",
"groupTunnels": "Đường hầm",
"groupJumpHosts": "Người dẫn chương trình Jump",
- "groupQuickActions": "Hành động nhanh chóng",
"groupFeatureFlags": "Cờ tính năng",
"groupAdvanced": "Cấu hình nâng cao",
"preview": "Xem trước",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "Lưu trữ khóa SSH hoặc mật khẩu",
"tmuxMonitor": "Giám sát Tmux",
"tmuxMonitorDesc": "Giám sát phiên tmux trên các máy chủ",
- "snippets": "Những đoạn trích",
- "runSnippetDesc": "Chạy trong tab cửa sổ terminal đang hoạt động",
- "noSnippetsFound": "Không tìm thấy đoạn mã nào khớp với \"{{search}}\"",
"recentActivity": "Hoạt động gần đây",
"serversAndHosts": "Máy chủ & Host",
"noHostsFound": "Không tìm thấy máy chủ nào khớp với \"{{search}}\"",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "Tab trước",
"hotkeysJumpTab": "Chuyển đến tab"
},
- "snippets": {
- "title": "Đoạn mã",
- "createSnippetTitle": "Tạo đoạn mã",
- "createSnippetDescription": "Tạo đoạn mã lệnh mới để thực thi nhanh",
- "nameLabel": "Tên",
- "namePlaceholder": "Ví dụ: Khởi động lại Nginx",
- "descriptionLabel": "Mô tả",
- "descriptionPlaceholder": "Mô tả tùy chọn",
- "optional": "Tùy chọn",
- "folderLabel": "Thư mục",
- "noFolder": "Không thư mục (Chưa phân loại)",
- "commandLabel": "Lệnh",
- "commandPlaceholder": "Ví dụ: sudo systemctl restart nginx",
- "typeLabel": "Kiểu",
- "typeCommand": "Yêu cầu",
- "typeNote": "Ghi chú",
- "noteLabel": "Ghi chú",
- "notePlaceholder": "Ví dụ: một lời nhắc hoặc văn bản nhắc nhở có thể tái sử dụng.",
- "cancel": "Hủy",
- "createSnippetButton": "Tạo đoạn mã",
- "createFolderTitle": "Tạo thư mục",
- "createFolderDescription": "Sắp xếp đoạn mã vào các thư mục",
- "folderNameLabel": "Tên thư mục",
- "folderNamePlaceholder": "Ví dụ: Lệnh hệ thống, Docker Scripts",
- "folderColorLabel": "Màu thư mục",
- "folderIconLabel": "Biểu tượng thư mục",
- "previewLabel": "Xem trước",
- "folderNameFallback": "Tên thư mục",
- "createFolderButton": "Tạo thư mục",
- "targetTerminals": "Terminal đích",
- "selectAll": "Tất cả",
- "selectNone": "Không chọn",
- "noTerminalTabsOpen": "Không có tab terminal nào mở",
- "searchPlaceholder": "Tìm kiếm snippet...",
- "newSnippet": "Snippet mới",
- "newFolder": "Thư mục mới",
- "run": "Chạy",
- "noSnippetsInFolder": "Không có snippet nào trong thư mục này",
- "uncategorized": "Chưa phân loại",
- "editSnippetTitle": "Chỉnh sửa snippet",
- "editSnippetDescription": "Cập nhật snippet lệnh này",
- "saveSnippetButton": "Lưu thay đổi",
- "createSuccess": "Đã tạo snippet thành công",
- "createFailed": "Tạo snippet thất bại",
- "updateSuccess": "Đã cập nhật snippet thành công",
- "updateFailed": "Cập nhật snippet thất bại",
- "deleteFailed": "Xóa snippet thất bại",
- "folderCreateSuccess": "Đã tạo thư mục thành công",
- "folderCreateFailed": "Tạo thư mục thất bại",
- "editFolderTitle": "Chỉnh sửa thư mục",
- "editFolderDescription": "Đổi tên hoặc thay đổi hình thức của thư mục này",
- "saveFolderButton": "Lưu thay đổi",
- "editFolder": "Chỉnh sửa thư mục",
- "deleteFolder": "Xóa thư mục",
- "folderDeleteSuccess": "Đã xóa thư mục \"{{name}}\"",
- "folderDeleteFailed": "Xóa thư mục thất bại",
- "folderEditSuccess": "Đã cập nhật thư mục thành công",
- "folderEditFailed": "Cập nhật thư mục thất bại",
- "confirmRunMessage": "Chạy \"{{name}}\"?",
- "confirmRunButton": "Chạy",
- "runSuccess": "Đã chạy \"{{name}}\" trên {{count}} terminal",
- "pasteSuccess": "Đã dán \"{{name}}\" vào {{count}} thiết bị đầu cuối",
- "pasteToTerminal": "Dán",
- "copySuccess": "Đã sao chép \"{{name}}\" vào clipboard",
- "shareTitle": "Chia sẻ snippet",
- "shareUser": "Người dùng",
- "shareRole": "Vai trò",
- "selectUser": "Chọn người dùng...",
- "selectRole": "Chọn vai trò...",
- "shareSuccess": "Đã chia sẻ đoạn mã thành công",
- "shareFailed": "Chia sẻ đoạn mã thất bại",
- "revokeSuccess": "Đã thu hồi quyền truy cập",
- "revokeFailed": "Thu hồi quyền truy cập thất bại",
- "currentAccess": "Quyền truy cập hiện tại",
- "shareLoadError": "Tải dữ liệu chia sẻ thất bại",
- "loading": "Đang tải...",
- "close": "Đóng",
- "reorderFailed": "Lưu thứ tự đoạn mã thất bại",
- "importExport": "Nhập / Xuất",
- "exportBtn": "Xuất JSON",
- "importBtn": "Nhập JSON",
- "exportSuccess": "Đã xuất đoạn mã thành công",
- "exportFailed": "Xuất đoạn mã thất bại",
- "importTitle": "Nhập đoạn mã",
- "importDescription": "Nhập đoạn mã và thư mục từ tệp JSON được Termix xuất ra",
- "importDropOrClick": "Kéo thả tệp JSON vào đây hoặc nhấn để duyệt",
- "importSelectedFile": "Đã chọn: {{name}}",
- "importOverwrite": "Ghi đè đoạn mã hiện có cùng tên và thư mục",
- "importStartBtn": "Nhập",
- "importSuccess": "Nhập xong: đã thêm {{snippets}} đoạn mã, cập nhật {{updated}}, bỏ qua {{skipped}}, thêm {{folders}} thư mục",
- "importFailed": "Nhập đoạn mã thất bại",
- "importInvalidFile": "Tệp không hợp lệ: cần một đối tượng JSON với mảng đoạn mã hoặc thư mục",
- "targetHostsLabel": "Máy chủ đích",
- "targetHostsHint": "Gán máy chủ để chạy đoạn mã này trực tiếp mà không cần mở terminal.",
- "noHostsAvailable": "Chưa cấu hình máy chủ nào",
- "clearTargetHosts": "Xóa tất cả",
- "hasTargetHosts": "Có máy chủ đích",
- "runOnTargets": "Chạy trên máy đích",
- "directRunSuccess": "Đã chạy \"{{name}}\" trên {{count}} máy chủ",
- "directRunPartialFail": "\"{{name}}\" chạy thất bại trên một hoặc nhiều máy chủ",
- "executionResultTitle": "Kết quả thực thi: {{name}}",
- "executionResultDescription": "Kết quả từ việc chạy đoạn mã trên từng máy đích.",
- "executionSuccess": "Thành công",
- "executionFailed": "Thất bại",
- "expandAll": "Mở rộng tất cả các thư mục",
- "collapseAll": "Thu gọn tất cả các thư mục",
- "settings": "Cài đặt đoạn mã",
- "variablesHint": "Biến: $HOST, $USER, $PORT, $NAME, ${INPUT_1:Label}",
- "variablesDialogTitle": "Chạy \"{{name}}\"",
- "variablesDialogDescription": "Hãy điền đầy đủ các giá trị cần thiết cho đoạn mã này trước khi chạy.",
- "variablesPreviewLabel": "Xem trước",
- "variablesConfirmButton": "Chạy"
- },
"keybindings": {
"title": "Phím tắt",
"description": "Tùy chỉnh các phím tắt sao chép, dán và điều khiển trong terminal, hoặc gán phím để gửi văn bản hoặc chạy một đoạn mã.",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "Nhấp chuột và nhấn phím",
"recording": "Nhấn các phím...",
"comboRequiredError": "Trước tiên, hãy nhấn tổ hợp phím.",
- "textRequiredError": "Nhập văn bản cần gửi",
- "controlCodeRequiredError": "Nhập một chữ cái duy nhất cho mã điều khiển.",
- "snippetRequiredError": "Chọn một đoạn trích",
"conflictWarning": "Sự kết hợp này đã được liên kết với {{combo}}. Lưu lại sẽ khiến cả hai đều bốc cháy.",
"actionLabel": "Hoạt động",
- "actionCopy": "Sao chép lựa chọn",
- "actionPaste": "Dán",
- "actionSendControlCode": "Gửi tín hiệu Ctrl+chữ cái",
- "actionSendText": "Gửi tin nhắn",
- "actionRunSnippet": "Chạy đoạn mã hiện có",
"actionNextTab": "Chuyển sang tab tiếp theo",
"actionPreviousTab": "Chuyển về tab trước",
- "actionOpenCommandPalette": "Mở tìm kiếm nhanh",
- "controlCodeLabel": "Thư",
- "textLabel": "Văn bản để gửi",
- "appendEnterLabel": "Nhấn Enter sau khi gửi",
- "snippetLabel": "Đoạn trích",
- "selectSnippetPlaceholder": "Chọn một đoạn trích",
- "orphanedSnippetWarning": "Không tìm thấy đoạn mã.",
- "clipboardPermissionNote": "Một số trình duyệt có thể yêu cầu quyền truy cập clipboard khi sử dụng lần đầu."
+ "actionOpenCommandPalette": "Mở tìm kiếm nhanh"
},
"userProfile": {
"donateTitle": "Duy trì Termix",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "Chủ đề là bắt buộc",
"ntfyToken": "Mã truy cập (tùy chọn)",
"channelSaveFailed": "Lỗi khi lưu kênh"
- },
- "macros": {
- "title": "Macro",
- "description": "Chạy một chuỗi lệnh đã lưu trong cửa sổ dòng lệnh và phản hồi lại những gì máy chủ gửi về.",
- "target": "Mục tiêu",
- "noTerminal": "Không có thiết bị đầu cuối nào đang mở",
- "empty": "Chưa có macro nào.",
- "emptyHint": "Tạo macro để tự động hóa quy trình trên thiết bị đầu cuối, chẳng hạn như đăng nhập, khởi động lại dịch vụ hoặc trả lời các lời nhắc.",
- "create": "Macro mới",
- "back": "Quay lại phần macro",
- "name": "Tên",
- "namePlaceholder": "Khởi động lại máy chủ web",
- "nameRequired": "Đặt tên cho macro trước khi lưu.",
- "descriptionLabel": "Sự miêu tả",
- "descriptionPlaceholder": "Chức năng của macro này (tùy chọn)",
- "unsaved": "Thay đổi chưa được lưu",
- "save": "Cứu",
- "saved": "Macro đã được lưu",
- "saveFailed": "Không thể lưu macro",
- "delete": "Xóa bỏ",
- "deleteTitle": "Xóa macro này?",
- "deleteBody": "\"{{name}}\" sẽ bị xóa vĩnh viễn. Không thể hoàn tác.",
- "deleted": "Macro đã bị xóa",
- "cancel": "Hủy bỏ",
- "run": "Chạy",
- "stop": "Dừng lại",
- "running": "Đang chạy...",
- "completed": "Macro đã hoàn thành",
- "failed": "Macro bị lỗi",
- "loadFailed": "Không thể tải macro",
- "needTerminal": "Trước tiên hãy mở và kết nối thiết bị đầu cuối.",
- "noSteps": "Macro này chưa có bước nào. Hãy thêm một bước bên dưới.",
- "addStep": "Thêm bước",
- "stepNumber": "Bước {{number}}",
- "removeStep": "Xóa bước",
- "confirmRunTitle": "Chạy macro này?",
- "confirmRunBody": "\"{{name}}\" sẽ gửi lệnh đến {{host}}.",
- "confirmRunAction": "Chạy macro",
- "stepTypes": {
- "send": {
- "label": "Gửi lệnh",
- "help": "Nhập văn bản vào cửa sổ dòng lệnh, giống như khi bạn tự nhập văn bản."
- },
- "wait": {
- "label": "Chờ kết quả đầu ra",
- "help": "Tạm dừng cho đến khi máy chủ in ra đoạn văn bản bạn đã đặt tên, ví dụ như lời nhắc nhập mật khẩu."
- },
- "delay": {
- "label": "Tạm dừng",
- "help": "Chờ một khoảng thời gian cố định trước khi thực hiện bước tiếp theo."
- },
- "if": {
- "label": "Nếu đầu ra chứa",
- "help": "Kiểm tra tất cả những gì máy chủ đã in ra cho đến nay, sau đó chạy một trong hai nhánh."
- },
- "repeat": {
- "label": "Lặp lại",
- "help": "Thực thi các bước bên trong nó một số lần nhất định."
- }
- },
- "send": {
- "text": "Lệnh hoặc văn bản",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "Nhấn Enter sau khi gửi",
- "pressEnterHelp": "Tắt tùy chọn này để nhập văn bản mà không cần gửi đi."
- },
- "wait": {
- "pattern": "Chờ cho đến khi đầu ra chứa",
- "patternPlaceholder": "mật khẩu:",
- "timeout": "Bỏ cuộc sau",
- "onTimeout": "Nếu nó không bao giờ xuất hiện",
- "stop": "Dừng macro",
- "continue": "Cứ tiếp tục đi"
- },
- "delay": {
- "duration": "Chờ đợi"
- },
- "ifStep": {
- "pattern": "Nếu đầu ra chứa",
- "patternPlaceholder": "Đang hoạt động: đang hoạt động (đang chạy)",
- "then": "Sau đó hãy làm như thế này",
- "else": "Nếu không thì hãy làm như thế này",
- "emptyBranch": "Không có bước nào"
- },
- "repeat": {
- "count": "Số lần",
- "times": "lần"
- },
- "match": {
- "useRegex": "So khớp với biểu thức chính quy",
- "useRegexHelp": "Bỏ phần này đi để văn bản khớp chính xác với nội dung đã viết.",
- "invalidRegex": "Đây không phải là biểu thức chính quy hợp lệ, nó sẽ được so khớp như văn bản thuần túy."
- },
- "seconds": "giây",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/zh_CN.json b/src/ui/locales/translated/zh_CN.json
index 61cd5c793..33a51252d 100644
--- a/src/ui/locales/translated/zh_CN.json
+++ b/src/ui/locales/translated/zh_CN.json
@@ -409,7 +409,6 @@
"sidebarSettings": "侧边栏设置...",
"hosts": "主机",
"snippets": "代码片段",
- "macros": "宏",
"hostManager": "主机管理器",
"credentials": "凭据",
"connections": "连接",
@@ -869,7 +868,6 @@
"groupTags": "标签和置顶",
"groupTunnels": "隧道",
"groupJumpHosts": "跳板机",
- "groupQuickActions": "快速操作",
"groupFeatureFlags": "功能标志",
"groupAdvanced": "高级配置",
"preview": "预览",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "存储 SSH 密钥或密码",
"tmuxMonitor": "Tmux 监控器",
"tmuxMonitorDesc": "监控主机上的 tmux 会话",
- "snippets": "代码片段",
- "runSnippetDesc": "在活动终端标签页中运行",
- "noSnippetsFound": "未找到与“{{search}}”匹配的代码片段",
"recentActivity": "最近活动",
"serversAndHosts": "服务器与主机",
"noHostsFound": "未找到匹配 \"{{search}}\" 的主机",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "上一个标签页",
"hotkeysJumpTab": "跳转到标签页"
},
- "snippets": {
- "title": "代码片段",
- "createSnippetTitle": "创建代码片段",
- "createSnippetDescription": "创建新的命令代码片段以便快速执行",
- "nameLabel": "名称",
- "namePlaceholder": "例如:重启 Nginx",
- "descriptionLabel": "描述",
- "descriptionPlaceholder": "可选描述",
- "optional": "可选",
- "folderLabel": "文件夹",
- "noFolder": "无文件夹(未分类)",
- "commandLabel": "命令",
- "commandPlaceholder": "例如:sudo systemctl restart nginx",
- "typeLabel": "类型",
- "typeCommand": "命令",
- "typeNote": "笔记",
- "noteLabel": "笔记",
- "notePlaceholder": "例如,可重复使用的提示或提醒文本",
- "cancel": "取消",
- "createSnippetButton": "创建代码片段",
- "createFolderTitle": "创建文件夹",
- "createFolderDescription": "将代码片段整理到文件夹中",
- "folderNameLabel": "文件夹名称",
- "folderNamePlaceholder": "例如:系统命令、Docker 脚本",
- "folderColorLabel": "文件夹颜色",
- "folderIconLabel": "文件夹图标",
- "previewLabel": "预览",
- "folderNameFallback": "文件夹名称",
- "createFolderButton": "创建文件夹",
- "targetTerminals": "目标终端",
- "selectAll": "全选",
- "selectNone": "取消全选",
- "noTerminalTabsOpen": "没有打开的终端标签页",
- "searchPlaceholder": "搜索代码片段...",
- "newSnippet": "新建代码片段",
- "newFolder": "新建文件夹",
- "run": "运行",
- "noSnippetsInFolder": "此文件夹中没有代码片段",
- "uncategorized": "未分类",
- "editSnippetTitle": "编辑代码片段",
- "editSnippetDescription": "更新此命令代码片段",
- "saveSnippetButton": "保存更改",
- "createSuccess": "代码片段创建成功",
- "createFailed": "创建代码片段失败",
- "updateSuccess": "代码片段更新成功",
- "updateFailed": "更新代码片段失败",
- "deleteFailed": "删除代码片段失败",
- "folderCreateSuccess": "文件夹创建成功",
- "folderCreateFailed": "创建文件夹失败",
- "editFolderTitle": "编辑文件夹",
- "editFolderDescription": "重命名或更改此文件夹的外观",
- "saveFolderButton": "保存更改",
- "editFolder": "编辑文件夹",
- "deleteFolder": "删除文件夹",
- "folderDeleteSuccess": "文件夹“{{name}}”已删除",
- "folderDeleteFailed": "删除文件夹失败",
- "folderEditSuccess": "文件夹更新成功",
- "folderEditFailed": "更新文件夹失败",
- "confirmRunMessage": "运行“{{name}}”?",
- "confirmRunButton": "运行",
- "runSuccess": "已在 {{count}} 个终端中运行“{{name}}”",
- "pasteSuccess": "已将“{{name}}”粘贴到 {{count}} 终端中",
- "pasteToTerminal": "粘贴",
- "copySuccess": "已将“{{name}}”复制到剪贴板",
- "shareTitle": "共享代码片段",
- "shareUser": "用户",
- "shareRole": "角色",
- "selectUser": "选择一个用户...",
- "selectRole": "选择一个角色...",
- "shareSuccess": "代码片段共享成功",
- "shareFailed": "共享代码片段失败",
- "revokeSuccess": "访问权限已撤销",
- "revokeFailed": "撤销访问权限失败",
- "currentAccess": "当前访问权限",
- "shareLoadError": "加载共享数据失败",
- "loading": "加载中...",
- "close": "关闭",
- "reorderFailed": "保存代码片段顺序失败",
- "importExport": "导入 / 导出",
- "exportBtn": "导出 JSON",
- "importBtn": "导入 JSON",
- "exportSuccess": "代码片段导出成功",
- "exportFailed": "导出代码片段失败",
- "importTitle": "导入代码片段",
- "importDescription": "从 Termix 导出的 JSON 文件导入代码片段和文件夹",
- "importDropOrClick": "将 JSON 文件拖放到此处或点击浏览",
- "importSelectedFile": "已选择:{{name}}",
- "importOverwrite": "覆盖具有相同名称和文件夹的现有代码片段",
- "importStartBtn": "导入",
- "importSuccess": "导入完成:添加了 {{snippets}} 个代码片段,更新了 {{updated}} 个,跳过了 {{skipped}} 个,添加了 {{folders}} 个文件夹",
- "importFailed": "导入代码片段失败",
- "importInvalidFile": "无效文件:应为包含 snippets 或 folders 数组的 JSON 对象",
- "targetHostsLabel": "目标主机",
- "targetHostsHint": "分配主机,以便无需打开终端即可直接运行此代码片段。",
- "noHostsAvailable": "未配置任何主机",
- "clearTargetHosts": "清除全部",
- "hasTargetHosts": "已分配目标主机",
- "runOnTargets": "在目标上运行",
- "directRunSuccess": "已在 {{count}} 台主机上运行 \"{{name}}\"",
- "directRunPartialFail": "\"{{name}}\" 在一台或多台主机上运行失败",
- "executionResultTitle": "执行结果:{{name}}",
- "executionResultDescription": "在每台目标主机上运行代码片段的输出。",
- "executionSuccess": "成功",
- "executionFailed": "失败",
- "expandAll": "展开所有文件夹",
- "collapseAll": "收起所有文件夹",
- "settings": "代码片段设置",
- "variablesHint": "变量:$HOST、$USER、$PORT、$NAME、${INPUT_1:Label}",
- "variablesDialogTitle": "运行“{{name}}”",
- "variablesDialogDescription": "运行此代码片段之前,请填写所需的值。",
- "variablesPreviewLabel": "预览",
- "variablesConfirmButton": "运行"
- },
"keybindings": {
"title": "键盘快捷键",
"description": "自定义终端复制、粘贴和控制快捷键,或绑定按键以发送文本或运行代码片段。",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "点击并按下按键",
"recording": "按下按键……",
"comboRequiredError": "先按下组合键",
- "textRequiredError": "请输入要发送的文本",
- "controlCodeRequiredError": "请输入单个字母作为控制码",
- "snippetRequiredError": "选择代码片段",
"conflictWarning": "此组合已绑定到 {{combo}}。保存后两者都会生效。",
"actionLabel": "操作",
- "actionCopy": "复制选择",
- "actionPaste": "粘贴",
- "actionSendControlCode": "发送 Ctrl+字母信号",
- "actionSendText": "发送文本",
- "actionRunSnippet": "运行现有代码片段",
"actionNextTab": "切换到下一个标签页",
"actionPreviousTab": "切换到上一个标签页",
- "actionOpenCommandPalette": "打开快速搜索",
- "controlCodeLabel": "字母",
- "textLabel": "发送的文本",
- "appendEnterLabel": "发送后按回车键",
- "snippetLabel": "片段",
- "selectSnippetPlaceholder": "选择代码片段",
- "orphanedSnippetWarning": "未找到代码片段",
- "clipboardPermissionNote": "某些浏览器在首次使用时可能会提示授予剪贴板权限。"
+ "actionOpenCommandPalette": "打开快速搜索"
},
"userProfile": {
"donateTitle": "让 Termix 持续运转",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "主题为必填项",
"ntfyToken": "访问令牌(选填)",
"channelSaveFailed": "保存渠道失败"
- },
- "macros": {
- "title": "宏",
- "description": "在终端中运行已保存的一系列命令,并对服务器返回的内容做出反应。",
- "target": "目标",
- "noTerminal": "没有终端打开",
- "empty": "暂无宏",
- "emptyHint": "创建一个宏来自动化终端流程,例如登录、重启服务或回答提示。",
- "create": "新宏",
- "back": "返回宏",
- "name": "名称",
- "namePlaceholder": "重启 Web 服务器",
- "nameRequired": "保存之前,请给宏命名。",
- "descriptionLabel": "描述",
- "descriptionPlaceholder": "此宏的功能(可选)",
- "unsaved": "未保存的更改",
- "save": "保存",
- "saved": "宏已保存",
- "saveFailed": "无法保存宏",
- "delete": "删除",
- "deleteTitle": "删除这个宏?",
- "deleteBody": "“{{name}}”将被永久删除。此操作无法撤销。",
- "deleted": "宏已删除",
- "cancel": "取消",
- "run": "运行",
- "stop": "停止",
- "running": "运行中...",
- "completed": "宏已完成",
- "failed": "宏失败",
- "loadFailed": "无法加载宏。",
- "needTerminal": "首先打开并连接终端",
- "noSteps": "此宏目前尚无步骤。请在下方添加步骤。",
- "addStep": "添加步骤",
- "stepNumber": "步骤 {{number}}",
- "removeStep": "移除步骤",
- "confirmRunTitle": "运行这个宏?",
- "confirmRunBody": "\"{{name}}\" 将向 {{host}} 发送命令。",
- "confirmRunAction": "运行宏",
- "stepTypes": {
- "send": {
- "label": "发送命令",
- "help": "在终端中输入文本,就像你手动输入一样。"
- },
- "wait": {
- "label": "等待输出",
- "help": "暂停,直到服务器打印出您指定的文本,例如密码提示。"
- },
- "delay": {
- "label": "暂停",
- "help": "等待一段固定时间后再执行下一步操作。"
- },
- "if": {
- "label": "如果输出包含",
- "help": "检查服务器目前为止打印的所有内容,然后运行两个分支中的一个。"
- },
- "repeat": {
- "label": "重复",
- "help": "将其中的步骤执行设定的次数。"
- }
- },
- "send": {
- "text": "命令或文本",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "发送后按回车键",
- "pressEnterHelp": "关闭此功能即可输入文本而不提交。"
- },
- "wait": {
- "pattern": "等待输出包含",
- "patternPlaceholder": "密码:",
- "timeout": "放弃",
- "onTimeout": "如果它从未出现",
- "stop": "停止宏",
- "continue": "无论如何都继续"
- },
- "delay": {
- "duration": "等待"
- },
- "ifStep": {
- "pattern": "如果输出包含",
- "patternPlaceholder": "活跃:正在运行",
- "then": "然后这样做",
- "else": "否则就这样做",
- "emptyBranch": "没有步骤"
- },
- "repeat": {
- "count": "次数",
- "times": "次"
- },
- "match": {
- "useRegex": "使用正则表达式进行匹配",
- "useRegexHelp": "关闭此选项以完全按原文匹配文本。",
- "invalidRegex": "这不是有效的正则表达式,它将被匹配为纯文本。"
- },
- "seconds": "秒",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/locales/translated/zh_TW.json b/src/ui/locales/translated/zh_TW.json
index a906d7b7c..d5b28f942 100644
--- a/src/ui/locales/translated/zh_TW.json
+++ b/src/ui/locales/translated/zh_TW.json
@@ -409,7 +409,6 @@
"sidebarSettings": "側邊欄設定...",
"hosts": "主機",
"snippets": "程式碼片段",
- "macros": "宏",
"hostManager": "主機管理員",
"credentials": "憑證",
"connections": "連線",
@@ -869,7 +868,6 @@
"groupTags": "標籤和別針",
"groupTunnels": "隧道",
"groupJumpHosts": "跳轉主機",
- "groupQuickActions": "快速行動",
"groupFeatureFlags": "功能標誌",
"groupAdvanced": "進階配置",
"preview": "預覽",
@@ -1068,9 +1066,6 @@
"addCredentialDesc": "儲存 SSH 金鑰或密碼",
"tmuxMonitor": "Tmux 監視器",
"tmuxMonitorDesc": "監控您所有主機的 tmux 會話",
- "snippets": "片段",
- "runSnippetDesc": "在活動終端選項卡中運行",
- "noSnippetsFound": "找不到與「{{search}}」相符的程式碼片段",
"recentActivity": "近期活動",
"serversAndHosts": "伺服器與主機",
"noHostsFound": "找不到符合「{{search}}」的主機",
@@ -1864,119 +1859,6 @@
"hotkeysPrevTab": "上一個索引標籤",
"hotkeysJumpTab": "跳到標籤頁"
},
- "snippets": {
- "title": "指令片段",
- "createSnippetTitle": "建立片段",
- "createSnippetDescription": "建立新的指令片段以便快速執行",
- "nameLabel": "名稱",
- "namePlaceholder": "例如:重新啟動 Nginx",
- "descriptionLabel": "說明",
- "descriptionPlaceholder": "選用說明",
- "optional": "選用",
- "folderLabel": "資料夾",
- "noFolder": "無資料夾(未分類)",
- "commandLabel": "指令",
- "commandPlaceholder": "例如:sudo systemctl restart nginx",
- "typeLabel": "類型",
- "typeCommand": "命令",
- "typeNote": "筆記",
- "noteLabel": "筆記",
- "notePlaceholder": "例如,可重複使用的提示或提醒文字",
- "cancel": "取消",
- "createSnippetButton": "建立片段",
- "createFolderTitle": "建立資料夾",
- "createFolderDescription": "將您的片段整理至資料夾",
- "folderNameLabel": "資料夾名稱",
- "folderNamePlaceholder": "例如:系統指令、Docker 指令碼",
- "folderColorLabel": "資料夾顏色",
- "folderIconLabel": "資料夾圖示",
- "previewLabel": "預覽",
- "folderNameFallback": "資料夾名稱",
- "createFolderButton": "建立資料夾",
- "targetTerminals": "目標終端機",
- "selectAll": "全選",
- "selectNone": "取消全選",
- "noTerminalTabsOpen": "無終端機分頁開啟",
- "searchPlaceholder": "搜尋指令片段...",
- "newSnippet": "新增指令片段",
- "newFolder": "新增資料夾",
- "run": "執行",
- "noSnippetsInFolder": "此資料夾中無指令片段",
- "uncategorized": "未分類",
- "editSnippetTitle": "編輯指令片段",
- "editSnippetDescription": "更新此指令片段",
- "saveSnippetButton": "儲存變更",
- "createSuccess": "指令片段建立成功",
- "createFailed": "建立指令片段失敗",
- "updateSuccess": "指令片段更新成功",
- "updateFailed": "更新指令片段失敗",
- "deleteFailed": "刪除指令片段失敗",
- "folderCreateSuccess": "資料夾建立成功",
- "folderCreateFailed": "建立資料夾失敗",
- "editFolderTitle": "編輯資料夾",
- "editFolderDescription": "重新命名或變更此資料夾的外觀",
- "saveFolderButton": "儲存變更",
- "editFolder": "編輯資料夾",
- "deleteFolder": "刪除資料夾",
- "folderDeleteSuccess": "資料夾 \"{{name}}\" 已刪除",
- "folderDeleteFailed": "刪除資料夾失敗",
- "folderEditSuccess": "資料夾更新成功",
- "folderEditFailed": "更新資料夾失敗",
- "confirmRunMessage": "執行 \"{{name}}\"?",
- "confirmRunButton": "執行",
- "runSuccess": "已在 {{count}} 個終端機執行 \"{{name}}\"",
- "pasteSuccess": "已將「{{name}}」貼到 {{count}} 終端機中",
- "pasteToTerminal": "貼上",
- "copySuccess": "已將 \"{{name}}\" 複製到剪貼簿",
- "shareTitle": "分享指令片段",
- "shareUser": "使用者",
- "shareRole": "角色",
- "selectUser": "選擇使用者...",
- "selectRole": "選擇角色...",
- "shareSuccess": "片段分享成功",
- "shareFailed": "分享片段失敗",
- "revokeSuccess": "已撤銷存取權限",
- "revokeFailed": "撤銷存取失敗",
- "currentAccess": "目前的存取權限",
- "shareLoadError": "載入分享資料失敗",
- "loading": "載入中...",
- "close": "關閉",
- "reorderFailed": "儲存片段排序失敗",
- "importExport": "匯入 / 匯出",
- "exportBtn": "匯出 JSON",
- "importBtn": "匯入 JSON",
- "exportSuccess": "片段匯出成功",
- "exportFailed": "匯出片段失敗",
- "importTitle": "匯入片段",
- "importDescription": "從 Termix 匯出的 JSON 檔案匯入片段和資料夾",
- "importDropOrClick": "將 JSON 檔案拖放至此,或點擊瀏覽",
- "importSelectedFile": "已選取:{{name}}",
- "importOverwrite": "覆寫具有相同名稱和資料夾的現有片段",
- "importStartBtn": "匯入",
- "importSuccess": "匯入完成:已新增 {{snippets}} 個片段、{{updated}} 個已更新、{{skipped}} 個略過、{{folders}} 個資料夾",
- "importFailed": "匯入片段失敗",
- "importInvalidFile": "無效的檔案:需要包含片段或資料夾陣列的 JSON 物件",
- "targetHostsLabel": "目標主機",
- "targetHostsHint": "指定主機以直接執行此片段,無需開啟終端機。",
- "noHostsAvailable": "未設定主機",
- "clearTargetHosts": "清除全部",
- "hasTargetHosts": "有目標主機",
- "runOnTargets": "在目標上執行",
- "directRunSuccess": "已在 {{count}} 台主機上執行「{{name}}」",
- "directRunPartialFail": "「{{name}}」在一或多台主機上執行失敗",
- "executionResultTitle": "執行結果:{{name}}",
- "executionResultDescription": "在每個目標主機上執行片段所產生的輸出。",
- "executionSuccess": "成功",
- "executionFailed": "失敗",
- "expandAll": "展開所有資料夾",
- "collapseAll": "折疊所有資料夾",
- "settings": "程式碼片段設定",
- "variablesHint": "變數: $HOST、 $USER、 $PORT、 $NAME、${INPUT_1:Label}",
- "variablesDialogTitle": "運行“{{name}}",
- "variablesDialogDescription": "在運行此程式碼片段之前,請填寫所需的值。",
- "variablesPreviewLabel": "預覽",
- "variablesConfirmButton": "跑步"
- },
"keybindings": {
"title": "鍵盤快速鍵",
"description": "自訂終端複製、貼上和控制快捷鍵,或綁定按鍵以傳送文字或執行程式碼片段。",
@@ -2000,26 +1882,11 @@
"pressKeysPlaceholder": "點擊並按下按鍵",
"recording": "按下按鍵…",
"comboRequiredError": "先按下組合鍵",
- "textRequiredError": "請輸入要發送的文字",
- "controlCodeRequiredError": "請輸入一個字母作為控制碼",
- "snippetRequiredError": "選擇一段文字",
"conflictWarning": "此組合已綁定至 {{combo}}。保存後兩者都會生效。",
"actionLabel": "行動",
- "actionCopy": "複製選擇",
- "actionPaste": "貼上",
- "actionSendControlCode": "發送 Ctrl+字母訊號",
- "actionSendText": "傳簡訊",
- "actionRunSnippet": "運行現有程式碼片段",
"actionNextTab": "切換到下一個標籤頁",
"actionPreviousTab": "切換到上一個標籤頁",
- "actionOpenCommandPalette": "打開快速搜尋",
- "controlCodeLabel": "信",
- "textLabel": "發送的文字",
- "appendEnterLabel": "發送後按回車鍵",
- "snippetLabel": "片段",
- "selectSnippetPlaceholder": "選擇一段文字",
- "orphanedSnippetWarning": "未找到程式碼片段",
- "clipboardPermissionNote": "某些瀏覽器在首次使用時可能會提示授予剪貼簿權限。"
+ "actionOpenCommandPalette": "打開快速搜尋"
},
"userProfile": {
"donateTitle": "讓 Termix 持續營運",
@@ -2240,100 +2107,5 @@
"ntfyTopicRequired": "主題為必填",
"ntfyToken": "存取權杖(選填)",
"channelSaveFailed": "無法儲存頻道"
- },
- "macros": {
- "title": "宏",
- "description": "在終端機中執行已儲存的一系列命令,並對伺服器傳回的內容做出反應。",
- "target": "目標",
- "noTerminal": "沒有終端機打開",
- "empty": "暫無宏",
- "emptyHint": "建立一個巨集來自動化終端流程,例如登入、重新啟動服務或回答提示。",
- "create": "新宏",
- "back": "回傳宏",
- "name": "姓名",
- "namePlaceholder": "重啟Web伺服器",
- "nameRequired": "儲存之前,請先為巨集命名。",
- "descriptionLabel": "描述",
- "descriptionPlaceholder": "此巨集的功能(選用)",
- "unsaved": "未儲存的更改",
- "save": "節省",
- "saved": "巨集已儲存",
- "saveFailed": "無法保存巨集",
- "delete": "刪除",
- "deleteTitle": "刪除這個巨集?",
- "deleteBody": "“{{name}}”將永久刪除。此操作無法撤銷。",
- "deleted": "巨集已刪除",
- "cancel": "取消",
- "run": "跑步",
- "stop": "停止",
- "running": "跑步...",
- "completed": "巨集已完成",
- "failed": "巨集失敗",
- "loadFailed": "無法載入巨集。",
- "needTerminal": "首先打開並連接終端。",
- "noSteps": "此巨集目前尚無步驟。請在下方新增步驟。",
- "addStep": "新增步驟",
- "stepNumber": "步驟 {{number}}",
- "removeStep": "移除步驟",
- "confirmRunTitle": "運行這個巨集?",
- "confirmRunBody": "\"{{name}}\" 將向 {{host}} 傳送指令。",
- "confirmRunAction": "運行巨集",
- "stepTypes": {
- "send": {
- "label": "發送命令",
- "help": "在終端機中輸入文本,就像你手動輸入一樣。"
- },
- "wait": {
- "label": "等待輸出",
- "help": "暫停,直到伺服器列印您指定的文本,例如密碼提示。"
- },
- "delay": {
- "label": "暫停",
- "help": "等待一段固定時間後再執行下一步操作。"
- },
- "if": {
- "label": "如果輸出包含",
- "help": "檢查伺服器目前為止列印的所有內容,然後執行兩個分支中的一個。"
- },
- "repeat": {
- "label": "重複",
- "help": "將其中的步驟執行設定的次數。"
- }
- },
- "send": {
- "text": "命令或文字",
- "textPlaceholder": "systemctl restart nginx",
- "pressEnter": "發送後按回車鍵",
- "pressEnterHelp": "關閉此功能即可輸入文字而不提交。"
- },
- "wait": {
- "pattern": "等待輸出包含",
- "patternPlaceholder": "密碼:",
- "timeout": "放棄",
- "onTimeout": "如果它從未出現",
- "stop": "停止宏",
- "continue": "無論如何,繼續前進。"
- },
- "delay": {
- "duration": "等待"
- },
- "ifStep": {
- "pattern": "如果輸出包含",
- "patternPlaceholder": "活躍:正在運行",
- "then": "然後這樣做",
- "else": "否則就這樣做",
- "emptyBranch": "沒有步驟"
- },
- "repeat": {
- "count": "次數",
- "times": "時代"
- },
- "match": {
- "useRegex": "使用正規表示式進行匹配",
- "useRegexHelp": "請刪除此行以完全按照原文填寫。",
- "invalidRegex": "這不是有效的正規表示式,它將被匹配為純文字。"
- },
- "seconds": "秒",
- "secondsShort": "{{count}}s"
}
}
diff --git a/src/ui/plugin-host/app.ts b/src/ui/plugin-host/app.ts
index 3769a8959..c7b1cfeaa 100644
--- a/src/ui/plugin-host/app.ts
+++ b/src/ui/plugin-host/app.ts
@@ -20,7 +20,16 @@ import {
registerHostBadge,
registerHostContextMenuItem,
} from "@/sidebar/host-contributions";
-import { registerPaletteEntry } from "@/shell/palette-registry";
+import {
+ registerPaletteEntry,
+ registerPaletteGroup,
+ type PaletteItemDef,
+} from "@/shell/palette-registry";
+import {
+ registerKeybindingAction,
+ registerKeybindingDefault,
+ type KeybindingEditorProps,
+} from "@/shell/keybinding-registry";
import { registerHostProtocol } from "@/sidebar/host-protocols";
import {
registerDashboardCard,
@@ -278,6 +287,63 @@ export function createPluginApp(
);
},
+ registerPaletteGroup(group) {
+ return track(
+ registerPaletteGroup({
+ id: group.id,
+ pluginId,
+ titleKey: key(group.titleKey),
+ order: group.order,
+ showWhenEmpty: group.showWhenEmpty,
+ load: group.load as unknown as () => Promise,
+ }),
+ );
+ },
+
+ registerKeybindingAction(action) {
+ const declared = (manifest.contributes?.keybindingActions ?? []).some(
+ (entry) => entry.id === action.id,
+ );
+ if (!declared) {
+ throw new Error(
+ `${pluginId}: keybinding action "${action.id}" is not declared in manifest contributes.keybindingActions`,
+ );
+ }
+ const validate = action.validate;
+ return track(
+ registerKeybindingAction({
+ id: action.id,
+ pluginId,
+ labelKey: key(action.titleKey),
+ scope: action.scope ?? "session",
+ editor: action.editor
+ ? (scoped(action.editor) as ComponentType)
+ : undefined,
+ summary: action.summary
+ ? (scoped(action.summary) as ComponentType)
+ : undefined,
+ validate: validate
+ ? (value) => {
+ const problem = validate(value);
+ return problem ? key(problem) : null;
+ }
+ : undefined,
+ run: action.run,
+ }),
+ );
+ },
+
+ registerKeybindingDefault(binding) {
+ return track(
+ registerKeybindingDefault({
+ id: binding.id,
+ pluginId,
+ combo: binding.combo,
+ descriptionKey: key(binding.descriptionKey),
+ }),
+ );
+ },
+
registerDashboardCard(card) {
requireDeclared("card", card.id, "dashboard card");
return track(
diff --git a/src/ui/plugin-host/bridge.ts b/src/ui/plugin-host/bridge.ts
index 287f733c8..5bc60da7f 100644
--- a/src/ui/plugin-host/bridge.ts
+++ b/src/ui/plugin-host/bridge.ts
@@ -37,6 +37,7 @@ import {
parseCustomKeybindings,
patchOpenTab,
} from "@/api/open-tabs-api";
+import { runKeybindingAction } from "@/shell/keybinding-registry";
import { usePluginScope } from "./scope";
import { knownPluginIds, usePluginStore } from "./plugin-store";
import { useUiPreferencesContext } from "@/contexts/UiPreferencesContext";
@@ -361,6 +362,8 @@ export const pluginHostBridge: PluginHostBridge = {
patchOpenTab: (instanceId, updates) => patchOpenTab(instanceId, updates),
getCustomKeybindings: async () =>
parseCustomKeybindings((await getUserPreferences()).customKeybindings),
+ runKeybindingAction: (action, context) =>
+ runKeybindingAction(action, context),
getClientPreference: (name) => getCookie(name),
setClientPreference: (name, value) => {
void setCookie(name, value, 365);
diff --git a/src/ui/plugin-host/sdk-ui.ts b/src/ui/plugin-host/sdk-ui.ts
index d3bd54033..2b181ddc9 100644
--- a/src/ui/plugin-host/sdk-ui.ts
+++ b/src/ui/plugin-host/sdk-ui.ts
@@ -46,7 +46,6 @@ export {
export type * from "@/components/card-grid/types";
export { ConnectionScreen } from "@/components/connection/ConnectionScreen";
export * from "@/components/connection/connection-status";
-export { SnippetVariablesDialog } from "@/components/SnippetVariablesDialog";
export {
FullScreenAppWrapper,
type FullScreenAppPhase,
@@ -101,6 +100,7 @@ export { useAdaptivePolling } from "@/hooks/use-adaptive-polling";
export { WidgetTitle } from "@/lib/widget-title";
export { runVisibleInterval } from "@/lib/visible-interval";
export { useIsMobile } from "@/hooks/use-mobile";
+export { EmptyState } from "@/components/empty-state";
export { PassphraseDialog } from "@/ssh/dialogs/PassphraseDialog";
export { HostKeyVerificationDialog } from "@/ssh/dialogs/HostKeyVerificationDialog";
diff --git a/src/ui/plugin-host/testing-host.tsx b/src/ui/plugin-host/testing-host.tsx
index e29110411..ba511988f 100644
--- a/src/ui/plugin-host/testing-host.tsx
+++ b/src/ui/plugin-host/testing-host.tsx
@@ -45,6 +45,11 @@ import {
listSettingsComponents,
} from "@/settings/settings-components";
import { getSlotContributions, listActions } from "@/shell/action-registry";
+import { listPaletteGroups, loadPaletteGroup } from "@/shell/palette-registry";
+import {
+ listKeybindingActions,
+ listKeybindingDefaults,
+} from "@/shell/keybinding-registry";
import {
getLoginMethodUI,
getSecondFactorUI,
@@ -240,6 +245,18 @@ export async function renderPlugin(
loginMethods: () => mine(listLoginMethodUIs()).map((method) => method.id),
secondFactors: () =>
mine(listSecondFactorUIs()).map((factor) => factor.id),
+ paletteGroups: () => mine(listPaletteGroups()).map((group) => group.id),
+ keybindingActions: () =>
+ mine(listKeybindingActions()).map((action) => action.id),
+ keybindingDefaults: () =>
+ mine(listKeybindingDefaults()).map((binding) => binding.id),
+ },
+
+ async loadPaletteGroup(id) {
+ const group =
+ listPaletteGroups().find((entry) => entry.id === id) ??
+ missing("a palette group", id);
+ return (await loadPaletteGroup(group)) as never;
},
renderTab(type, props = {}) {
diff --git a/src/ui/shell/CommandPalette.tsx b/src/ui/shell/CommandPalette.tsx
index 68b74737c..aba762f94 100644
--- a/src/ui/shell/CommandPalette.tsx
+++ b/src/ui/shell/CommandPalette.tsx
@@ -23,22 +23,25 @@ import {
Clock,
Folder,
Pencil,
- Play,
- Clipboard,
} from "lucide-react";
import { getRecentActivity, type RecentActivityItem } from "@/main-axios";
-import { listSnippets } from "@/lib/snippet-provider";
-import type { Host, TabType, Tab, Snippet } from "@/types/ui-types";
+import type { Host, TabType, Tab } from "@/types/ui-types";
import { canEditHost } from "@/sidebar/host-permissions";
import { RAIL_UTILITY_ITEMS, useRailItems } from "@/sidebar/rail-items";
-import { useSnippetRunner } from "@/hooks/use-snippet-runner.tsx";
import {
defaultConnectAction,
hostActionsFor,
runHostAction,
useHostActions,
} from "@/sidebar/host-contributions";
-import { paletteEntriesFor, usePaletteEntries } from "./palette-registry";
+import {
+ filterPaletteItems,
+ loadPaletteGroup,
+ paletteEntriesFor,
+ usePaletteEntries,
+ usePaletteGroups,
+ type PaletteItemDef,
+} from "./palette-registry";
import { activityTarget } from "@/lib/activity-types";
import { shell } from "@/plugin-host/shell-bridge";
@@ -71,9 +74,11 @@ export function CommandPalette({
const [recentActivity, setRecentActivity] = useState(
[],
);
- const [snippets, setSnippets] = useState([]);
+ const paletteGroups = usePaletteGroups();
+ const [groupItems, setGroupItems] = useState<
+ Record
+ >({});
const [selectedValue, setSelectedValue] = useState("");
- const { runSnippet, dialog: runSnippetDialog } = useSnippetRunner();
useEffect(() => {
if (isOpen) {
@@ -82,12 +87,24 @@ export function CommandPalette({
getRecentActivity(5)
.then(setRecentActivity)
.catch(() => {});
- listSnippets()
- .then((data) => setSnippets(data as unknown as Snippet[]))
- .catch(() => {});
}
}, [isOpen]);
+ useEffect(() => {
+ if (!isOpen) return;
+ let cancelled = false;
+ for (const group of paletteGroups) {
+ void loadPaletteGroup(group).then((items) => {
+ if (!cancelled) {
+ setGroupItems((prev) => ({ ...prev, [group.id]: items }));
+ }
+ });
+ }
+ return () => {
+ cancelled = true;
+ };
+ }, [isOpen, paletteGroups]);
+
useEffect(() => {
if (!isOpen) return;
@@ -130,16 +147,6 @@ export function CommandPalette({
groupedHosts.push({ folder, hosts: fhosts });
}
- const filteredSnippets = search.trim()
- ? snippets.filter((s) => {
- const query = search.toLowerCase();
- return (
- s.name.toLowerCase().includes(query) ||
- s.content.toLowerCase().includes(query)
- );
- })
- : [];
-
useEffect(() => {
if (!isOpen) return;
const firstHost = filteredHosts[0];
@@ -159,10 +166,7 @@ export function CommandPalette({
};
const showHostResultsFirst = search.trim().length > 0;
- // Closing the palette unmounts this component (AppShell only renders it
- // while commandPaletteOpen is true), so keep rendering just the variables
- // dialog after close if a snippet run is still pending its inputs.
- if (!isOpen) return runSnippetDialog;
+ if (!isOpen) return null;
return (
)}
- {filteredSnippets.length > 0 && (
- <>
-
-
- {filteredSnippets.map((snippet) => (
- {
- if (!activeTargetTab) return;
- handleAction(() =>
- runSnippet(snippet, [activeTargetTab]),
- );
- }}
- className={cn(
- "group flex items-center gap-3 px-3 py-2.5 rounded-none hover:bg-accent-brand/10 cursor-pointer",
- !activeTargetTab && "pointer-events-none opacity-50",
- )}
- >
-
- {snippet.isNote ? (
-
- ) : (
-
- )}
-
-
-
- {snippet.name}
-
-
- {snippet.content}
-
-
-
- {activeTargetTab
- ? t("commandPalette.runSnippetDesc")
- : t("newUi.sidebar.snippets.noTerminalTabsOpen")}
-
-
- ))}
-
- >
- )}
+ {paletteGroups.map((group) => {
+ const items = filterPaletteItems(
+ groupItems[group.id] ?? [],
+ search,
+ group.showWhenEmpty,
+ );
+ if (items.length === 0) return null;
+ return (
+
+
+
+ {items.map((item) => {
+ const Icon = item.icon;
+ const blocked = !!item.needsTarget && !activeTargetTab;
+ return (
+ {
+ if (blocked) return;
+ handleAction(() =>
+ item.run({ targetTab: activeTargetTab, shell }),
+ );
+ }}
+ className={cn(
+ "group flex items-center gap-3 px-3 py-2.5 rounded-none hover:bg-accent-brand/10 cursor-pointer",
+ blocked && "pointer-events-none opacity-50",
+ )}
+ >
+
+ {Icon && (
+
+ )}
+
+
+
+ {item.title}
+
+ {item.description && (
+
+ {item.description}
+
+ )}
+
+ {(blocked || item.hint) && (
+
+ {blocked
+ ? t("commandPalette.noTargetTab")
+ : item.hint}
+
+ )}
+
+ );
+ })}
+
+
+ );
+ })}
{paletteEntriesFor(paletteEntries, "global").length > 0 && (
<>
@@ -661,8 +676,6 @@ export function CommandPalette({
-
- {runSnippetDialog}
);
}
diff --git a/src/ui/shell/keybinding-registry.ts b/src/ui/shell/keybinding-registry.ts
new file mode 100644
index 000000000..764143ab1
--- /dev/null
+++ b/src/ui/shell/keybinding-registry.ts
@@ -0,0 +1,150 @@
+import type { ComponentType } from "react";
+import type { KeybindingAction, KeyCombo } from "@/types/keybindings";
+import { byOrderThenId, createRegistry } from "@/lib/registry";
+
+export interface KeybindingEditorProps {
+ action: KeybindingAction;
+ onChange: (action: KeybindingAction) => void;
+}
+
+export interface KeybindingRunContext {
+ sessionId?: string;
+ host?: {
+ ip?: string;
+ username?: string;
+ port?: number | string;
+ name?: string;
+ } | null;
+ send?: (data: string) => void;
+}
+
+/**
+ * An action a key can be bound to. The shell registers its own; plugins add
+ * theirs with app.registerKeybindingAction. `id` is what a saved binding
+ * stores as action.type.
+ */
+export interface KeybindingActionDef {
+ id: string;
+ pluginId?: string;
+ /** A full translation key. */
+ labelKey: string;
+ scope: "session" | "global";
+ editor?: ComponentType;
+ summary?: ComponentType;
+ /** A full translation key when the action cannot be saved yet. */
+ validate?: (action: KeybindingAction) => string | null;
+ run?: (action: KeybindingAction, context: KeybindingRunContext) => void;
+ order?: number;
+}
+
+/** A built-in key the user can rebind. */
+export interface KeybindingDefaultDef {
+ id: string;
+ pluginId?: string;
+ combo: KeyCombo;
+ /** A full translation key. */
+ descriptionKey: string;
+ order?: number;
+}
+
+/** Fired for the shell's own actions; AppShell runs them. */
+export const GLOBAL_KEYBINDING_EVENT = "termix:global-keybinding";
+
+function shellAction(id: string, labelKey: string, order: number) {
+ return {
+ id,
+ labelKey,
+ scope: "global" as const,
+ order,
+ run: (action: KeybindingAction) => {
+ window.dispatchEvent(
+ new CustomEvent(GLOBAL_KEYBINDING_EVENT, {
+ detail: { type: action.type },
+ }),
+ );
+ },
+ };
+}
+
+/** The actions the shell runs itself. */
+const SHELL_ACTIONS: KeybindingActionDef[] = [
+ shellAction("nextTab", "newUi.sidebar.keybindings.actionNextTab", 1000),
+ shellAction(
+ "previousTab",
+ "newUi.sidebar.keybindings.actionPreviousTab",
+ 1001,
+ ),
+ shellAction(
+ "openCommandPalette",
+ "newUi.sidebar.keybindings.actionOpenCommandPalette",
+ 1002,
+ ),
+ shellAction(
+ "reconnectSession",
+ "newUi.sidebar.keybindings.actionReconnectSession",
+ 1003,
+ ),
+];
+
+export const SHELL_KEYBINDING_ACTION_IDS = SHELL_ACTIONS.map(
+ (action) => action.id,
+);
+
+const actions = createRegistry(byOrderThenId);
+const defaults = createRegistry(byOrderThenId);
+
+function install(): void {
+ for (const action of SHELL_ACTIONS) {
+ if (!actions.get(action.id)) actions.register(action);
+ }
+}
+install();
+
+/** Adds a plugin's action. A shell action's id cannot be taken over. */
+export function registerKeybindingAction(
+ action: KeybindingActionDef,
+): () => void {
+ if (SHELL_KEYBINDING_ACTION_IDS.includes(action.id)) {
+ throw new Error(`Keybinding action "${action.id}" belongs to the shell`);
+ }
+ const existing = actions.get(action.id);
+ if (existing && existing.pluginId !== action.pluginId) {
+ throw new Error(
+ `Keybinding action "${action.id}" is already registered by ${existing.pluginId}`,
+ );
+ }
+ return actions.register(action);
+}
+
+export const registerKeybindingDefault = defaults.register;
+export const listKeybindingActions = actions.list;
+export const useKeybindingActions = actions.useList;
+export const getKeybindingAction = actions.get;
+export const listKeybindingDefaults = defaults.list;
+export const useKeybindingDefaults = defaults.useList;
+
+/** Back to the shell's own actions and no defaults, for tests. */
+export function resetKeybindingRegistry(): void {
+ actions.reset();
+ defaults.reset();
+ install();
+}
+
+/**
+ * Runs a bound action through whoever registered it. False when nothing
+ * running handles its type (its plugin is off), so the caller can let the
+ * key through.
+ */
+export function runKeybindingAction(
+ action: KeybindingAction,
+ context: KeybindingRunContext = {},
+): boolean {
+ const def = actions.get(action.type);
+ if (!def?.run) return false;
+ try {
+ def.run(action, context);
+ } catch (error) {
+ console.error(`[keybindings] ${action.type} failed`, error);
+ }
+ return true;
+}
diff --git a/src/ui/shell/palette-registry.ts b/src/ui/shell/palette-registry.ts
index b5b835625..47f27f810 100644
--- a/src/ui/shell/palette-registry.ts
+++ b/src/ui/shell/palette-registry.ts
@@ -1,5 +1,5 @@
import type { ComponentType } from "react";
-import type { Host } from "@/types/ui-types";
+import type { Host, Tab } from "@/types/ui-types";
import { byOrderThenId, createRegistry } from "@/lib/registry";
import type { TabShellCallbacks } from "./tab-registry";
@@ -38,3 +38,59 @@ export function paletteEntriesFor(
}
});
}
+
+/** One row of a palette group. */
+export interface PaletteItemDef {
+ id: string;
+ title: string;
+ description?: string;
+ icon?: ComponentType<{ className?: string }>;
+ keywords?: string[];
+ needsTarget?: boolean;
+ hint?: string;
+ run: (context: { targetTab?: Tab; shell: TabShellCallbacks }) => void;
+}
+
+/** A searchable group of items a plugin fills, e.g. its saved commands. */
+export interface PaletteGroupDef {
+ id: string;
+ pluginId?: string;
+ titleKey: string;
+ order?: number;
+ load: () => PaletteItemDef[] | Promise;
+ showWhenEmpty?: boolean;
+}
+
+const groups = createRegistry(byOrderThenId);
+
+export const registerPaletteGroup = groups.register;
+export const usePaletteGroups = groups.useList;
+export const listPaletteGroups = groups.list;
+export const resetPaletteGroups = groups.reset;
+
+/** A group's items, or none when its loader throws. */
+export async function loadPaletteGroup(
+ group: PaletteGroupDef,
+): Promise {
+ try {
+ const items = await group.load();
+ return Array.isArray(items) ? items : [];
+ } catch {
+ return [];
+ }
+}
+
+/** The items the query matches, by title, description or keyword. */
+export function filterPaletteItems(
+ items: PaletteItemDef[],
+ query: string,
+ showWhenEmpty = false,
+): PaletteItemDef[] {
+ const needle = query.trim().toLowerCase();
+ if (!needle) return showWhenEmpty ? items : [];
+ return items.filter((item) =>
+ [item.title, item.description ?? "", ...(item.keywords ?? [])].some(
+ (text) => text.toLowerCase().includes(needle),
+ ),
+ );
+}
diff --git a/src/ui/shell/shell-layout.ts b/src/ui/shell/shell-layout.ts
index 27cbca195..2213c72e2 100644
--- a/src/ui/shell/shell-layout.ts
+++ b/src/ui/shell/shell-layout.ts
@@ -18,16 +18,6 @@ import { listPanes, mapTabIds } from "./split/split-tree";
* snapshots, through app.tabs.getLayout and applyLayout.
*/
-/**
- * Core tab types worth saving in a layout. "dashboard" is left out on
- * purpose: the shell always keeps one alive as the fallback tab, so it is
- * not a meaningful part of an arrangement.
- */
-const CORE_CAPTURABLE = new Set(["macros"]);
-
-/** Core types reopened as singletons, with an optional preselected host. */
-const CORE_SINGLETON = new Set(["macros"]);
-
/** Core tab types that are never part of a saved arrangement. */
const CORE_UNSAVED = new Set([
"dashboard",
@@ -43,20 +33,16 @@ const CORE_UNSAVED = new Set([
* it, and a layout keeps it so nothing is lost when the plugin comes back.
*/
function isUnregisteredPluginTabType(type: string): boolean {
- return (
- !CORE_UNSAVED.has(type) && !CORE_CAPTURABLE.has(type) && !getTabType(type)
- );
+ return !CORE_UNSAVED.has(type) && !getTabType(type);
}
export function isCapturableTabType(type: string): boolean {
- if (CORE_CAPTURABLE.has(type)) return true;
if (isUnregisteredPluginTabType(type)) return true;
const def = getTabType(type);
return !!def && def.inLayouts !== false;
}
function opensAsSingleton(type: string): boolean {
- if (CORE_SINGLETON.has(type)) return true;
const def = getTabType(type);
return !!def && (!!def.singleton || !!def.hostless);
}
diff --git a/src/ui/shell/tab-registry.ts b/src/ui/shell/tab-registry.ts
index fa0df8d6b..7de98dc69 100644
--- a/src/ui/shell/tab-registry.ts
+++ b/src/ui/shell/tab-registry.ts
@@ -73,7 +73,7 @@ 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. */
+ /** Panels that type into a terminal act on the last one of these focused. */
commandTarget?: boolean;
/** Paints its own background; its frame stays transparent. */
ownBackground?: boolean;
diff --git a/src/ui/shell/tabUtils.tsx b/src/ui/shell/tabUtils.tsx
index 1edbeb2d6..7b899f143 100644
--- a/src/ui/shell/tabUtils.tsx
+++ b/src/ui/shell/tabUtils.tsx
@@ -1,6 +1,5 @@
/* eslint-disable react-refresh/only-export-components */
import {
- Braces,
LayoutDashboard,
LayoutPanelLeft,
Server,
@@ -28,10 +27,6 @@ const DashboardTab = lazy(() =>
default: m.DashboardTab,
})),
);
-// Rail panels promoted to full tabs.
-const MacrosPanel = lazy(() =>
- import("@/sidebar/MacrosPanel").then((m) => ({ default: m.MacrosPanel })),
-);
/** Download a likely next tab without starting a connection or mounting UI. */
export function preloadTabSurface(type: TabType): void {
@@ -100,9 +95,6 @@ export function tabIcon(type: TabType) {
return ;
case "admin-settings":
return ;
- case "macros":
- return ;
-
case "split-screen":
return ;
default: {
@@ -163,23 +155,11 @@ function RegisteredTab({
);
}
-/**
- * Everything the promoted rail panels need from AppShell. Passed as one bag
- * rather than more positional params, which renderTabContent already has too
- * many of.
- */
-export type PromotedPanelProps = {
- terminalTabs?: Tab[];
- targetTerminalTabId?: string;
- storageMode?: "local" | "cloud";
-};
-
export interface TabRenderContext {
shell: TabShellCallbacks;
isVisible?: boolean;
isFocusedPane?: boolean;
inSplit?: boolean;
- panelProps?: PromotedPanelProps;
}
export function renderTabContent(tab: Tab, context: TabRenderContext) {
@@ -188,7 +168,6 @@ export function renderTabContent(tab: Tab, context: TabRenderContext) {
isVisible = true,
isFocusedPane = true,
inSplit = false,
- panelProps,
} = context;
switch (tab.type) {
@@ -204,17 +183,6 @@ export function renderTabContent(tab: Tab, context: TabRenderContext) {
case "split-screen":
return null;
- case "macros":
- return withTabSuspense(
-
-
- ,
- );
-
case "host-manager":
case "user-profile":
case "admin-settings":
diff --git a/src/ui/sidebar/AdminAuditLogSection.tsx b/src/ui/sidebar/AdminAuditLogSection.tsx
index a4346c6c2..947f0db55 100644
--- a/src/ui/sidebar/AdminAuditLogSection.tsx
+++ b/src/ui/sidebar/AdminAuditLogSection.tsx
@@ -26,7 +26,7 @@ const RESOURCE_TYPES = [
"user",
"host",
"credential",
- "snippet",
+ "plugin",
"session",
"api_key",
"setting",
diff --git a/src/ui/sidebar/AdminUserDialogs.tsx b/src/ui/sidebar/AdminUserDialogs.tsx
index 3883d2276..c3e59f563 100644
--- a/src/ui/sidebar/AdminUserDialogs.tsx
+++ b/src/ui/sidebar/AdminUserDialogs.tsx
@@ -245,7 +245,7 @@ export function AdminEditUserDialog({
{editUserRolesLoading ? (
- {t("newUi.sidebar.snippets.loading")}
+ {t("common.loading")}
) : (
<>
diff --git a/src/ui/sidebar/HostEditor.tsx b/src/ui/sidebar/HostEditor.tsx
index ece6e5187..89b716daf 100644
--- a/src/ui/sidebar/HostEditor.tsx
+++ b/src/ui/sidebar/HostEditor.tsx
@@ -6,7 +6,6 @@ import { Input } from "@/components/input";
import { PasswordInput } from "@/components/password-input";
import { Globe, Pencil, Plus, Shield, Upload, X } from "lucide-react";
import { toast } from "sonner";
-import { listSnippets } from "@/lib/snippet-provider";
import { SectionCard, SettingRow, FakeSwitch } from "@/components/section-card";
import {
createSSHHost,
@@ -28,7 +27,6 @@ import {
applyHostDraft,
buildHostEditorPayload,
createHostEditorForm,
- mapSnippetResponse,
omitOwnerSshAuthFromSharedEdit,
type HostAuthType,
type HostProtocols,
@@ -116,7 +114,6 @@ export function HostEditor({
};
const [saving, setSaving] = useState(false);
- const [snippets, setSnippets] = useState<{ id: number; name: string }[]>([]);
const [isOidcUser, setIsOidcUser] = useState(false);
const [showSecretSources, setShowSecretSources] = useState(false);
const [quickCredentialName, setQuickCredentialName] = useState("");
@@ -131,14 +128,6 @@ export function HostEditor({
.catch(() => {});
}, []);
- useEffect(() => {
- listSnippets(
- adminTargetUserId ? { targetUserId: adminTargetUserId } : undefined,
- )
- .then((res) => setSnippets(mapSnippetResponse(res)))
- .catch(() => {});
- }, [adminTargetUserId]);
-
useEffect(() => {
if (host) {
setForm(createHostEditorForm(host));
@@ -1122,7 +1111,7 @@ export function HostEditor({
updateForm={updateForm}
host={host}
credentials={availableCredentials}
- snippets={snippets}
+ adminTargetUserId={adminTargetUserId}
protocols={protocols as unknown as Record}
/>
);
diff --git a/src/ui/sidebar/HostEditorData.ts b/src/ui/sidebar/HostEditorData.ts
index 7d13444b9..026401431 100644
--- a/src/ui/sidebar/HostEditorData.ts
+++ b/src/ui/sidebar/HostEditorData.ts
@@ -14,13 +14,6 @@ export type { HostProtocols };
export type HostAuthType = Host["authType"];
-type SnippetListItem = {
- id: number;
- name?: string;
- title?: string;
-};
-type SnippetResponse = SnippetListItem[] | { snippets?: SnippetListItem[] };
-
/**
* Whether the Connection Origin control is meaningful for a host.
*
@@ -33,18 +26,6 @@ export function connectionOriginAppliesTo(protocols: HostProtocols): boolean {
return Object.values(protocols).some(Boolean);
}
-export function mapSnippetResponse(
- res: unknown,
-): { id: number; name: string }[] {
- const snippetRes = res as SnippetResponse;
- return (
- Array.isArray(snippetRes) ? snippetRes : (snippetRes.snippets ?? [])
- ).map((s) => ({
- id: s.id,
- name: s.name ?? s.title ?? `Snippet ${s.id}`,
- }));
-}
-
/** Overlays a plugin's draft on a new host's form. */
export function applyHostDraft(
form: HostEditorForm,
@@ -110,7 +91,6 @@ export function createHostEditorForm(
"local" | "remote" | null,
localOnly: host?.localOnly ?? false,
forceKeyboardInteractive: host?.forceKeyboardInteractive ?? false,
- startupSnippetId: host?.terminalConfig?.startupSnippetId ?? null,
sudoPassword: host?.hasSudoPassword
? "existing_sudo_password"
: (host?.sudoPassword ?? ""),
@@ -127,8 +107,6 @@ export function createHostEditorForm(
portKnockSequence:
host?.portKnockSequence ??
([] as { port: number; protocol: "tcp" | "udp"; delay: number }[]),
- quickActions:
- host?.quickActions ?? ([] as { name: string; snippetId: string }[]),
rdpCredentialId: host?.rdpCredentialId ?? "",
rdpUser: host?.rdpUser ?? "",
rdpPassword: host?.hasRdpPassword
@@ -320,10 +298,6 @@ export function buildHostEditorPayload(
// a number, and a string id does not compare equal on Postgres/MySQL.
jumpHosts: form.jumpHosts.map((j) => ({ hostId: Number(j.hostId) })),
portKnockSequence: form.portKnockSequence,
- quickActions: form.quickActions.map((a) => ({
- name: a.name,
- snippetId: Number(a.snippetId),
- })),
statusCheckEnabled: form.statusCheckEnabled,
statusCheckInterval: form.statusCheckInterval,
// Left out with SSH off, so the stored values stay as they are.
@@ -342,7 +316,6 @@ export function buildHostEditorPayload(
agentIdentity: usesAgent ? form.agentIdentity || null : null,
environmentVariables: form.environmentVariables,
},
- terminalConfig: { startupSnippetId: form.startupSnippetId ?? null },
}
: {}),
};
diff --git a/src/ui/sidebar/HostExportDialog.tsx b/src/ui/sidebar/HostExportDialog.tsx
index a9674d605..43336dd73 100644
--- a/src/ui/sidebar/HostExportDialog.tsx
+++ b/src/ui/sidebar/HostExportDialog.tsx
@@ -29,7 +29,6 @@ const GROUPS: { key: FieldGroup; label: string }[] = [
{ key: "tags", label: "groupTags" },
{ key: "proxy", label: "groupProxy" },
{ key: "jumpHosts", label: "groupJumpHosts" },
- { key: "quickActions", label: "groupQuickActions" },
{ key: "featureFlags", label: "groupFeatureFlags" },
{ key: "advanced", label: "groupAdvanced" },
];
@@ -40,7 +39,6 @@ const DEFAULT_GROUPS: FieldGroup[] = [
"tags",
"proxy",
"jumpHosts",
- "quickActions",
"featureFlags",
"advanced",
];
diff --git a/src/ui/sidebar/HostManagerData.ts b/src/ui/sidebar/HostManagerData.ts
index cbd670d49..304dcf227 100644
--- a/src/ui/sidebar/HostManagerData.ts
+++ b/src/ui/sidebar/HostManagerData.ts
@@ -10,7 +10,6 @@ type RawSSHHost = SSHHostWithStatus & {
hasVncPassword?: boolean;
hasTelnetPassword?: boolean;
};
-type HostQuickAction = Host["quickActions"][number];
type HostJumpHost = NonNullable[number];
type RawCredential = {
isShared?: boolean;
@@ -107,10 +106,6 @@ export function sshHostToHost(h: SSHHostWithStatus): Host {
telnetUser: h.telnetUser,
telnetPassword: h.telnetPassword ?? "",
hasTelnetPassword: !!host.hasTelnetPassword || !!h.telnetPassword,
- quickActions: (h.quickActions ?? []).map((a) => ({
- name: a.name,
- snippetId: String(a.snippetId),
- })),
jumpHosts: (parseJson(h.jumpHosts) ?? []).map((j) => ({
hostId: String(j.hostId ?? j.hostid ?? j),
})),
diff --git a/src/ui/sidebar/HostManagerTabs.tsx b/src/ui/sidebar/HostManagerTabs.tsx
index 208acb839..9a87890e3 100644
--- a/src/ui/sidebar/HostManagerTabs.tsx
+++ b/src/ui/sidebar/HostManagerTabs.tsx
@@ -30,7 +30,7 @@ export interface HostEditorSectionRenderProps {
) => void;
host?: unknown;
credentials?: unknown[];
- snippets?: unknown[];
+ adminTargetUserId?: string;
protocols: Record;
}
diff --git a/src/ui/sidebar/KeybindingsDialog.tsx b/src/ui/sidebar/KeybindingsDialog.tsx
index 466a0a3b9..a94c7e350 100644
--- a/src/ui/sidebar/KeybindingsDialog.tsx
+++ b/src/ui/sidebar/KeybindingsDialog.tsx
@@ -6,12 +6,15 @@ import {
parseCustomKeybindings,
} from "@/api/open-tabs-api";
import { saveUserPreferences } from "@/main-axios";
-import { listSnippets } from "@/lib/snippet-provider";
-import { BUILT_IN_DEFAULTS } from "@/lib/default-keybindings";
+import {
+ useKeybindingActions,
+ useKeybindingDefaults,
+ type KeybindingActionDef,
+} from "@/shell/keybinding-registry";
import type {
CustomKeybinding,
+ KeybindingAction,
KeyCombo,
- KeybindingActionType,
} from "@/types/keybindings";
import {
Dialog,
@@ -24,11 +27,6 @@ import { Button } from "@/components/button";
import { Input } from "@/components/input";
import { Pencil, Plus, RotateCcw, Trash2 } from "lucide-react";
-interface SnippetOption {
- id: number;
- name: string;
-}
-
function formatCombo(combo: KeyCombo): string {
const parts: string[] = [];
if (combo.ctrl) parts.push("Ctrl");
@@ -39,24 +37,29 @@ function formatCombo(combo: KeyCombo): string {
return parts.join(" + ");
}
-const ACTION_LABEL_KEYS: Record = {
- copy: "newUi.sidebar.keybindings.actionCopy",
- paste: "newUi.sidebar.keybindings.actionPaste",
- sendControlCode: "newUi.sidebar.keybindings.actionSendControlCode",
- sendText: "newUi.sidebar.keybindings.actionSendText",
- runSnippet: "newUi.sidebar.keybindings.actionRunSnippet",
- nextTab: "newUi.sidebar.keybindings.actionNextTab",
- previousTab: "newUi.sidebar.keybindings.actionPreviousTab",
- openCommandPalette: "newUi.sidebar.keybindings.actionOpenCommandPalette",
- reconnectSession: "newUi.sidebar.keybindings.actionReconnectSession",
-};
-
function generateId(): string {
return typeof crypto !== "undefined" && "randomUUID" in crypto
? crypto.randomUUID()
: `kb-${Date.now()}-${Math.random().toString(36).slice(2)}`;
}
+/** An action without the keys an editor cleared, so it saves clean. */
+function withoutEmpty(action: KeybindingAction): KeybindingAction {
+ const out: KeybindingAction = { type: action.type };
+ for (const [key, value] of Object.entries(action)) {
+ if (key !== "type" && value !== undefined && value !== null) {
+ out[key] = value;
+ }
+ }
+ return out;
+}
+
+/**
+ * Appearance > Keybindings. The actions come from the keybinding registry:
+ * the shell's own and whatever the running plugins register, each with its
+ * own parameter editor. A saved binding whose plugin is off stays in the
+ * list and keeps working once the plugin is back.
+ */
export function KeybindingsDialog({
open,
onOpenChange,
@@ -65,9 +68,10 @@ export function KeybindingsDialog({
onOpenChange: (v: boolean) => void;
}) {
const { t } = useTranslation();
+ const actions = useKeybindingActions();
+ const defaults = useKeybindingDefaults();
const [bindings, setBindings] = useState([]);
const [loading, setLoading] = useState(false);
- const [snippets, setSnippets] = useState([]);
const [formOpen, setFormOpen] = useState(false);
const [editingId, setEditingId] = useState(null);
const [overridesDefaultId, setOverridesDefaultId] = useState<
@@ -76,32 +80,22 @@ export function KeybindingsDialog({
const [recordedCombo, setRecordedCombo] = useState(null);
const [recording, setRecording] = useState(false);
- const [actionType, setActionType] =
- useState("sendText");
- const [text, setText] = useState("");
- const [appendEnter, setAppendEnter] = useState(false);
- const [controlCode, setControlCode] = useState("");
- const [snippetId, setSnippetId] = useState("");
+ const [draft, setDraft] = useState({ type: "" });
+
+ const actionById = useMemo(
+ () => new Map(actions.map((action) => [action.id, action])),
+ [actions],
+ );
+ const draftDef: KeybindingActionDef | undefined = actionById.get(draft.type);
useEffect(() => {
if (!open) return;
setLoading(true);
- Promise.all([
- getUserPreferences().catch(() => null),
- listSnippets().catch(() => []),
- ])
- .then(([prefs, snippetList]) => {
- setBindings(
- prefs ? parseCustomKeybindings(prefs.customKeybindings) : [],
- );
- const list = Array.isArray(snippetList) ? snippetList : [];
- setSnippets(
- list.map((s: { id: number; name: string }) => ({
- id: s.id,
- name: s.name,
- })),
- );
- })
+ getUserPreferences()
+ .then((prefs) =>
+ setBindings(parseCustomKeybindings(prefs.customKeybindings)),
+ )
+ .catch(() => setBindings([]))
.finally(() => setLoading(false));
}, [open]);
@@ -125,16 +119,19 @@ export function KeybindingsDialog({
return null;
}, [recordedCombo, bindings, editingId, t]);
+ function actionLabel(type: string): string {
+ const def = actionById.get(type);
+ return def
+ ? t(def.labelKey)
+ : t("newUi.sidebar.keybindings.unavailableAction", { type });
+ }
+
function resetForm() {
setEditingId(null);
setOverridesDefaultId(undefined);
setRecordedCombo(null);
setRecording(false);
- setActionType("sendText");
- setText("");
- setAppendEnter(false);
- setControlCode("");
- setSnippetId("");
+ setDraft({ type: actions[0]?.id ?? "" });
}
function openAddForm() {
@@ -147,11 +144,7 @@ export function KeybindingsDialog({
setOverridesDefaultId(kb.overridesDefaultId);
setRecordedCombo(kb.combo);
setRecording(false);
- setActionType(kb.action.type);
- setText(kb.action.text ?? "");
- setAppendEnter(kb.action.appendEnter ?? false);
- setControlCode(kb.action.controlCode ?? "");
- setSnippetId(kb.action.snippetId ?? "");
+ setDraft({ ...kb.action });
setFormOpen(true);
}
@@ -212,16 +205,14 @@ export function KeybindingsDialog({
toast.error(t("newUi.sidebar.keybindings.comboRequiredError"));
return;
}
- if (actionType === "sendText" && !text.trim()) {
- toast.error(t("newUi.sidebar.keybindings.textRequiredError"));
+ if (!draftDef) {
+ toast.error(t("newUi.sidebar.keybindings.actionRequiredError"));
return;
}
- if (actionType === "sendControlCode" && !/^[a-zA-Z]$/.test(controlCode)) {
- toast.error(t("newUi.sidebar.keybindings.controlCodeRequiredError"));
- return;
- }
- if (actionType === "runSnippet" && !snippetId) {
- toast.error(t("newUi.sidebar.keybindings.snippetRequiredError"));
+ const action = withoutEmpty(draft);
+ const problem = draftDef.validate?.(action);
+ if (problem) {
+ toast.error(t(problem));
return;
}
@@ -233,21 +224,7 @@ export function KeybindingsDialog({
const newBinding: CustomKeybinding = {
id: editingId ?? generateId(),
combo: recordedCombo,
- action: {
- type: actionType,
- text: actionType === "sendText" ? text : undefined,
- controlCode:
- actionType === "sendControlCode"
- ? controlCode.toLowerCase()
- : undefined,
- snippetId: actionType === "runSnippet" ? snippetId : undefined,
- appendEnter:
- actionType === "runSnippet"
- ? true
- : actionType === "sendText"
- ? appendEnter
- : undefined,
- },
+ action,
enabled: true,
overridesDefaultId,
createdAt: existing?.createdAt ?? now,
@@ -264,6 +241,7 @@ export function KeybindingsDialog({
}
const customOnlyBindings = bindings.filter((kb) => !kb.overridesDefaultId);
+ const Editor = draftDef?.editor;
return (
<>
@@ -279,78 +257,80 @@ export function KeybindingsDialog({
-
-
- {t("newUi.sidebar.keybindings.defaultsHeading")}
-
- {BUILT_IN_DEFAULTS.map((def) => {
- const override = bindings.find(
- (kb) => kb.overridesDefaultId === def.id,
- );
- return (
-
-
-
- {override
- ? formatCombo(override.combo)
- : formatCombo(def.combo)}
-
-
- {t(def.descriptionKey)}
-
+ {defaults.length > 0 && (
+
+
+ {t("newUi.sidebar.keybindings.defaultsHeading")}
+
+ {defaults.map((def) => {
+ const override = bindings.find(
+ (kb) => kb.overridesDefaultId === def.id,
+ );
+ return (
+
+
+
+ {override
+ ? formatCombo(override.combo)
+ : formatCombo(def.combo)}
+
+
+ {t(def.descriptionKey)}
+
+
+
+ {override ? (
+ <>
+
+ {t("newUi.sidebar.keybindings.customizedBadge")}
+
+
openEditForm(override)}
+ >
+
+
+
handleDelete(override.id)}
+ title={t(
+ "newUi.sidebar.keybindings.resetToDefault",
+ )}
+ >
+
+
+ >
+ ) : (
+ <>
+
+ {t("newUi.sidebar.keybindings.defaultBadge")}
+
+
+ openOverrideDefaultForm(def.id, def.combo)
+ }
+ title={t("newUi.sidebar.keybindings.customize")}
+ >
+
+
+ >
+ )}
+
-
- {override ? (
- <>
-
- {t("newUi.sidebar.keybindings.customizedBadge")}
-
-
openEditForm(override)}
- >
-
-
-
handleDelete(override.id)}
- title={t(
- "newUi.sidebar.keybindings.resetToDefault",
- )}
- >
-
-
- >
- ) : (
- <>
-
- {t("newUi.sidebar.keybindings.defaultBadge")}
-
-
- openOverrideDefaultForm(def.id, def.combo)
- }
- title={t("newUi.sidebar.keybindings.customize")}
- >
-
-
- >
- )}
-
-
- );
- })}
-
+ );
+ })}
+
+ )}
@@ -372,9 +352,7 @@ export function KeybindingsDialog({
) : (
customOnlyBindings.map((kb) => {
- const orphaned =
- kb.action.type === "runSnippet" &&
- !snippets.some((s) => String(s.id) === kb.action.snippetId);
+ const Summary = actionById.get(kb.action.type)?.summary;
return (
- {t(ACTION_LABEL_KEYS[kb.action.type])}
- {orphaned && (
-
+ {actionLabel(kb.action.type)}
+ {Summary && (
+ <>
{" "}
- (
- {t(
- "newUi.sidebar.keybindings.orphanedSnippetWarning",
- )}
- )
-
+ {}} />
+ >
)}
@@ -482,104 +456,22 @@ export function KeybindingsDialog({
{t("newUi.sidebar.keybindings.actionLabel")}
- setActionType(e.target.value as KeybindingActionType)
- }
+ value={draft.type}
+ onChange={(e) => setDraft({ type: e.target.value })}
className="h-8 border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
>
-
- {t("newUi.sidebar.keybindings.actionCopy")}
-
-
- {t("newUi.sidebar.keybindings.actionPaste")}
-
-
- {t("newUi.sidebar.keybindings.actionSendControlCode")}
-
-
- {t("newUi.sidebar.keybindings.actionSendText")}
-
-
- {t("newUi.sidebar.keybindings.actionRunSnippet")}
-
-
- {t("newUi.sidebar.keybindings.actionNextTab")}
-
-
- {t("newUi.sidebar.keybindings.actionPreviousTab")}
-
-
- {t("newUi.sidebar.keybindings.actionOpenCommandPalette")}
-
-
- {t("newUi.sidebar.keybindings.actionReconnectSession")}
-
+ {!draftDef && draft.type && (
+ {actionLabel(draft.type)}
+ )}
+ {actions.map((action) => (
+
+ {t(action.labelKey)}
+
+ ))}
- {actionType === "paste" && (
-
- {t("newUi.sidebar.keybindings.clipboardPermissionNote")}
-
- )}
- {actionType === "sendControlCode" && (
-
-
- {t("newUi.sidebar.keybindings.controlCodeLabel")}
-
- setControlCode(e.target.value)}
- placeholder="w"
- className="w-16 font-mono"
- />
-
- )}
-
- {actionType === "sendText" && (
-
-
- {t("newUi.sidebar.keybindings.textLabel")}
-
-
- )}
-
- {actionType === "runSnippet" && (
-
-
- {t("newUi.sidebar.keybindings.snippetLabel")}
-
- setSnippetId(e.target.value)}
- className="h-8 border border-border bg-background px-2 text-xs outline-none focus:ring-1 focus:ring-ring"
- >
-
- {t("newUi.sidebar.keybindings.selectSnippetPlaceholder")}
-
- {snippets.map((s) => (
-
- {s.name}
-
- ))}
-
-
- )}
+ {Editor &&
}
setFormOpen(false)}>
diff --git a/src/ui/sidebar/UserProfilePanel.tsx b/src/ui/sidebar/UserProfilePanel.tsx
index 865d15372..184f5c7df 100644
--- a/src/ui/sidebar/UserProfilePanel.tsx
+++ b/src/ui/sidebar/UserProfilePanel.tsx
@@ -498,7 +498,6 @@ export function UserProfilePanel({
pinAppRail?: boolean | null;
expandAppRailOnHover?: boolean | null;
showPinAppRailButton?: boolean | null;
- confirmSnippetExecution?: boolean | null;
disableUpdateCheck?: boolean | null;
confirmTabClose?: boolean | null;
hiddenRailTabs?: string | null;
@@ -620,13 +619,6 @@ export function UserProfilePanel({
const [showPinAppRailButton, setShowPinAppRailButton] = useState(() =>
readRailPreference("showPinAppRailButton"),
);
- // Read value is unused; the setter still backs the cloud-sync/reset/
- // snapshot machinery for this localStorage-backed pref below. The
- // folder-collapse setting itself now lives in the snippets plugin's own
- // user settings.
- const [_confirmSnippetExecution, setConfirmSnippetExecution] = useState(
- () => localStorage.getItem("confirmSnippetExecution") === "true",
- );
const [disableUpdateCheck, setDisableUpdateCheck] = useState(
() => localStorage.getItem("disableUpdateCheck") === "true",
);
@@ -763,8 +755,6 @@ export function UserProfilePanel({
"pinAppRail",
"expandAppRailOnHover",
"showPinAppRailButton",
- "snippetShowCommands",
- "confirmSnippetExecution",
"disableUpdateCheck",
"confirmTabClose",
"hiddenRailTabs",
@@ -830,13 +820,6 @@ export function UserProfilePanel({
);
window.dispatchEvent(new Event("showPinAppRailButtonChanged"));
}
- if (prefs.confirmSnippetExecution != null) {
- setConfirmSnippetExecution(prefs.confirmSnippetExecution);
- localStorage.setItem(
- "confirmSnippetExecution",
- String(prefs.confirmSnippetExecution),
- );
- }
if (prefs.disableUpdateCheck != null) {
setDisableUpdateCheck(prefs.disableUpdateCheck);
localStorage.setItem(
@@ -902,8 +885,6 @@ export function UserProfilePanel({
setShowPinAppRailButton(false);
localStorage.setItem("showPinAppRailButton", "false");
window.dispatchEvent(new Event("showPinAppRailButtonChanged"));
- setConfirmSnippetExecution(false);
- localStorage.setItem("confirmSnippetExecution", "false");
setDisableUpdateCheck(false);
localStorage.setItem("disableUpdateCheck", "false");
setConfirmTabClose(false);
@@ -924,7 +905,6 @@ export function UserProfilePanel({
pinAppRail: false,
expandAppRailOnHover: true,
showPinAppRailButton: false,
- confirmSnippetExecution: false,
disableUpdateCheck: false,
confirmTabClose: false,
hiddenRailTabs: "[]",
@@ -1007,14 +987,6 @@ export function UserProfilePanel({
localStorage.setItem("showPinAppRailButton", String(restoredShowPinButton));
window.dispatchEvent(new Event("showPinAppRailButtonChanged"));
- const restoredConfirmSnippet =
- restore("confirmSnippetExecution", "false") === "true";
- setConfirmSnippetExecution(restoredConfirmSnippet);
- localStorage.setItem(
- "confirmSnippetExecution",
- String(restoredConfirmSnippet),
- );
-
const restoredUpdateCheck =
restore("disableUpdateCheck", "false") === "true";
setDisableUpdateCheck(restoredUpdateCheck);
diff --git a/src/ui/sidebar/host-export-payload.ts b/src/ui/sidebar/host-export-payload.ts
index 155e1b427..21400c344 100644
--- a/src/ui/sidebar/host-export-payload.ts
+++ b/src/ui/sidebar/host-export-payload.ts
@@ -4,7 +4,6 @@ export type FieldGroup =
| "tags"
| "proxy"
| "jumpHosts"
- | "quickActions"
| "featureFlags"
| "advanced";
@@ -37,7 +36,6 @@ const FIELD_GROUP_KEYS: Record = {
"socks5ProxyChain",
],
jumpHosts: ["jumpHosts"],
- quickActions: ["quickActions"],
// Every plugin's host settings, enable switches included.
featureFlags: ["pluginSettings", "forceKeyboardInteractive"],
advanced: [
diff --git a/src/ui/sidebar/quick-connect-host.ts b/src/ui/sidebar/quick-connect-host.ts
index 7ade420e7..e665d81fa 100644
--- a/src/ui/sidebar/quick-connect-host.ts
+++ b/src/ui/sidebar/quick-connect-host.ts
@@ -71,7 +71,6 @@ export function createQuickConnectHost(input: QuickConnectInput): Host {
ram: null,
lastAccess: new Date().toISOString(),
pin: false,
- quickActions: [],
enableSsh: true,
sshPort: input.port,
quickConnectSavable:
diff --git a/src/ui/sidebar/rail-items.ts b/src/ui/sidebar/rail-items.ts
index 10d78066a..4a0d8aefc 100644
--- a/src/ui/sidebar/rail-items.ts
+++ b/src/ui/sidebar/rail-items.ts
@@ -1,5 +1,4 @@
import {
- Braces,
Cloud,
KeyRound,
Plug,
@@ -93,14 +92,6 @@ export const RAIL_ITEMS: RailItemDef[] = [
separatorAfter: true,
mobilePrimary: true,
},
- {
- id: "macros",
- icon: Braces,
- labelKey: "nav.macros",
- separatorAfter: true,
- promotable: true,
- rightDockable: true,
- },
{
id: "sync",
icon: Cloud,
diff --git a/src/ui/sidebar/tree/SidebarTree.tsx b/src/ui/sidebar/tree/SidebarTree.tsx
index 6ac29f715..408e6733f 100644
--- a/src/ui/sidebar/tree/SidebarTree.tsx
+++ b/src/ui/sidebar/tree/SidebarTree.tsx
@@ -636,15 +636,8 @@ export function SidebarTree({
hostId: Number(j.hostId),
})),
portKnockSequence: host.portKnockSequence ?? [],
- quickActions: (host.quickActions ?? []).map((a) => ({
- name: a.name,
- snippetId: Number(a.snippetId),
- })),
statusCheckEnabled: host.statusCheckEnabled,
statusCheckInterval: host.statusCheckInterval,
- terminalConfig: host.terminalConfig?.startupSnippetId
- ? { startupSnippetId: host.terminalConfig.startupSnippetId }
- : null,
sshOptions: host.sshOptions ?? null,
};
const created = await createSSHHost(duplicateHost);
diff --git a/src/ui/tests/lib/apply-ui-preset.test.ts b/src/ui/tests/lib/apply-ui-preset.test.ts
index 84212c640..7f8a0fb69 100644
--- a/src/ui/tests/lib/apply-ui-preset.test.ts
+++ b/src/ui/tests/lib/apply-ui-preset.test.ts
@@ -81,10 +81,10 @@ describe("presetHiddenRailTabs", () => {
it("hides plugin items that do not opt in when the preset asks", () => {
expect(
presetHiddenRailTabs(
- { hiddenTabs: ["macros"], hidePluginItems: true },
+ { hiddenTabs: ["quick-connect"], hidePluginItems: true },
items,
),
- ).toEqual(["macros", "docker-like"]);
+ ).toEqual(["quick-connect", "docker-like"]);
});
it("leaves plugin items alone otherwise", () => {
diff --git a/src/ui/tests/shell/keybinding-registry.test.ts b/src/ui/tests/shell/keybinding-registry.test.ts
new file mode 100644
index 000000000..98829e755
--- /dev/null
+++ b/src/ui/tests/shell/keybinding-registry.test.ts
@@ -0,0 +1,81 @@
+import { afterEach, describe, expect, it, vi } from "vitest";
+import {
+ GLOBAL_KEYBINDING_EVENT,
+ SHELL_KEYBINDING_ACTION_IDS,
+ getKeybindingAction,
+ listKeybindingActions,
+ registerKeybindingAction,
+ resetKeybindingRegistry,
+ runKeybindingAction,
+} from "../../shell/keybinding-registry";
+
+afterEach(() => resetKeybindingRegistry());
+
+describe("keybinding registry", () => {
+ it("starts with the shell's own actions, all global", () => {
+ expect(listKeybindingActions().map((action) => action.id)).toEqual(
+ SHELL_KEYBINDING_ACTION_IDS,
+ );
+ expect(getKeybindingAction("nextTab")?.scope).toBe("global");
+ });
+
+ it("runs a shell action by telling the shell", () => {
+ const listener = vi.fn();
+ window.addEventListener(GLOBAL_KEYBINDING_EVENT, listener);
+ try {
+ expect(runKeybindingAction({ type: "openCommandPalette" })).toBe(true);
+ expect((listener.mock.calls[0][0] as CustomEvent).detail).toEqual({
+ type: "openCommandPalette",
+ });
+ } finally {
+ window.removeEventListener(GLOBAL_KEYBINDING_EVENT, listener);
+ }
+ });
+
+ it("runs a plugin's action with the caller's context, until it is disposed", () => {
+ const run = vi.fn();
+ const dispose = registerKeybindingAction({
+ id: "sample.run",
+ pluginId: "sample",
+ labelKey: "sample:run",
+ scope: "session",
+ run,
+ });
+ const send = vi.fn();
+ expect(runKeybindingAction({ type: "sample.run", x: "1" }, { send })).toBe(
+ true,
+ );
+ expect(run).toHaveBeenCalledWith({ type: "sample.run", x: "1" }, { send });
+ dispose();
+ expect(runKeybindingAction({ type: "sample.run" })).toBe(false);
+ });
+
+ it("answers false for a type nothing running handles", () => {
+ expect(runKeybindingAction({ type: "gone" })).toBe(false);
+ });
+
+ it("refuses a shell id and a second plugin claiming a type", () => {
+ expect(() =>
+ registerKeybindingAction({
+ id: "nextTab",
+ pluginId: "sample",
+ labelKey: "x",
+ scope: "global",
+ }),
+ ).toThrow(/belongs to the shell/);
+ registerKeybindingAction({
+ id: "sample.run",
+ pluginId: "sample",
+ labelKey: "x",
+ scope: "session",
+ });
+ expect(() =>
+ registerKeybindingAction({
+ id: "sample.run",
+ pluginId: "other",
+ labelKey: "x",
+ scope: "session",
+ }),
+ ).toThrow(/already registered/);
+ });
+});
diff --git a/src/ui/tests/shell/palette-registry.test.ts b/src/ui/tests/shell/palette-registry.test.ts
new file mode 100644
index 000000000..187767ec8
--- /dev/null
+++ b/src/ui/tests/shell/palette-registry.test.ts
@@ -0,0 +1,61 @@
+import { afterEach, describe, expect, it } from "vitest";
+import {
+ filterPaletteItems,
+ listPaletteGroups,
+ loadPaletteGroup,
+ registerPaletteGroup,
+ resetPaletteGroups,
+ type PaletteItemDef,
+} from "../../shell/palette-registry";
+
+afterEach(() => resetPaletteGroups());
+
+const item = (
+ id: string,
+ title: string,
+ extra: Partial = {},
+): PaletteItemDef => ({ id, title, run: () => {}, ...extra });
+
+describe("palette groups", () => {
+ it("orders groups and loads their items", async () => {
+ registerPaletteGroup({
+ id: "b",
+ titleKey: "b",
+ order: 2,
+ load: () => [item("1", "One")],
+ });
+ registerPaletteGroup({
+ id: "a",
+ titleKey: "a",
+ order: 1,
+ load: async () => [item("2", "Two")],
+ });
+ const groups = listPaletteGroups();
+ expect(groups.map((group) => group.id)).toEqual(["a", "b"]);
+ expect((await loadPaletteGroup(groups[0])).map((i) => i.id)).toEqual(["2"]);
+ });
+
+ it("answers no items when a loader throws", async () => {
+ const group = {
+ id: "broken",
+ titleKey: "x",
+ load: async () => {
+ throw new Error("offline");
+ },
+ };
+ expect(await loadPaletteGroup(group)).toEqual([]);
+ });
+
+ it("matches title, description and keywords, and hides items until typed", () => {
+ const items = [
+ item("1", "Restart nginx", { description: "systemctl restart" }),
+ item("2", "Disk usage", { keywords: ["df"] }),
+ ];
+ expect(filterPaletteItems(items, "")).toEqual([]);
+ expect(filterPaletteItems(items, "", true)).toEqual(items);
+ expect(filterPaletteItems(items, "SYSTEMCTL").map((i) => i.id)).toEqual([
+ "1",
+ ]);
+ expect(filterPaletteItems(items, "df").map((i) => i.id)).toEqual(["2"]);
+ });
+});
diff --git a/src/ui/tests/sidebar/HostCredentialList.test.tsx b/src/ui/tests/sidebar/HostCredentialList.test.tsx
index 1f862958c..78bf4f28b 100644
--- a/src/ui/tests/sidebar/HostCredentialList.test.tsx
+++ b/src/ui/tests/sidebar/HostCredentialList.test.tsx
@@ -42,7 +42,6 @@ function makeHost(overrides: Partial): Host {
ram: null,
lastAccess: "",
authType: "credential",
- quickActions: [],
enableSsh: true,
sshPort: 22,
...overrides,
diff --git a/src/ui/tests/sidebar/HostEditorData.test.ts b/src/ui/tests/sidebar/HostEditorData.test.ts
index bbc4f601e..fe4e3402e 100644
--- a/src/ui/tests/sidebar/HostEditorData.test.ts
+++ b/src/ui/tests/sidebar/HostEditorData.test.ts
@@ -428,17 +428,16 @@ describe("connectionOriginAppliesTo", () => {
});
describe("terminal fields", () => {
- it("sends only the startup snippet in terminalConfig, and the SSH options on their own", () => {
+ it("sends no terminalConfig, and the SSH options on their own", () => {
const form = {
...createHostEditorForm(null),
- startupSnippetId: 4,
keepaliveInterval: 30,
agentForwarding: true,
environmentVariables: [{ key: "LANG", value: "C" }],
};
const payload = buildHostEditorPayload(form, sshOnly);
- expect(payload.terminalConfig).toEqual({ startupSnippetId: 4 });
+ expect(payload).not.toHaveProperty("terminalConfig");
expect(payload.sshOptions).toMatchObject({
keepaliveInterval: 30,
keepaliveCountMax: 5,
@@ -457,7 +456,7 @@ describe("terminal fields", () => {
expect(form.keepaliveInterval).toBe(15);
expect(form.allowLegacyAlgorithms).toBe(false);
- expect(form.startupSnippetId).toBe(2);
+ expect(form).not.toHaveProperty("startupSnippetId");
});
it("leaves the terminal fields out with SSH off", () => {
diff --git a/src/ui/tests/sidebar/KeybindingsDialog.test.tsx b/src/ui/tests/sidebar/KeybindingsDialog.test.tsx
new file mode 100644
index 000000000..4cebd2bd8
--- /dev/null
+++ b/src/ui/tests/sidebar/KeybindingsDialog.test.tsx
@@ -0,0 +1,79 @@
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { cleanup, fireEvent, render, screen } from "@testing-library/react";
+
+const saved = vi.hoisted(() => ({ value: null as string | null }));
+
+vi.mock("@/api/open-tabs-api", async (importOriginal) => ({
+ ...(await importOriginal>()),
+ getUserPreferences: async () => ({ customKeybindings: saved.value }),
+}));
+vi.mock("@/main-axios", () => ({ saveUserPreferences: vi.fn() }));
+
+const { KeybindingsDialog } = await import("../../sidebar/KeybindingsDialog");
+const {
+ registerKeybindingAction,
+ registerKeybindingDefault,
+ resetKeybindingRegistry,
+} = await import("../../shell/keybinding-registry");
+
+afterEach(() => {
+ cleanup();
+ resetKeybindingRegistry();
+ saved.value = null;
+});
+
+const combo = {
+ key: "k",
+ isCode: false,
+ ctrl: true,
+ alt: false,
+ shift: false,
+ meta: false,
+};
+
+describe("KeybindingsDialog", () => {
+ it("lists registered defaults and keeps a binding whose plugin is off", async () => {
+ registerKeybindingDefault({
+ id: "default-x",
+ pluginId: "sample",
+ combo,
+ descriptionKey: "sample:builtInX",
+ });
+ saved.value = JSON.stringify([
+ {
+ id: "a",
+ combo,
+ action: { type: "gone.action" },
+ enabled: true,
+ createdAt: "",
+ updatedAt: "",
+ },
+ ]);
+ render( {}} />);
+ expect(await screen.findByText("sample:builtInX")).toBeTruthy();
+ expect(
+ await screen.findByText("newUi.sidebar.keybindings.unavailableAction"),
+ ).toBeTruthy();
+ });
+
+ it("offers every registered action and draws the chosen one's editor", async () => {
+ registerKeybindingAction({
+ id: "sample.send",
+ pluginId: "sample",
+ labelKey: "sample:send",
+ scope: "session",
+ editor: () => sample editor ,
+ });
+ render( {}} />);
+ fireEvent.click(
+ await screen.findByText("newUi.sidebar.keybindings.addBinding"),
+ );
+ const select = (await screen.findByRole("option", { name: "sample:send" }))
+ .parentElement as HTMLSelectElement;
+ expect([...select.options].map((option) => option.value)).toContain(
+ "nextTab",
+ );
+ fireEvent.change(select, { target: { value: "sample.send" } });
+ expect(await screen.findByText("sample editor")).toBeTruthy();
+ });
+});
diff --git a/src/ui/tests/sidebar/host-export-payload.test.ts b/src/ui/tests/sidebar/host-export-payload.test.ts
index 357537bd8..8b70af424 100644
--- a/src/ui/tests/sidebar/host-export-payload.test.ts
+++ b/src/ui/tests/sidebar/host-export-payload.test.ts
@@ -14,7 +14,6 @@ const ALL_GROUPS = new Set([
"tags",
"proxy",
"jumpHosts",
- "quickActions",
"featureFlags",
"advanced",
]);
diff --git a/src/ui/tests/sidebar/rail-items.test.ts b/src/ui/tests/sidebar/rail-items.test.ts
index 3bd75a4bd..93381d98f 100644
--- a/src/ui/tests/sidebar/rail-items.test.ts
+++ b/src/ui/tests/sidebar/rail-items.test.ts
@@ -52,13 +52,12 @@ describe("RAIL_ITEMS", () => {
// "port-forwarding" to the tunnels plugin's, "serial" to the serial
// plugin's, "collab" to the session-sharing plugin's, and "session-logs"
// to the session-recording plugin's, "termix-id" to the termix-identity
- // plugin's, "ssh-tools" to the ssh-terminal plugin's.
+ // plugin's, "ssh-tools" and "macros" to the ssh-terminal plugin's.
expect(RAIL_ITEMS.map((item) => item.id)).toEqual([
"hosts",
"credentials",
"connections",
"quick-connect",
- "macros",
"sync",
]);
});
@@ -80,7 +79,7 @@ describe("RAIL_ITEMS", () => {
[...RAIL_ITEMS, ...RAIL_UTILITY_ITEMS]
.filter((item) => item.promotable)
.map((item) => item.id),
- ).toEqual(["macros"]);
+ ).toEqual([]);
});
it("derives promotableIds from the promotable flag", () => {
@@ -117,7 +116,7 @@ describe("RAIL_ITEMS", () => {
});
it("only offers reference panels in the right dock", () => {
- expect(rightDockableIds()).toEqual(["connections", "macros"]);
+ expect(rightDockableIds()).toEqual(["connections"]);
});
});
@@ -131,10 +130,10 @@ describe("registered rail items", () => {
});
it("places items after their anchor, keeping their own order", () => {
- registerRailItem(item("second", { after: "macros", order: 2 }));
- registerRailItem(item("first", { after: "macros", order: 1 }));
+ registerRailItem(item("second", { after: "connections", order: 2 }));
+ registerRailItem(item("first", { after: "connections", order: 1 }));
const ids = visibleRailItems().map((entry) => entry.id);
- const at = ids.indexOf("macros");
+ const at = ids.indexOf("connections");
expect(ids.slice(at + 1, at + 3)).toEqual(["first", "second"]);
});
diff --git a/src/ui/tests/sidebar/sidebar-tree-visible-rows.test.ts b/src/ui/tests/sidebar/sidebar-tree-visible-rows.test.ts
index c00f1995a..656834cb6 100644
--- a/src/ui/tests/sidebar/sidebar-tree-visible-rows.test.ts
+++ b/src/ui/tests/sidebar/sidebar-tree-visible-rows.test.ts
@@ -25,7 +25,6 @@ function host(id: string, name: string): Host {
authType: "password",
pin: false,
enableSsh: true,
- quickActions: [],
} as Host;
}
diff --git a/src/ui/tests/sidebar/tree/HostItem/HostItem.test.tsx b/src/ui/tests/sidebar/tree/HostItem/HostItem.test.tsx
index 13898258d..b6f651570 100644
--- a/src/ui/tests/sidebar/tree/HostItem/HostItem.test.tsx
+++ b/src/ui/tests/sidebar/tree/HostItem/HostItem.test.tsx
@@ -69,7 +69,6 @@ const baseHost: Host = {
enableRdp: true,
enableVnc: true,
enableTelnet: true,
- quickActions: [],
} as unknown as Host;
const noop = () => {};