@skyvexsoftware/stratos-sdk 0.6.0 → 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";
@@ -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,8 +17,8 @@
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";
@@ -55,99 +55,6 @@ const NODE_BUILTINS = [
55
55
  "worker_threads",
56
56
  "zlib",
57
57
  ];
58
- /**
59
- * Rollup plugin that transforms ESM import statements for externalized modules
60
- * into runtime lookups from window.__stratos_modules__.
61
- *
62
- * This allows plugin UI bundles to remain valid ES modules that can be loaded
63
- * via dynamic import() while resolving shared dependencies from the shell's
64
- * global registry.
65
- */
66
- function stratosExternals() {
67
- return {
68
- name: "stratos-externals",
69
- renderChunk(code) {
70
- const s = new MagicString(code);
71
- for (const modId of UI_EXTERNALS) {
72
- const escaped = modId.replace(/[-/\\^$*+?.()|[\]{}@]/g, "\\$&");
73
- const global = `window.__stratos_modules__[${JSON.stringify(modId)}]`;
74
- const patterns = [
75
- // 1. import Default, { named } from 'mod';
76
- [
77
- new RegExp(`import\\s+([\\w$]+)\\s*,\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
78
- (_, def, names) => {
79
- const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
80
- return `const ${def} = ${global}.default || ${global};\nconst {${destructured}} = ${global};`;
81
- },
82
- ],
83
- // 2. import { named } from 'mod';
84
- [
85
- new RegExp(`import\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
86
- (_, names) => {
87
- const destructured = names.replace(/([\w$]+)\s+as\s+([\w$]+)/g, "$1: $2");
88
- return `const {${destructured}} = ${global};`;
89
- },
90
- ],
91
- // 3. import * as ns from 'mod';
92
- [
93
- new RegExp(`import\\s+\\*\\s+as\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
94
- (_, name) => `const ${name} = ${global};`,
95
- ],
96
- // 4. import Default from 'mod';
97
- [
98
- new RegExp(`import\\s+([\\w$]+)\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
99
- (_, def) => `const ${def} = ${global}.default || ${global};`,
100
- ],
101
- // 5. import 'mod';
102
- [new RegExp(`import\\s+['"]${escaped}['"]\\s*;?`, "g"), () => ""],
103
- // 6. export { name } from 'mod';
104
- [
105
- new RegExp(`export\\s*\\{([^}]+)\\}\\s+from\\s+['"]${escaped}['"]\\s*;?`, "g"),
106
- (_, names) => {
107
- const parts = names.split(",").map((n) => n.trim());
108
- const decls = [];
109
- const exportNames = [];
110
- for (const part of parts) {
111
- const asMatch = part.match(/^([\w$]+)\s+as\s+([\w$]+)$/);
112
- if (asMatch) {
113
- decls.push(`const ${asMatch[2]} = ${global}[${JSON.stringify(asMatch[1])}];`);
114
- exportNames.push(asMatch[2]);
115
- }
116
- else {
117
- exportNames.push(part);
118
- }
119
- }
120
- if (decls.length > 0) {
121
- return `${decls.join("\n")}\nconst {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
122
- }
123
- return `const {${exportNames.join(", ")}} = ${global};\nexport {${exportNames.join(", ")}};`;
124
- },
125
- ],
126
- ];
127
- // Collect all matches first, then apply in reverse order to preserve offsets
128
- const matches = [];
129
- for (const [re, replacer] of patterns) {
130
- re.lastIndex = 0;
131
- let match;
132
- while ((match = re.exec(code)) !== null) {
133
- matches.push({
134
- start: match.index,
135
- end: match.index + match[0].length,
136
- replacement: replacer(match[0], ...match.slice(1)),
137
- });
138
- }
139
- }
140
- // Apply in reverse so earlier offsets stay valid
141
- matches
142
- .sort((a, b) => b.start - a.start)
143
- .forEach((m) => s.overwrite(m.start, m.end, m.replacement));
144
- }
145
- if (!s.hasChanged())
146
- return null;
147
- return { code: s.toString(), map: s.generateMap({ hires: true }) };
148
- },
149
- };
150
- }
151
58
  /**
152
59
  * Rollup plugin that copies plugin.json and assets/ to dist/ after the build.
153
60
  * Only runs during the UI build (not background) to avoid duplicate copies.
@@ -204,6 +111,34 @@ export function createPluginConfig(options) {
204
111
  }
205
112
  function createUIConfig(pluginDir, entry, extraConfig) {
206
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 ?? {};
207
142
  return {
208
143
  root: pluginDir,
209
144
  // Skip WebSocket token validation so /@vite/client can connect from
@@ -220,18 +155,9 @@ function createUIConfig(pluginDir, entry, extraConfig) {
220
155
  host: "localhost",
221
156
  },
222
157
  },
223
- resolve: {
224
- // Redirect React (and friends) to shim modules that proxy to
225
- // window.__stratos_modules__. Applies during source transforms AND
226
- // esbuild pre-bundling, so any third-party dep that imports React
227
- // (lucide-react, radix, etc.) resolves to the shell's single instance —
228
- // avoiding the two-React "Cannot read properties of null (reading
229
- // 'useContext')" crash.
230
- alias: reactShimAliases(),
231
- },
232
158
  optimizeDeps: {
233
159
  // Exclude non-React externals from pre-bundling (they come from the
234
- // shell). React subpaths are handled by the alias above — their imports
160
+ // shell). React subpaths are handled by the alias below — their imports
235
161
  // are rewritten to shim files, which esbuild can bundle normally.
236
162
  exclude: UI_EXTERNALS.filter((id) => !id.startsWith("react") && id !== "react-dom"),
237
163
  },
@@ -254,8 +180,13 @@ function createUIConfig(pluginDir, entry, extraConfig) {
254
180
  jsx: "automatic",
255
181
  jsxImportSource: "react",
256
182
  },
257
- // Merge extra config: plugins are concatenated, other keys shallow-merged
258
- ...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
+ },
259
190
  plugins: [
260
191
  serveExternals(),
261
192
  hmrBaseRewrite(),
@@ -271,6 +202,14 @@ function createBackgroundConfig(pluginDir, entry) {
271
202
  const isProduction = process.env.NODE_ENV === "production";
272
203
  return {
273
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
+ },
274
213
  build: {
275
214
  outDir: path.resolve(pluginDir, "dist/background"),
276
215
  emptyOutDir: true,
@@ -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.6.0",
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",