@skyvexsoftware/stratos-sdk 0.20.0 → 0.21.0

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 CHANGED
@@ -94,7 +94,10 @@ The `vite` option accepts any Vite config to merge in (plugins, resolve, css, et
94
94
  ```
95
95
 
96
96
  - `type`: `"airline"` (scoped to a virtual airline) or `"user"` (user-installed)
97
- - `availableSettings`: Optional array of setting definitions (boolean, text, number, list, etc.)
97
+ - `available_settings`: Optional array of setting definitions (boolean, text, number, list, etc.)
98
+ - `min_core_version`: Optional oldest Stratos Core the plugin runs on (e.g. `"2.49.0"`)
99
+
100
+ plugin.json keys are snake_case. Uploads using the old camelCase names (`availableSettings`, `minCoreVersion`, `includeSourceMaps`, `attachesFilesToPirep`) are refused.
98
101
 
99
102
  ## API Reference
100
103
 
@@ -3,11 +3,17 @@
3
3
  * stratos-deploy — Bundle and upload a Stratos plugin.
4
4
  *
5
5
  * Zips dist/ into bundle.zip. If SKYVEX_API_TOKEN is set, uploads to the
6
- * Skyvex API. Otherwise outputs the zip path for manual upload.
6
+ * Skyvex Software API. Otherwise outputs the zip path for manual upload.
7
+ *
8
+ * SKYVEX_WHATS_NEW, when set, is sent as the version's What's new, so the
9
+ * upload goes straight to review (or waits as a draft while the listing is
10
+ * short of its screenshot pairs). Without it, the upload waits as a draft
11
+ * to submit from the Developer centre.
7
12
  *
8
13
  * Usage:
9
14
  * stratos-deploy # from plugin root after pnpm build
10
15
  * pnpm bundle # if package.json has "bundle": "pnpm build && stratos-deploy"
16
+ * SKYVEX_WHATS_NEW="- Fixed the fuel rounding" pnpm bundle
11
17
  */
12
18
  export {};
13
19
  //# sourceMappingURL=deploy.d.ts.map
@@ -3,11 +3,17 @@
3
3
  * stratos-deploy — Bundle and upload a Stratos plugin.
4
4
  *
5
5
  * Zips dist/ into bundle.zip. If SKYVEX_API_TOKEN is set, uploads to the
6
- * Skyvex API. Otherwise outputs the zip path for manual upload.
6
+ * Skyvex Software API. Otherwise outputs the zip path for manual upload.
7
+ *
8
+ * SKYVEX_WHATS_NEW, when set, is sent as the version's What's new, so the
9
+ * upload goes straight to review (or waits as a draft while the listing is
10
+ * short of its screenshot pairs). Without it, the upload waits as a draft
11
+ * to submit from the Developer centre.
7
12
  *
8
13
  * Usage:
9
14
  * stratos-deploy # from plugin root after pnpm build
10
15
  * pnpm bundle # if package.json has "bundle": "pnpm build && stratos-deploy"
16
+ * SKYVEX_WHATS_NEW="- Fixed the fuel rounding" pnpm bundle
11
17
  */
12
18
  import * as fs from "fs";
13
19
  import * as path from "path";
@@ -21,6 +27,7 @@ const releaseMode = releaseModeArg?.split("=")[1] ?? "automatic";
21
27
  if (releaseMode !== "automatic" && releaseMode !== "manual") {
22
28
  fatal("--release-mode must be 'automatic' or 'manual'");
23
29
  }
30
+ const whatsNew = process.env.SKYVEX_WHATS_NEW?.trim() ?? "";
24
31
  // ── Helpers ──────────────────────────────────────────────────────────
25
32
  function fatal(message) {
26
33
  console.error(`\n Error: ${message}\n`);
@@ -62,7 +69,8 @@ function createBundle(cwd) {
62
69
  fs.unlinkSync(bundlePath);
63
70
  }
64
71
  try {
65
- execSync("zip -r ../bundle.zip .", {
72
+ // Source maps stay local: they're for debugging, not for pilots.
73
+ execSync('zip -r ../bundle.zip . -x "*.map"', {
66
74
  cwd: path.join(cwd, "dist"),
67
75
  stdio: "pipe",
68
76
  });
@@ -84,6 +92,10 @@ function upload(pluginId, bundlePath, token) {
84
92
  parts.push(Buffer.from("\r\n"));
85
93
  // Release mode field
86
94
  parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="release_mode"\r\n\r\n${releaseMode}\r\n`));
95
+ // What's new, when given: the upload then goes straight to review
96
+ if (whatsNew) {
97
+ parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="changelog"\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n${whatsNew}\r\n`));
98
+ }
87
99
  parts.push(Buffer.from(`--${boundary}--\r\n`));
88
100
  const body = Buffer.concat(parts);
89
101
  const url = new URL(`${API_BASE}/plugins/${pluginId}/versions`);
@@ -127,7 +139,31 @@ async function main() {
127
139
  console.log(" Uploading to Skyvex...");
128
140
  const result = await upload(id, bundlePath, token);
129
141
  if (result.ok) {
130
- console.log(" Published! Server is signing and publishing to CDN.\n");
142
+ let status = "";
143
+ let message = "";
144
+ try {
145
+ const body = JSON.parse(result.body);
146
+ status = body.status ?? "";
147
+ message = body.message ?? "";
148
+ }
149
+ catch {
150
+ // An older server answers without a status.
151
+ }
152
+ if (status === "draft" && whatsNew) {
153
+ // What's new was sent, so the server says why it's still a draft
154
+ // (the listing is short of its screenshots).
155
+ console.log(` ${message || "Uploaded as a draft."}\n`);
156
+ }
157
+ else if (status === "draft") {
158
+ console.log(" Uploaded as a draft: there's no What's new, so it hasn't gone to review.");
159
+ console.log(" Submit it with What's new in the Developer centre, or set SKYVEX_WHATS_NEW next time.\n");
160
+ }
161
+ else if (status === "pending_review") {
162
+ console.log(" Uploaded and sent to review.\n");
163
+ }
164
+ else {
165
+ console.log(" Published! Server is signing and publishing to CDN.\n");
166
+ }
131
167
  fs.unlinkSync(bundlePath);
132
168
  }
133
169
  else if (result.status === 422) {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `DynamicIcon`: any Lucide icon by name, loaded from Stratos on demand.
3
+ *
4
+ * Import it from `@skyvexsoftware/stratos-sdk/icons`. That subpath is bundled
5
+ * into your plugin (it's small), and it asks the host for the icon at
6
+ * runtime: Stratos core 2.49.0 and later load each icon as its own chunk, only
7
+ * when it's shown, so your plugin carries none of the icon set. On an older
8
+ * core it shows a small generic icon instead.
9
+ *
10
+ * Until the icon loads it renders an empty box of the same size, so nothing
11
+ * moves. Icons you know at build time are still best imported by name from
12
+ * `lucide-react` (bundled and tree-shaken).
13
+ */
14
+ import type { ComponentType, SVGProps } from "react";
15
+ type IconComponent = ComponentType<SVGProps<SVGSVGElement> & {
16
+ size?: number | string;
17
+ strokeWidth?: number | string;
18
+ absoluteStrokeWidth?: boolean;
19
+ }>;
20
+ export type DynamicIconProps = Omit<SVGProps<SVGSVGElement>, "ref"> & {
21
+ /** A Lucide icon name: `plane`, `book-open` (or `BookOpen`). */
22
+ name: string;
23
+ size?: number | string;
24
+ strokeWidth?: number | string;
25
+ absoluteStrokeWidth?: boolean;
26
+ /** Shown for a name Lucide doesn't have. Defaults to the generic icon. */
27
+ fallback?: IconComponent;
28
+ };
29
+ /** A small generic icon: a rounded square with a dot. */
30
+ export declare const GenericIcon: IconComponent;
31
+ export declare function DynamicIcon({ name, size, fallback, className, style, ...props }: DynamicIconProps): import("react").JSX.Element;
32
+ export default DynamicIcon;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `DynamicIcon`: any Lucide icon by name, loaded from Stratos on demand.
4
+ *
5
+ * Import it from `@skyvexsoftware/stratos-sdk/icons`. That subpath is bundled
6
+ * into your plugin (it's small), and it asks the host for the icon at
7
+ * runtime: Stratos core 2.49.0 and later load each icon as its own chunk, only
8
+ * when it's shown, so your plugin carries none of the icon set. On an older
9
+ * core it shows a small generic icon instead.
10
+ *
11
+ * Until the icon loads it renders an empty box of the same size, so nothing
12
+ * moves. Icons you know at build time are still best imported by name from
13
+ * `lucide-react` (bundled and tree-shaken).
14
+ */
15
+ import { useEffect, useState } from "react";
16
+ function hostIcons() {
17
+ const modules = globalThis.__stratos_modules__;
18
+ const icons = modules?.["stratos:icons"];
19
+ return icons && typeof icons.loadIcon === "function" ? icons : null;
20
+ }
21
+ /** A small generic icon: a rounded square with a dot. */
22
+ export const GenericIcon = ({ size = 24, strokeWidth = 2, ...props }) => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [_jsx("rect", { x: "4", y: "4", width: "16", height: "16", rx: "4" }), _jsx("circle", { cx: "12", cy: "12", r: "1.5", fill: "currentColor", stroke: "none" })] }));
23
+ export function DynamicIcon({ name, size = 24, fallback, className, style, ...props }) {
24
+ const host = hostIcons();
25
+ const [icon, setIcon] = useState(() => host?.getLoadedIcon?.(name));
26
+ useEffect(() => {
27
+ let current = true;
28
+ const icons = hostIcons();
29
+ if (!icons) {
30
+ setIcon(null);
31
+ return;
32
+ }
33
+ const ready = icons.getLoadedIcon?.(name);
34
+ if (ready !== undefined) {
35
+ setIcon(ready);
36
+ return;
37
+ }
38
+ setIcon(undefined);
39
+ void icons.loadIcon(name).then((loaded) => {
40
+ if (current)
41
+ setIcon(loaded);
42
+ });
43
+ return () => {
44
+ current = false;
45
+ };
46
+ }, [name]);
47
+ if (icon === undefined) {
48
+ // Loading: hold the space so nothing shifts.
49
+ const box = {
50
+ display: "inline-block",
51
+ width: size,
52
+ height: size,
53
+ flexShrink: 0,
54
+ ...style,
55
+ };
56
+ return _jsx("span", { "aria-hidden": "true", className: className, style: box });
57
+ }
58
+ const Icon = icon ?? fallback ?? GenericIcon;
59
+ return _jsx(Icon, { size: size, className: className, style: style, ...props });
60
+ }
61
+ export default DynamicIcon;
@@ -24,6 +24,16 @@ export type PluginConfigStore = {
24
24
  set<T>(key: string, value: T): Promise<void>;
25
25
  delete(key: string): Promise<void>;
26
26
  getAll(): Promise<Record<string, unknown>>;
27
+ /**
28
+ * Listen for the airline changing this plugin's settings. Fires once the
29
+ * new values are in place (`get` already returns them) with the keys that
30
+ * changed. While a flight is in progress or a crash recovery is pending the
31
+ * change waits and fires when the flight ends, so it never fires mid-flight.
32
+ * Returns an unsubscribe function; listeners are also removed when the
33
+ * plugin stops. Undefined on Stratos cores older than 2.49.0, so call it as
34
+ * `ctx.config.onChange?.(...)`.
35
+ */
36
+ onChange?(listener: (changedKeys: string[]) => void): () => void;
27
37
  };
28
38
  /**
29
39
  * IPC registration helper for background plugins.
@@ -41,10 +41,12 @@ export type PluginManifest = {
41
41
  /** Optional homepage URL for the plugin (e.g. "https://www.example.com/"). Rendered as a clickable link in the plugin info panel. Must be a fully-qualified http(s) URL. */
42
42
  homepage?: string;
43
43
  /** Typed settings this plugin declares. User-scoped settings render in the Settings page; airline-scoped settings are managed by the VA platform. */
44
- availableSettings?: PluginSettingDefinition[];
44
+ available_settings?: PluginSettingDefinition[];
45
45
  /** Whether to include source maps in the published CDN bundle. Defaults to false — source maps are stripped on approval. Set to true if you want end users to have access to source maps for debugging. */
46
- includeSourceMaps?: boolean;
47
- /** Advisory: this plugin attaches files to the PIREP, which changes how flight submissions are encoded for the airline. Surfaced as a warning in the airline's plugin list; the shell does not enforce it. */
48
- attachesFilesToPirep?: boolean;
46
+ include_source_maps?: boolean;
47
+ /** Advisory: this plugin attaches files to the PIREP, which changes how flight submissions are encoded for the airline. Stratos does not read or enforce it yet. */
48
+ attaches_files_to_pirep?: boolean;
49
+ /** The oldest Stratos Core the plugin runs on (semver, e.g. "2.49.0"). An older core doesn't install the plugin (an update keeps the version already there) and doesn't start one already installed. Set it when you use an SDK feature that needs a newer core. */
50
+ min_core_version?: string;
49
51
  };
50
52
  //# sourceMappingURL=manifest.d.ts.map
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @page Settings
3
3
  *
4
- * Typed plugin setting definitions — the controls a plugin declares in its manifest's availableSettings.
4
+ * Typed plugin setting definitions: the controls a plugin declares in its manifest's available_settings.
5
5
  */
6
6
  /** All supported setting control types */
7
7
  export type PluginSettingType = "boolean" | "text" | "longtext" | "number" | "range" | "list" | "radio" | "date" | "json";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @page Settings
3
3
  *
4
- * Typed plugin setting definitions — the controls a plugin declares in its manifest's availableSettings.
4
+ * Typed plugin setting definitions: the controls a plugin declares in its manifest's available_settings.
5
5
  */
6
6
  export {};
@@ -6,9 +6,9 @@ import { buttonVariants } from "./button";
6
6
  const AlertDialog = AlertDialogPrimitive.Root;
7
7
  const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
8
8
  const AlertDialogPortal = AlertDialogPrimitive.Portal;
9
- const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(AlertDialogPrimitive.Overlay, { className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80", className), ...props, ref: ref })));
9
+ const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(AlertDialogPrimitive.Overlay, { className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 ease-out-strong data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 duration-200", className), ...props, ref: ref })));
10
10
  AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
11
- const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Content, { ref: ref, className: cn("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg", className), ...props })] })));
11
+ const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Content, { ref: ref, className: cn("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 ease-out-strong fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg", className), ...props })] })));
12
12
  AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
13
13
  const AlertDialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props }));
14
14
  AlertDialogHeader.displayName = "AlertDialogHeader";
package/dist/ui/button.js CHANGED
@@ -8,7 +8,7 @@ import * as React from "react";
8
8
  import { Slot } from "@radix-ui/react-slot";
9
9
  import { cva } from "class-variance-authority";
10
10
  import { cn } from "../utils/cn";
11
- const buttonVariants = cva("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background cursor-pointer transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", {
11
+ const buttonVariants = cva("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background cursor-pointer focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", {
12
12
  variants: {
13
13
  variant: {
14
14
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
package/dist/ui/dialog.js CHANGED
@@ -7,9 +7,9 @@ const Dialog = DialogPrimitive.Root;
7
7
  const DialogTrigger = DialogPrimitive.Trigger;
8
8
  const DialogPortal = DialogPrimitive.Portal;
9
9
  const DialogClose = DialogPrimitive.Close;
10
- const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/40", className), ...props })));
10
+ const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 ease-out-strong data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/40 duration-200", className), ...props })));
11
11
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
12
- const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg", className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground focus:outline-hidden absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
12
+ const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 bg-background ease-out-strong fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg", className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground focus:outline-hidden absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none", children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
13
13
  DialogContent.displayName = DialogPrimitive.Content.displayName;
14
14
  function DialogHeader({ className, ...props }) {
15
15
  return (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
@@ -5,6 +5,6 @@ import { cn } from "../utils/cn";
5
5
  const Popover = PopoverPrimitive.Root;
6
6
  const PopoverTrigger = PopoverPrimitive.Trigger;
7
7
  const PopoverAnchor = PopoverPrimitive.Anchor;
8
- const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("bg-popover text-popover-foreground border-border data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 rounded-md border p-4 shadow-md outline-none", className), ...props }) })));
8
+ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn("bg-popover text-popover-foreground border-border ease-out-strong data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-md border p-4 shadow-md outline-none duration-200", className), ...props }) })));
9
9
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
10
10
  export { Popover, PopoverTrigger, PopoverAnchor, PopoverContent };
package/dist/ui/select.js CHANGED
@@ -13,7 +13,7 @@ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
13
13
  const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: _jsx(ChevronDown, { className: "h-4 w-4" }) })));
14
14
  SelectScrollDownButton.displayName =
15
15
  SelectPrimitive.ScrollDownButton.displayName;
16
- const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md", position === "popper" &&
16
+ const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, className: cn("bg-popover text-popover-foreground ease-out-strong data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-hidden rounded-md border shadow-md duration-150", position === "popper" &&
17
17
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" &&
18
18
  "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"), children: children }), _jsx(SelectScrollDownButton, {})] }) })));
19
19
  SelectContent.displayName = SelectPrimitive.Content.displayName;
package/dist/ui/switch.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../utils/cn";
4
- const Switch = React.forwardRef(({ checked = false, onCheckedChange, disabled, className, ...props }, ref) => (_jsx("button", { type: "button", role: "switch", "aria-checked": checked, ref: ref, disabled: disabled, onClick: () => onCheckedChange?.(!checked), className: cn("relative inline-flex shrink-0 cursor-pointer rounded-md transition-all duration-150", "h-[18px] w-[32px]", "focus-visible:outline-hidden focus-visible:ring-ring focus-visible:ring-2", checked
4
+ const Switch = React.forwardRef(({ checked = false, onCheckedChange, disabled, className, ...props }, ref) => (_jsx("button", { type: "button", role: "switch", "aria-checked": checked, ref: ref, disabled: disabled, onClick: () => onCheckedChange?.(!checked), className: cn("ease-out-strong relative inline-flex shrink-0 cursor-pointer rounded-md transition-[background-color,border-color] duration-150", "h-[18px] w-[32px]", "focus-visible:outline-hidden focus-visible:ring-ring focus-visible:ring-2", checked
5
5
  ? "border-primary bg-primary border"
6
- : "border-border-interactive bg-muted border", disabled && "cursor-not-allowed opacity-50", className), ...props, children: _jsx("span", { className: cn("pointer-events-none absolute top-[2px] h-[12px] w-[12px] rounded-sm transition-[left] duration-150", checked ? "bg-primary-foreground left-[16px]" : "left-[2px] bg-white") }) })));
6
+ : "border-border-interactive bg-muted border", disabled && "cursor-not-allowed opacity-50", className), ...props, children: _jsx("span", { className: cn(
7
+ // The thumb slides on transform so a rapid toggle retargets
8
+ // mid-flight instead of animating a layout property.
9
+ "ease-out-strong pointer-events-none absolute left-[2px] top-[2px] h-[12px] w-[12px] rounded-sm transition-transform duration-150", checked ? "bg-primary-foreground translate-x-[14px]" : "bg-white") }) })));
7
10
  Switch.displayName = "Switch";
8
11
  export { Switch };
package/dist/ui/tabs.js CHANGED
@@ -5,7 +5,7 @@ import { cn } from "../utils/cn";
5
5
  const Tabs = TabsPrimitive.Root;
6
6
  const TabsList = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.List, { ref: ref, className: cn("bg-muted text-muted-foreground inline-flex h-10 items-center justify-center rounded-md p-1", className), ...props })));
7
7
  TabsList.displayName = TabsPrimitive.List.displayName;
8
- const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, className: cn("ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:outline-hidden inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm", className), ...props })));
8
+ const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Trigger, { ref: ref, className: cn("ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:outline-hidden inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm", className), ...props })));
9
9
  TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
10
10
  const TabsContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, className: cn("ring-offset-background focus-visible:ring-ring focus-visible:outline-hidden mt-2 focus-visible:ring-2 focus-visible:ring-offset-2", className), ...props })));
11
11
  TabsContent.displayName = TabsPrimitive.Content.displayName;
@@ -5,6 +5,6 @@ import { cn } from "../utils/cn";
5
5
  const TooltipProvider = TooltipPrimitive.Provider;
6
6
  const Tooltip = TooltipPrimitive.Root;
7
7
  const TooltipTrigger = TooltipPrimitive.Trigger;
8
- const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md border px-3 py-1.5 text-sm shadow-md", className), ...props })));
8
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 ease-out-strong data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 origin-[var(--radix-tooltip-content-transform-origin)] overflow-hidden rounded-md border px-3 py-1.5 text-sm shadow-md duration-[125ms] data-[state=instant-open]:animate-none", className), ...props })));
9
9
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
10
10
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyvexsoftware/stratos-sdk",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Plugin SDK for Stratos — types, hooks, and UI components",
5
5
  "author": {
6
6
  "name": "Skyvex Software Pty Ltd",
@@ -17,6 +17,7 @@
17
17
  "simconnect"
18
18
  ],
19
19
  "type": "module",
20
+ "sideEffects": false,
20
21
  "main": "dist/index.js",
21
22
  "types": "dist/index.d.ts",
22
23
  "exports": {
@@ -40,6 +41,10 @@
40
41
  "types": "./dist/helpers/index.d.ts",
41
42
  "import": "./dist/helpers/index.js"
42
43
  },
44
+ "./icons": {
45
+ "types": "./dist/dynamic-icon/index.d.ts",
46
+ "import": "./dist/dynamic-icon/index.js"
47
+ },
43
48
  "./vite": {
44
49
  "types": "./dist/vite/plugin-config.d.ts",
45
50
  "import": "./dist/vite/plugin-config.js"
@@ -59,6 +64,9 @@
59
64
  "helpers": [
60
65
  "dist/helpers/index.d.ts"
61
66
  ],
67
+ "icons": [
68
+ "dist/dynamic-icon/index.d.ts"
69
+ ],
62
70
  "vite": [
63
71
  "dist/vite/plugin-config.d.ts"
64
72
  ]
@@ -92,7 +100,6 @@
92
100
  "@radix-ui/react-separator": "^1.1.0",
93
101
  "@radix-ui/react-slider": "^1.3.0",
94
102
  "@radix-ui/react-slot": "^1.2.0",
95
- "@radix-ui/react-switch": "^1.2.0",
96
103
  "@radix-ui/react-tabs": "^1.1.0",
97
104
  "@radix-ui/react-tooltip": "^1.1.0",
98
105
  "@tanstack/react-query": "^5.99.0",
@@ -113,14 +120,13 @@
113
120
  "devDependencies": {
114
121
  "@radix-ui/react-alert-dialog": "^1.1.15",
115
122
  "@radix-ui/react-dialog": "^1.1.15",
116
- "@radix-ui/react-label": "^2.1.8",
123
+ "@radix-ui/react-label": "2.1.7",
117
124
  "@radix-ui/react-popover": "^1.1.15",
118
125
  "@radix-ui/react-radio-group": "^1.3.8",
119
126
  "@radix-ui/react-select": "^2.1.8",
120
- "@radix-ui/react-separator": "^1.1.4",
127
+ "@radix-ui/react-separator": "1.1.7",
121
128
  "@radix-ui/react-slider": "^1.3.6",
122
- "@radix-ui/react-slot": "^1.2.4",
123
- "@radix-ui/react-switch": "^1.2.6",
129
+ "@radix-ui/react-slot": "1.2.3",
124
130
  "@radix-ui/react-tabs": "^1.1.8",
125
131
  "@radix-ui/react-tooltip": "^1.1.12",
126
132
  "@types/react": "^19.1.2",