@skyvexsoftware/stratos-sdk 0.5.9 → 0.7.3

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
@@ -172,15 +172,17 @@ Available: Button, Card, Dialog, Input, Label, Select, Badge, Separator, Tabs, T
172
172
 
173
173
  ### Icons
174
174
 
175
- The full [Lucide](https://lucide.dev) icon set is available via `STRATOS_ICONS`:
175
+ Import icons directly from `lucide-react` they're tree-shaken, so only the icons you use are added to your bundle:
176
176
 
177
177
  ```tsx
178
- import { STRATOS_ICONS, STRATOS_ICON_NAMES } from "@skyvexsoftware/stratos-sdk";
179
-
180
- const Icon = STRATOS_ICONS["Helicopter"];
178
+ import { Plane, Map, Settings, Helicopter } from "lucide-react";
181
179
  ```
182
180
 
183
- Or import icons directly from `lucide-react` in your plugin.
181
+ The SDK's Vite config shims `react`, `react-dom`, and their jsx subpaths so every copy of React in your plugin graph (including lucide-react's internals) resolves to the shell's single instance.
182
+
183
+ If you need dynamic lookup (e.g. icons chosen by name from a remote config), `import { icons } from "lucide-react"` gives you the full map — at the cost of bundling the whole set into your plugin. Prefer a curated static map when possible.
184
+
185
+ Earlier SDK versions exposed `STRATOS_ICONS` and `STRATOS_ICON_NAMES` as convenience re-exports of lucide's `icons` object. Those exports are still available but **deprecated** — prefer the patterns above in new code.
184
186
 
185
187
  ### Types
186
188
 
package/dist/icons.d.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  /**
2
- * Re-exports the full lucide-react icon set.
2
+ * Re-exports of lucide-react's icon registry.
3
3
  *
4
- * Both Stratos and SkyVex keep lucide-react on the same version
5
- * so the available icons are always in sync.
4
+ * @deprecated Since SDK 0.7.2. Import from `lucide-react` directly static
5
+ * imports tree-shake, and the SDK's Vite config shims `react` so lucide's
6
+ * internals resolve to the shell's React instance (no bundle duplication,
7
+ * no two-React crashes). For genuinely dynamic name→component lookup, use
8
+ * `import { icons } from "lucide-react"` which costs ~60 KB gzipped in your
9
+ * plugin bundle — prefer a curated static map where you can.
6
10
  *
7
- * - STRATOS_ICONS: Record<string, LucideIcon> PascalCase keys (e.g. "BookOpen")
8
- * - STRATOS_ICON_NAMES: string[] sorted list of all available icon names
11
+ * Retained as a thin re-export so existing plugin bundles that were built
12
+ * against SDK 0.6.x and earlier keep working when core upgrades the SDK.
13
+ * Safe to remove in a future major release once no shipped plugin references
14
+ * these exports.
9
15
  */
10
16
  import { icons } from "lucide-react";
17
+ /** @deprecated Import from `lucide-react` directly. See file docstring. */
11
18
  export declare const STRATOS_ICONS: typeof icons;
19
+ /** @deprecated Import from `lucide-react` directly. See file docstring. */
12
20
  export declare const STRATOS_ICON_NAMES: string[];
13
21
  //# sourceMappingURL=icons.d.ts.map
package/dist/icons.js CHANGED
@@ -1,12 +1,20 @@
1
1
  /**
2
- * Re-exports the full lucide-react icon set.
2
+ * Re-exports of lucide-react's icon registry.
3
3
  *
4
- * Both Stratos and SkyVex keep lucide-react on the same version
5
- * so the available icons are always in sync.
4
+ * @deprecated Since SDK 0.7.2. Import from `lucide-react` directly static
5
+ * imports tree-shake, and the SDK's Vite config shims `react` so lucide's
6
+ * internals resolve to the shell's React instance (no bundle duplication,
7
+ * no two-React crashes). For genuinely dynamic name→component lookup, use
8
+ * `import { icons } from "lucide-react"` which costs ~60 KB gzipped in your
9
+ * plugin bundle — prefer a curated static map where you can.
6
10
  *
7
- * - STRATOS_ICONS: Record<string, LucideIcon> PascalCase keys (e.g. "BookOpen")
8
- * - STRATOS_ICON_NAMES: string[] sorted list of all available icon names
11
+ * Retained as a thin re-export so existing plugin bundles that were built
12
+ * against SDK 0.6.x and earlier keep working when core upgrades the SDK.
13
+ * Safe to remove in a future major release once no shipped plugin references
14
+ * these exports.
9
15
  */
10
16
  import { icons } from "lucide-react";
17
+ /** @deprecated Import from `lucide-react` directly. See file docstring. */
11
18
  export const STRATOS_ICONS = icons;
19
+ /** @deprecated Import from `lucide-react` directly. See file docstring. */
12
20
  export const STRATOS_ICON_NAMES = Object.keys(icons).sort();
package/dist/index.js CHANGED
@@ -22,7 +22,10 @@ export { useSocket, useSimulatorData, useProtocolUrl, useNotifications, useSyste
22
22
  export { SOCKET_EVENTS } from "./shared-types/socket-events";
23
23
  // ── UI Primitives ──────────────────────────────────────────────────────
24
24
  export { AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, Badge, badgeVariants, Button, buttonVariants, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Input, Label, RadioGroup, RadioGroupItem, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Slider, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./ui";
25
- // ── Icons ─────────────────────────────────────────────────────────────
25
+ // ── Icons (deprecated) ────────────────────────────────────────────────
26
+ // Kept as a thin re-export of lucide-react's icons registry so plugin
27
+ // bundles built against earlier SDK versions keep working when core
28
+ // upgrades. New code should import from lucide-react directly.
26
29
  export { STRATOS_ICONS, STRATOS_ICON_NAMES } from "./icons";
27
30
  // ── Utilities ──────────────────────────────────────────────────────────
28
31
  export { cn } from "./utils/cn";
@@ -0,0 +1,6 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const createRoot: any;
4
+ export declare const hydrateRoot: any;
5
+ export declare const version: any;
6
+ //# sourceMappingURL=react-dom-client.d.ts.map
@@ -0,0 +1,12 @@
1
+ // GENERATED by scripts/gen-react-shims.mjs — do not edit by hand.
2
+ // Proxies "react-dom/client" imports to window.__stratos_modules__["react-dom/client"]
3
+ // so every copy of React in the plugin graph (source, lucide-react,
4
+ // radix, etc.) resolves to the shell's single instance.
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const __m = globalThis.__stratos_modules__?.["react-dom/client"];
7
+ if (!__m)
8
+ throw new Error("Stratos: window.__stratos_modules__[\"react-dom/client\"] is not available. The shell must initialize it before plugin modules evaluate.");
9
+ export default __m.default ?? __m;
10
+ export const createRoot = __m.createRoot;
11
+ export const hydrateRoot = __m.hydrateRoot;
12
+ export const version = __m.version;
@@ -0,0 +1,17 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: any;
4
+ export declare const createPortal: any;
5
+ export declare const flushSync: any;
6
+ export declare const preconnect: any;
7
+ export declare const prefetchDNS: any;
8
+ export declare const preinit: any;
9
+ export declare const preinitModule: any;
10
+ export declare const preload: any;
11
+ export declare const preloadModule: any;
12
+ export declare const requestFormReset: any;
13
+ export declare const unstable_batchedUpdates: any;
14
+ export declare const useFormState: any;
15
+ export declare const useFormStatus: any;
16
+ export declare const version: any;
17
+ //# sourceMappingURL=react-dom.d.ts.map
@@ -0,0 +1,23 @@
1
+ // GENERATED by scripts/gen-react-shims.mjs — do not edit by hand.
2
+ // Proxies "react-dom" imports to window.__stratos_modules__["react-dom"]
3
+ // so every copy of React in the plugin graph (source, lucide-react,
4
+ // radix, etc.) resolves to the shell's single instance.
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const __m = globalThis.__stratos_modules__?.["react-dom"];
7
+ if (!__m)
8
+ throw new Error("Stratos: window.__stratos_modules__[\"react-dom\"] is not available. The shell must initialize it before plugin modules evaluate.");
9
+ export default __m.default ?? __m;
10
+ export const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = __m.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
11
+ export const createPortal = __m.createPortal;
12
+ export const flushSync = __m.flushSync;
13
+ export const preconnect = __m.preconnect;
14
+ export const prefetchDNS = __m.prefetchDNS;
15
+ export const preinit = __m.preinit;
16
+ export const preinitModule = __m.preinitModule;
17
+ export const preload = __m.preload;
18
+ export const preloadModule = __m.preloadModule;
19
+ export const requestFormReset = __m.requestFormReset;
20
+ export const unstable_batchedUpdates = __m.unstable_batchedUpdates;
21
+ export const useFormState = __m.useFormState;
22
+ export const useFormStatus = __m.useFormStatus;
23
+ export const version = __m.version;
@@ -0,0 +1,5 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const Fragment: any;
4
+ export declare const jsxDEV: any;
5
+ //# sourceMappingURL=react-jsx-dev-runtime.d.ts.map
@@ -0,0 +1,11 @@
1
+ // GENERATED by scripts/gen-react-shims.mjs — do not edit by hand.
2
+ // Proxies "react/jsx-dev-runtime" imports to window.__stratos_modules__["react/jsx-dev-runtime"]
3
+ // so every copy of React in the plugin graph (source, lucide-react,
4
+ // radix, etc.) resolves to the shell's single instance.
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const __m = globalThis.__stratos_modules__?.["react/jsx-dev-runtime"];
7
+ if (!__m)
8
+ throw new Error("Stratos: window.__stratos_modules__[\"react/jsx-dev-runtime\"] is not available. The shell must initialize it before plugin modules evaluate.");
9
+ export default __m.default ?? __m;
10
+ export const Fragment = __m.Fragment;
11
+ export const jsxDEV = __m.jsxDEV;
@@ -0,0 +1,6 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const Fragment: any;
4
+ export declare const jsx: any;
5
+ export declare const jsxs: any;
6
+ //# sourceMappingURL=react-jsx-runtime.d.ts.map
@@ -0,0 +1,12 @@
1
+ // GENERATED by scripts/gen-react-shims.mjs — do not edit by hand.
2
+ // Proxies "react/jsx-runtime" imports to window.__stratos_modules__["react/jsx-runtime"]
3
+ // so every copy of React in the plugin graph (source, lucide-react,
4
+ // radix, etc.) resolves to the shell's single instance.
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const __m = globalThis.__stratos_modules__?.["react/jsx-runtime"];
7
+ if (!__m)
8
+ throw new Error("Stratos: window.__stratos_modules__[\"react/jsx-runtime\"] is not available. The shell must initialize it before plugin modules evaluate.");
9
+ export default __m.default ?? __m;
10
+ export const Fragment = __m.Fragment;
11
+ export const jsx = __m.jsx;
12
+ export const jsxs = __m.jsxs;
@@ -0,0 +1,44 @@
1
+ declare const _default: any;
2
+ export default _default;
3
+ export declare const __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: any;
4
+ export declare const __COMPILER_RUNTIME: any;
5
+ export declare const act: any;
6
+ export declare const cache: any;
7
+ export declare const captureOwnerStack: any;
8
+ export declare const Children: any;
9
+ export declare const cloneElement: any;
10
+ export declare const Component: any;
11
+ export declare const createContext: any;
12
+ export declare const createElement: any;
13
+ export declare const createRef: any;
14
+ export declare const forwardRef: any;
15
+ export declare const Fragment: any;
16
+ export declare const isValidElement: any;
17
+ export declare const lazy: any;
18
+ export declare const memo: any;
19
+ export declare const Profiler: any;
20
+ export declare const PureComponent: any;
21
+ export declare const startTransition: any;
22
+ export declare const StrictMode: any;
23
+ export declare const Suspense: any;
24
+ export declare const unstable_useCacheRefresh: any;
25
+ export declare const use: any;
26
+ export declare const useActionState: any;
27
+ export declare const useCallback: any;
28
+ export declare const useContext: any;
29
+ export declare const useDebugValue: any;
30
+ export declare const useDeferredValue: any;
31
+ export declare const useEffect: any;
32
+ export declare const useId: any;
33
+ export declare const useImperativeHandle: any;
34
+ export declare const useInsertionEffect: any;
35
+ export declare const useLayoutEffect: any;
36
+ export declare const useMemo: any;
37
+ export declare const useOptimistic: any;
38
+ export declare const useReducer: any;
39
+ export declare const useRef: any;
40
+ export declare const useState: any;
41
+ export declare const useSyncExternalStore: any;
42
+ export declare const useTransition: any;
43
+ export declare const version: any;
44
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1,50 @@
1
+ // GENERATED by scripts/gen-react-shims.mjs — do not edit by hand.
2
+ // Proxies "react" imports to window.__stratos_modules__["react"]
3
+ // so every copy of React in the plugin graph (source, lucide-react,
4
+ // radix, etc.) resolves to the shell's single instance.
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const __m = globalThis.__stratos_modules__?.["react"];
7
+ if (!__m)
8
+ throw new Error("Stratos: window.__stratos_modules__[\"react\"] is not available. The shell must initialize it before plugin modules evaluate.");
9
+ export default __m.default ?? __m;
10
+ export const __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = __m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
11
+ export const __COMPILER_RUNTIME = __m.__COMPILER_RUNTIME;
12
+ export const act = __m.act;
13
+ export const cache = __m.cache;
14
+ export const captureOwnerStack = __m.captureOwnerStack;
15
+ export const Children = __m.Children;
16
+ export const cloneElement = __m.cloneElement;
17
+ export const Component = __m.Component;
18
+ export const createContext = __m.createContext;
19
+ export const createElement = __m.createElement;
20
+ export const createRef = __m.createRef;
21
+ export const forwardRef = __m.forwardRef;
22
+ export const Fragment = __m.Fragment;
23
+ export const isValidElement = __m.isValidElement;
24
+ export const lazy = __m.lazy;
25
+ export const memo = __m.memo;
26
+ export const Profiler = __m.Profiler;
27
+ export const PureComponent = __m.PureComponent;
28
+ export const startTransition = __m.startTransition;
29
+ export const StrictMode = __m.StrictMode;
30
+ export const Suspense = __m.Suspense;
31
+ export const unstable_useCacheRefresh = __m.unstable_useCacheRefresh;
32
+ export const use = __m.use;
33
+ export const useActionState = __m.useActionState;
34
+ export const useCallback = __m.useCallback;
35
+ export const useContext = __m.useContext;
36
+ export const useDebugValue = __m.useDebugValue;
37
+ export const useDeferredValue = __m.useDeferredValue;
38
+ export const useEffect = __m.useEffect;
39
+ export const useId = __m.useId;
40
+ export const useImperativeHandle = __m.useImperativeHandle;
41
+ export const useInsertionEffect = __m.useInsertionEffect;
42
+ export const useLayoutEffect = __m.useLayoutEffect;
43
+ export const useMemo = __m.useMemo;
44
+ export const useOptimistic = __m.useOptimistic;
45
+ export const useReducer = __m.useReducer;
46
+ export const useRef = __m.useRef;
47
+ export const useState = __m.useState;
48
+ export const useSyncExternalStore = __m.useSyncExternalStore;
49
+ export const useTransition = __m.useTransition;
50
+ export const version = __m.version;
@@ -8,7 +8,7 @@ const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
8
8
  const AlertDialogPortal = AlertDialogPrimitive.Portal;
9
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 })));
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 top-[50%] left-[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 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/card.js CHANGED
@@ -4,10 +4,10 @@ function Card({ className, ...props }) {
4
4
  return (_jsx("div", { "data-slot": "card", className: cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className), ...props }));
5
5
  }
6
6
  function CardHeader({ className, ...props }) {
7
- return (_jsx("div", { "data-slot": "card-header", className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className), ...props }));
7
+ return (_jsx("div", { "data-slot": "card-header", className: cn("@container/card-header has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6 grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6", className), ...props }));
8
8
  }
9
9
  function CardTitle({ className, ...props }) {
10
- return (_jsx("div", { "data-slot": "card-title", className: cn("leading-none font-semibold", className), ...props }));
10
+ return (_jsx("div", { "data-slot": "card-title", className: cn("font-semibold leading-none", className), ...props }));
11
11
  }
12
12
  function CardDescription({ className, ...props }) {
13
13
  return (_jsx("div", { "data-slot": "card-description", className: cn("text-muted-foreground text-sm", className), ...props }));
@@ -19,6 +19,6 @@ function CardContent({ className, ...props }) {
19
19
  return (_jsx("div", { "data-slot": "card-content", className: cn("px-6", className), ...props }));
20
20
  }
21
21
  function CardFooter({ className, ...props }) {
22
- return (_jsx("div", { "data-slot": "card-footer", className: cn("flex items-center px-6 [.border-t]:pt-6", className), ...props }));
22
+ return (_jsx("div", { "data-slot": "card-footer", className: cn("[.border-t]:pt-6 flex items-center px-6", className), ...props }));
23
23
  }
24
24
  export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
package/dist/ui/dialog.js CHANGED
@@ -9,7 +9,7 @@ const DialogPortal = DialogPrimitive.Portal;
9
9
  const DialogClose = DialogPrimitive.Close;
10
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/80", 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] bg-background fixed top-[50%] left-[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 absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] 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" })] })] })] })));
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 }));
@@ -17,7 +17,7 @@ function DialogHeader({ className, ...props }) {
17
17
  function DialogFooter({ className, ...props }) {
18
18
  return (_jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props }));
19
19
  }
20
- const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn("text-lg leading-none font-semibold tracking-tight", className), ...props })));
20
+ const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props })));
21
21
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
22
22
  const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn("text-muted-foreground text-sm", className), ...props })));
23
23
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
package/dist/ui/input.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../utils/cn";
3
3
  function Input({ className, type, ...props }) {
4
- return (_jsx("input", { type: type, "data-slot": "input", className: cn("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }));
4
+ return (_jsx("input", { type: type, "data-slot": "input", className: cn("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }));
5
5
  }
6
6
  export { Input };
package/dist/ui/label.js CHANGED
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as LabelPrimitive from "@radix-ui/react-label";
3
3
  import { cn } from "../utils/cn";
4
4
  function Label({ className, ...props }) {
5
- return (_jsx(LabelPrimitive.Root, { "data-slot": "label", className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className), ...props }));
5
+ return (_jsx(LabelPrimitive.Root, { "data-slot": "label", className: cn("flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50", className), ...props }));
6
6
  }
7
7
  export { Label };
@@ -5,6 +5,6 @@ import { Circle } from "lucide-react";
5
5
  import { cn } from "../utils/cn";
6
6
  const RadioGroup = React.forwardRef(({ className, ...props }, ref) => (_jsx(RadioGroupPrimitive.Root, { className: cn("grid gap-2", className), ...props, ref: ref })));
7
7
  RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
8
- const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn("border-primary text-primary focus-visible:ring-ring aspect-square h-4 w-4 rounded-full border shadow-sm focus:outline-hidden focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50", className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx(Circle, { className: "fill-primary h-2.5 w-2.5" }) }) })));
8
+ const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => (_jsx(RadioGroupPrimitive.Item, { ref: ref, className: cn("border-primary text-primary focus-visible:ring-ring focus:outline-hidden aspect-square h-4 w-4 rounded-full border shadow-sm focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50", className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: _jsx(Circle, { className: "fill-primary h-2.5 w-2.5" }) }) })));
9
9
  RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
10
10
  export { RadioGroup, RadioGroupItem };
package/dist/ui/select.js CHANGED
@@ -6,7 +6,7 @@ import { cn } from "../utils/cn";
6
6
  const Select = SelectPrimitive.Root;
7
7
  const SelectGroup = SelectPrimitive.Group;
8
8
  const SelectValue = SelectPrimitive.Value;
9
- const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, className: cn("border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })] })));
9
+ const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Trigger, { ref: ref, className: cn("border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring focus:outline-hidden flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })] })));
10
10
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
11
11
  const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollUpButton, { ref: ref, className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: _jsx(ChevronUp, { className: "h-4 w-4" }) })));
12
12
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
@@ -17,9 +17,9 @@ const SelectContent = React.forwardRef(({ className, children, position = "poppe
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;
20
- const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, className: cn("py-1.5 pr-2 pl-8 text-sm font-semibold", className), ...props })));
20
+ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className), ...props })));
21
21
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
22
- const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, className: cn("focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] })));
22
+ const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(SelectPrimitive.Item, { ref: ref, className: cn("focus:bg-accent focus:text-accent-foreground outline-hidden relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "h-4 w-4" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] })));
23
23
  SelectItem.displayName = SelectPrimitive.Item.displayName;
24
24
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Separator, { ref: ref, className: cn("bg-muted -mx-1 my-1 h-px", className), ...props })));
25
25
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
package/dist/ui/slider.js CHANGED
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import * as SliderPrimitive from "@radix-ui/react-slider";
4
4
  import { cn } from "../utils/cn";
5
- const Slider = React.forwardRef(({ className, ...props }, ref) => (_jsxs(SliderPrimitive.Root, { ref: ref, className: cn("relative flex w-full touch-none items-center select-none", className), ...props, children: [_jsx(SliderPrimitive.Track, { className: "bg-primary/20 relative h-1.5 w-full grow overflow-hidden rounded-full", children: _jsx(SliderPrimitive.Range, { className: "bg-primary absolute h-full" }) }), _jsx(SliderPrimitive.Thumb, { className: "border-primary/50 bg-background focus-visible:ring-ring block h-4 w-4 rounded-full border shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" })] })));
5
+ const Slider = React.forwardRef(({ className, ...props }, ref) => (_jsxs(SliderPrimitive.Root, { ref: ref, className: cn("relative flex w-full touch-none select-none items-center", className), ...props, children: [_jsx(SliderPrimitive.Track, { className: "bg-primary/20 relative h-1.5 w-full grow overflow-hidden rounded-full", children: _jsx(SliderPrimitive.Range, { className: "bg-primary absolute h-full" }) }), _jsx(SliderPrimitive.Thumb, { className: "border-primary/50 bg-background focus-visible:ring-ring focus-visible:outline-hidden block h-4 w-4 rounded-full border shadow-sm transition-colors focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50" })] })));
6
6
  Slider.displayName = SliderPrimitive.Root.displayName;
7
7
  export { Slider };
package/dist/ui/tabs.js CHANGED
@@ -5,8 +5,8 @@ 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 inline-flex items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-all focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden 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 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 })));
9
9
  TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
10
- const TabsContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(TabsPrimitive.Content, { ref: ref, className: cn("ring-offset-background focus-visible:ring-ring mt-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden", className), ...props })));
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;
12
12
  export { Tabs, TabsList, TabsTrigger, TabsContent };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../utils/cn";
3
3
  function Textarea({ className, ...props }) {
4
- return (_jsx("textarea", { "data-slot": "textarea", className: cn("border-input bg-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 flex min-h-[60px] w-full rounded-md border px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }));
4
+ return (_jsx("textarea", { "data-slot": "textarea", className: cn("border-input bg-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 shadow-xs flex min-h-[60px] w-full rounded-md border px-3 py-2 text-base outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }));
5
5
  }
6
6
  export { Textarea };
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Canonical list of modules provided by the Stratos shell at runtime via
3
- * `window.__stratos_modules__`. This is the single source of truth — both
4
- * `vite-plugin.config.ts` (build-time) and `serve-externals.ts` (dev-time)
5
- * import from here.
2
+ * Re-exports `UI_EXTERNALS` from the standalone stratos-externals module.
3
+ * Kept for backwards compatibility with SDK consumers importing from
4
+ * `./externals.js`. The canonical definition lives in `stratos-externals.ts`
5
+ * so that `@sdk/stratos-externals` can be imported by tests without dragging
6
+ * in the rest of the SDK's Vite plugin graph.
6
7
  */
7
- export declare const UI_EXTERNALS: readonly ["react", "react-dom", "react/jsx-runtime", "@tanstack/react-router", "@tanstack/react-query", "@skyvexsoftware/stratos-sdk", "sonner", "socket.io-client", "maplibre-gl", "react-map-gl/maplibre"];
8
- export type UIExternal = (typeof UI_EXTERNALS)[number];
8
+ export { UI_EXTERNALS } from "./stratos-externals.js";
9
+ export type { UIExternal } from "./stratos-externals.js";
9
10
  //# sourceMappingURL=externals.d.ts.map
@@ -1,18 +1,8 @@
1
1
  /**
2
- * Canonical list of modules provided by the Stratos shell at runtime via
3
- * `window.__stratos_modules__`. This is the single source of truth — both
4
- * `vite-plugin.config.ts` (build-time) and `serve-externals.ts` (dev-time)
5
- * import from here.
2
+ * Re-exports `UI_EXTERNALS` from the standalone stratos-externals module.
3
+ * Kept for backwards compatibility with SDK consumers importing from
4
+ * `./externals.js`. The canonical definition lives in `stratos-externals.ts`
5
+ * so that `@sdk/stratos-externals` can be imported by tests without dragging
6
+ * in the rest of the SDK's Vite plugin graph.
6
7
  */
7
- export const UI_EXTERNALS = [
8
- "react",
9
- "react-dom",
10
- "react/jsx-runtime",
11
- "@tanstack/react-router",
12
- "@tanstack/react-query",
13
- "@skyvexsoftware/stratos-sdk",
14
- "sonner",
15
- "socket.io-client",
16
- "maplibre-gl",
17
- "react-map-gl/maplibre",
18
- ];
8
+ export { UI_EXTERNALS } from "./stratos-externals.js";
@@ -15,19 +15,11 @@
15
15
  * background: { entry: 'src/background/index.ts' },
16
16
  * });
17
17
  */
18
- import type { Plugin, UserConfig } from "vite";
18
+ import type { UserConfig } from "vite";
19
19
  import { UI_EXTERNALS } from "./externals.js";
20
+ import { stratosExternals } from "./stratos-externals.js";
20
21
  /** Modules external in background builds (available in Electron main process) */
21
22
  declare const BG_EXTERNALS: string[];
22
- /**
23
- * Rollup plugin that transforms ESM import statements for externalized modules
24
- * into runtime lookups from window.__stratos_modules__.
25
- *
26
- * This allows plugin UI bundles to remain valid ES modules that can be loaded
27
- * via dynamic import() while resolving shared dependencies from the shell's
28
- * global registry.
29
- */
30
- declare function stratosExternals(): Plugin;
31
23
  export type PluginBuildOptions = {
32
24
  /** Plugin root directory. Defaults to process.cwd(). */
33
25
  pluginDir?: string;
@@ -17,12 +17,13 @@
17
17
  */
18
18
  import * as fs from "fs";
19
19
  import * as path from "path";
20
- import MagicString from "magic-string";
21
20
  import { UI_EXTERNALS } from "./externals.js";
21
+ import { stratosExternals } from "./stratos-externals.js";
22
22
  import { serveExternals } from "./serve-externals.js";
23
23
  import { stratosDevServer } from "./stratos-dev-server.js";
24
24
  import { cssInject } from "./css-inject.js";
25
25
  import { hmrBaseRewrite } from "./hmr-base-rewrite.js";
26
+ import { reactShimAliases } from "./react-shim.js";
26
27
  /** Modules external in background builds (available in Electron main process) */
27
28
  const BG_EXTERNALS = ["electron", "socket.io-client"];
28
29
  /** Node.js built-in modules to externalize in background builds */
@@ -54,99 +55,6 @@ const NODE_BUILTINS = [
54
55
  "worker_threads",
55
56
  "zlib",
56
57
  ];
57
- /**
58
- * Rollup plugin that transforms ESM import statements for externalized modules
59
- * into runtime lookups from window.__stratos_modules__.
60
- *
61
- * This allows plugin UI bundles to remain valid ES modules that can be loaded
62
- * via dynamic import() while resolving shared dependencies from the shell's
63
- * global registry.
64
- */
65
- function stratosExternals() {
66
- return {
67
- name: "stratos-externals",
68
- renderChunk(code) {
69
- const s = new MagicString(code);
70
- for (const modId of UI_EXTERNALS) {
71
- const escaped = modId.replace(/[-/\\^$*+?.()|[\]{}@]/g, "\\$&");
72
- const global = `window.__stratos_modules__[${JSON.stringify(modId)}]`;
73
- const patterns = [
74
- // 1. import Default, { named } from 'mod';
75
- [
76
- new RegExp(`import\\s+([\\w$]+)\\s*,\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
77
- (_, def, names) => {
78
- const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
79
- return `const ${def} = ${global}.default || ${global};\nconst {${destructured}} = ${global};`;
80
- },
81
- ],
82
- // 2. import { named } from 'mod';
83
- [
84
- new RegExp(`import\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
85
- (_, names) => {
86
- const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
87
- return `const {${destructured}} = ${global};`;
88
- },
89
- ],
90
- // 3. import * as ns from 'mod';
91
- [
92
- new RegExp(`import\\s+\\*\\s+as\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
93
- (_, name) => `const ${name} = ${global};`,
94
- ],
95
- // 4. import Default from 'mod';
96
- [
97
- new RegExp(`import\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
98
- (_, def) => `const ${def} = ${global}.default || ${global};`,
99
- ],
100
- // 5. import 'mod';
101
- [new RegExp(`import\\s+['"]${escaped}['"]\\s*;?`, "g"), () => ""],
102
- // 6. export { name } from 'mod';
103
- [
104
- new RegExp(`export\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
105
- (_, names) => {
106
- const parts = names.split(",").map((n) => n.trim());
107
- const decls = [];
108
- const exportNames = [];
109
- for (const part of parts) {
110
- const asMatch = part.match(/^([\w$]+)\s+as\s+([\w$]+)$/);
111
- if (asMatch) {
112
- decls.push(`const ${asMatch[2]} = ${global}[${JSON.stringify(asMatch[1])}];`);
113
- exportNames.push(asMatch[2]);
114
- }
115
- else {
116
- exportNames.push(part);
117
- }
118
- }
119
- if (decls.length > 0) {
120
- return `${decls.join("\n")}\nconst {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
121
- }
122
- return `const {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
123
- },
124
- ],
125
- ];
126
- // Collect all matches first, then apply in reverse order to preserve offsets
127
- const matches = [];
128
- for (const [re, replacer] of patterns) {
129
- re.lastIndex = 0;
130
- let match;
131
- while ((match = re.exec(code)) !== null) {
132
- matches.push({
133
- start: match.index,
134
- end: match.index + match[0].length,
135
- replacement: replacer(match[0], ...match.slice(1)),
136
- });
137
- }
138
- }
139
- // Apply in reverse so earlier offsets stay valid
140
- matches
141
- .sort((a, b) => b.start - a.start)
142
- .forEach((m) => s.overwrite(m.start, m.end, m.replacement));
143
- }
144
- if (!s.hasChanged())
145
- return null;
146
- return { code: s.toString(), map: s.generateMap({ hires: true }) };
147
- },
148
- };
149
- }
150
58
  /**
151
59
  * Rollup plugin that copies plugin.json and assets/ to dist/ after the build.
152
60
  * Only runs during the UI build (not background) to avoid duplicate copies.
@@ -203,6 +111,34 @@ export function createPluginConfig(options) {
203
111
  }
204
112
  function createUIConfig(pluginDir, entry, extraConfig) {
205
113
  const isProduction = process.env.NODE_ENV === "production";
114
+ // Merge user-supplied resolve.alias with our React shims. We always use
115
+ // the array form because the shim aliases rely on regex matchers for
116
+ // exact-subpath matching (e.g. `react` vs `react/jsx-runtime`). User
117
+ // aliases can come in either form, so normalise object form into array
118
+ // entries first.
119
+ const userAlias = extraConfig?.resolve?.alias;
120
+ const userAliasArray = Array.isArray(userAlias)
121
+ ? userAlias
122
+ : userAlias
123
+ ? Object.entries(userAlias).map(([find, replacement]) => ({
124
+ find,
125
+ replacement: replacement,
126
+ }))
127
+ : [];
128
+ // Alias resolution order:
129
+ // 1. React shim aliases — must win; swapping them would break the
130
+ // single-React invariant.
131
+ // 2. User-supplied aliases — can override the default `@/` below.
132
+ // 3. Default `@/` → `src/` — the convention every plugin wants, so
133
+ // they don't have to wire it up in each vite.config.ts.
134
+ const mergedAlias = [
135
+ ...reactShimAliases(),
136
+ ...userAliasArray,
137
+ { find: /^@\//, replacement: path.resolve(pluginDir, "src") + "/" },
138
+ ];
139
+ // Strip resolve and plugins out of extraConfig before spreading, since we
140
+ // merge those explicitly below.
141
+ const { resolve: _omitResolve, plugins: _omitPlugins, ...restExtra } = extraConfig ?? {};
206
142
  return {
207
143
  root: pluginDir,
208
144
  // Skip WebSocket token validation so /@vite/client can connect from
@@ -220,11 +156,9 @@ function createUIConfig(pluginDir, entry, extraConfig) {
220
156
  },
221
157
  },
222
158
  optimizeDeps: {
223
- // Exclude non-React externals from pre-bundling (they come from the shell).
224
- // React packages are NOT excluded they need to be pre-bundled so that
225
- // @vitejs/plugin-react can resolve react-refresh for Fast Refresh.
226
- // serveExternals() still rewrites all source-level React imports to
227
- // window.__stratos_modules__, so the pre-bundled React is never loaded.
159
+ // Exclude non-React externals from pre-bundling (they come from the
160
+ // shell). React subpaths are handled by the alias below their imports
161
+ // are rewritten to shim files, which esbuild can bundle normally.
228
162
  exclude: UI_EXTERNALS.filter((id) => !id.startsWith("react") && id !== "react-dom"),
229
163
  },
230
164
  build: {
@@ -246,8 +180,13 @@ function createUIConfig(pluginDir, entry, extraConfig) {
246
180
  jsx: "automatic",
247
181
  jsxImportSource: "react",
248
182
  },
249
- // Merge extra config: plugins are concatenated, other keys shallow-merged
250
- ...extraConfig,
183
+ // Shallow-merge remaining extra config (server, optimizeDeps, build,
184
+ // etc.). resolve and plugins are merged explicitly below.
185
+ ...restExtra,
186
+ resolve: {
187
+ ...extraConfig?.resolve,
188
+ alias: mergedAlias,
189
+ },
251
190
  plugins: [
252
191
  serveExternals(),
253
192
  hmrBaseRewrite(),
@@ -263,6 +202,14 @@ function createBackgroundConfig(pluginDir, entry) {
263
202
  const isProduction = process.env.NODE_ENV === "production";
264
203
  return {
265
204
  root: pluginDir,
205
+ resolve: {
206
+ // Same default `@/` → `src/` alias as the UI config, so background
207
+ // code can share import conventions with the UI without each plugin
208
+ // wiring it up in its vite.config.ts.
209
+ alias: [
210
+ { find: /^@\//, replacement: path.resolve(pluginDir, "src") + "/" },
211
+ ],
212
+ },
266
213
  build: {
267
214
  outDir: path.resolve(pluginDir, "dist/background"),
268
215
  emptyOutDir: true,
@@ -0,0 +1,3 @@
1
+ import type { Alias } from "vite";
2
+ export declare function reactShimAliases(): Alias[];
3
+ //# sourceMappingURL=react-shim.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Resolve React (and friends) to shim modules that proxy to
3
+ * `window.__stratos_modules__`.
4
+ *
5
+ * Without aliasing, any dep that imports React — `lucide-react`, `@radix-ui/*`,
6
+ * anything with React as a peer — gets pre-bundled by Vite with its own copy
7
+ * of React. The plugin's source code has its React imports rewritten to the
8
+ * shell's `window.__stratos_modules__.react` (by serve-externals /
9
+ * stratos-externals), so the plugin ends up with two separate React instances
10
+ * — breaking hooks ("Cannot read properties of null (reading 'useContext')"),
11
+ * context, and refs.
12
+ *
13
+ * The alias redirects React imports (source AND inside pre-bundled deps) to
14
+ * physical shim files shipped with the SDK. Each shim reads from
15
+ * `window.__stratos_modules__[...]` and re-exports the enumerated names
16
+ * statically. Every React import in the entire graph resolves to the SAME
17
+ * instance the shell provides. Third-party UI libraries just work.
18
+ *
19
+ * The trade-off: ES modules require static named exports, so each shim
20
+ * enumerates the exports it covers. Lists target React 19 — update the shim
21
+ * files when upgrading React.
22
+ */
23
+ import { fileURLToPath } from "url";
24
+ import * as path from "path";
25
+ // Resolve shim paths relative to this file. At consume time (compiled SDK in
26
+ // node_modules), this file lives in dist/vite/ and shims live in dist/shims/.
27
+ const here = path.dirname(fileURLToPath(import.meta.url));
28
+ const shimDir = path.resolve(here, "..", "shims");
29
+ export function reactShimAliases() {
30
+ return [
31
+ { find: /^react$/, replacement: path.join(shimDir, "react.js") },
32
+ { find: /^react-dom$/, replacement: path.join(shimDir, "react-dom.js") },
33
+ {
34
+ find: /^react-dom\/client$/,
35
+ replacement: path.join(shimDir, "react-dom-client.js"),
36
+ },
37
+ {
38
+ find: /^react\/jsx-runtime$/,
39
+ replacement: path.join(shimDir, "react-jsx-runtime.js"),
40
+ },
41
+ {
42
+ find: /^react\/jsx-dev-runtime$/,
43
+ replacement: path.join(shimDir, "react-jsx-dev-runtime.js"),
44
+ },
45
+ ];
46
+ }
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from "vite";
2
+ /**
3
+ * Canonical list of modules provided by the Stratos shell at runtime via
4
+ * `window.__stratos_modules__`. Plugin builds mark these as Rollup externals
5
+ * so imports survive bundling, and `stratosExternals` rewrites them into
6
+ * runtime lookups against the shell's global registry at render time.
7
+ */
8
+ export declare const UI_EXTERNALS: readonly ["react", "react-dom", "react/jsx-runtime", "@tanstack/react-router", "@tanstack/react-query", "@skyvexsoftware/stratos-sdk", "sonner", "socket.io-client", "maplibre-gl", "react-map-gl/maplibre"];
9
+ export type UIExternal = (typeof UI_EXTERNALS)[number];
10
+ export declare function stratosExternals(): Plugin;
11
+ //# sourceMappingURL=stratos-externals.d.ts.map
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Rollup plugin that rewrites ESM import statements for shell-provided
3
+ * modules into `window.__stratos_modules__` lookups at render time.
4
+ *
5
+ * Plugin builds mark shared deps (react, tanstack, the SDK, etc.) as Rollup
6
+ * externals so imports survive bundling; this `renderChunk` pass then turns
7
+ * each surviving `import ... from "mod"` into a runtime lookup against the
8
+ * global registry the shell sets up, keeping the plugin's ESM output valid.
9
+ *
10
+ * Lives in its own file (not plugin-config.ts) so tests can import
11
+ * `stratosExternals` + `UI_EXTERNALS` without dragging in the rest of the
12
+ * SDK's Vite plugin graph — which keeps cross-package TypeScript resolution
13
+ * from walking into unrelated files outside consumers' rootDir.
14
+ */
15
+ import MagicString from "magic-string";
16
+ /**
17
+ * Canonical list of modules provided by the Stratos shell at runtime via
18
+ * `window.__stratos_modules__`. Plugin builds mark these as Rollup externals
19
+ * so imports survive bundling, and `stratosExternals` rewrites them into
20
+ * runtime lookups against the shell's global registry at render time.
21
+ */
22
+ export const UI_EXTERNALS = [
23
+ "react",
24
+ "react-dom",
25
+ "react/jsx-runtime",
26
+ "@tanstack/react-router",
27
+ "@tanstack/react-query",
28
+ "@skyvexsoftware/stratos-sdk",
29
+ "sonner",
30
+ "socket.io-client",
31
+ "maplibre-gl",
32
+ "react-map-gl/maplibre",
33
+ ];
34
+ export function stratosExternals() {
35
+ return {
36
+ name: "stratos-externals",
37
+ renderChunk(code) {
38
+ const s = new MagicString(code);
39
+ for (const modId of UI_EXTERNALS) {
40
+ const escaped = modId.replace(/[-/\\^$*+?.()|[\]{}@]/g, "\\$&");
41
+ const global = `window.__stratos_modules__[${JSON.stringify(modId)}]`;
42
+ const patterns = [
43
+ // 1. import Default, { named } from 'mod';
44
+ [
45
+ new RegExp(`import\\s+([\\w$]+)\\s*,\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
46
+ (_, def, names) => {
47
+ const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
48
+ return `const ${def} = ${global}.default || ${global};\nconst {${destructured}} = ${global};`;
49
+ },
50
+ ],
51
+ // 2. import { named } from 'mod';
52
+ [
53
+ new RegExp(`import\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
54
+ (_, names) => {
55
+ const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
56
+ return `const {${destructured}} = ${global};`;
57
+ },
58
+ ],
59
+ // 3. import * as ns from 'mod';
60
+ [
61
+ new RegExp(`import\\s+\\*\\s+as\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
62
+ (_, name) => `const ${name} = ${global};`,
63
+ ],
64
+ // 4. import Default from 'mod';
65
+ [
66
+ new RegExp(`import\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
67
+ (_, def) => `const ${def} = ${global}.default || ${global};`,
68
+ ],
69
+ // 5. import 'mod';
70
+ [new RegExp(`import\\s+['"]${escaped}['"]\\s*;?`, "g"), () => ""],
71
+ // 6. export { name } from 'mod';
72
+ [
73
+ new RegExp(`export\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
74
+ (_, names) => {
75
+ const parts = names.split(",").map((n) => n.trim());
76
+ const decls = [];
77
+ const exportNames = [];
78
+ for (const part of parts) {
79
+ const asMatch = part.match(/^([\w$]+)\s+as\s+([\w$]+)$/);
80
+ if (asMatch) {
81
+ decls.push(`const ${asMatch[2]} = ${global}[${JSON.stringify(asMatch[1])}];`);
82
+ exportNames.push(asMatch[2]);
83
+ }
84
+ else {
85
+ exportNames.push(part);
86
+ }
87
+ }
88
+ if (decls.length > 0) {
89
+ return `${decls.join("\n")}\nconst {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
90
+ }
91
+ return `const {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
92
+ },
93
+ ],
94
+ ];
95
+ // Collect all matches first, then apply in reverse order to preserve offsets
96
+ const matches = [];
97
+ for (const [re, replacer] of patterns) {
98
+ re.lastIndex = 0;
99
+ let match;
100
+ while ((match = re.exec(code)) !== null) {
101
+ matches.push({
102
+ start: match.index,
103
+ end: match.index + match[0].length,
104
+ replacement: replacer(match[0], ...match.slice(1)),
105
+ });
106
+ }
107
+ }
108
+ // Apply in reverse so earlier offsets stay valid
109
+ matches
110
+ .sort((a, b) => b.start - a.start)
111
+ .forEach((m) => s.overwrite(m.start, m.end, m.replacement));
112
+ }
113
+ if (!s.hasChanged())
114
+ return null;
115
+ return { code: s.toString(), map: s.generateMap({ hires: true }) };
116
+ },
117
+ };
118
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyvexsoftware/stratos-sdk",
3
- "version": "0.5.9",
3
+ "version": "0.7.3",
4
4
  "description": "Plugin SDK for Stratos — types, hooks, and UI components",
5
5
  "author": {
6
6
  "name": "Skyvex Software Pty Ltd",
@@ -75,7 +75,8 @@
75
75
  "access": "public"
76
76
  },
77
77
  "scripts": {
78
- "build": "tsc -b",
78
+ "gen:shims": "node scripts/gen-react-shims.mjs",
79
+ "build": "node scripts/gen-react-shims.mjs && tsc -b",
79
80
  "lint": "eslint src --ext .ts,.tsx",
80
81
  "tsc": "tsc --noEmit",
81
82
  "typecheck": "tsc --noEmit",
@@ -109,17 +110,19 @@
109
110
  "devDependencies": {
110
111
  "@radix-ui/react-alert-dialog": "^1.1.15",
111
112
  "@radix-ui/react-dialog": "^1.1.15",
112
- "@radix-ui/react-label": "^2.1.7",
113
+ "@radix-ui/react-label": "^2.1.8",
113
114
  "@radix-ui/react-radio-group": "^1.3.8",
114
115
  "@radix-ui/react-select": "^2.1.8",
115
116
  "@radix-ui/react-separator": "^1.1.4",
116
117
  "@radix-ui/react-slider": "^1.3.6",
117
- "@radix-ui/react-slot": "^1.2.0",
118
+ "@radix-ui/react-slot": "^1.2.4",
118
119
  "@radix-ui/react-switch": "^1.2.6",
119
120
  "@radix-ui/react-tabs": "^1.1.8",
120
121
  "@radix-ui/react-tooltip": "^1.1.12",
121
122
  "@types/react": "^19.1.2",
123
+ "@types/react-dom": "^19.1.2",
122
124
  "@vitejs/plugin-react": "^5.1.4",
125
+ "react-dom": "^19.0.0",
123
126
  "class-variance-authority": "^0.7.1",
124
127
  "lucide-react": "^0.577.0",
125
128
  "socket.io-client": "^4.8.3",