@oceanum/eidos 0.9.0 → 0.9.1

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
@@ -19,13 +19,63 @@ A lightweight, reactive JavaScript library for embedding and controlling EIDOS v
19
19
  npm install @oceanum/eidos
20
20
  ```
21
21
 
22
- ### Basic Usage
22
+ ### React Usage (Recommended)
23
+
24
+ ```tsx
25
+ import { EidosProvider, useEidosSpec } from "@oceanum/eidos";
26
+
27
+ // Define your EIDOS specification
28
+ const initialSpec = {
29
+ version: "0.9",
30
+ id: "my-app",
31
+ name: "My Visualization",
32
+ root: {
33
+ id: "root",
34
+ nodeType: "world",
35
+ children: [],
36
+ },
37
+ data: [],
38
+ };
39
+
40
+ function App() {
41
+ return (
42
+ <EidosProvider
43
+ initialSpec={initialSpec}
44
+ options={{
45
+ renderer: "https://render.eidos.oceanum.io",
46
+ eventListener: (event) => console.log("Event:", event),
47
+ }}
48
+ >
49
+ <YourComponents />
50
+ </EidosProvider>
51
+ );
52
+ }
53
+
54
+ // In any child component
55
+ function YourComponent() {
56
+ const spec = useEidosSpec();
57
+
58
+ const addLayer = () => {
59
+ // Mutate spec directly - changes propagate automatically to iframe
60
+ spec.root.children.push({
61
+ id: "new-layer",
62
+ nodeType: "worldlayer",
63
+ layerType: "track",
64
+ });
65
+ };
66
+
67
+ return <button onClick={addLayer}>Add Layer</button>;
68
+ }
69
+ ```
70
+
71
+ ### Vanilla JavaScript Usage
23
72
 
24
73
  ```javascript
25
- import { embed } from "@oceanum/eidos";
74
+ import { render } from "@oceanum/eidos";
26
75
 
27
76
  // Define your EIDOS specification
28
77
  const spec = {
78
+ version: "0.9",
29
79
  id: "my-app",
30
80
  name: "My Visualization",
31
81
  root: {
@@ -34,24 +84,134 @@ const spec = {
34
84
  children: [],
35
85
  },
36
86
  data: [],
37
- transforms: [],
38
87
  };
39
88
 
40
- // Embed in a container element
89
+ // Render in a container element
41
90
  const container = document.getElementById("eidos-container");
42
- const eidos = await embed(container, spec, (event) => {
43
- console.log("Received event:", event);
91
+ const result = await render(container, spec, {
92
+ renderer: "https://render.eidos.oceanum.io",
93
+ eventListener: (event) => {
94
+ console.log("Received event:", event);
95
+ },
44
96
  });
45
97
 
46
98
  // Mutate the spec naturally - changes propagate automatically
47
- eidos.name = "Updated Visualization";
48
- eidos.root.children.push({
99
+ result.spec.name = "Updated Visualization";
100
+ result.spec.root.children.push({
49
101
  id: "layer-1",
50
102
  nodeType: "worldlayer",
51
103
  layerType: "track",
52
104
  });
105
+
106
+ // Clean up when done
107
+ result.destroy();
108
+ ```
109
+
110
+ ## Advanced Usage
111
+
112
+ ### Managing Multiple EIDOS Instances
113
+
114
+ The React Context API allows you to easily manage multiple EIDOS instances in the same application. Each `EidosProvider` creates its own isolated context with its own iframe and spec.
115
+
116
+ **Option 1: Multiple Providers (Recommended)**
117
+
118
+ ```tsx
119
+ import { EidosProvider, useEidosSpec } from "@oceanum/eidos";
120
+
121
+ function App() {
122
+ return (
123
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
124
+ {/* Map view */}
125
+ <EidosProvider
126
+ initialSpec={mapSpec}
127
+ options={{ renderer: EIDOS_URL }}
128
+ >
129
+ <MapControls />
130
+ </EidosProvider>
131
+
132
+ {/* Chart view */}
133
+ <EidosProvider
134
+ initialSpec={chartSpec}
135
+ options={{ renderer: EIDOS_URL }}
136
+ >
137
+ <ChartControls />
138
+ </EidosProvider>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function MapControls() {
144
+ const spec = useEidosSpec(); // Gets mapSpec from nearest provider
145
+ // Mutations only affect this instance
146
+ const zoomIn = () => {
147
+ spec.root.viewState.zoom += 1;
148
+ };
149
+ return <button onClick={zoomIn}>Zoom In</button>;
150
+ }
151
+
152
+ function ChartControls() {
153
+ const spec = useEidosSpec(); // Gets chartSpec from nearest provider
154
+ // Completely isolated from MapControls
155
+ const updateData = () => {
156
+ spec.data[0].dataSpec = newData;
157
+ };
158
+ return <button onClick={updateData}>Update Data</button>;
159
+ }
160
+ ```
161
+
162
+ **Option 2: Low-Level Render API**
163
+
164
+ For more control, use the `render()` function directly:
165
+
166
+ ```tsx
167
+ import { render } from "@oceanum/eidos";
168
+ import { useEffect, useRef, useState } from "react";
169
+
170
+ function MultiInstance() {
171
+ const container1 = useRef(null);
172
+ const container2 = useRef(null);
173
+ const [specs, setSpecs] = useState({ map: null, chart: null });
174
+
175
+ useEffect(() => {
176
+ const init = async () => {
177
+ const map = await render(container1.current, mapSpec, { renderer: EIDOS_URL });
178
+ const chart = await render(container2.current, chartSpec, { renderer: EIDOS_URL });
179
+
180
+ setSpecs({ map: map.spec, chart: chart.spec });
181
+
182
+ return () => {
183
+ map.destroy();
184
+ chart.destroy();
185
+ };
186
+ };
187
+
188
+ init();
189
+ }, []);
190
+
191
+ const updateMap = () => {
192
+ if (specs.map) {
193
+ specs.map.root.viewState.zoom += 1;
194
+ }
195
+ };
196
+
197
+ return (
198
+ <div>
199
+ <div ref={container1} style={{ width: '50%', height: '100%' }} />
200
+ <div ref={container2} style={{ width: '50%', height: '100%' }} />
201
+ <button onClick={updateMap}>Update Map</button>
202
+ </div>
203
+ );
204
+ }
53
205
  ```
54
206
 
207
+ **Key Points for Multiple Instances:**
208
+
209
+ - ✅ Each instance has its own iframe and spec proxy
210
+ - ✅ Changes to one instance don't affect others
211
+ - ✅ `useEidosSpec()` always returns the spec from the nearest `EidosProvider` ancestor
212
+ - ✅ Use unique `id` values in your specs to avoid conflicts
213
+ - ✅ Each instance can connect to the same or different renderer URLs
214
+
55
215
  ## Framework Integration
56
216
 
57
217
  - [React Integration](./docs/eidos/react.md) - Hooks, components, and patterns
@@ -61,6 +221,100 @@ eidos.root.children.push({
61
221
 
62
222
  ## API Reference
63
223
 
224
+ ### React Components and Hooks
225
+
226
+ #### `<EidosProvider>`
227
+
228
+ Renders an EIDOS iframe and provides the spec proxy to all child components via React Context.
229
+
230
+ **Props:**
231
+ - `initialSpec` (required): The EIDOS specification object
232
+ - `options` (optional): Render options
233
+ - `renderer` (string): URL of the EIDOS renderer (default: `https://render.eidos.oceanum.io`)
234
+ - `eventListener` (function): Callback for events from the renderer
235
+ - `authToken` (string | function): Authentication token for data fetching
236
+ - `containerStyle` (optional): CSS styles for the iframe container (default: `{ width: '100%', height: '100%', position: 'absolute' }`)
237
+ - `onInitialized` (optional): Callback called with the spec proxy after initialization
238
+
239
+ **Example:**
240
+ ```tsx
241
+ <EidosProvider
242
+ initialSpec={mySpec}
243
+ options={{
244
+ renderer: "https://render.eidos.oceanum.io",
245
+ eventListener: (event) => console.log(event),
246
+ }}
247
+ containerStyle={{ width: '100vw', height: '100vh' }}
248
+ onInitialized={(spec) => console.log('Ready!', spec)}
249
+ >
250
+ <YourComponents />
251
+ </EidosProvider>
252
+ ```
253
+
254
+ #### `useEidosSpec()`
255
+
256
+ Hook that returns the EIDOS spec proxy from the nearest `EidosProvider` ancestor.
257
+
258
+ **Returns:** `Proxy<EidosSpec>` - The spec proxy (read and mutate directly)
259
+
260
+ **Throws:** Error if not used within an `EidosProvider`
261
+
262
+ **Example:**
263
+ ```tsx
264
+ function MyComponent() {
265
+ const spec = useEidosSpec();
266
+
267
+ // Read from spec
268
+ const layerCount = spec.root.children.length;
269
+
270
+ // Mutate spec - changes sync to iframe automatically
271
+ const addLayer = () => {
272
+ spec.root.children.push({
273
+ id: 'new-layer',
274
+ nodeType: 'worldlayer',
275
+ });
276
+ };
277
+
278
+ return <button onClick={addLayer}>Add Layer ({layerCount})</button>;
279
+ }
280
+ ```
281
+
282
+ ### Core Functions
283
+
284
+ #### `render(element, spec, options)`
285
+
286
+ Low-level function to render EIDOS in a DOM element. Use this for vanilla JavaScript or when you need more control than `EidosProvider` offers.
287
+
288
+ **Parameters:**
289
+ - `element` (HTMLElement): Container element for the iframe
290
+ - `spec` (EidosSpec): The EIDOS specification
291
+ - `options` (RenderOptions): Configuration options
292
+ - `renderer` (string): Renderer URL
293
+ - `eventListener` (function): Event callback
294
+ - `authToken` (string | function): Auth token
295
+ - `id` (string): Optional override for spec ID
296
+
297
+ **Returns:** `Promise<RenderResult>`
298
+ - `spec`: The reactive spec proxy
299
+ - `iframe`: The iframe element
300
+ - `updateAuth(token)`: Function to update auth token
301
+ - `destroy()`: Cleanup function
302
+
303
+ **Example:**
304
+ ```javascript
305
+ const result = await render(container, spec, {
306
+ renderer: EIDOS_URL,
307
+ eventListener: (e) => console.log(e),
308
+ });
309
+
310
+ result.spec.data.push(newData);
311
+
312
+ // Clean up
313
+ result.destroy();
314
+ ```
315
+
316
+ ### Additional Documentation
317
+
64
318
  - [Core API](./docs/eidos/api.md) - Complete API documentation
65
319
  - [Events](./docs/eidos/events.md) - Event handling and communication
66
320
  - [Validation](./docs/eidos/validation.md) - Schema validation details
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oceanum/eidos",
3
3
  "private": false,
4
- "version": "0.9.0",
4
+ "version": "0.9.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "scripts": {
@@ -9,12 +9,12 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "ajv": "^8.17.1",
12
- "valtio": "^2.1.5"
12
+ "valtio": "^2.3.0"
13
13
  },
14
14
  "devDependencies": {
15
15
  "json-schema-to-typescript": "^15.0.4"
16
16
  },
17
17
  "peerDependencies": {
18
- "react": "^19.1.0"
18
+ "react": "^19.2.3"
19
19
  }
20
20
  }
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
- export * from "./lib/render";
2
- export * from "./schema/interfaces";
3
- export * from "./lib/react";
1
+ export * from './lib/render';
2
+ export type { RenderOptions, RenderResult } from './lib/render';
3
+ export * from './schema/interfaces';
4
+ export {
5
+ useEidosSpec,
6
+ EidosProvider
7
+ } from './lib/react';
package/src/lib/react.tsx CHANGED
@@ -1,8 +1,70 @@
1
+ import { createContext, useContext, useEffect, useRef, useState, ReactNode } from 'react';
1
2
  import { useSnapshot } from 'valtio';
2
3
  import { Proxy } from 'valtio/vanilla';
3
4
  import { EidosSpec } from '../schema/interfaces';
5
+ import { render, RenderOptions } from './render';
4
6
 
5
- export const useEidosSpec = (spec: Proxy<EidosSpec>) => {
6
- const specSnapshot = useSnapshot(spec);
7
- return specSnapshot;
7
+ // Context to hold the EIDOS spec proxy
8
+ const EidosSpecContext = createContext<Proxy<EidosSpec> | null>(null);
9
+
10
+ // Hook to get the spec proxy from context
11
+ // Can be used for both reading and mutations - valtio tracks reads automatically
12
+ export const useEidosSpec = () => {
13
+ const spec = useContext(EidosSpecContext);
14
+ if (!spec) {
15
+ throw new Error('useEidosSpec must be used within an EidosProvider');
16
+ }
17
+ return spec;
18
+ };
19
+
20
+ // Props for the EidosProvider component
21
+ interface EidosProviderProps {
22
+ children: ReactNode;
23
+ initialSpec: EidosSpec;
24
+ options?: Omit<RenderOptions, 'id'>;
25
+ containerStyle?: React.CSSProperties;
26
+ onInitialized?: (spec: Proxy<EidosSpec>) => void;
27
+ }
28
+
29
+ // Provider component that renders EIDOS and provides spec to children
30
+ export const EidosProvider = ({
31
+ children,
32
+ initialSpec,
33
+ options = {},
34
+ containerStyle = { width: '100%', height: '100%', position: 'absolute' },
35
+ onInitialized,
36
+ }: EidosProviderProps) => {
37
+ const containerRef = useRef<HTMLDivElement>(null);
38
+ const [spec, setSpec] = useState<Proxy<EidosSpec> | null>(null);
39
+ const initializedRef = useRef(false);
40
+
41
+ useEffect(() => {
42
+ if (!containerRef.current || initializedRef.current) return;
43
+
44
+ initializedRef.current = true;
45
+
46
+ const initEidos = async () => {
47
+ try {
48
+ const result = await render(containerRef.current!, initialSpec, options);
49
+ setSpec(result.spec);
50
+ onInitialized?.(result.spec);
51
+ } catch (error) {
52
+ console.error('Failed to initialize EIDOS:', error);
53
+ initializedRef.current = false;
54
+ }
55
+ };
56
+
57
+ initEidos();
58
+ }, [initialSpec, options, onInitialized]);
59
+
60
+ return (
61
+ <div style={{ position: 'relative', width: '100%', height: '100%' }}>
62
+ <div ref={containerRef} style={containerStyle} />
63
+ {spec && (
64
+ <EidosSpecContext.Provider value={spec}>
65
+ {children}
66
+ </EidosSpecContext.Provider>
67
+ )}
68
+ </div>
69
+ );
8
70
  };
package/src/lib/render.ts CHANGED
@@ -1,25 +1,66 @@
1
- import { proxy, subscribe, snapshot } from "valtio/vanilla";
2
- import { validateSchema } from "./eidosmodel";
3
- import { EidosSpec } from "../schema/interfaces";
1
+ import { proxy, subscribe, snapshot, Proxy } from 'valtio/vanilla';
2
+ import { validateSchema } from './eidosmodel';
3
+ import { EidosSpec } from '../schema/interfaces';
4
4
 
5
- const DEFAULT_RENDERER = "https://render.eidos.oceanum.io";
5
+ const DEFAULT_RENDERER = 'https://render.eidos.oceanum.io';
6
+
7
+ /**
8
+ * Options for rendering an EIDOS spec
9
+ */
10
+ export interface RenderOptions {
11
+ /** The unique identifier for the EIDOS spec (optional, defaults to spec.id) */
12
+ id?: string;
13
+ /** Optional callback for handling events from the renderer */
14
+ eventListener?: (payload: unknown) => void;
15
+ /** URL of the EIDOS renderer */
16
+ renderer?: string;
17
+ /** Authentication token to pass to the renderer for data fetching */
18
+ authToken?: string | (() => string | Promise<string>);
19
+ }
20
+
21
+ /**
22
+ * Result from the render function
23
+ */
24
+ export interface RenderResult {
25
+ /** The reactive EIDOS spec proxy */
26
+ spec: Proxy<EidosSpec>;
27
+ /** Update the auth token sent to the renderer */
28
+ updateAuth: (
29
+ token: string | (() => string | Promise<string>),
30
+ ) => Promise<void>;
31
+ /** The iframe element */
32
+ iframe: HTMLIFrameElement;
33
+ /** Destroy the renderer and clean up resources */
34
+ destroy: () => void;
35
+ }
6
36
 
7
37
  /**
8
38
  * Embed the EIDOS iframe and set up message passing
9
39
  * @param element HTML element to render the EIDOS spec into
10
40
  * @param spec The EIDOS specification object
11
- * @param id The unique identifier for the EIDOS spec (optional, defaults to spec.id)
12
- * @param eventListener Optional callback for handling events from the renderer
13
- * @param renderer URL of the EIDOS renderer
14
- * @returns A proxy object that can be used with useEidosSpec
41
+ * @param options Render options including id, eventListener, renderer URL, and authToken
42
+ * @returns A RenderResult object containing the spec proxy and control methods
15
43
  */
16
44
  const render = async (
17
45
  element: HTMLElement,
18
46
  spec: EidosSpec,
19
- id?: string,
20
- eventListener?: (payload: unknown) => void,
21
- renderer = DEFAULT_RENDERER
22
- ): Promise<Proxy<EidosSpec>> => {
47
+ options: RenderOptions = {},
48
+ ): Promise<RenderResult> => {
49
+ const { id, eventListener, renderer = DEFAULT_RENDERER, authToken } = options;
50
+
51
+ // Check if this container is already initialized
52
+ // We check both for an existing iframe and a marker on the container itself
53
+ // This prevents double-mounting even if destroy() was called but the container is being reused
54
+ const existingIframe = element.querySelector('iframe[data-eidos-renderer]');
55
+ const isInitialized = element.getAttribute('data-eidos-initialized') === 'true';
56
+
57
+ if (existingIframe || isInitialized) {
58
+ throw new Error('EIDOS renderer already mounted in this container. Call destroy() before re-rendering.');
59
+ }
60
+
61
+ // Mark container as initialized
62
+ element.setAttribute('data-eidos-initialized', 'true');
63
+
23
64
  // Validate the spec before creating proxy
24
65
  try {
25
66
  await validateSchema(spec);
@@ -32,55 +73,104 @@ const render = async (
32
73
  const _id = id || spec.id;
33
74
 
34
75
  return new Promise((resolve, reject) => {
35
- const iframe = document.createElement("iframe");
76
+ const iframe = document.createElement('iframe');
36
77
  iframe.src = `${renderer}?id=${_id}`;
37
- iframe.width = "100%";
38
- iframe.height = "100%";
39
- iframe.frameBorder = "0";
78
+ iframe.width = '100%';
79
+ iframe.height = '100%';
80
+ iframe.frameBorder = '0';
81
+ // Mark this as an EIDOS iframe for duplicate detection
82
+ iframe.setAttribute('data-eidos-renderer', 'true');
40
83
  element.appendChild(iframe);
41
84
 
85
+ let messageHandler: ((event: MessageEvent) => void) | null = null;
86
+ let unsubscribe: (() => void) | null = null;
87
+
42
88
  iframe.onload = () => {
43
89
  const win = iframe.contentWindow;
44
90
 
45
- window.addEventListener("message", (event) => {
91
+ // Helper to send auth token to iframe
92
+ const sendAuth = async (
93
+ token: string | (() => string | Promise<string>),
94
+ ) => {
95
+ const resolvedToken =
96
+ typeof token === 'function' ? await token() : token;
97
+ win?.postMessage(
98
+ {
99
+ id: _id,
100
+ type: 'auth',
101
+ payload: resolvedToken,
102
+ },
103
+ '*',
104
+ );
105
+ };
106
+
107
+ messageHandler = (event: MessageEvent) => {
46
108
  if (event.source !== win) return;
47
- if (event.data.id !== id) return;
48
109
 
49
- if (event.data.type === "init") {
110
+ // Check ID only if both message and expected ID exist
111
+ // Some EIDOS renderers may not include ID in event messages
112
+ if (event.data.id && event.data.id !== _id) return;
113
+
114
+ if (event.data.type === 'init') {
50
115
  // Send initial spec
51
116
  win?.postMessage(
52
117
  {
53
118
  id: _id,
54
- type: "spec",
119
+ type: 'spec',
55
120
  payload: structuredClone(eidos),
56
121
  },
57
- "*"
122
+ '*',
58
123
  );
59
- } else {
60
- eventListener?.(event.data.payload);
124
+ // Send auth token on init if provided
125
+ if (authToken) {
126
+ sendAuth(authToken);
127
+ }
128
+ } else if (event.data.type || event.data.action || event.data.control) {
129
+ // Process as event - EIDOS events may not have 'type' but have 'action' and 'control'
130
+ eventListener?.(event.data);
61
131
  }
62
- });
132
+ };
133
+ window.addEventListener('message', messageHandler);
63
134
 
64
135
  // Subscribe to changes and send patches to renderer
65
- subscribe(eidos, () => {
136
+ unsubscribe = subscribe(eidos, () => {
66
137
  win?.postMessage(
67
138
  {
68
139
  id: _id,
69
- type: "spec",
140
+ type: 'spec',
70
141
  payload: snapshot(eidos),
71
142
  },
72
- "*"
143
+ '*',
73
144
  );
74
145
  });
75
146
 
76
- resolve(eidos);
147
+ // Send initial auth token if provided (in case init already happened)
148
+ if (authToken) {
149
+ sendAuth(authToken);
150
+ }
151
+
152
+ resolve({
153
+ spec: eidos,
154
+ updateAuth: sendAuth,
155
+ iframe,
156
+ destroy: () => {
157
+ if (messageHandler) {
158
+ window.removeEventListener('message', messageHandler);
159
+ }
160
+ if (unsubscribe) {
161
+ unsubscribe();
162
+ }
163
+ iframe.remove();
164
+ // Clear the initialization marker when explicitly destroyed
165
+ element.removeAttribute('data-eidos-initialized');
166
+ },
167
+ });
77
168
  };
78
169
 
79
170
  iframe.onerror = () => {
80
- reject(new Error("Failed to load EIDOS renderer"));
171
+ reject(new Error('Failed to load EIDOS renderer'));
81
172
  };
82
173
  });
83
- return eidos;
84
174
  };
85
175
 
86
176
  export { render };
@@ -1,22 +1,22 @@
1
1
  /**
2
2
  * Auto-generated TypeScript interfaces for EIDOS schemas
3
3
  * Generated from: https://schemas.oceanum.io/eidos/root.json
4
- *
4
+ *
5
5
  * Each interface corresponds to a definition in the EIDOS schema bundle.
6
6
  * These interfaces can be used for type validation and IDE support.
7
- *
7
+ *
8
8
  * Do not modify this file directly - regenerate using:
9
9
  * npx nx run eidos:generate-types
10
10
  */
11
11
 
12
- const 08Constant = "0.8" as const;
13
- const WorldNodeType = "world" as const;
14
- const PlotNodeType = "plot" as const;
15
- const DocumentNodeType = "document" as const;
16
- const GridNodeType = "grid" as const;
17
- const MenuNodeType = "menu" as const;
18
- const WorldlayerNodeType = "worldlayer" as const;
19
- const ControlgroupNodeType = "controlgroup" as const;
12
+ const Constant = '0.9' as const;
13
+ const WorldNodeType = 'world' as const;
14
+ const PlotNodeType = 'plot' as const;
15
+ const DocumentNodeType = 'document' as const;
16
+ const GridNodeType = 'grid' as const;
17
+ const MenuNodeType = 'menu' as const;
18
+ const WorldlayerNodeType = 'worldlayer' as const;
19
+ const ControlgroupNodeType = 'controlgroup' as const;
20
20
 
21
21
  /**
22
22
  * EIDOS specification
@@ -26,10 +26,10 @@ export interface EidosSpec {
26
26
  /**
27
27
  * Version of EIDOS
28
28
  */
29
- version?: 08Constant;
29
+ version?: Constant;
30
30
  /**
31
31
  * Unique identifier for this specification. Must be URL-safe using only lowercase letters, numbers, hyphens, and underscores. This ID is used for referencing the specification in URLs, file systems, and databases. It should be descriptive but concise.
32
- *
32
+ *
33
33
  * @example
34
34
  * "seastate-demo"
35
35
  * "plot-demo"
@@ -38,7 +38,7 @@ export interface EidosSpec {
38
38
  id: string;
39
39
  /**
40
40
  * Human-readable display name for this specification. This is the name shown to users in lists, menus, and interfaces. It can contain spaces, special characters, and mixed case. Should be descriptive and meaningful to end users.
41
- *
41
+ *
42
42
  * @example
43
43
  * "DemoOverlay"
44
44
  * "DemoPlot"
@@ -47,7 +47,7 @@ export interface EidosSpec {
47
47
  name: string;
48
48
  /**
49
49
  * Detailed description of what this specification does and its purpose. This is primarily for documentation and metadata purposes - it's not typically displayed in the main interface but may be shown in tooltips, help text, or specification listings. Useful for developers and content creators.
50
- *
50
+ *
51
51
  * @example
52
52
  * "Demonstration overlay"
53
53
  * "Demonstration grid layout"
@@ -66,7 +66,7 @@ export interface EidosSpec {
66
66
  /**
67
67
  * Data Sources
68
68
  * Array of data source definitions that provide data to the visualization components. Each data source can be a static dataset, OceanQL query, Oceanum Datamesh source, or other supported data provider. Data sources are referenced by ID throughout the specification.
69
- *
69
+ *
70
70
  * @example
71
71
  * [{"id":"sig-wave-height-trki","dataType":"oceanumDatamesh","dataSpec":{"datasource":"oceanum_wave_trki_era5_v1_grid","variables":["hs","tps"],"geofilter":{"type":"feature","geom":{"type":"Feature","geometry":{"type":"Point","coordinates":[174.3,-38.5]}}},"timefilter":{"times":["2018-01-01 00:00:00Z","2019-01-01 00:00:00Z"]}}}]
72
72
  */
@@ -110,7 +110,7 @@ export interface EidosTheme {
110
110
  /**
111
111
  * Color scheme for the view
112
112
  */
113
- preset?: "default" | "dark";
113
+ preset?: 'default' | 'dark';
114
114
  style?: EidosStyle;
115
115
  }
116
116
 
@@ -121,7 +121,7 @@ export interface EidosTheme {
121
121
  export interface EidosData {
122
122
  /**
123
123
  * Unique identifier for this data source within the specification. Must be alphanumeric with hyphens and underscores only. This ID is used to reference the data source from visualization layers and components.
124
- *
124
+ *
125
125
  * @example
126
126
  * "sig-wave-height-trki"
127
127
  * "ship-positions"
@@ -130,13 +130,13 @@ export interface EidosData {
130
130
  id: string;
131
131
  /**
132
132
  * Type of data source that determines how the data is accessed and processed. Each type requires different configuration in the dataSpec property.
133
- *
133
+ *
134
134
  * @example
135
135
  * "dataset"
136
136
  * "geojson"
137
137
  * "transform"
138
138
  */
139
- dataType: "oceanql" | "zarr" | "dataset" | "geojson" | "transform";
139
+ dataType: 'oceanql' | 'zarr' | 'dataset' | 'geojson' | 'transform';
140
140
  dataSpec: Dataset | Transform | Geojson | Oceanquery | Zarr;
141
141
  }
142
142
 
@@ -147,7 +147,7 @@ export interface EidosData {
147
147
  export interface World {
148
148
  /**
149
149
  * Unique identifier for this world node within the specification. Used for referencing this node in events, interactions, and programmatic access.
150
- *
150
+ *
151
151
  * @example
152
152
  * "map-1"
153
153
  * "world-view"
@@ -206,7 +206,7 @@ export interface Plot {
206
206
  */
207
207
  title?: string;
208
208
  nodeType?: PlotNodeType;
209
- plotType?: "vega" | "vega-lite";
209
+ plotType?: 'vega' | 'vega-lite';
210
210
  plotSpec: any & PlotSpec;
211
211
  /**
212
212
  * Actions to enable for the plot
@@ -245,7 +245,7 @@ export interface Document {
245
245
  export interface Grid {
246
246
  /**
247
247
  * Unique identifier for this grid node within the specification. Used for referencing this node in events, interactions, and programmatic access.
248
- *
248
+ *
249
249
  * @example
250
250
  * "main-grid"
251
251
  * "dashboard-layout"
@@ -283,7 +283,7 @@ export interface Menu {
283
283
  /**
284
284
  * Location of menu relative to content
285
285
  */
286
- position?: "top" | "left" | "bottom" | "right";
286
+ position?: 'top' | 'left' | 'bottom' | 'right';
287
287
  /**
288
288
  * Whether menu is open
289
289
  */
@@ -356,7 +356,7 @@ export interface Dataset {
356
356
 
357
357
  /**
358
358
  * Transform
359
- * Specification for data transform.
359
+ * Specification for data transform.
360
360
  */
361
361
  export interface Transform {
362
362
  /**
@@ -374,7 +374,7 @@ export interface Transform {
374
374
  /**
375
375
  * Output data type
376
376
  */
377
- outputType?: "dataset" | "geojson";
377
+ outputType?: 'dataset' | 'geojson';
378
378
  /**
379
379
  * Transform code
380
380
  * Transform code as body of function
@@ -460,7 +460,7 @@ export interface Zarr {
460
460
  export interface Worldlayer {
461
461
  /**
462
462
  * Unique identifier for this layer within the world node. Used for layer management, visibility control, and programmatic access.
463
- *
463
+ *
464
464
  * @example
465
465
  * "wave-height"
466
466
  * "ship-tracks"
@@ -469,7 +469,7 @@ export interface Worldlayer {
469
469
  id: string;
470
470
  /**
471
471
  * Display name for this layer shown in the layer selector and legend. Should be descriptive and user-friendly.
472
- *
472
+ *
473
473
  * @example
474
474
  * "Significant Wave Height"
475
475
  * "Ship Positions"
@@ -479,7 +479,7 @@ export interface Worldlayer {
479
479
  nodeType: WorldlayerNodeType;
480
480
  /**
481
481
  * Reference to a data source defined in the root 'data' array. This connects the layer to its data source for visualization.
482
- *
482
+ *
483
483
  * @example
484
484
  * "hs-1"
485
485
  * "sig-wave-height-trki"
@@ -530,7 +530,7 @@ export interface ControlGroup {
530
530
  */
531
531
  id: string;
532
532
  nodeType?: ControlgroupNodeType;
533
- orientation?: "horizontal" | "vertical";
533
+ orientation?: 'horizontal' | 'vertical';
534
534
  /**
535
535
  * Control list
536
536
  */
@@ -550,10 +550,10 @@ export interface View {
550
550
  /**
551
551
  * Type of world view
552
552
  */
553
- viewType?: "map" | "globe";
553
+ viewType?: 'map' | 'globe';
554
554
  /**
555
555
  * Longitude coordinate of the map center in decimal degrees (-180 to 180). Positive values are East, negative values are West.
556
- *
556
+ *
557
557
  * @example
558
558
  * 174.3
559
559
  * -122.4
@@ -562,7 +562,7 @@ export interface View {
562
562
  longitude: number;
563
563
  /**
564
564
  * Latitude coordinate of the map center in decimal degrees (-90 to 90). Positive values are North, negative values are South.
565
- *
565
+ *
566
566
  * @example
567
567
  * -38.5
568
568
  * 37.7
@@ -583,7 +583,7 @@ export interface View {
583
583
  maxZoom?: number;
584
584
  /**
585
585
  * Initial zoom level of the map. Higher values show more detail. Typically ranges from 0 (world view) to 20+ (street level).
586
- *
586
+ *
587
587
  * @example
588
588
  * 2
589
589
  * 8
@@ -642,7 +642,7 @@ export interface DocumentStyle {
642
642
  /**
643
643
  * Justify content
644
644
  */
645
- justifyContent?: "left" | "center" | "right";
645
+ justifyContent?: 'left' | 'center' | 'right';
646
646
  }
647
647
 
648
648
  /**
@@ -933,10 +933,17 @@ export interface MapHoverInfo {
933
933
  /**
934
934
  * Layer specification
935
935
  */
936
- export type Layerspec = Feature | Gridded | Label | Scenegraph | Seasurface | Track | Wmts;
936
+ export type Layerspec =
937
+ | Feature
938
+ | Gridded
939
+ | Label
940
+ | Scenegraph
941
+ | Seasurface
942
+ | Track
943
+ | Wmts;
937
944
 
938
945
  export interface Timeselect {
939
- mode: "nearest" | "exact" | "range";
946
+ mode: 'nearest' | 'exact' | 'range';
940
947
  /**
941
948
  * Time tolerance duration for nearest time select
942
949
  */
@@ -945,7 +952,7 @@ export interface Timeselect {
945
952
  * Time aggregation
946
953
  * Aggregation method for time range
947
954
  */
948
- aggregate?: "last" | "first" | "sum" | "mean" | "max" | "min";
955
+ aggregate?: 'last' | 'first' | 'sum' | 'mean' | 'max' | 'min';
949
956
  /**
950
957
  * Data field to group by
951
958
  */
@@ -959,7 +966,7 @@ export interface Levelselect {
959
966
  /**
960
967
  * Level selection mode
961
968
  */
962
- mode?: "nearest" | "exact" | "range";
969
+ mode?: 'nearest' | 'exact' | 'range';
963
970
  /**
964
971
  * Level tolerance for nearest level select
965
972
  */
@@ -978,7 +985,7 @@ export interface Control {
978
985
  /**
979
986
  * Control type
980
987
  */
981
- nodeType: "points" | "polygon" | "bbox" | "radius" | "drop" | "measure";
988
+ nodeType: 'points' | 'polygon' | 'bbox' | 'radius' | 'drop' | 'measure';
982
989
  /**
983
990
  * Control id
984
991
  */
@@ -1009,17 +1016,17 @@ export interface Control {
1009
1016
  /**
1010
1017
  * Base layer type
1011
1018
  */
1012
- export type Baselayerpreset = "oceanum" | "terrain";
1019
+ export type Baselayerpreset = 'oceanum' | 'terrain';
1013
1020
 
1014
1021
  /**
1015
1022
  * ResampleType
1016
1023
  */
1017
- export type Resampletype = "mean" | "nearest";
1024
+ export type Resampletype = 'mean' | 'nearest';
1018
1025
 
1019
1026
  /**
1020
1027
  * Aggregate Ops
1021
1028
  */
1022
- export type AggregateOps = "mean" | "min" | "max" | "std" | "sum";
1029
+ export type AggregateOps = 'mean' | 'min' | 'max' | 'std' | 'sum';
1023
1030
 
1024
1031
  /**
1025
1032
  * Feature
@@ -1029,7 +1036,7 @@ export interface Feature {
1029
1036
  /**
1030
1037
  * Type
1031
1038
  */
1032
- type: "Feature";
1039
+ type: 'Feature';
1033
1040
  geometry: Geometry;
1034
1041
  /**
1035
1042
  * Properties
@@ -1049,7 +1056,14 @@ export interface Feature {
1049
1056
  * Geometry
1050
1057
  * Geometry Model
1051
1058
  */
1052
- export type Geometry = Point | Multipoint | Linestring | Multilinestring | Polygon | Multipolygon | Geometrycollection;
1059
+ export type Geometry =
1060
+ | Point
1061
+ | Multipoint
1062
+ | Linestring
1063
+ | Multilinestring
1064
+ | Polygon
1065
+ | Multipolygon
1066
+ | Geometrycollection;
1053
1067
 
1054
1068
  /**
1055
1069
  * Point