@zendir/ui 0.1.8 → 0.1.10

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.
Files changed (92) hide show
  1. package/dist/index.js +0 -169
  2. package/dist/index.js.map +1 -1
  3. package/dist/react/context/DisplaySettingsContext.js +0 -12
  4. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  5. package/dist/react/core/AstroIcon.js +1 -816
  6. package/dist/react/core/AstroIcon.js.map +1 -1
  7. package/dist/react/core/index.d.ts +0 -1
  8. package/dist/react/index.d.ts +2 -42
  9. package/dist/react/utils/index.js +0 -8
  10. package/dist/react/utils/index.js.map +1 -1
  11. package/dist/react.js +0 -169
  12. package/dist/react.js.map +1 -1
  13. package/package.json +1 -1
  14. package/dist/react/3d/EarthViewer.js +0 -836
  15. package/dist/react/3d/EarthViewer.js.map +0 -1
  16. package/dist/react/3d/SolarSystemViewer.js +0 -372
  17. package/dist/react/3d/SolarSystemViewer.js.map +0 -1
  18. package/dist/react/3d/ZenSpace3D.js +0 -1253
  19. package/dist/react/3d/ZenSpace3D.js.map +0 -1
  20. package/dist/react/3d/ZenSpace3DCesium.js +0 -186
  21. package/dist/react/3d/ZenSpace3DCesium.js.map +0 -1
  22. package/dist/react/3d/ZenSpace3DShaders.js +0 -94
  23. package/dist/react/3d/ZenSpace3DShaders.js.map +0 -1
  24. package/dist/react/3d/ZenSpace3DUtils.js +0 -213
  25. package/dist/react/3d/ZenSpace3DUtils.js.map +0 -1
  26. package/dist/react/3d/threeLoader.js +0 -18
  27. package/dist/react/3d/threeLoader.js.map +0 -1
  28. package/dist/react/cards/AccessCard.js +0 -410
  29. package/dist/react/cards/AccessCard.js.map +0 -1
  30. package/dist/react/cards/OrbitCard.js +0 -372
  31. package/dist/react/cards/OrbitCard.js.map +0 -1
  32. package/dist/react/cards/SpacecraftCard.js +0 -941
  33. package/dist/react/cards/SpacecraftCard.js.map +0 -1
  34. package/dist/react/cards/TelemetryCard.js +0 -742
  35. package/dist/react/cards/TelemetryCard.js.map +0 -1
  36. package/dist/react/cards/TelemetryStreamCard.js +0 -309
  37. package/dist/react/cards/TelemetryStreamCard.js.map +0 -1
  38. package/dist/react/charts/GroundTrackMap.js +0 -1123
  39. package/dist/react/charts/GroundTrackMap.js.map +0 -1
  40. package/dist/react/charts/GroundTrackMapLeaflet.js +0 -571
  41. package/dist/react/charts/GroundTrackMapLeaflet.js.map +0 -1
  42. package/dist/react/charts/groundTrackMapLeafletTiles.js +0 -11
  43. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +0 -1
  44. package/dist/react/charts/groundTrackMapLeafletUtils.js +0 -109
  45. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +0 -1
  46. package/dist/react/charts/unified/AstroChart.js +0 -1405
  47. package/dist/react/charts/unified/AstroChart.js.map +0 -1
  48. package/dist/react/charts/unified/PowerOverviewChart.js +0 -488
  49. package/dist/react/charts/unified/PowerOverviewChart.js.map +0 -1
  50. package/dist/react/charts/unified/domain.js +0 -3168
  51. package/dist/react/charts/unified/domain.js.map +0 -1
  52. package/dist/react/charts/unified/generators.js +0 -518
  53. package/dist/react/charts/unified/generators.js.map +0 -1
  54. package/dist/react/charts/unified/presets.js +0 -999
  55. package/dist/react/charts/unified/presets.js.map +0 -1
  56. package/dist/react/charts/unified/sync.js +0 -219
  57. package/dist/react/charts/unified/sync.js.map +0 -1
  58. package/dist/react/charts/unified/theme.js +0 -562
  59. package/dist/react/charts/unified/theme.js.map +0 -1
  60. package/dist/react/charts/unified/useChartStream.js +0 -226
  61. package/dist/react/charts/unified/useChartStream.js.map +0 -1
  62. package/dist/react/chatgpt/AppCard.js +0 -306
  63. package/dist/react/chatgpt/AppCard.js.map +0 -1
  64. package/dist/react/chatgpt/index.js +0 -166
  65. package/dist/react/chatgpt/index.js.map +0 -1
  66. package/dist/react/hooks/useSpacecraftPosition.js +0 -89
  67. package/dist/react/hooks/useSpacecraftPosition.js.map +0 -1
  68. package/dist/react/hooks/useTelemetry.js +0 -73
  69. package/dist/react/hooks/useTelemetry.js.map +0 -1
  70. package/dist/react/hooks/useZendirSession.js +0 -148
  71. package/dist/react/hooks/useZendirSession.js.map +0 -1
  72. package/dist/react/visualizations/EclipseTimerCard.js +0 -250
  73. package/dist/react/visualizations/EclipseTimerCard.js.map +0 -1
  74. package/dist/react/visualizations/LinkBudgetCard.js +0 -444
  75. package/dist/react/visualizations/LinkBudgetCard.js.map +0 -1
  76. package/dist/react/visualizations/NavBallCard.js +0 -243
  77. package/dist/react/visualizations/NavBallCard.js.map +0 -1
  78. package/dist/react/visualizations/PropulsionCard.js +0 -298
  79. package/dist/react/visualizations/PropulsionCard.js.map +0 -1
  80. package/dist/react/visualizations/SensorFootprintCard.js +0 -326
  81. package/dist/react/visualizations/SensorFootprintCard.js.map +0 -1
  82. package/dist/react/visualizations/ThermalHeatmapCard.js +0 -372
  83. package/dist/react/visualizations/ThermalHeatmapCard.js.map +0 -1
  84. package/dist/shaders/atmosphere.frag.js +0 -5
  85. package/dist/shaders/atmosphere.frag.js.map +0 -1
  86. package/dist/shaders/atmosphere.vert.js +0 -5
  87. package/dist/shaders/atmosphere.vert.js.map +0 -1
  88. package/dist/shaders/stars.frag.js +0 -5
  89. package/dist/shaders/stars.frag.js.map +0 -1
  90. package/dist/shaders/stars.vert.js +0 -5
  91. package/dist/shaders/stars.vert.js.map +0 -1
  92. package/dist/style.css +0 -143
@@ -1,166 +0,0 @@
1
- import { useState, useCallback, useEffect, useSyncExternalStore } from "react";
2
- const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
3
- function useOpenAiGlobal(key) {
4
- const getSnapshot = useCallback(() => {
5
- var _a;
6
- return (_a = window.openai) == null ? void 0 : _a[key];
7
- }, [key]);
8
- const subscribe = useCallback((onChange) => {
9
- const handleSetGlobal = (event) => {
10
- var _a, _b;
11
- const customEvent = event;
12
- if (((_b = (_a = customEvent.detail) == null ? void 0 : _a.globals) == null ? void 0 : _b[key]) !== void 0) {
13
- onChange();
14
- }
15
- };
16
- window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal, { passive: true });
17
- return () => window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
18
- }, [key]);
19
- return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
20
- }
21
- function useWidgetState(defaultState) {
22
- const widgetStateFromWindow = useOpenAiGlobal("widgetState");
23
- const [widgetState, _setWidgetState] = useState(() => {
24
- if (widgetStateFromWindow != null) {
25
- return widgetStateFromWindow;
26
- }
27
- return typeof defaultState === "function" ? defaultState() : defaultState;
28
- });
29
- useEffect(() => {
30
- if (widgetStateFromWindow != null) {
31
- _setWidgetState(widgetStateFromWindow);
32
- }
33
- }, [widgetStateFromWindow]);
34
- const setWidgetState = useCallback((state) => {
35
- _setWidgetState((prevState) => {
36
- var _a, _b;
37
- const newState = typeof state === "function" ? state(prevState) : state;
38
- (_b = (_a = window.openai) == null ? void 0 : _a.setWidgetState) == null ? void 0 : _b.call(_a, newState);
39
- return newState;
40
- });
41
- }, []);
42
- return [widgetState, setWidgetState];
43
- }
44
- function useToolOutput() {
45
- return useOpenAiGlobal("toolOutput");
46
- }
47
- function useToolInput() {
48
- return useOpenAiGlobal("toolInput");
49
- }
50
- function useChatGPTTheme() {
51
- const theme = useOpenAiGlobal("theme") ?? "dark";
52
- const isDark = theme === "dark";
53
- return {
54
- theme,
55
- isDark,
56
- colors: isDark ? {
57
- background: "#09090b",
58
- surface: "#18181b",
59
- surfaceHover: "#27272a",
60
- border: "#3f3f46",
61
- text: "#fafafa",
62
- textMuted: "#a1a1aa",
63
- accent: "#22d3ee"
64
- } : {
65
- background: "#ffffff",
66
- surface: "#f4f4f5",
67
- surfaceHover: "#e4e4e7",
68
- border: "#d4d4d8",
69
- text: "#18181b",
70
- textMuted: "#636370",
71
- // WCAG AA: 5.90:1 on #fff, 5.39:1 on #f4f4f5, 4.66:1 on #e4e4e7 (was #71717a → 4.49:1 FAIL on surface)
72
- accent: "#0ea5e9"
73
- }
74
- };
75
- }
76
- function useLocale() {
77
- return useOpenAiGlobal("locale") ?? "en-US";
78
- }
79
- function useDisplayMode() {
80
- const mode = useOpenAiGlobal("displayMode") ?? "inline";
81
- const maxHeight = useOpenAiGlobal("maxHeight");
82
- const requestMode = useCallback(async (newMode) => {
83
- var _a, _b;
84
- await ((_b = (_a = window.openai) == null ? void 0 : _a.requestDisplayMode) == null ? void 0 : _b.call(_a, { mode: newMode }));
85
- }, []);
86
- const close = useCallback(() => {
87
- var _a, _b;
88
- (_b = (_a = window.openai) == null ? void 0 : _a.requestClose) == null ? void 0 : _b.call(_a);
89
- }, []);
90
- return { mode, maxHeight, requestMode, close };
91
- }
92
- function useCallTool() {
93
- const [isLoading, setIsLoading] = useState(false);
94
- const [error, setError] = useState(null);
95
- const callTool = useCallback(async (name, args) => {
96
- var _a;
97
- if (!((_a = window.openai) == null ? void 0 : _a.callTool)) {
98
- throw new Error("window.openai.callTool not available");
99
- }
100
- setIsLoading(true);
101
- setError(null);
102
- try {
103
- const result = await window.openai.callTool(name, args);
104
- return result;
105
- } catch (err) {
106
- const error2 = err instanceof Error ? err : new Error(String(err));
107
- setError(error2);
108
- throw error2;
109
- } finally {
110
- setIsLoading(false);
111
- }
112
- }, []);
113
- return { callTool, isLoading, error };
114
- }
115
- function useSendMessage() {
116
- return useCallback(async (prompt) => {
117
- var _a, _b;
118
- await ((_b = (_a = window.openai) == null ? void 0 : _a.sendFollowUpMessage) == null ? void 0 : _b.call(_a, { prompt }));
119
- }, []);
120
- }
121
- function useIntrinsicHeight(_ref) {
122
- }
123
- function useMaxHeight() {
124
- const [maxHeight, setMaxHeight] = useState(
125
- () => {
126
- var _a;
127
- return (_a = window.openai) == null ? void 0 : _a.maxHeight;
128
- }
129
- );
130
- useEffect(() => {
131
- const handler = (event) => {
132
- var _a;
133
- if (((_a = event.detail) == null ? void 0 : _a.globals) && "maxHeight" in event.detail.globals) {
134
- setMaxHeight(event.detail.globals.maxHeight);
135
- }
136
- };
137
- window.addEventListener("openai:set_globals", handler);
138
- return () => window.removeEventListener("openai:set_globals", handler);
139
- }, []);
140
- return maxHeight;
141
- }
142
- function useOpenExternal() {
143
- return useCallback((href) => {
144
- var _a, _b;
145
- (_b = (_a = window.openai) == null ? void 0 : _a.openExternal) == null ? void 0 : _b.call(_a, { href });
146
- }, []);
147
- }
148
- function isInChatGPT() {
149
- return typeof window !== "undefined" && window.openai !== void 0;
150
- }
151
- export {
152
- isInChatGPT,
153
- useCallTool,
154
- useChatGPTTheme,
155
- useDisplayMode,
156
- useIntrinsicHeight,
157
- useLocale,
158
- useMaxHeight,
159
- useOpenAiGlobal,
160
- useOpenExternal,
161
- useSendMessage,
162
- useToolInput,
163
- useToolOutput,
164
- useWidgetState
165
- };
166
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/react/chatgpt/index.ts"],"sourcesContent":["/**\n * ChatGPT Apps SDK Integration\n * \n * Hooks and utilities for integrating Zendir UI components with ChatGPT's\n * window.openai API. Based on: https://developers.openai.com/apps-sdk/build/chatgpt-ui/\n * \n * Key capabilities:\n * - window.openai.toolInput / toolOutput - Tool data from MCP server\n * - window.openai.widgetState / setWidgetState - Persistent UI state\n * - window.openai.callTool - Invoke other MCP tools from widget\n * - window.openai.sendFollowUpMessage - Insert user messages\n * - window.openai.theme - Light/dark theme detection\n * - window.openai.locale - Localization\n * - window.openai.requestDisplayMode - Fullscreen/PiP modes\n * - window.openai.notifyIntrinsicHeight - Dynamic height reporting\n */\n\nimport { useState, useEffect, useCallback, useSyncExternalStore } from 'react';\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\nexport interface OpenAiGlobals {\n /** Arguments supplied when the tool was invoked */\n toolInput?: Record<string, unknown>;\n /** Your structuredContent - the model reads this */\n toolOutput?: Record<string, unknown>;\n /** Metadata payload - only widget sees it */\n toolResponseMetadata?: Record<string, unknown>;\n /** Snapshot of UI state persisted between renders */\n widgetState?: Record<string, unknown>;\n /** Current theme */\n theme?: 'light' | 'dark';\n /** Current locale (e.g., 'en-US') */\n locale?: string;\n /** Display mode */\n displayMode?: 'inline' | 'pip' | 'fullscreen';\n /** Maximum allowed height */\n maxHeight?: number;\n /** View type */\n view?: 'conversation' | 'modal';\n /** User agent info */\n userAgent?: string;\n /** Safe area insets */\n safeArea?: { top: number; bottom: number; left: number; right: number };\n \n // API Methods\n setWidgetState?: (state: Record<string, unknown>) => void;\n callTool?: (name: string, args: Record<string, unknown>) => Promise<unknown>;\n sendFollowUpMessage?: (options: { prompt: string }) => Promise<void>;\n uploadFile?: (file: File) => Promise<{ fileId: string }>;\n getFileDownloadUrl?: (options: { fileId: string }) => Promise<{ downloadUrl: string }>;\n requestDisplayMode?: (options: { mode: 'inline' | 'pip' | 'fullscreen' }) => Promise<void>;\n requestModal?: (options: unknown) => Promise<void>;\n notifyIntrinsicHeight?: (height: number) => void;\n openExternal?: (options: { href: string }) => void;\n requestClose?: () => void;\n}\n\ndeclare global {\n interface Window {\n openai?: OpenAiGlobals;\n }\n}\n\n// ============================================================================\n// Core Hook: useOpenAiGlobal\n// ============================================================================\n\nconst SET_GLOBALS_EVENT_TYPE = 'openai:set_globals';\n\ninterface SetGlobalsEvent extends CustomEvent {\n detail: { globals: Partial<OpenAiGlobals> };\n}\n\n/**\n * Subscribe to a specific window.openai global value\n */\nexport function useOpenAiGlobal<K extends keyof OpenAiGlobals>(key: K): OpenAiGlobals[K] {\n const getSnapshot = useCallback(() => {\n return window.openai?.[key];\n }, [key]);\n\n const subscribe = useCallback((onChange: () => void) => {\n const handleSetGlobal = (event: Event) => {\n const customEvent = event as SetGlobalsEvent;\n if (customEvent.detail?.globals?.[key] !== undefined) {\n onChange();\n }\n };\n\n window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal, { passive: true });\n return () => window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);\n }, [key]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n// ============================================================================\n// Widget State Hook\n// ============================================================================\n\ntype SetStateAction<T> = T | ((prevState: T) => T);\n\n/**\n * Manage widget state that persists across renders via window.openai.setWidgetState\n */\nexport function useWidgetState<T extends Record<string, unknown>>(\n defaultState: T | (() => T)\n): readonly [T, (state: SetStateAction<T>) => void] {\n const widgetStateFromWindow = useOpenAiGlobal('widgetState') as T | undefined;\n\n const [widgetState, _setWidgetState] = useState<T>(() => {\n if (widgetStateFromWindow != null) {\n return widgetStateFromWindow;\n }\n return typeof defaultState === 'function' ? (defaultState as () => T)() : defaultState;\n });\n\n useEffect(() => {\n if (widgetStateFromWindow != null) {\n _setWidgetState(widgetStateFromWindow);\n }\n }, [widgetStateFromWindow]);\n\n const setWidgetState = useCallback((state: SetStateAction<T>) => {\n _setWidgetState((prevState: T) => {\n const newState = typeof state === 'function' ? (state as (p: T) => T)(prevState) : state;\n window.openai?.setWidgetState?.(newState);\n return newState;\n });\n }, []);\n\n return [widgetState, setWidgetState] as const;\n}\n\n// ============================================================================\n// Tool Output Hook\n// ============================================================================\n\n/**\n * Read the tool output (structuredContent) from the MCP server response\n */\nexport function useToolOutput<T = Record<string, unknown>>(): T | undefined {\n return useOpenAiGlobal('toolOutput') as T | undefined;\n}\n\n/**\n * Read the tool input (arguments) from the tool invocation\n */\nexport function useToolInput<T = Record<string, unknown>>(): T | undefined {\n return useOpenAiGlobal('toolInput') as T | undefined;\n}\n\n// ============================================================================\n// Theme Hook\n// ============================================================================\n\nexport type ThemeMode = 'light' | 'dark';\n\n/**\n * Get current ChatGPT theme and provide CSS-friendly values\n * Named useChatGPTTheme to avoid conflict with the core useTheme from ThemeProvider\n */\nexport function useChatGPTTheme(): {\n theme: ThemeMode;\n isDark: boolean;\n colors: {\n background: string;\n surface: string;\n surfaceHover: string;\n border: string;\n text: string;\n textMuted: string;\n accent: string;\n };\n} {\n const theme = useOpenAiGlobal('theme') ?? 'dark';\n const isDark = theme === 'dark';\n\n return {\n theme,\n isDark,\n colors: isDark ? {\n background: '#09090b',\n surface: '#18181b',\n surfaceHover: '#27272a',\n border: '#3f3f46',\n text: '#fafafa',\n textMuted: '#a1a1aa',\n accent: '#22d3ee',\n } : {\n background: '#ffffff',\n surface: '#f4f4f5',\n surfaceHover: '#e4e4e7',\n border: '#d4d4d8',\n text: '#18181b',\n textMuted: '#636370', // WCAG AA: 5.90:1 on #fff, 5.39:1 on #f4f4f5, 4.66:1 on #e4e4e7 (was #71717a → 4.49:1 FAIL on surface)\n accent: '#0ea5e9',\n },\n };\n}\n\n// ============================================================================\n// Locale Hook\n// ============================================================================\n\n/**\n * Get current locale for internationalization\n */\nexport function useLocale(): string {\n return useOpenAiGlobal('locale') ?? 'en-US';\n}\n\n// ============================================================================\n// Display Mode Hook\n// ============================================================================\n\n/**\n * Manage widget display mode (inline, PiP, fullscreen)\n */\nexport function useDisplayMode(): {\n mode: 'inline' | 'pip' | 'fullscreen';\n maxHeight?: number;\n requestMode: (mode: 'inline' | 'pip' | 'fullscreen') => Promise<void>;\n close: () => void;\n} {\n const mode = useOpenAiGlobal('displayMode') ?? 'inline';\n const maxHeight = useOpenAiGlobal('maxHeight');\n\n const requestMode = useCallback(async (newMode: 'inline' | 'pip' | 'fullscreen') => {\n await window.openai?.requestDisplayMode?.({ mode: newMode });\n }, []);\n\n const close = useCallback(() => {\n window.openai?.requestClose?.();\n }, []);\n\n return { mode, maxHeight, requestMode, close };\n}\n\n// ============================================================================\n// Tool Calling Hook\n// ============================================================================\n\n/**\n * Call another MCP tool from within a widget\n */\nexport function useCallTool(): {\n callTool: <T = unknown>(name: string, args: Record<string, unknown>) => Promise<T>;\n isLoading: boolean;\n error: Error | null;\n} {\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const callTool = useCallback(async <T = unknown>(\n name: string,\n args: Record<string, unknown>\n ): Promise<T> => {\n if (!window.openai?.callTool) {\n throw new Error('window.openai.callTool not available');\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await window.openai.callTool(name, args);\n return result as T;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n return { callTool, isLoading, error };\n}\n\n// ============================================================================\n// Follow-up Message Hook\n// ============================================================================\n\n/**\n * Send a follow-up message as if the user asked it\n */\nexport function useSendMessage(): (prompt: string) => Promise<void> {\n return useCallback(async (prompt: string) => {\n await window.openai?.sendFollowUpMessage?.({ prompt });\n }, []);\n}\n\n// ============================================================================\n// Dynamic Height Hook\n// ============================================================================\n\n/**\n * DEPRECATED: Do not use notifyIntrinsicHeight!\n * \n * According to OpenAI Apps SDK examples (via DeepWiki), the correct pattern is:\n * - The HOST provides maxHeight via window.openai.maxHeight\n * - Widgets READ this and constrain themselves\n * - Widgets NEVER report height back to the host\n * \n * Calling notifyIntrinsicHeight causes infinite resize loops in MCPJam sandbox\n * because the sandbox responds by resizing the container, which triggers another\n * resize event.\n * \n * Use useMaxHeight() instead to get the host's height constraint.\n * \n * @deprecated Use useMaxHeight() instead\n */\nexport function useIntrinsicHeight(_ref: { current: HTMLElement | null }): void {\n // NO-OP: Do not call notifyIntrinsicHeight - it causes infinite loops\n // The host provides maxHeight, widgets should not report height back\n}\n\n/**\n * Get the maximum height available for the widget from the host environment.\n * This is the CORRECT pattern per OpenAI Apps SDK examples.\n * \n * The host (ChatGPT/MCPJam) provides maxHeight, and widgets should constrain\n * themselves to this height. Widgets should NOT report their height back.\n * \n * @returns The maximum height in pixels, or undefined if not available\n */\nexport function useMaxHeight(): number | undefined {\n const [maxHeight, setMaxHeight] = useState<number | undefined>(\n () => window.openai?.maxHeight\n );\n \n useEffect(() => {\n const handler = (event: CustomEvent) => {\n if (event.detail?.globals && 'maxHeight' in event.detail.globals) {\n setMaxHeight(event.detail.globals.maxHeight);\n }\n };\n \n window.addEventListener('openai:set_globals', handler as EventListener);\n return () => window.removeEventListener('openai:set_globals', handler as EventListener);\n }, []);\n \n return maxHeight;\n}\n\n// ============================================================================\n// External Link Hook\n// ============================================================================\n\n/**\n * Open vetted external links in the user's browser\n */\nexport function useOpenExternal(): (href: string) => void {\n return useCallback((href: string) => {\n window.openai?.openExternal?.({ href });\n }, []);\n}\n\n// ============================================================================\n// Utility: Check if running in ChatGPT\n// ============================================================================\n\nexport function isInChatGPT(): boolean {\n return typeof window !== 'undefined' && window.openai !== undefined;\n}\n\n// ============================================================================\n// Re-exports (types are already exported above)\n// ============================================================================\n\n"],"names":["error"],"mappings":"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"}
@@ -1,89 +0,0 @@
1
- import { useState, useRef, useCallback, useEffect } from "react";
2
- const DEFAULT_MAX_HISTORY = 500;
3
- function useSpacecraftPosition(options) {
4
- const {
5
- client,
6
- interval = 1e3,
7
- enabled = true,
8
- trackHistory = true,
9
- maxHistory = DEFAULT_MAX_HISTORY
10
- } = options;
11
- const [spacecraft, setSpacecraft] = useState([]);
12
- const [groundTracks, setGroundTracks] = useState(/* @__PURE__ */ new Map());
13
- const [isLoading, setIsLoading] = useState(false);
14
- const [error, setError] = useState(null);
15
- const [lastUpdate, setLastUpdate] = useState(null);
16
- const intervalRef = useRef(null);
17
- const startTimeRef = useRef(Date.now());
18
- const fetchPositions = useCallback(async () => {
19
- setIsLoading(true);
20
- setError(null);
21
- try {
22
- const positions = await client.fetchSpacecraftPositions();
23
- setSpacecraft(positions);
24
- setLastUpdate(Date.now());
25
- if (trackHistory) {
26
- const elapsed = (Date.now() - startTimeRef.current) / 1e3;
27
- setGroundTracks((prev) => {
28
- const newTracks = new Map(prev);
29
- for (const sc of positions) {
30
- if (!sc.id) continue;
31
- const point = {
32
- lat: sc.latitude,
33
- lon: sc.longitude,
34
- t: elapsed
35
- };
36
- const existing = newTracks.get(sc.id) || [];
37
- const updated = [...existing, point];
38
- if (updated.length > maxHistory) {
39
- newTracks.set(sc.id, updated.slice(-maxHistory));
40
- } else {
41
- newTracks.set(sc.id, updated);
42
- }
43
- }
44
- return newTracks;
45
- });
46
- }
47
- } catch (err) {
48
- const message = err instanceof Error ? err.message : "Failed to fetch positions";
49
- setError(message);
50
- } finally {
51
- setIsLoading(false);
52
- }
53
- }, [client, trackHistory, maxHistory]);
54
- useEffect(() => {
55
- if (!enabled) {
56
- if (intervalRef.current) {
57
- clearInterval(intervalRef.current);
58
- intervalRef.current = null;
59
- }
60
- return;
61
- }
62
- startTimeRef.current = Date.now();
63
- fetchPositions();
64
- intervalRef.current = setInterval(fetchPositions, interval);
65
- return () => {
66
- if (intervalRef.current) {
67
- clearInterval(intervalRef.current);
68
- intervalRef.current = null;
69
- }
70
- };
71
- }, [enabled, interval, fetchPositions]);
72
- const getSpacecraft = useCallback(
73
- (id) => spacecraft.find((sc) => sc.id === id),
74
- [spacecraft]
75
- );
76
- return {
77
- spacecraft,
78
- groundTracks,
79
- isLoading,
80
- error,
81
- lastUpdate,
82
- getSpacecraft,
83
- refresh: fetchPositions
84
- };
85
- }
86
- export {
87
- useSpacecraftPosition
88
- };
89
- //# sourceMappingURL=useSpacecraftPosition.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useSpacecraftPosition.js","sources":["../../../src/react/hooks/useSpacecraftPosition.ts"],"sourcesContent":["/**\n * @zendir/ui - useSpacecraftPosition Hook\n * \n * React hook for fetching and tracking spacecraft positions.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk client instance.\n * The client must be passed as a prop (allows using any compatible client).\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { SpacecraftPosition } from '../types';\n\n// Re-export the canonical type for consumers\nexport type { SpacecraftPosition };\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Compact ground track point for real-time orbit visualization\n * Uses abbreviated property names for performance in streaming contexts\n * Note: For full ground track data, use GroundTrackPoint from '../types'\n */\nexport interface CompactGroundTrackPoint {\n /** Latitude in degrees */\n lat: number;\n /** Longitude in degrees */\n lon: number;\n /** Timestamp (epoch ms or ISO string) */\n t: number;\n}\n\n/** Client interface (compatible with @zendir/sdk ZendirClient) */\nexport interface PositionClientInterface {\n fetchSpacecraftPositions: () => Promise<SpacecraftPosition[]>;\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseSpacecraftPositionOptions {\n /** Zendir client instance (or any compatible client) */\n client: PositionClientInterface;\n /** Polling interval in ms (default: 1000) */\n interval?: number;\n /** Enable polling */\n enabled?: boolean;\n /** Track ground track history */\n trackHistory?: boolean;\n /** Maximum history points */\n maxHistory?: number;\n}\n\nexport interface UseSpacecraftPositionResult {\n /** All spacecraft positions */\n spacecraft: SpacecraftPosition[];\n /** Ground track history per spacecraft */\n groundTracks: Map<string, CompactGroundTrackPoint[]>;\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Last update timestamp */\n lastUpdate: number | null;\n /** Get specific spacecraft */\n getSpacecraft: (id: string) => SpacecraftPosition | undefined;\n /** Manually refresh positions */\n refresh: () => Promise<void>;\n}\n\nconst DEFAULT_MAX_HISTORY = 500;\n\nexport function useSpacecraftPosition(\n options: UseSpacecraftPositionOptions\n): UseSpacecraftPositionResult {\n const {\n client,\n interval = 1000,\n enabled = true,\n trackHistory = true,\n maxHistory = DEFAULT_MAX_HISTORY,\n } = options;\n\n const [spacecraft, setSpacecraft] = useState<SpacecraftPosition[]>([]);\n const [groundTracks, setGroundTracks] = useState<Map<string, CompactGroundTrackPoint[]>>(new Map());\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [lastUpdate, setLastUpdate] = useState<number | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const startTimeRef = useRef<number>(Date.now());\n\n const fetchPositions = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const positions = await client.fetchSpacecraftPositions();\n setSpacecraft(positions);\n setLastUpdate(Date.now());\n\n // Update ground tracks\n if (trackHistory) {\n const elapsed = (Date.now() - startTimeRef.current) / 1000;\n\n setGroundTracks(prev => {\n const newTracks = new Map(prev);\n\n for (const sc of positions) {\n // Skip if no id\n if (!sc.id) continue;\n \n const point: CompactGroundTrackPoint = {\n lat: sc.latitude,\n lon: sc.longitude,\n t: elapsed,\n };\n\n const existing = newTracks.get(sc.id) || [];\n const updated = [...existing, point];\n\n if (updated.length > maxHistory) {\n newTracks.set(sc.id, updated.slice(-maxHistory));\n } else {\n newTracks.set(sc.id, updated);\n }\n }\n\n return newTracks;\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch positions';\n setError(message);\n } finally {\n setIsLoading(false);\n }\n }, [client, trackHistory, maxHistory]);\n\n // Set up polling\n useEffect(() => {\n if (!enabled) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n return;\n }\n\n startTimeRef.current = Date.now();\n\n // Initial fetch\n fetchPositions();\n\n // Set up polling\n intervalRef.current = setInterval(fetchPositions, interval);\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [enabled, interval, fetchPositions]);\n\n const getSpacecraft = useCallback(\n (id: string) => spacecraft.find(sc => sc.id === id),\n [spacecraft]\n );\n\n return {\n spacecraft,\n groundTracks,\n isLoading,\n error,\n lastUpdate,\n getSpacecraft,\n refresh: fetchPositions,\n };\n}\n\nexport default useSpacecraftPosition;\n\n\n\n\n"],"names":[],"mappings":";AAwEA,MAAM,sBAAsB;AAErB,SAAS,sBACd,SAC6B;AAC7B,QAAM;AAAA,IACJ;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV,eAAe;AAAA,IACf,aAAa;AAAA,EAAA,IACX;AAEJ,QAAM,CAAC,YAAY,aAAa,IAAI,SAA+B,CAAA,CAAE;AACrE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAiD,oBAAI,KAAK;AAClG,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,cAAc,OAA8C,IAAI;AACtE,QAAM,eAAe,OAAe,KAAK,IAAA,CAAK;AAE9C,QAAM,iBAAiB,YAAY,YAAY;AAC7C,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,QAAI;AACF,YAAM,YAAY,MAAM,OAAO,yBAAA;AAC/B,oBAAc,SAAS;AACvB,oBAAc,KAAK,KAAK;AAGxB,UAAI,cAAc;AAChB,cAAM,WAAW,KAAK,IAAA,IAAQ,aAAa,WAAW;AAEtD,wBAAgB,CAAA,SAAQ;AACtB,gBAAM,YAAY,IAAI,IAAI,IAAI;AAE9B,qBAAW,MAAM,WAAW;AAE1B,gBAAI,CAAC,GAAG,GAAI;AAEZ,kBAAM,QAAiC;AAAA,cACrC,KAAK,GAAG;AAAA,cACR,KAAK,GAAG;AAAA,cACR,GAAG;AAAA,YAAA;AAGL,kBAAM,WAAW,UAAU,IAAI,GAAG,EAAE,KAAK,CAAA;AACzC,kBAAM,UAAU,CAAC,GAAG,UAAU,KAAK;AAEnC,gBAAI,QAAQ,SAAS,YAAY;AAC/B,wBAAU,IAAI,GAAG,IAAI,QAAQ,MAAM,CAAC,UAAU,CAAC;AAAA,YACjD,OAAO;AACL,wBAAU,IAAI,GAAG,IAAI,OAAO;AAAA,YAC9B;AAAA,UACF;AAEA,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAS,OAAO;AAAA,IAClB,UAAA;AACE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,UAAU,CAAC;AAGrC,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAEA,iBAAa,UAAU,KAAK,IAAA;AAG5B,mBAAA;AAGA,gBAAY,UAAU,YAAY,gBAAgB,QAAQ;AAE1D,WAAO,MAAM;AACX,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,UAAU,cAAc,CAAC;AAEtC,QAAM,gBAAgB;AAAA,IACpB,CAAC,OAAe,WAAW,KAAK,CAAA,OAAM,GAAG,OAAO,EAAE;AAAA,IAClD,CAAC,UAAU;AAAA,EAAA;AAGb,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -1,73 +0,0 @@
1
- import { useState, useRef, useCallback, useEffect } from "react";
2
- const MAX_HISTORY = 100;
3
- function useTelemetry(options) {
4
- const { client, spacecraftId, interval = 2e3, enabled = true } = options;
5
- const [telemetry, setTelemetry] = useState(null);
6
- const [attitude, setAttitude] = useState(null);
7
- const [history, setHistory] = useState([]);
8
- const [isLoading, setIsLoading] = useState(false);
9
- const [error, setError] = useState(null);
10
- const [lastUpdate, setLastUpdate] = useState(null);
11
- const intervalRef = useRef(null);
12
- const fetchTelemetry = useCallback(async () => {
13
- if (!spacecraftId) return;
14
- setIsLoading(true);
15
- setError(null);
16
- try {
17
- const [telemetryData, attitudeData] = await Promise.all([
18
- client.getTelemetry(spacecraftId),
19
- client.getAttitude(spacecraftId)
20
- ]);
21
- setTelemetry(telemetryData);
22
- setAttitude(attitudeData);
23
- setLastUpdate(Date.now());
24
- setHistory((prev) => {
25
- const newHistory = [...prev, telemetryData];
26
- if (newHistory.length > MAX_HISTORY) {
27
- return newHistory.slice(-MAX_HISTORY);
28
- }
29
- return newHistory;
30
- });
31
- } catch (err) {
32
- const message = err instanceof Error ? err.message : "Failed to fetch telemetry";
33
- setError(message);
34
- } finally {
35
- setIsLoading(false);
36
- }
37
- }, [client, spacecraftId]);
38
- useEffect(() => {
39
- if (!enabled || !spacecraftId) {
40
- if (intervalRef.current) {
41
- clearInterval(intervalRef.current);
42
- intervalRef.current = null;
43
- }
44
- return;
45
- }
46
- fetchTelemetry();
47
- intervalRef.current = setInterval(fetchTelemetry, interval);
48
- return () => {
49
- if (intervalRef.current) {
50
- clearInterval(intervalRef.current);
51
- intervalRef.current = null;
52
- }
53
- };
54
- }, [enabled, spacecraftId, interval, fetchTelemetry]);
55
- useEffect(() => {
56
- setHistory([]);
57
- setTelemetry(null);
58
- setAttitude(null);
59
- }, [spacecraftId]);
60
- return {
61
- telemetry,
62
- attitude,
63
- history,
64
- isLoading,
65
- error,
66
- lastUpdate,
67
- refresh: fetchTelemetry
68
- };
69
- }
70
- export {
71
- useTelemetry
72
- };
73
- //# sourceMappingURL=useTelemetry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTelemetry.js","sources":["../../../src/react/hooks/useTelemetry.ts"],"sourcesContent":["/**\n * @zendir/ui - useTelemetry Hook\n * \n * React hook for fetching and updating spacecraft telemetry data.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk client instance.\n * The client must be passed as a prop (allows using any compatible client).\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { TelemetryData } from '../types';\n\n// ============================================================================\n// Types (defined locally to avoid hard dependency on @zendir/sdk)\n// ============================================================================\n\n/** Attitude data from spacecraft */\nexport interface AttitudeData {\n quaternion?: { x: number; y: number; z: number; w: number };\n euler?: { roll: number; pitch: number; yaw: number };\n angularVelocity?: { x: number; y: number; z: number };\n timestamp?: string;\n}\n\n/** Client interface (compatible with @zendir/sdk ZendirClient) */\nexport interface TelemetryClientInterface {\n getTelemetry: (spacecraftId: string) => Promise<unknown>;\n getAttitude: (spacecraftId: string) => Promise<AttitudeData>;\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseTelemetryOptions {\n /** Zendir client instance (or any compatible client) */\n client: TelemetryClientInterface;\n /** Spacecraft ID to fetch telemetry for */\n spacecraftId: string;\n /** Polling interval in ms (default: 2000) */\n interval?: number;\n /** Enable polling */\n enabled?: boolean;\n}\n\nexport interface UseTelemetryResult {\n /** Latest telemetry data */\n telemetry: TelemetryData | null;\n /** Latest attitude data */\n attitude: AttitudeData | null;\n /** Telemetry history */\n history: TelemetryData[];\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Last update timestamp */\n lastUpdate: number | null;\n /** Manually refresh telemetry */\n refresh: () => Promise<void>;\n}\n\nconst MAX_HISTORY = 100;\n\nexport function useTelemetry(options: UseTelemetryOptions): UseTelemetryResult {\n const { client, spacecraftId, interval = 2000, enabled = true } = options;\n\n const [telemetry, setTelemetry] = useState<TelemetryData | null>(null);\n const [attitude, setAttitude] = useState<AttitudeData | null>(null);\n const [history, setHistory] = useState<TelemetryData[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [lastUpdate, setLastUpdate] = useState<number | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const fetchTelemetry = useCallback(async () => {\n if (!spacecraftId) return;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const [telemetryData, attitudeData] = await Promise.all([\n client.getTelemetry(spacecraftId),\n client.getAttitude(spacecraftId),\n ]);\n\n setTelemetry(telemetryData as unknown as TelemetryData);\n setAttitude(attitudeData);\n setLastUpdate(Date.now());\n\n // Add to history\n setHistory(prev => {\n const newHistory = [...prev, telemetryData as unknown as TelemetryData];\n if (newHistory.length > MAX_HISTORY) {\n return newHistory.slice(-MAX_HISTORY);\n }\n return newHistory;\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to fetch telemetry';\n setError(message);\n } finally {\n setIsLoading(false);\n }\n }, [client, spacecraftId]);\n\n // Set up polling\n useEffect(() => {\n if (!enabled || !spacecraftId) {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n return;\n }\n\n // Initial fetch\n fetchTelemetry();\n\n // Set up polling\n intervalRef.current = setInterval(fetchTelemetry, interval);\n\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [enabled, spacecraftId, interval, fetchTelemetry]);\n\n // Clear history when spacecraft changes\n useEffect(() => {\n setHistory([]);\n setTelemetry(null);\n setAttitude(null);\n }, [spacecraftId]);\n\n return {\n telemetry,\n attitude,\n history,\n isLoading,\n error,\n lastUpdate,\n refresh: fetchTelemetry,\n };\n}\n\nexport default useTelemetry;\n\n\n\n\n"],"names":[],"mappings":";AA8DA,MAAM,cAAc;AAEb,SAAS,aAAa,SAAkD;AAC7E,QAAM,EAAE,QAAQ,cAAc,WAAW,KAAM,UAAU,SAAS;AAElE,QAAM,CAAC,WAAW,YAAY,IAAI,SAA+B,IAAI;AACrE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA8B,IAAI;AAClE,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,CAAA,CAAE;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,cAAc,OAA8C,IAAI;AAEtE,QAAM,iBAAiB,YAAY,YAAY;AAC7C,QAAI,CAAC,aAAc;AAEnB,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,QAAI;AACF,YAAM,CAAC,eAAe,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,QACtD,OAAO,aAAa,YAAY;AAAA,QAChC,OAAO,YAAY,YAAY;AAAA,MAAA,CAChC;AAED,mBAAa,aAAyC;AACtD,kBAAY,YAAY;AACxB,oBAAc,KAAK,KAAK;AAGxB,iBAAW,CAAA,SAAQ;AACjB,cAAM,aAAa,CAAC,GAAG,MAAM,aAAyC;AACtE,YAAI,WAAW,SAAS,aAAa;AACnC,iBAAO,WAAW,MAAM,CAAC,WAAW;AAAA,QACtC;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAS,OAAO;AAAA,IAClB,UAAA;AACE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,CAAC;AAGzB,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,cAAc;AAC7B,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAGA,mBAAA;AAGA,gBAAY,UAAU,YAAY,gBAAgB,QAAQ;AAE1D,WAAO,MAAM;AACX,UAAI,YAAY,SAAS;AACvB,sBAAc,YAAY,OAAO;AACjC,oBAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,cAAc,UAAU,cAAc,CAAC;AAGpD,YAAU,MAAM;AACd,eAAW,CAAA,CAAE;AACb,iBAAa,IAAI;AACjB,gBAAY,IAAI;AAAA,EAClB,GAAG,CAAC,YAAY,CAAC;AAEjB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -1,148 +0,0 @@
1
- import { useState, useRef, useEffect, useCallback } from "react";
2
- let loadState = "pending";
3
- let sdkModule = null;
4
- const subscribers = /* @__PURE__ */ new Set();
5
- function subscribe(callback) {
6
- subscribers.add(callback);
7
- return () => subscribers.delete(callback);
8
- }
9
- function notifySubscribers() {
10
- subscribers.forEach((cb) => cb());
11
- }
12
- async function loadZendirSDK() {
13
- if (loadState === "loaded") {
14
- return sdkModule;
15
- }
16
- if (loadState === "failed") {
17
- return null;
18
- }
19
- if (loadState === "loading") {
20
- return new Promise((resolve) => {
21
- const unsub = subscribe(() => {
22
- unsub();
23
- resolve(sdkModule);
24
- });
25
- });
26
- }
27
- loadState = "loading";
28
- try {
29
- const mod = await import(
30
- /* @vite-ignore */
31
- "@zendir/sdk"
32
- );
33
- sdkModule = { getZendirClient: mod.getZendirClient };
34
- loadState = "loaded";
35
- notifySubscribers();
36
- return sdkModule;
37
- } catch (err) {
38
- loadState = "failed";
39
- notifySubscribers();
40
- return null;
41
- }
42
- }
43
- function useZendirSession(options = {}) {
44
- const { apiKey, baseUrl, autoConnect = false, debug = false } = options;
45
- const [sdkLoaded, setSdkLoaded] = useState(loadState === "loaded");
46
- const [sdkFailed, setSdkFailed] = useState(loadState === "failed");
47
- const clientRef = useRef(null);
48
- const [session, setSession] = useState(null);
49
- const [simulation, setSimulation] = useState(null);
50
- const [isLoading, setIsLoading] = useState(false);
51
- const [error, setError] = useState(null);
52
- useEffect(() => {
53
- let mounted = true;
54
- const unsubscribe = subscribe(() => {
55
- if (mounted) {
56
- setSdkLoaded(loadState === "loaded");
57
- setSdkFailed(loadState === "failed");
58
- }
59
- });
60
- if (loadState === "pending") {
61
- loadZendirSDK().then((mod) => {
62
- if (mounted && mod) {
63
- clientRef.current = mod.getZendirClient({ apiKey, baseUrl, debug });
64
- }
65
- });
66
- } else if (loadState === "loaded" && sdkModule) {
67
- clientRef.current = sdkModule.getZendirClient({ apiKey, baseUrl, debug });
68
- }
69
- return () => {
70
- mounted = false;
71
- unsubscribe();
72
- };
73
- }, [apiKey, baseUrl, debug]);
74
- useEffect(() => {
75
- if (sdkFailed) {
76
- setError("@zendir/sdk is not installed. Install with: npm install @zendir/sdk");
77
- }
78
- }, [sdkFailed]);
79
- const connect = useCallback(async () => {
80
- if (!clientRef.current) {
81
- setError("@zendir/sdk is not available");
82
- return;
83
- }
84
- setIsLoading(true);
85
- setError(null);
86
- try {
87
- const sessionInfo = await clientRef.current.createSession();
88
- setSession(sessionInfo);
89
- } catch (err) {
90
- const message = err instanceof Error ? err.message : "Failed to connect";
91
- setError(message);
92
- throw err;
93
- } finally {
94
- setIsLoading(false);
95
- }
96
- }, []);
97
- const createSimulation = useCallback(async (params) => {
98
- if (!clientRef.current) {
99
- setError("@zendir/sdk is not available");
100
- return;
101
- }
102
- setIsLoading(true);
103
- setError(null);
104
- try {
105
- const simInfo = await clientRef.current.createSimulation({
106
- name: params.name,
107
- spacecraft: params.spacecraft
108
- });
109
- setSimulation(simInfo);
110
- } catch (err) {
111
- const message = err instanceof Error ? err.message : "Failed to create simulation";
112
- setError(message);
113
- throw err;
114
- } finally {
115
- setIsLoading(false);
116
- }
117
- }, []);
118
- const disconnect = useCallback(() => {
119
- if (clientRef.current) {
120
- clientRef.current.clearSession();
121
- }
122
- setSession(null);
123
- setSimulation(null);
124
- setError(null);
125
- }, []);
126
- useEffect(() => {
127
- if (autoConnect && apiKey && sdkLoaded && clientRef.current) {
128
- connect().catch(() => {
129
- });
130
- }
131
- }, [autoConnect, apiKey, sdkLoaded, connect]);
132
- return {
133
- session,
134
- simulation,
135
- isLoading,
136
- error,
137
- isConnected: !!session && session.status === "active",
138
- isSdkAvailable: sdkLoaded && !sdkFailed,
139
- client: clientRef.current,
140
- connect,
141
- createSimulation,
142
- disconnect
143
- };
144
- }
145
- export {
146
- useZendirSession
147
- };
148
- //# sourceMappingURL=useZendirSession.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useZendirSession.js","sources":["../../../src/react/hooks/useZendirSession.ts"],"sourcesContent":["/**\n * @zendir/ui - useZendirSession Hook\n * \n * React hook for managing Zendir API session lifecycle.\n * \n * OPTIONAL DEPENDENCY: Requires @zendir/sdk to be installed.\n * If not installed, the hook returns an error state.\n * \n * @example\n * ```tsx\n * import { useZendirSession } from '@zendir/ui/react';\n * \n * function App() {\n * const { session, connect, isLoading, error } = useZendirSession({\n * apiKey: 'your-api-key',\n * autoConnect: true,\n * });\n * \n * if (error) return <div>Error: {error}</div>;\n * if (isLoading) return <div>Connecting...</div>;\n * if (!session) return <button onClick={connect}>Connect</button>;\n * \n * return <div>Connected: {session.id}</div>;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\n\n// ============================================================================\n// Types (defined locally to avoid hard dependency on @zendir/sdk)\n// ============================================================================\n\n/** Session info from Zendir API */\nexport interface SessionInfo {\n id: string;\n status: 'active' | 'expired' | 'error';\n createdAt?: string;\n expiresAt?: string;\n}\n\n/** Simulation info from Zendir API */\nexport interface SimulationInfo {\n id: string;\n name?: string;\n status: 'running' | 'paused' | 'stopped';\n spacecraftCount?: number;\n}\n\n/** Zendir client interface (matches @zendir/sdk) */\nexport interface ZendirClientInterface {\n createSession: () => Promise<SessionInfo>;\n createSimulation: (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => Promise<SimulationInfo>;\n clearSession: () => void;\n}\n\n// ============================================================================\n// Dynamic SDK Loading\n// ============================================================================\n\ntype LoadState = 'pending' | 'loading' | 'loaded' | 'failed';\nlet loadState: LoadState = 'pending';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet sdkModule: { getZendirClient: (options?: any) => any } | null = null;\nconst subscribers = new Set<() => void>();\n\nfunction subscribe(callback: () => void) {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n}\n\nfunction notifySubscribers() {\n subscribers.forEach(cb => cb());\n}\n\n/**\n * Dynamically load @zendir/sdk\n * Returns null if not available\n */\nasync function loadZendirSDK(): Promise<typeof sdkModule> {\n if (loadState === 'loaded') {\n return sdkModule;\n }\n \n if (loadState === 'failed') {\n return null;\n }\n \n if (loadState === 'loading') {\n // Wait for the existing load to complete\n return new Promise(resolve => {\n const unsub = subscribe(() => {\n unsub();\n resolve(sdkModule);\n });\n });\n }\n \n loadState = 'loading';\n \n try {\n // @vite-ignore: optional peer — do not resolve at build time when SDK is not installed\n const mod = await import(/* @vite-ignore */ '@zendir/sdk');\n sdkModule = { getZendirClient: mod.getZendirClient };\n loadState = 'loaded';\n notifySubscribers();\n return sdkModule;\n } catch (err) {\n loadState = 'failed';\n notifySubscribers();\n return null;\n }\n}\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\nexport interface UseZendirSessionOptions {\n /** API key (optional - can be set via client) */\n apiKey?: string;\n /** Base URL override */\n baseUrl?: string;\n /** Auto-connect on mount */\n autoConnect?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface UseZendirSessionResult {\n /** Current session info */\n session: SessionInfo | null;\n /** Current simulation info */\n simulation: SimulationInfo | null;\n /** Loading state */\n isLoading: boolean;\n /** Error message */\n error: string | null;\n /** Connected status */\n isConnected: boolean;\n /** Whether SDK is available */\n isSdkAvailable: boolean;\n /** Zendir client instance (null if SDK not loaded) */\n client: ZendirClientInterface | null;\n /** Connect/create session */\n connect: () => Promise<void>;\n /** Create simulation */\n createSimulation: (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => Promise<void>;\n /** Disconnect/clear session */\n disconnect: () => void;\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\nexport function useZendirSession(\n options: UseZendirSessionOptions = {}\n): UseZendirSessionResult {\n const { apiKey, baseUrl, autoConnect = false, debug = false } = options;\n\n const [sdkLoaded, setSdkLoaded] = useState(loadState === 'loaded');\n const [sdkFailed, setSdkFailed] = useState(loadState === 'failed');\n const clientRef = useRef<ZendirClientInterface | null>(null);\n \n const [session, setSession] = useState<SessionInfo | null>(null);\n const [simulation, setSimulation] = useState<SimulationInfo | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Load SDK on mount\n useEffect(() => {\n let mounted = true;\n \n const unsubscribe = subscribe(() => {\n if (mounted) {\n setSdkLoaded(loadState === 'loaded');\n setSdkFailed(loadState === 'failed');\n }\n });\n \n if (loadState === 'pending') {\n loadZendirSDK().then((mod) => {\n if (mounted && mod) {\n clientRef.current = mod.getZendirClient({ apiKey, baseUrl, debug });\n }\n });\n } else if (loadState === 'loaded' && sdkModule) {\n clientRef.current = sdkModule.getZendirClient({ apiKey, baseUrl, debug });\n }\n \n return () => {\n mounted = false;\n unsubscribe();\n };\n }, [apiKey, baseUrl, debug]);\n\n // Update error if SDK failed to load\n useEffect(() => {\n if (sdkFailed) {\n setError('@zendir/sdk is not installed. Install with: npm install @zendir/sdk');\n }\n }, [sdkFailed]);\n\n const connect = useCallback(async () => {\n if (!clientRef.current) {\n setError('@zendir/sdk is not available');\n return;\n }\n \n setIsLoading(true);\n setError(null);\n\n try {\n const sessionInfo = await clientRef.current.createSession();\n setSession(sessionInfo);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to connect';\n setError(message);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n const createSimulation = useCallback(async (params: {\n name?: string;\n spacecraft: Array<{\n name: string;\n tle?: { line1: string; line2: string };\n catalogNumber?: number;\n }>;\n }) => {\n if (!clientRef.current) {\n setError('@zendir/sdk is not available');\n return;\n }\n \n setIsLoading(true);\n setError(null);\n\n try {\n const simInfo = await clientRef.current.createSimulation({\n name: params.name,\n spacecraft: params.spacecraft,\n });\n setSimulation(simInfo);\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to create simulation';\n setError(message);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n const disconnect = useCallback(() => {\n if (clientRef.current) {\n clientRef.current.clearSession();\n }\n setSession(null);\n setSimulation(null);\n setError(null);\n }, []);\n\n // Auto-connect on mount if enabled and SDK is loaded\n useEffect(() => {\n if (autoConnect && apiKey && sdkLoaded && clientRef.current) {\n connect().catch(() => {\n // Error already set in state\n });\n }\n }, [autoConnect, apiKey, sdkLoaded, connect]);\n\n return {\n session,\n simulation,\n isLoading,\n error,\n isConnected: !!session && session.status === 'active',\n isSdkAvailable: sdkLoaded && !sdkFailed,\n client: clientRef.current,\n connect,\n createSimulation,\n disconnect,\n };\n}\n\n/**\n * Check if @zendir/sdk is available\n */\nexport function isZendirSdkAvailable(): boolean {\n return loadState === 'loaded' && sdkModule !== null;\n}\n\n/**\n * Preload the @zendir/sdk module\n * Call this early in your app to ensure SDK is ready\n */\nexport async function preloadZendirSdk(): Promise<boolean> {\n const mod = await loadZendirSDK();\n return mod !== null;\n}\n\nexport default useZendirSession;\n"],"names":[],"mappings":"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"}