@termix-ssh/plugin-sdk 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/cli/commands/changelog.mjs +25 -0
- package/cli/commands/dev.mjs +158 -0
- package/cli/commands/validate.mjs +16 -24
- package/cli/index.mjs +8 -0
- package/cli/lib/tmxplug.mjs +1 -1
- package/dist/backend.d.ts +6 -2
- package/dist/changelog.d.ts +67 -0
- package/dist/changelog.js +195 -0
- package/dist/frontend.d.ts +67 -1
- package/dist/host/{chunk-XGWE7BTJ.js → chunk-3KG44G55.js} +6 -1
- package/dist/host/{chunk-XH5NXL6Y.js → chunk-7GL25GFS.js} +8013 -7606
- package/dist/host/{chunk-F3SDZDDG.js → chunk-COGRSHKO.js} +17 -2
- package/dist/host/{linked-server-FZGWQW32.js → linked-server-BD573BEG.js} +1 -1
- package/dist/host/{rbac-api-6KDMBVDZ.js → rbac-api-7GHDJLU4.js} +1 -1
- package/dist/host/testing-host.js +38 -5
- package/dist/host/types/src/types/credential-sidebar-preferences.d.ts +12 -1
- package/dist/host/types/src/types/host-sidebar-preferences.d.ts +18 -1
- package/dist/host/types/src/types/onboarding.d.ts +29 -0
- package/dist/host/types/src/types/plugin-onboarding.d.ts +39 -0
- package/dist/host/types/src/types/ui-preferences.d.ts +13 -4
- package/dist/host/types/src/types/ui-types.d.ts +1 -1
- package/dist/host/types/src/ui/api/plugins-api.d.ts +194 -0
- package/dist/host/types/src/ui/components/connection/ConnectionLogPanel.d.ts +2 -1
- package/dist/host/types/src/ui/components/connection/ConnectionScreen.d.ts +5 -5
- package/dist/host/types/src/ui/components/list-kit.d.ts +102 -0
- package/dist/host/types/src/ui/components/panel-layout.d.ts +6 -1
- package/dist/host/types/src/ui/components/surface/surface-scope.d.ts +5 -1
- package/dist/host/types/src/ui/contexts/UiPreferencesContext.d.ts +15 -2
- package/dist/host/types/src/ui/hooks/use-permissions.d.ts +2 -0
- package/dist/host/types/src/ui/lib/embedded-frame.d.ts +14 -0
- package/dist/host/types/src/ui/onboarding/onboarding-registry.d.ts +49 -0
- package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -0
- package/dist/host/types/src/ui/plugins/open-plugins.d.ts +5 -0
- package/dist/host/types/src/ui/sidebar/host-contributions.d.ts +2 -0
- package/dist/host/types/src/ui/sidebar/rail-items.d.ts +10 -0
- package/dist/host/ui.js +982 -698
- package/dist/host/{use-permissions-NULZL43J.js → use-permissions-SKWXHWDJ.js} +3 -1
- package/dist/manifest.d.ts +10 -2
- package/dist/manifest.js +46 -1
- package/dist/testing/setup.js +8 -0
- package/dist/testing.d.ts +12 -0
- package/dist/vitest-preset.d.ts +3 -0
- package/dist/vitest-preset.js +6 -0
- package/package.json +5 -2
- package/schema/manifest.schema.json +6 -1
- package/cli/lib/changelog.mjs +0 -126
- package/schema/changelog.schema.json +0 -67
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
export type ListRowTone = "brand" | "muted" | "success" | "warning" | "destructive" | "none";
|
|
3
|
+
/** Scrolling list body. Rows sit flush, divided by their own borders. */
|
|
4
|
+
export declare function PanelList({ empty, className, children, }: {
|
|
5
|
+
/** Shown instead of the rows when there are none. */
|
|
6
|
+
empty?: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* One row in a sidebar list, drawn like the host list: a coloured stripe on
|
|
12
|
+
* the left, a name with an optional line under it, and a row of action
|
|
13
|
+
* buttons that opens under it on hover.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ListRow({ title, icon, badges, meta, stripe, tone, color, selected, active, dimmed, leading, trailing, actions, onClick, className, children, ...rest }: {
|
|
16
|
+
title: React.ReactNode;
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/** Small chips after the title. */
|
|
19
|
+
badges?: React.ReactNode;
|
|
20
|
+
/** Muted line under the title, like a description or Facts. */
|
|
21
|
+
meta?: React.ReactNode;
|
|
22
|
+
/** Position in the list, odd rows get the zebra tint. */
|
|
23
|
+
stripe?: number;
|
|
24
|
+
tone?: ListRowTone;
|
|
25
|
+
/** A custom stripe colour, like a group's own colour. */
|
|
26
|
+
color?: string;
|
|
27
|
+
selected?: boolean;
|
|
28
|
+
/** Keeps the tray open, like while its menu is showing. */
|
|
29
|
+
active?: boolean;
|
|
30
|
+
dimmed?: boolean;
|
|
31
|
+
/** Sits between the stripe and the text, like a drag handle. */
|
|
32
|
+
leading?: React.ReactNode;
|
|
33
|
+
/** Always visible on the right, like a switch. */
|
|
34
|
+
trailing?: React.ReactNode;
|
|
35
|
+
/** Buttons under the row that open on hover, like the host list. */
|
|
36
|
+
actions?: React.ReactNode;
|
|
37
|
+
onClick?: () => void;
|
|
38
|
+
className?: string;
|
|
39
|
+
/** Extra lines under the meta. */
|
|
40
|
+
children?: React.ReactNode;
|
|
41
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "title" | "onClick" | "children">): React.JSX.Element;
|
|
42
|
+
/** Square icon button for a ListRow tray. */
|
|
43
|
+
export declare function ListRowAction({ label, tone, className, children, ...rest }: {
|
|
44
|
+
label: string;
|
|
45
|
+
tone?: "default" | "brand" | "destructive";
|
|
46
|
+
className?: string;
|
|
47
|
+
children: React.ReactNode;
|
|
48
|
+
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
|
|
49
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
50
|
+
}): React.JSX.Element;
|
|
51
|
+
/** Small uppercase chip for a ListRow's badges. */
|
|
52
|
+
export declare function ListBadge({ tone, className, children, }: {
|
|
53
|
+
tone?: "muted" | "brand" | "warning" | "destructive" | "success";
|
|
54
|
+
className?: string;
|
|
55
|
+
children: React.ReactNode;
|
|
56
|
+
}): React.JSX.Element;
|
|
57
|
+
/** A collapsible group in a ListRow list, drawn like a host folder. */
|
|
58
|
+
export declare function ListRowFolder({ name, icon, count, open, onToggle, stripe, actions, dropActive, onDropItem, emptyText, className, children, }: {
|
|
59
|
+
name: React.ReactNode;
|
|
60
|
+
icon?: React.ReactNode;
|
|
61
|
+
count?: number;
|
|
62
|
+
open: boolean;
|
|
63
|
+
onToggle: () => void;
|
|
64
|
+
stripe?: number;
|
|
65
|
+
/** Shown on hover, usually a DropdownMenu trigger. */
|
|
66
|
+
actions?: React.ReactNode;
|
|
67
|
+
/** True while something is being dragged that could drop in here. */
|
|
68
|
+
dropActive?: boolean;
|
|
69
|
+
onDropItem?: () => void;
|
|
70
|
+
emptyText?: React.ReactNode;
|
|
71
|
+
className?: string;
|
|
72
|
+
children?: React.ReactNode;
|
|
73
|
+
}): React.JSX.Element;
|
|
74
|
+
/** The one "add" button, placed beside a panel's search box. */
|
|
75
|
+
export declare function AddButton({ label, compact, className, ...rest }: {
|
|
76
|
+
label?: string;
|
|
77
|
+
/** Icon only, for tight toolbars. */
|
|
78
|
+
compact?: boolean;
|
|
79
|
+
className?: string;
|
|
80
|
+
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
|
|
81
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
82
|
+
}): React.JSX.Element;
|
|
83
|
+
/** The one Save bar, for a PanelShell or InlineView footer. */
|
|
84
|
+
export declare function FormFooter({ onCancel, onSave, saveLabel, cancelLabel, saving, dirty, status, disabled, onDelete, deleteLabel, extra, saveType, form, }: {
|
|
85
|
+
onCancel?: () => void;
|
|
86
|
+
onSave?: () => void;
|
|
87
|
+
saveLabel?: string;
|
|
88
|
+
cancelLabel?: string;
|
|
89
|
+
saving?: boolean;
|
|
90
|
+
/** Shows the unsaved changes note. */
|
|
91
|
+
dirty?: boolean;
|
|
92
|
+
/** Replaces the unsaved note, like an error count. */
|
|
93
|
+
status?: React.ReactNode;
|
|
94
|
+
disabled?: boolean;
|
|
95
|
+
onDelete?: () => void;
|
|
96
|
+
deleteLabel?: string;
|
|
97
|
+
/** More buttons, placed before Save. */
|
|
98
|
+
extra?: React.ReactNode;
|
|
99
|
+
saveType?: "button" | "submit";
|
|
100
|
+
/** Id of the form a submit Save belongs to. */
|
|
101
|
+
form?: string;
|
|
102
|
+
}): React.JSX.Element;
|
|
@@ -37,13 +37,18 @@ export declare function BackButton({ onClick, label, className, }: {
|
|
|
37
37
|
className?: string;
|
|
38
38
|
}): React.JSX.Element;
|
|
39
39
|
/** Search box sized to match toolbar buttons. Esc clears it. */
|
|
40
|
-
export declare function PanelSearch({ value, onChange, placeholder, fill, autoFocus, className, }: {
|
|
40
|
+
export declare function PanelSearch({ value, onChange, placeholder, fill, autoFocus, disabled, title, onEnter, inputRef, className, }: {
|
|
41
41
|
value: string;
|
|
42
42
|
onChange: (next: string) => void;
|
|
43
43
|
placeholder?: string;
|
|
44
44
|
/** Takes the whole toolbar, for panels where search is the only control. */
|
|
45
45
|
fill?: boolean;
|
|
46
46
|
autoFocus?: boolean;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
title?: string;
|
|
49
|
+
/** Runs on Enter, for searches that query a server. */
|
|
50
|
+
onEnter?: () => void;
|
|
51
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
47
52
|
className?: string;
|
|
48
53
|
}): React.JSX.Element;
|
|
49
54
|
/** Square segmented control. */
|
|
@@ -13,8 +13,10 @@ export interface ConfirmOptions {
|
|
|
13
13
|
/** Red confirm button. Defaults to true, since most confirms delete. */
|
|
14
14
|
destructive?: boolean;
|
|
15
15
|
}
|
|
16
|
-
export declare function SurfaceScope({ kind, onEditingChange, className, children, }: {
|
|
16
|
+
export declare function SurfaceScope({ kind, onEditingChange, onClose, className, children, }: {
|
|
17
17
|
kind?: SurfaceKind;
|
|
18
|
+
/** Closes whatever this surface is, like the tab it fills. */
|
|
19
|
+
onClose?: () => void;
|
|
18
20
|
/** Gets the widest open inline view, so a sidebar can widen for it. */
|
|
19
21
|
onEditingChange?: (editing: EditingWidth) => void;
|
|
20
22
|
className?: string;
|
|
@@ -22,6 +24,8 @@ export declare function SurfaceScope({ kind, onEditingChange, className, childre
|
|
|
22
24
|
}): React.JSX.Element;
|
|
23
25
|
/** Which kind of surface this code renders in, or null outside any. */
|
|
24
26
|
export declare function useSurfaceKind(): SurfaceKind | null;
|
|
27
|
+
/** Closes the surface this code renders in, or null when it can't be closed. */
|
|
28
|
+
export declare function useSurfaceClose(): (() => void) | null;
|
|
25
29
|
/**
|
|
26
30
|
* Asks a yes or no question over the surface that called it. Enter confirms
|
|
27
31
|
* and Esc cancels, before a terminal can see the key.
|
|
@@ -8,7 +8,18 @@ interface UiPreferencesContextValue {
|
|
|
8
8
|
setOverride: <A extends UiAreaKey, K extends keyof UiAreaPreferences[A]>(area: A, key: K, value: UiAreaPreferences[A][K] | null) => void;
|
|
9
9
|
clearArea: (area: UiAreaKey) => void;
|
|
10
10
|
clearAllOverrides: () => void;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Records onboarding steps as seen (per-step max, never lowered).
|
|
13
|
+
* `completed` stamps completedAt the first time a run finishes, and
|
|
14
|
+
* `baselineDone` clears the carried-over-user flag.
|
|
15
|
+
*/
|
|
16
|
+
markOnboardingSeen: (seen: Record<string, number>, options?: {
|
|
17
|
+
completed?: boolean;
|
|
18
|
+
skipped?: boolean;
|
|
19
|
+
baselineDone?: boolean;
|
|
20
|
+
}) => void;
|
|
21
|
+
/** Sends any queued change now instead of after the debounce. */
|
|
22
|
+
flushNow: () => Promise<void>;
|
|
12
23
|
/** A plugin's own area, from the presets it declared. */
|
|
13
24
|
resolvePlugin: (pluginId: string, presets: UiPluginPresets | undefined) => Record<string, unknown>;
|
|
14
25
|
setPluginOverride: (pluginId: string, key: string, value: unknown) => void;
|
|
@@ -23,8 +34,10 @@ interface UiPreferencesContextValue {
|
|
|
23
34
|
* deep and treats null as "clear this", so handing a knob back to the preset is
|
|
24
35
|
* a single PUT rather than a read-modify-write of the whole document.
|
|
25
36
|
*/
|
|
26
|
-
export declare function UiPreferencesProvider({ children }: {
|
|
37
|
+
export declare function UiPreferencesProvider({ children, initial, }: {
|
|
27
38
|
children: ReactNode;
|
|
39
|
+
/** Already fetched (by the onboarding gate), so no second request. */
|
|
40
|
+
initial?: UiPreferences | null;
|
|
28
41
|
}): import("react").JSX.Element;
|
|
29
42
|
export declare function useUiPreferencesContext(): UiPreferencesContextValue | null;
|
|
30
43
|
/**
|
|
@@ -7,6 +7,8 @@ export interface PermissionsState {
|
|
|
7
7
|
}
|
|
8
8
|
/** The same answer usePermissions().has gives, for code outside a component. */
|
|
9
9
|
export declare function hasPermission(permission: string): Promise<boolean>;
|
|
10
|
+
/** A sync has() over the current grants, for code outside a component. */
|
|
11
|
+
export declare function loadPermissionChecker(): Promise<(permission: string) => boolean>;
|
|
10
12
|
/**
|
|
11
13
|
* Warms the permissions cache before the shell mounts, so permission-gated
|
|
12
14
|
* rail items are already known on first paint instead of popping in once
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two places this web UI runs only to sign someone in for another app:
|
|
3
|
+
* the Termix mobile app's WebView, and the iframe a desktop app shows a
|
|
4
|
+
* server's login in while linking. Neither should ever show onboarding.
|
|
5
|
+
*/
|
|
6
|
+
export interface EmbeddedFrameWindow extends Window {
|
|
7
|
+
IS_ELECTRON_WEBVIEW?: boolean;
|
|
8
|
+
ReactNativeWebView?: {
|
|
9
|
+
postMessage: (msg: string) => void;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare function isInMobileWebView(): boolean;
|
|
13
|
+
export declare function isInElectronWebView(): boolean;
|
|
14
|
+
export declare function isEmbeddedAuthFrame(): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
3
|
+
import type { OnboardingMode, OnboardingSection, OnboardingStepProps } from "@termix-ssh/plugin-sdk/frontend";
|
|
4
|
+
import type { SlotContribution } from "../shell/action-registry";
|
|
5
|
+
export type { OnboardingMode, OnboardingSection, OnboardingStepProps };
|
|
6
|
+
export interface OnboardingCtx {
|
|
7
|
+
mode: OnboardingMode;
|
|
8
|
+
isAdmin: boolean;
|
|
9
|
+
isDesktop: boolean;
|
|
10
|
+
has: (permission: string) => boolean;
|
|
11
|
+
/** True on a fresh install until an admin confirms the plugin picker. */
|
|
12
|
+
pluginSetupPending: boolean;
|
|
13
|
+
/** A linked desktop runs what its server runs. */
|
|
14
|
+
pluginsManagedElsewhere: boolean;
|
|
15
|
+
/** Whether a sign in plugin offers a passkey or authenticator to set up. */
|
|
16
|
+
canEnrollSecondFactor?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface OnboardingStepDef {
|
|
19
|
+
/** The key stored in the seen map: a core id, or `<pluginId>:<id>`. */
|
|
20
|
+
id: string;
|
|
21
|
+
pluginId?: string;
|
|
22
|
+
version: number;
|
|
23
|
+
titleKey: string;
|
|
24
|
+
descriptionKey?: string;
|
|
25
|
+
icon?: LucideIcon | ComponentType<{
|
|
26
|
+
size?: number;
|
|
27
|
+
className?: string;
|
|
28
|
+
}>;
|
|
29
|
+
Component: ComponentType<OnboardingStepProps>;
|
|
30
|
+
audience: "all" | "admin";
|
|
31
|
+
permission?: string;
|
|
32
|
+
/** Position in the flow; plugin steps get theirs from their section. */
|
|
33
|
+
position: number;
|
|
34
|
+
isRelevant?: (ctx: OnboardingCtx) => boolean;
|
|
35
|
+
/** Shown in a partial run even when already seen. */
|
|
36
|
+
forcePending?: (ctx: OnboardingCtx) => boolean;
|
|
37
|
+
/** Skip stays hidden until this step has been confirmed. */
|
|
38
|
+
required?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Where each section of plugin steps sits among the core steps. */
|
|
41
|
+
export declare const SECTION_POSITION: Record<OnboardingSection, number>;
|
|
42
|
+
export declare const onboardingSteps: import("../lib/registry").Registry<OnboardingStepDef>;
|
|
43
|
+
/** Plugin steps sort inside their section by order, then key. */
|
|
44
|
+
export declare function sectionPosition(section: OnboardingSection | undefined, order?: number): number;
|
|
45
|
+
export declare function sortSteps(steps: OnboardingStepDef[]): OnboardingStepDef[];
|
|
46
|
+
/** Steps still on the deprecated onboarding.steps slot, read as version 1. */
|
|
47
|
+
export declare function legacyStepDefs(contributions: SlotContribution[]): OnboardingStepDef[];
|
|
48
|
+
/** Core, registered and legacy steps in flow order, registered winning ties. */
|
|
49
|
+
export declare function mergeStepDefs(core: OnboardingStepDef[], registered: OnboardingStepDef[], legacy: OnboardingStepDef[]): OnboardingStepDef[];
|
|
@@ -87,6 +87,7 @@ export { getAdaptiveResourceBudget, markAdaptiveResourceUsed, runAdaptiveBackgro
|
|
|
87
87
|
export { getDeviceId } from "../lib/device-id";
|
|
88
88
|
export { createQuickConnectHost } from "../sidebar/quick-connect-host";
|
|
89
89
|
export { BackButton, Facts, GroupHeading, PANEL, PanelSearch, PanelShell, Segmented, ViewToggle, type PanelDensity, type PanelViewMode, } from "../components/panel-layout";
|
|
90
|
+
export { AddButton, FormFooter, ListBadge, ListRow, ListRowAction, ListRowFolder, PanelList, type ListRowTone, } from "../components/list-kit";
|
|
90
91
|
export { DataView, type DataColumn, type GridColumns, } from "../components/data-view";
|
|
91
92
|
export { TabStrip, type TabStripItem } from "../components/tab-strip";
|
|
92
93
|
export { CardMasonry } from "../components/card-masonry";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Asks the shell to open the Plugins tab. */
|
|
2
|
+
export declare const OPEN_PLUGINS_EVENT = "termix:open-plugins";
|
|
3
|
+
/** Asks the shell to open Settings at a plugin's page; detail is the plugin id. */
|
|
4
|
+
export declare const OPEN_PLUGIN_SETTINGS_EVENT = "termix:open-plugin-settings";
|
|
5
|
+
export declare function openPluginSettings(pluginId: string): void;
|
|
@@ -56,6 +56,8 @@ export interface HostContextMenuItemDef {
|
|
|
56
56
|
}
|
|
57
57
|
export declare const registerHostAction: (item: HostActionDef) => () => void;
|
|
58
58
|
export declare const useHostActions: () => HostActionDef[];
|
|
59
|
+
/** Plugin actions sort by order; connect actions default to the end. */
|
|
60
|
+
export declare function actionOrder(action: HostActionDef): number;
|
|
59
61
|
export declare const listHostActions: () => HostActionDef[];
|
|
60
62
|
export declare const registerHostBadge: (item: HostBadgeDef) => () => void;
|
|
61
63
|
export declare const useHostBadges: () => HostBadgeDef[];
|
|
@@ -87,6 +87,16 @@ export declare function permittedRailItems(items: RailItemDef[], permissions: {
|
|
|
87
87
|
* the items the user's permissions hide.
|
|
88
88
|
*/
|
|
89
89
|
export declare function useRailItems(): RailItemDef[];
|
|
90
|
+
/**
|
|
91
|
+
* Items in the user's order. Listed ids come first in that order within the
|
|
92
|
+
* default sequence; unlisted ones keep their default place relative to it.
|
|
93
|
+
*/
|
|
94
|
+
export declare function applyRailOrder(items: RailItemDef[], order: string[] | undefined): RailItemDef[];
|
|
95
|
+
/**
|
|
96
|
+
* The order after moving `id` to sit before `beforeId` (or to the end of its
|
|
97
|
+
* band when null), as a full list of the given items' ids.
|
|
98
|
+
*/
|
|
99
|
+
export declare function moveRailItem(items: RailItemDef[], id: string, beforeId: string | null): string[];
|
|
90
100
|
/** Places outside the rail that still need a title, like the Settings tab. */
|
|
91
101
|
export declare const RAIL_UTILITY_ITEMS: RailItemDef[];
|
|
92
102
|
/** Ids that may be opened in the right dock. */
|