@skyvexsoftware/stratos-sdk 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,7 @@ export { useVaApi } from "./useVaApi";
11
11
  export { useShellConfig } from "./useShellConfig";
12
12
  export { useShellNavigation } from "./useShellNavigation";
13
13
  export { useShellToast } from "./useShellToast";
14
+ export { useTheme } from "./useTheme";
14
15
  export { useSocket, useSimulatorData, useProtocolUrl, useNotifications, useSystemMetrics, } from "./useSimulatorData";
15
16
  export { SOCKET_EVENTS } from "../shared-types/socket-events";
16
17
  export type { ConnectionState, NotificationPayload, ProtocolUrlPayload, SimulatorDataPayload, SimulatorStatusPayload, SocketEventName, SystemMetricsPayload, } from "../shared-types/socket-events";
@@ -11,6 +11,7 @@ export { useVaApi } from "./useVaApi";
11
11
  export { useShellConfig } from "./useShellConfig";
12
12
  export { useShellNavigation } from "./useShellNavigation";
13
13
  export { useShellToast } from "./useShellToast";
14
+ export { useTheme } from "./useTheme";
14
15
  export { useSocket, useSimulatorData, useProtocolUrl, useNotifications, useSystemMetrics, } from "./useSimulatorData";
15
16
  // Socket.io event catalog — re-exported from shared-types for convenience
16
17
  export { SOCKET_EVENTS } from "../shared-types/socket-events";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The theme Stratos is showing, `"light"` or `"dark"` (with "system" already
3
+ * resolved), and a re-render whenever it changes. Use it for anything the
4
+ * CSS theme variables can't reach, such as a map style or a logo.
5
+ */
6
+ export declare function useTheme(): "light" | "dark";
7
+ //# sourceMappingURL=useTheme.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { useSyncExternalStore } from "react";
2
+ // The shell marks dark mode with the "dark" class on <html>, so watching that
3
+ // one attribute follows every way the theme changes (Settings, "system" mode
4
+ // following the OS, the dev footer).
5
+ function subscribe(onChange) {
6
+ const observer = new MutationObserver(onChange);
7
+ observer.observe(document.documentElement, {
8
+ attributes: true,
9
+ attributeFilter: ["class"],
10
+ });
11
+ return () => observer.disconnect();
12
+ }
13
+ function getSnapshot() {
14
+ return document.documentElement.classList.contains("dark") ? "dark" : "light";
15
+ }
16
+ /**
17
+ * The theme Stratos is showing, `"light"` or `"dark"` (with "system" already
18
+ * resolved), and a re-render whenever it changes. Use it for anything the
19
+ * CSS theme variables can't reach, such as a map style or a logo.
20
+ */
21
+ export function useTheme() {
22
+ return useSyncExternalStore(subscribe, getSnapshot);
23
+ }
package/dist/index.d.ts CHANGED
@@ -33,11 +33,14 @@ export { useVaApi } from "./hooks/useVaApi";
33
33
  export { useShellConfig } from "./hooks/useShellConfig";
34
34
  export { useShellNavigation } from "./hooks/useShellNavigation";
35
35
  export { useShellToast } from "./hooks/useShellToast";
36
+ export { useTheme } from "./hooks/useTheme";
36
37
  export { useSocket, useSimulatorData, useProtocolUrl, useNotifications, useSystemMetrics, } from "./hooks/useSimulatorData";
37
38
  export { SOCKET_EVENTS } from "./shared-types/socket-events";
38
39
  export type { AutoStartConfirmPayload, ConnectionState, LogEntryPayload, NotificationPayload, ProtocolUrlPayload, SimulatorDataPayload, SimulatorStatusPayload, SocketEventName, SoundPayload, SystemMetricsPayload, } from "./shared-types/socket-events";
39
40
  export type { PluginDialogKind, PluginDialogSelectOption, PluginDialogSpec, PluginDialogResult, PluginDialogClosePayload, } from "./shared-types/plugin-dialogs";
40
41
  export { AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, Badge, badgeVariants, Button, buttonVariants, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Combobox, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Input, Label, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, 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";
41
42
  export type { BadgeProps, ButtonProps, ComboboxOption, ComboboxProps, } from "./ui";
43
+ export { StratosMap } from "./ui/stratos-map";
44
+ export type { StratosMapProps } from "./ui/stratos-map";
42
45
  export { cn } from "./utils/cn";
43
46
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -23,11 +23,13 @@ export { useVaApi } from "./hooks/useVaApi";
23
23
  export { useShellConfig } from "./hooks/useShellConfig";
24
24
  export { useShellNavigation } from "./hooks/useShellNavigation";
25
25
  export { useShellToast } from "./hooks/useShellToast";
26
+ export { useTheme } from "./hooks/useTheme";
26
27
  export { useSocket, useSimulatorData, useProtocolUrl, useNotifications, useSystemMetrics, } from "./hooks/useSimulatorData";
27
28
  // ── Socket.io Event Catalog ──────────────────────────────────────────────
28
29
  export { SOCKET_EVENTS } from "./shared-types/socket-events";
29
30
  // ── UI Primitives ──────────────────────────────────────────────────────
30
31
  export { AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, Badge, badgeVariants, Button, buttonVariants, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Combobox, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Input, Label, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, 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";
32
+ export { StratosMap } from "./ui/stratos-map";
31
33
  // ── Icons (internal) ──────────────────────────────────────────────────
32
34
  // Kept at runtime for plugin bundles that still read them; out of the
33
35
  // published types. Plugins import from lucide-react or use DynamicIcon.
@@ -16,6 +16,8 @@ export { Switch } from "./switch";
16
16
  export { Textarea } from "./textarea";
17
17
  export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./select";
18
18
  export { Separator } from "./separator";
19
+ export { StratosMap } from "./stratos-map";
20
+ export type { StratosMapProps } from "./stratos-map";
19
21
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
20
22
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip";
21
23
  //# sourceMappingURL=index.d.ts.map
package/dist/ui/index.js CHANGED
@@ -13,5 +13,6 @@ export { Switch } from "./switch";
13
13
  export { Textarea } from "./textarea";
14
14
  export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./select";
15
15
  export { Separator } from "./separator";
16
+ export { StratosMap } from "./stratos-map";
16
17
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs";
17
18
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip";
@@ -0,0 +1,8 @@
1
+ import type maplibregl from "maplibre-gl";
2
+ /**
3
+ * The Stratos look on top of the base style: English place names (else the
4
+ * local name), no village, hamlet, highway shield or minor road labels, and
5
+ * softer halos.
6
+ */
7
+ export declare function applyStratosLabels(map: maplibregl.Map): void;
8
+ //# sourceMappingURL=stratos-map-labels.d.ts.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The Stratos look on top of the base style: English place names (else the
3
+ * local name), no village, hamlet, highway shield or minor road labels, and
4
+ * softer halos.
5
+ */
6
+ export function applyStratosLabels(map) {
7
+ const layers = map.getStyle()?.layers;
8
+ if (!layers)
9
+ return;
10
+ for (const layer of layers) {
11
+ if (layer.type === "symbol" && layer.layout?.["text-field"] !== undefined) {
12
+ map.setLayoutProperty(layer.id, "text-field", [
13
+ "coalesce",
14
+ ["get", "name:en"],
15
+ ["get", "name"],
16
+ ]);
17
+ }
18
+ const id = layer.id.toLowerCase();
19
+ if (id.includes("place-village") ||
20
+ id.includes("place-hamlet") ||
21
+ id.includes("highway-shield") ||
22
+ id.includes("road_label_minor")) {
23
+ map.setLayoutProperty(layer.id, "visibility", "none");
24
+ }
25
+ if (layer.type === "symbol") {
26
+ try {
27
+ map.setPaintProperty(layer.id, "text-halo-width", 1.5);
28
+ map.setPaintProperty(layer.id, "text-halo-blur", 1);
29
+ }
30
+ catch {
31
+ // Some symbol layers don't take these.
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ import { type Ref } from "react";
2
+ import type { MapProps, MapRef } from "react-map-gl/maplibre";
3
+ export type StratosMapProps = Omit<MapProps, "mapStyle" | "attributionControl"> & {
4
+ /** Your own style in the light theme (URL or object). Defaults to Stratos's. */
5
+ lightStyle?: MapProps["mapStyle"];
6
+ /** Your own style in the dark theme (URL or object). Defaults to Stratos's. */
7
+ darkStyle?: MapProps["mapStyle"];
8
+ ref?: Ref<MapRef>;
9
+ };
10
+ /**
11
+ * react-map-gl's `Map` (every prop and child passes through) in Stratos's
12
+ * map style, light or dark to match the theme, with English labels and the
13
+ * attribution tucked behind its info button. It switches style when the theme changes, and
14
+ * carries `data-stratos-map-drawing` until it has drawn the style showing:
15
+ * the dev footer's Screenshot waits for that, so both shots show the right
16
+ * map.
17
+ */
18
+ export declare function StratosMap({ lightStyle, darkStyle, onLoad, ref, ...props }: StratosMapProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=stratos-map.d.ts.map
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef, useState } from "react";
3
+ import { Map } from "react-map-gl/maplibre";
4
+ import { useTheme } from "../hooks/useTheme";
5
+ import { applyStratosLabels } from "./stratos-map-labels";
6
+ const LIGHT_STYLE = "https://tiles.openfreemap.org/styles/positron";
7
+ const DARK_STYLE = "https://tiles.openfreemap.org/styles/dark";
8
+ // MapLibre draws the attribution open the moment the map is made and only
9
+ // tucks it away later, so keep it hidden until the map has loaded and it's
10
+ // tucked away, then fade it in.
11
+ const STYLES = `
12
+ [data-stratos-map]:not([data-attribution-ready]) .maplibregl-ctrl-attrib {
13
+ opacity: 0;
14
+ pointer-events: none;
15
+ }
16
+ @media (prefers-reduced-motion: no-preference) {
17
+ [data-stratos-map] .maplibregl-ctrl-attrib {
18
+ transition: opacity 200ms ease-out;
19
+ }
20
+ }
21
+ `;
22
+ const ATTRIBUTION = { compact: true };
23
+ const isStratosStyle = (style) => style === LIGHT_STYLE || style === DARK_STYLE;
24
+ /**
25
+ * react-map-gl's `Map` (every prop and child passes through) in Stratos's
26
+ * map style, light or dark to match the theme, with English labels and the
27
+ * attribution tucked behind its info button. It switches style when the theme changes, and
28
+ * carries `data-stratos-map-drawing` until it has drawn the style showing:
29
+ * the dev footer's Screenshot waits for that, so both shots show the right
30
+ * map.
31
+ */
32
+ export function StratosMap({ lightStyle = LIGHT_STYLE, darkStyle = DARK_STYLE, onLoad, ref, ...props }) {
33
+ const mapStyle = useTheme() === "dark" ? darkStyle : lightStyle;
34
+ const [loaded, setLoaded] = useState(false);
35
+ const [drawing, setDrawing] = useState(true);
36
+ // "style" while a style URL loads (a diffed switch reports idle on the old
37
+ // style meanwhile), "draw" until the map is next idle, null when drawn.
38
+ const phase = useRef("draw");
39
+ const shownStyle = useRef(mapStyle);
40
+ useLayoutEffect(() => {
41
+ if (shownStyle.current === mapStyle)
42
+ return;
43
+ shownStyle.current = mapStyle;
44
+ phase.current = typeof mapStyle === "string" ? "style" : "draw";
45
+ setDrawing(true);
46
+ }, [mapStyle]);
47
+ const handleLoad = (event) => {
48
+ const map = event.target;
49
+ // Tucked away behind its info button, not open until the first drag.
50
+ map
51
+ .getContainer()
52
+ .querySelector(".maplibregl-ctrl-attrib.maplibregl-compact-show")
53
+ ?.classList.remove("maplibregl-compact-show");
54
+ setLoaded(true);
55
+ // Stratos's label tweaks, again after each theme switch reloads the style.
56
+ if (isStratosStyle(shownStyle.current))
57
+ applyStratosLabels(map);
58
+ map.on("style.load", () => {
59
+ if (isStratosStyle(shownStyle.current))
60
+ applyStratosLabels(map);
61
+ });
62
+ map.on("styledata", () => {
63
+ if (phase.current === "style")
64
+ phase.current = "draw";
65
+ });
66
+ map.on("idle", () => {
67
+ if (phase.current !== "draw")
68
+ return;
69
+ phase.current = null;
70
+ setDrawing(false);
71
+ });
72
+ // Then the plugin's own onLoad, if it passed one.
73
+ onLoad?.(event);
74
+ };
75
+ return (
76
+ // No box of its own: the map sizes against the parent as it would bare.
77
+ _jsxs("div", { "data-stratos-map": "", "data-attribution-ready": loaded ? "" : undefined, "data-stratos-map-drawing": drawing ? "" : undefined, style: { display: "contents" }, children: [_jsx("style", { href: "stratos-map", precedence: "default", children: STYLES }), _jsx(Map, { ...props, ref: ref, mapStyle: mapStyle,
78
+ // Reload Stratos's style from scratch on a theme switch, so style.load
79
+ // fires and the label tweaks go back on (a diff would undo them).
80
+ styleDiffing: isStratosStyle(mapStyle) ? false : props.styleDiffing, attributionControl: ATTRIBUTION, onLoad: handleLoad })] }));
81
+ }
@@ -153,6 +153,10 @@ function createUIConfig(pluginDir, entry, extraConfig) {
153
153
  },
154
154
  rollupOptions: {
155
155
  external: [...UI_EXTERNALS],
156
+ // One file only. If the direct file:// import fails, or after a hot
157
+ // plugin update, Stratos loads ui/index.js from a blob: URL, where a
158
+ // relative import of another chunk can't resolve.
159
+ output: { inlineDynamicImports: true },
156
160
  },
157
161
  sourcemap: true,
158
162
  minify: isProduction ? "esbuild" : false,
@@ -170,6 +174,7 @@ function createUIConfig(pluginDir, entry, extraConfig) {
170
174
  alias: mergedAlias,
171
175
  },
172
176
  plugins: [
177
+ processEnvDefine(),
173
178
  serveExternals(),
174
179
  hmrBaseRewrite(),
175
180
  stratosDevServer({ pluginDir }),
@@ -180,6 +185,26 @@ function createUIConfig(pluginDir, entry, extraConfig) {
180
185
  ],
181
186
  };
182
187
  }
188
+ /**
189
+ * The renderer has no Node `process`, and library mode leaves
190
+ * `process.env.NODE_ENV` in the bundle, so libraries that read it (MapLibre,
191
+ * deck.gl, React builds) throw "process is not defined" at load. Replace it
192
+ * with the build's mode, and any other `process.env.X` with undefined.
193
+ */
194
+ function processEnvDefine() {
195
+ return {
196
+ name: "stratos-process-env-define",
197
+ config(_config, { mode }) {
198
+ const nodeEnv = mode === "production" ? "production" : "development";
199
+ return {
200
+ define: {
201
+ "process.env.NODE_ENV": JSON.stringify(nodeEnv),
202
+ "process.env": JSON.stringify({ NODE_ENV: nodeEnv }),
203
+ },
204
+ };
205
+ },
206
+ };
207
+ }
183
208
  /**
184
209
  * Shared background build configuration — used both for the production
185
210
  * build (createBackgroundConfig below) and the dev hot-rebuilder in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyvexsoftware/stratos-sdk",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Plugin SDK for Stratos — types, hooks, and UI components",
5
5
  "author": {
6
6
  "name": "Skyvex Software Pty Ltd",
@@ -107,10 +107,20 @@
107
107
  "class-variance-authority": "^0.7.0",
108
108
  "cmdk": "^1.0.0",
109
109
  "lucide-react": ">=0.300.0",
110
+ "maplibre-gl": "^5.0.0",
110
111
  "react": "^19.0.0",
112
+ "react-map-gl": "^8.0.0",
111
113
  "socket.io-client": "^4.0.0",
112
114
  "vite": "^7.0.0"
113
115
  },
116
+ "peerDependenciesMeta": {
117
+ "maplibre-gl": {
118
+ "optional": true
119
+ },
120
+ "react-map-gl": {
121
+ "optional": true
122
+ }
123
+ },
114
124
  "dependencies": {
115
125
  "axios": "^1.17.0",
116
126
  "clsx": "^2.1.1",
@@ -135,7 +145,9 @@
135
145
  "class-variance-authority": "^0.7.1",
136
146
  "cmdk": "^1.1.1",
137
147
  "lucide-react": "^0.577.0",
148
+ "maplibre-gl": "5.19.0",
138
149
  "react-dom": "^19.0.0",
150
+ "react-map-gl": "8.1.1",
139
151
  "socket.io-client": "^4.8.3",
140
152
  "typescript": "^5.8.3",
141
153
  "vite": "^7.3.1"