react-dashstream 0.0.6 → 0.0.7

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
@@ -11,9 +11,9 @@ npm install react-dashstream
11
11
  ## Quick start
12
12
 
13
13
  ```tsx
14
- import "dashstream/dist/index.css";
15
- import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "dashstream";
16
- import type { ServiceMeta } from "dashstream";
14
+ import "react-dashstream/dist/index.css";
15
+ import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
16
+ import type { ServiceMeta } from "react-dashstream";
17
17
 
18
18
  const services: ServiceMeta[] = [
19
19
  {
@@ -75,9 +75,16 @@ Two services with different topologies rotating in a 3D carousel.
75
75
  This is the example included in `example/Dashboard.tsx` inside this package.
76
76
 
77
77
  ```tsx
78
- import "dashstream/dist/index.css";
79
- import { AIOPsDashboard, Service, HumanNode, WebDispatcherNode, ServerNode, DatabaseNode } from "dashstream";
80
- import type { ServiceMeta } from "dashstream";
78
+ import "react-dashstream/dist/index.css";
79
+ import {
80
+ AIOPsDashboard,
81
+ Service,
82
+ HumanNode,
83
+ WebDispatcherNode,
84
+ ServerNode,
85
+ DatabaseNode,
86
+ } from "react-dashstream";
87
+ import type { ServiceMeta } from "react-dashstream";
81
88
 
82
89
  // ── Service metadata (drives the stats dialog) ──────────────────────────
83
90
 
@@ -249,6 +256,198 @@ export default function App() {
249
256
 
250
257
  ---
251
258
 
259
+ ## External data source monitoring
260
+
261
+ DashStream supports live data from any HTTP-accessible monitoring backend (Prometheus, Grafana, custom APIs, etc.) via built-in interval polling. When enabled, the dashboard polls your endpoint with PromQL-style queries, authenticates with header-based credentials, and automatically injects resolved values as props into your service components.
262
+
263
+ ### How it works
264
+
265
+ 1. Set `liveData={true}` on `AIOPsDashboard` along with `dataEndpoint` and `dataBindings`.
266
+ 2. On first load, a **credentials modal** prompts for an `access-key` and `access-secret-key` (stored in memory only — never persisted).
267
+ 3. The dashboard polls `GET <dataEndpoint>?query=<encodedQuery>` for each unique query, sending credentials as HTTP headers.
268
+ 4. Resolved values are automatically injected as props into child service components matched by `name`.
269
+ 5. The header shows a **"DATA REFRESH FAILED"** indicator if any queries fail.
270
+
271
+ ### Enabling live data
272
+
273
+ ```tsx
274
+ import "react-dashstream/dist/index.css";
275
+ import { AIOPsDashboard, Service, ServerNode, DatabaseNode } from "react-dashstream";
276
+
277
+ export default function App() {
278
+ return (
279
+ <AIOPsDashboard
280
+ brandName="LIVE MONITOR"
281
+ services={services}
282
+ liveData={true}
283
+ dataEndpoint="https://prometheus.example.com/api/v1/query"
284
+ dataRefreshInterval={30000}
285
+ dataBindings={{
286
+ "My Service": {
287
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
288
+ memLoad: 'memory_usage{instance="srv-01"}',
289
+ status: {
290
+ query: 'up{instance="srv-01"}',
291
+ transform: (v) => (Number(v) > 0 ? "online" : "offline"),
292
+ },
293
+ },
294
+ }}
295
+ dataTransform={(raw) => {
296
+ const n = Number(raw);
297
+ return isNaN(n) ? raw : n;
298
+ }}
299
+ serviceDataBindings={{
300
+ "My Service": [
301
+ { label: "Uptime", query: 'uptime{svc="my-service"}', unit: "%", color: "#00ff88" },
302
+ { label: "Latency", query: 'latency{svc="my-service"}', unit: "ms", color: "#00e5ff" },
303
+ {
304
+ label: "Error Rate",
305
+ query: 'error_rate{svc="my-service"}',
306
+ unit: "%",
307
+ color: "#ff2255",
308
+ },
309
+ ],
310
+ }}
311
+ >
312
+ <Service name="My Service" status="online" connections={[]}>
313
+ <ServerNode
314
+ ex={200}
315
+ ey={380}
316
+ compactOffset={{ x: -30, y: -20 }}
317
+ zIndex={8}
318
+ name="SRV-01"
319
+ subLabel="APP SERVER"
320
+ status="online"
321
+ cpuLoad={0}
322
+ memLoad={0}
323
+ />
324
+ <DatabaseNode
325
+ ex={460}
326
+ ey={380}
327
+ compactOffset={{ x: 30, y: -20 }}
328
+ zIndex={7}
329
+ name="DB-01"
330
+ subLabel="PRIMARY"
331
+ status="online"
332
+ capacity={55}
333
+ />
334
+ </Service>
335
+ </AIOPsDashboard>
336
+ );
337
+ }
338
+ ```
339
+
340
+ ### Live data props reference
341
+
342
+ | Prop | Type | Description |
343
+ | --------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------- |
344
+ | `liveData` | `boolean` | Enable the live data pipeline. Default `false`. |
345
+ | `dataEndpoint` | `string` | Base URL of the monitoring endpoint. Queries are sent as `GET <endpoint>?query=<encodedQuery>`. |
346
+ | `dataBindings` | `DataBindings` | Maps service name → prop name → PromQL query. Resolved values are injected as component props. |
347
+ | `dataTransform` | `(raw: unknown) => unknown` | Global transform applied to every raw response. Defaults to numeric parsing. |
348
+ | `dataRefreshInterval` | `number` | Polling interval in milliseconds. Default `60000` (1 minute). |
349
+ | `serviceDataBindings` | `Record<string, ServiceMetricBinding[]>` | Maps service name → metric rows for the service stats dialog. |
350
+
351
+ ### Data bindings
352
+
353
+ Each binding maps a prop name on a child service component to a query. Bindings can be a bare query string (uses the global `dataTransform`) or an object with a per-binding `transform`:
354
+
355
+ ```ts
356
+ type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
357
+ type DataBindings = Record<string, Record<string, DataBinding>>;
358
+ ```
359
+
360
+ The service name key must match the `name` prop on the corresponding child component. When the polled response arrives, the transform is applied and the result is injected as the named prop, overriding any static default.
361
+
362
+ ### Service metric bindings
363
+
364
+ These bind KPI rows in the **ServiceDialog** (the stats panel shown when a service is expanded) to live queries:
365
+
366
+ ```ts
367
+ interface ServiceMetricBinding {
368
+ label: string; // Row label (e.g. "Uptime")
369
+ query: string; // PromQL query string
370
+ unit?: string; // Unit suffix (e.g. "%", "ms")
371
+ color?: string; // Accent color for the row
372
+ transform?: (raw: unknown) => string; // Custom formatter
373
+ }
374
+ ```
375
+
376
+ When `serviceDataBindings` is provided, live metric values replace the static `ServiceMeta.metrics` for that service.
377
+
378
+ ### Endpoint contract
379
+
380
+ Your monitoring endpoint must satisfy this contract:
381
+
382
+ | Aspect | Requirement |
383
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------- |
384
+ | **Method** | `GET` |
385
+ | **URL format** | `<dataEndpoint>?query=<urlEncodedQuery>` |
386
+ | **Auth headers** | `access-key` and `access-secret-key` |
387
+ | **Response body** | Plain text or JSON. The default transform parses the trimmed body as a number; non-numeric values are returned as strings. |
388
+ | **Error handling** | Non-2xx responses are counted as failures. Partial failures are reported in the header. |
389
+
390
+ If your backend returns structured JSON (e.g. Prometheus instant-query format), provide a custom `dataTransform` to extract the scalar value:
391
+
392
+ ```tsx
393
+ dataTransform={(raw) => {
394
+ const parsed = JSON.parse(String(raw));
395
+ return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
396
+ }}
397
+ ```
398
+
399
+ ### Using the data hooks directly
400
+
401
+ For advanced use cases, you can access the live data context anywhere inside the dashboard tree:
402
+
403
+ ```tsx
404
+ import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
405
+
406
+ function CustomWidget() {
407
+ const { data, isRefreshing, lastRefreshError, lastRefreshTime } = useAIOpsData();
408
+
409
+ const cpuValue = useQueryResult('cpu_usage{instance="srv-01"}');
410
+
411
+ return (
412
+ <div>
413
+ {isRefreshing && <span>Refreshing...</span>}
414
+ {lastRefreshError && <span>Error: {lastRefreshError}</span>}
415
+ <span>CPU: {String(cpuValue)}</span>
416
+ </div>
417
+ );
418
+ }
419
+ ```
420
+
421
+ | Hook | Description |
422
+ | ------------------------ | --------------------------------------------------------------------------------------------------- |
423
+ | `useAIOpsData()` | Returns the full `DataContextValue`. Throws if called outside a live-data dashboard. |
424
+ | `useAIOpsDataOptional()` | Returns `DataContextValue` or `null` if no `DataProvider` is present. Safe in dual-mode components. |
425
+ | `useQueryResult(query)` | Returns the raw response for a specific query string, or `null` if not yet fetched. |
426
+
427
+ ### Standalone DataProvider
428
+
429
+ You can use `DataProvider` directly for custom layouts that don't use the full `AIOPsDashboard` shell:
430
+
431
+ ```tsx
432
+ import { DataProvider, useAIOpsData } from "react-dashstream";
433
+
434
+ function MyCustomDashboard() {
435
+ return (
436
+ <DataProvider
437
+ config={{
438
+ endpoint: "https://prometheus.example.com/api/v1/query",
439
+ queries: ['cpu_usage{instance="srv-01"}', 'memory_usage{instance="srv-01"}'],
440
+ refreshInterval: 15000,
441
+ }}
442
+ >
443
+ <MyCustomContent />
444
+ </DataProvider>
445
+ );
446
+ }
447
+ ```
448
+
449
+ ---
450
+
252
451
  ## Components
253
452
 
254
453
  ### Layout
@@ -287,7 +486,7 @@ All 3D models accept: `rotateX`, `rotateY`, `rotateZ`, `scale`, `autoRotate`.
287
486
  | --------------- | ------------------------------------ | ---------------------------------------------------------------- |
288
487
  | `SyncBridge` | `synced`, `latencyMs` | Database replication bridge between primary and standby. |
289
488
  | `NodeCallout` | `status`, `title`, `msg`, `ex`, `ey` | Alert callout with leader line (auto-rendered by `ServiceNode`). |
290
- | `HoloBase` | `size`, `color`, `widthRatio` | Neon holographic base platform (auto-rendered by `Service`). Position via `baseConfig` on `Service`. |
489
+ | `HoloBase` | `size`, `color`, `widthRatio` | Neon holographic base platform (auto-rendered by `Service`). |
291
490
  | `SvgConnection` | `x1`, `y1`, `x2`, `y2`, `show` | Animated dashed SVG connection line. |
292
491
 
293
492
  ### Dialogs
@@ -321,28 +520,6 @@ Compose compound nodes inside a `Service` container. Each node needs:
321
520
  - **`zIndex`** — Stacking order (higher tiers get higher values).
322
521
  - **`visibleAtPhase`** — When the node fades in during expansion (0–6). Use `2` for top-tier nodes, `3` for middle, etc.
323
522
 
324
- ### HoloBase position
325
-
326
- The holographic base platform rendered by each `Service` can be repositioned via `baseConfig`:
327
-
328
- ```tsx
329
- <Service
330
- name="My Service"
331
- baseConfig={{
332
- size: 90,
333
- color: "#00e5ff",
334
- widthRatio: 3,
335
- expandedX: 330, // absolute X in expanded view (default: container centre)
336
- expandedY: 500, // absolute Y in expanded view (default: 570)
337
- compactOffsetX: 0, // horizontal nudge in compact carousel view
338
- compactOffsetY: -20, // vertical nudge in compact carousel view
339
- }}
340
- ...
341
- >
342
- ```
343
-
344
- ### Connection lines
345
-
346
523
  Define connection lines between nodes via the `connections` prop on `Service`:
347
524
 
348
525
  ```tsx
@@ -407,7 +584,7 @@ type ComponentStatus = "online" | "warning" | "critical" | "offline";
407
584
  ## Theme constants
408
585
 
409
586
  ```ts
410
- import { STATUS_CFG, HOLO_CYAN, HOLO_BLUE, HOLO_SURFACE, HOLO_GLASS, makeFaceStyles } from "dashstream";
587
+ import { STATUS_CFG, HOLO_CYAN, HOLO_BLUE, HOLO_SURFACE, HOLO_GLASS, makeFaceStyles } from "react-dashstream";
411
588
  ```
412
589
 
413
590
  - `STATUS_CFG` — status-to-color lookup table
@@ -1 +1 @@
1
- {"version":3,"file":"AIOPsDashboard.d.ts","sourceRoot":"","sources":["../src/AIOPsDashboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,aAAa,CAAC;AAMrB;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,eAAe,CAAC;IACxB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAChC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAC7D;;;;OAIG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAMD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,mBAAmB,2CA4BhE"}
1
+ {"version":3,"file":"AIOPsDashboard.d.ts","sourceRoot":"","sources":["../src/AIOPsDashboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAI9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,aAAa,CAAC;AACrB,OAAO,cAAc,CAAC;AAMtB;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,eAAe,CAAC;IACxB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAChC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAC7D;;;;OAIG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAMD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,mBAAmB,2CA4BhE"}
@@ -70,26 +70,11 @@ export interface ServiceProps {
70
70
  /**
71
71
  * Configuration for the holographic base platform.
72
72
  * Defaults to `{ size: 90, color: "#00e5ff", widthRatio: 3 }`.
73
- *
74
- * Use `expandedX` / `expandedY` to reposition the base in the expanded
75
- * (topology) view. Values are absolute pixel coordinates; the base is
76
- * centred on this point. Defaults: centred horizontally, `y = 570`.
77
- *
78
- * Use `compactOffsetX` / `compactOffsetY` to nudge the base relative to
79
- * its default compact-carousel position. Defaults: `0`.
80
73
  */
81
74
  baseConfig?: {
82
75
  size?: number;
83
76
  color?: string;
84
77
  widthRatio?: number;
85
- /** Absolute X position in the expanded view (default: container centre). */
86
- expandedX?: number;
87
- /** Absolute Y position in the expanded view (default: 570). */
88
- expandedY?: number;
89
- /** Horizontal offset in the compact carousel view (default: 0). */
90
- compactOffsetX?: number;
91
- /** Vertical offset in the compact carousel view (default: 0). */
92
- compactOffsetY?: number;
93
78
  };
94
79
  /** Child nodes (ServiceNode, NodeCallout, bare ServiceNode with SyncBridge, etc.). */
95
80
  children: React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/components/Service.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../src/components/Service.tsx"],"names":[],"mappings":"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"}
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- @keyframes status-change-pulse{0%{opacity:0;transform:scale(.6)}10%{opacity:1;transform:scale(1.15)}25%{opacity:.1;transform:scale(.95)}40%{opacity:.9;transform:scale(1.1)}60%{opacity:.15;transform:scale(1)}to{opacity:0;transform:scale(1.3)}}.app{display:grid;grid-template-rows:52px 1fr;width:100%;height:100vh;overflow:hidden;position:relative;background:#020810;color:#c8dff0;font-family:var(--aiops-font-family, "Segoe UI", system-ui, -apple-system, sans-serif);-webkit-font-smoothing:antialiased}.app-header{display:flex;align-items:center;gap:18px;padding:0 24px;border-bottom:1px solid rgba(0,229,255,.08);background:#020810eb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);position:relative;z-index:10}.hdr-brand{display:flex;align-items:center;gap:8px}.brand-pulse{width:7px;height:7px;border-radius:50%;background:#00e5ff;box-shadow:0 0 8px #00e5ff,0 0 18px #00e5ff80;animation:holo-led-blink 2s linear infinite}.brand-text{font-size:10px;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff;opacity:.7}.hdr-right{display:flex;align-items:center;gap:8px;margin-left:auto}.hdr-tag{font-size:9px;letter-spacing:.2em;color:#00e5ff59;border:1px solid rgba(0,229,255,.15);padding:3px 8px;border-radius:2px}.scene{display:flex;flex-direction:column;align-items:center;overflow:hidden;padding:0;position:relative;z-index:1;gap:0;background:transparent}.float-node{display:flex;flex-direction:column;align-items:center;position:relative}.float-node--interactive:before{content:"";position:absolute;inset:-40px -35px -15px}.float-body{position:relative;animation:holo-float 4s ease-in-out infinite}.scan-line{position:absolute;top:0;left:0;right:0;height:1.5px;animation:holo-scan 3.5s linear infinite;pointer-events:none;z-index:10}.node-tag{font-size:24px;letter-spacing:.2em;text-transform:uppercase;font-family:var(--aiops-font-family, "Courier New", monospace);margin-top:24px;text-align:center;text-shadow:0 0 6px currentColor}.node-subtag{font-size:11px;letter-spacing:.15em;text-transform:uppercase;font-family:var(--aiops-font-family, "Courier New", monospace);margin-top:4px;text-align:center}.sync-bridge{display:flex;flex-direction:column;align-items:center;justify-content:center;width:110px;gap:6px;flex-shrink:0;font-family:var(--aiops-font-family, "Courier New", monospace)}.sync-arrows{display:flex;align-items:center;width:100%;gap:4px}.sync-line{flex:1;height:2px;border-radius:1px;animation:holo-beam-pulse 1.8s ease-in-out infinite}.sync-status{font-size:8px;letter-spacing:.18em;text-transform:uppercase;text-align:center}.sync-latency{font-size:6.5px;letter-spacing:.1em;text-align:center;font-family:var(--aiops-font-family, "Courier New", monospace)}.base-ring{position:absolute;top:50%;left:50%;border-style:solid;border-radius:50%;transform:translate(-50%,-50%) scaleY(.22);animation:holo-ring-pulse 2.5s ease-in-out infinite}.base-hotspot{position:absolute;top:50%;left:50%;width:8%;height:8%;border-radius:50%;transform:translate(-50%,-50%) scaleY(.22);background:radial-gradient(circle,#ffffff 0%,#ff8c00 35%,transparent 70%);box-shadow:0 0 20px #ff8c00,0 0 40px #ff8c0080}@keyframes refresh-error-pulse{0%,to{opacity:1;box-shadow:0 0 6px #f25,0 0 14px #f256}50%{opacity:.5;box-shadow:0 0 3px #f25}}.hdr-refresh-error{display:flex;align-items:center;gap:7px;padding:3px 10px;border:1px solid rgba(255,34,85,.35);border-radius:3px;background:#ff22550f;animation:refresh-error-pulse 2s ease-in-out infinite}.refresh-error-dot{width:6px;height:6px;border-radius:50%;background:#f25;box-shadow:0 0 6px #f25;flex-shrink:0}.refresh-error-text{font-size:8px;letter-spacing:.18em;text-transform:uppercase;color:#f25;white-space:nowrap;font-family:var(--aiops-font-family, "Courier New", monospace)}@keyframes cred-panel-glow{0%,to{border-color:#00e5ff2e;box-shadow:0 0 40px #00e5ff14,0 20px 80px #000c,inset 0 0 60px #00000080}50%{border-color:#00e5ff59;box-shadow:0 0 60px #00e5ff26,0 20px 80px #000c,inset 0 0 60px #00000080}}@keyframes cred-fade-in{0%{opacity:0}to{opacity:1}}@keyframes cred-panel-enter{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes cred-icon-pop{0%{opacity:0;transform:scale(.4) translateY(10px)}60%{opacity:1;transform:scale(1.1) translateY(-3px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes cred-scanline-sweep{0%{top:-2px;opacity:0}5%{opacity:.7}90%{opacity:.4}to{top:100%;opacity:0}}@keyframes cred-btn-shine{0%{left:-100%}to{left:200%}}.cred-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:#020810e0;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:cred-fade-in .4s ease-out}.cred-panel{position:relative;width:380px;padding:40px 36px 32px;border:1px solid rgba(0,229,255,.18);border-radius:6px;background:linear-gradient(170deg,#061226f7,#030a16fa);animation:cred-panel-enter .5s ease-out,cred-panel-glow 4s ease-in-out infinite;overflow:hidden}.cred-scanline{position:absolute;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 0%,rgba(0,229,255,.5) 50%,transparent 100%);animation:cred-scanline-sweep 4s linear infinite;pointer-events:none;z-index:2}.cred-corner{position:absolute;width:18px;height:18px;pointer-events:none;z-index:3}.cred-corner:before,.cred-corner:after{content:"";position:absolute;background:#00e5ff73}.cred-corner:before{top:0;left:0;width:18px;height:1px}.cred-corner:after{top:0;left:0;width:1px;height:18px}.cred-corner--tl{top:-1px;left:-1px}.cred-corner--tr{top:-1px;right:-1px;transform:scaleX(-1)}.cred-corner--bl{bottom:-1px;left:-1px;transform:scaleY(-1)}.cred-corner--br{bottom:-1px;right:-1px;transform:scale(-1)}.cred-icon{display:flex;justify-content:center;margin-bottom:18px;animation:cred-icon-pop .6s ease-out .15s both}.cred-title{text-align:center;font-size:13px;font-weight:700;letter-spacing:.28em;text-transform:uppercase;color:#00e5ff;margin-bottom:6px}.cred-subtitle{text-align:center;font-size:9px;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff59;margin-bottom:28px;font-family:var(--aiops-font-family, "Courier New", monospace)}.cred-field{margin-bottom:18px}.cred-label{display:block;font-size:8px;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff8c;margin-bottom:6px;font-family:var(--aiops-font-family, "Courier New", monospace)}.cred-input{width:100%;padding:10px 14px;border:1px solid rgba(0,229,255,.15);border-radius:4px;background:#000a19b3;color:#c8dff0;font-size:13px;font-family:var(--aiops-font-family, "Courier New", monospace);letter-spacing:.04em;outline:none;transition:border-color .25s,box-shadow .25s;box-sizing:border-box}.cred-input::placeholder{color:#c8dff02e;letter-spacing:.06em}.cred-btn{position:relative;display:block;width:100%;margin-top:26px;padding:12px 0;border:1px solid rgba(0,229,255,.3);border-radius:4px;background:#00e5ff0f;color:#00e5ff;font-size:11px;font-weight:700;letter-spacing:.3em;text-transform:uppercase;font-family:inherit;transition:background .25s,box-shadow .25s,opacity .25s;overflow:hidden}.cred-btn-glow{position:absolute;top:0;left:-100%;width:60%;height:100%;background:linear-gradient(90deg,transparent,rgba(0,229,255,.08),transparent);pointer-events:none;animation:cred-btn-shine 3s ease-in-out infinite}.cred-footer{margin-top:20px;text-align:center;font-size:7px;letter-spacing:.2em;text-transform:uppercase;color:#c8dff02e;font-family:var(--aiops-font-family, "Courier New", monospace)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{width:100%;height:100%;overflow:hidden}body{font-family:var(--aiops-font-family, "Segoe UI", system-ui, -apple-system, sans-serif);background-color:#020810;color:#c8dff0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#1e508c66;border-radius:2px}::-webkit-scrollbar-thumb:hover{background:#22d3ee4d}*{scrollbar-width:thin;scrollbar-color:rgba(30,80,140,.4) transparent}@keyframes holo-ring-pulse{0%,to{transform:scaleY(.22) scale(1)}50%{transform:scaleY(.22) scale(.965)}}@keyframes holo-beam-pulse{0%{opacity:.75;filter:brightness(1)}40%{opacity:1;filter:brightness(1.35)}70%{opacity:.55;filter:brightness(.7)}to{opacity:.75;filter:brightness(1)}}@keyframes holo-scan{0%{top:-3px;opacity:0}6%{opacity:1}90%{opacity:.6}to{top:110%;opacity:0}}@keyframes holo-led-blink{0%,88%,to{opacity:1}93%{opacity:.15}}@keyframes holo-float{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@keyframes holo-base-spin{0%{transform:translate(-50%) scaleY(.22) rotate(0)}to{transform:translate(-50%) scaleY(.22) rotate(360deg)}}@keyframes component-drill-pulse{0%,to{box-shadow:0 0 #f253}50%{box-shadow:0 0 12px 2px #ff225526}}@keyframes comp-dialog-border-glow{0%,to{border-color:var(--dialog-color, #00e5ff)33;box-shadow:0 0 30px var(--dialog-color, #00e5ff) 18,0 12px 60px #000000bf,inset 0 0 40px #0006}50%{border-color:var(--dialog-color, #00e5ff)66;box-shadow:0 0 50px var(--dialog-color, #00e5ff) 30,0 12px 60px #000000bf,inset 0 0 40px #0006}}@keyframes comp-dialog-icon-pop{0%{opacity:0;transform:scale(.3) translateY(14px)}60%{opacity:1;transform:scale(1.08) translateY(-3px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes comp-dialog-metric-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}
1
+ @keyframes status-change-pulse{0%{opacity:0;transform:scale(.6)}10%{opacity:1;transform:scale(1.15)}25%{opacity:.1;transform:scale(.95)}40%{opacity:.9;transform:scale(1.1)}60%{opacity:.15;transform:scale(1)}to{opacity:0;transform:scale(1.3)}}.app{display:grid;grid-template-rows:52px 1fr;width:100%;height:100vh;overflow:hidden;position:relative;background:#020810;color:#c8dff0;font-family:var(--aiops-font-family, "Segoe UI", system-ui, -apple-system, sans-serif);-webkit-font-smoothing:antialiased}.app-header{display:flex;align-items:center;gap:18px;padding:0 24px;border-bottom:1px solid rgba(0,229,255,.08);background:#020810eb;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);position:relative;z-index:10}.hdr-brand{display:flex;align-items:center;gap:8px}.brand-pulse{width:7px;height:7px;border-radius:50%;background:#00e5ff;box-shadow:0 0 8px #00e5ff,0 0 18px #00e5ff80;animation:holo-led-blink 2s linear infinite}.brand-text{font-size:10px;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff;opacity:.7}.hdr-right{display:flex;align-items:center;gap:8px;margin-left:auto}.hdr-tag{font-size:9px;letter-spacing:.2em;color:#00e5ff59;border:1px solid rgba(0,229,255,.15);padding:3px 8px;border-radius:2px}.scene{display:flex;flex-direction:column;align-items:center;overflow:hidden;padding:0;position:relative;z-index:1;gap:0;background:transparent}.float-node{display:flex;flex-direction:column;align-items:center;position:relative}.float-node--interactive:before{content:"";position:absolute;inset:-40px -35px -15px}.float-body{position:relative;animation:holo-float 4s ease-in-out infinite}.scan-line{position:absolute;top:0;left:0;right:0;height:1.5px;animation:holo-scan 3.5s linear infinite;pointer-events:none;z-index:10}.node-tag{font-size:24px;letter-spacing:.2em;text-transform:uppercase;font-family:var(--aiops-font-family, "Courier New", monospace);margin-top:14px;text-align:center;text-shadow:0 0 6px currentColor}.node-subtag{font-size:11px;letter-spacing:.15em;text-transform:uppercase;font-family:var(--aiops-font-family, "Courier New", monospace);margin-top:4px;text-align:center}.sync-bridge{display:flex;flex-direction:column;align-items:center;justify-content:center;width:110px;gap:6px;flex-shrink:0;font-family:var(--aiops-font-family, "Courier New", monospace)}.sync-arrows{display:flex;align-items:center;width:100%;gap:4px}.sync-line{flex:1;height:2px;border-radius:1px;animation:holo-beam-pulse 1.8s ease-in-out infinite}.sync-status{font-size:8px;letter-spacing:.18em;text-transform:uppercase;text-align:center}.sync-latency{font-size:6.5px;letter-spacing:.1em;text-align:center;font-family:var(--aiops-font-family, "Courier New", monospace)}.base-ring{position:absolute;top:50%;left:50%;border-style:solid;border-radius:50%;transform:translate(-50%,-50%) scaleY(.22);animation:holo-ring-pulse 2.5s ease-in-out infinite}.base-hotspot{position:absolute;top:50%;left:50%;width:8%;height:8%;border-radius:50%;transform:translate(-50%,-50%) scaleY(.22);background:radial-gradient(circle,#ffffff 0%,#ff8c00 35%,transparent 70%);box-shadow:0 0 20px #ff8c00,0 0 40px #ff8c0080}@keyframes refresh-error-pulse{0%,to{opacity:1;box-shadow:0 0 6px #f25,0 0 14px #f256}50%{opacity:.5;box-shadow:0 0 3px #f25}}.hdr-refresh-error{display:flex;align-items:center;gap:7px;padding:3px 10px;border:1px solid rgba(255,34,85,.35);border-radius:3px;background:#ff22550f;animation:refresh-error-pulse 2s ease-in-out infinite}.refresh-error-dot{width:6px;height:6px;border-radius:50%;background:#f25;box-shadow:0 0 6px #f25;flex-shrink:0}.refresh-error-text{font-size:8px;letter-spacing:.18em;text-transform:uppercase;color:#f25;white-space:nowrap;font-family:var(--aiops-font-family, "Courier New", monospace)}@keyframes cred-panel-glow{0%,to{border-color:#00e5ff2e;box-shadow:0 0 40px #00e5ff14,0 20px 80px #000c,inset 0 0 60px #00000080}50%{border-color:#00e5ff59;box-shadow:0 0 60px #00e5ff26,0 20px 80px #000c,inset 0 0 60px #00000080}}@keyframes cred-fade-in{0%{opacity:0}to{opacity:1}}@keyframes cred-panel-enter{0%{opacity:0;transform:translateY(20px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes cred-icon-pop{0%{opacity:0;transform:scale(.4) translateY(10px)}60%{opacity:1;transform:scale(1.1) translateY(-3px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes cred-scanline-sweep{0%{top:-2px;opacity:0}5%{opacity:.7}90%{opacity:.4}to{top:100%;opacity:0}}@keyframes cred-btn-shine{0%{left:-100%}to{left:200%}}.cred-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:#020810e0;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);animation:cred-fade-in .4s ease-out}.cred-panel{position:relative;width:380px;padding:40px 36px 32px;border:1px solid rgba(0,229,255,.18);border-radius:6px;background:linear-gradient(170deg,#061226f7,#030a16fa);animation:cred-panel-enter .5s ease-out,cred-panel-glow 4s ease-in-out infinite;overflow:hidden}.cred-scanline{position:absolute;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 0%,rgba(0,229,255,.5) 50%,transparent 100%);animation:cred-scanline-sweep 4s linear infinite;pointer-events:none;z-index:2}.cred-corner{position:absolute;width:18px;height:18px;pointer-events:none;z-index:3}.cred-corner:before,.cred-corner:after{content:"";position:absolute;background:#00e5ff73}.cred-corner:before{top:0;left:0;width:18px;height:1px}.cred-corner:after{top:0;left:0;width:1px;height:18px}.cred-corner--tl{top:-1px;left:-1px}.cred-corner--tr{top:-1px;right:-1px;transform:scaleX(-1)}.cred-corner--bl{bottom:-1px;left:-1px;transform:scaleY(-1)}.cred-corner--br{bottom:-1px;right:-1px;transform:scale(-1)}.cred-icon{display:flex;justify-content:center;margin-bottom:18px;animation:cred-icon-pop .6s ease-out .15s both}.cred-title{text-align:center;font-size:13px;font-weight:700;letter-spacing:.28em;text-transform:uppercase;color:#00e5ff;margin-bottom:6px}.cred-subtitle{text-align:center;font-size:9px;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff59;margin-bottom:28px;font-family:var(--aiops-font-family, "Courier New", monospace)}.cred-field{margin-bottom:18px}.cred-label{display:block;font-size:8px;font-weight:600;letter-spacing:.22em;text-transform:uppercase;color:#00e5ff8c;margin-bottom:6px;font-family:var(--aiops-font-family, "Courier New", monospace)}.cred-input{width:100%;padding:10px 14px;border:1px solid rgba(0,229,255,.15);border-radius:4px;background:#000a19b3;color:#c8dff0;font-size:13px;font-family:var(--aiops-font-family, "Courier New", monospace);letter-spacing:.04em;outline:none;transition:border-color .25s,box-shadow .25s;box-sizing:border-box}.cred-input::placeholder{color:#c8dff02e;letter-spacing:.06em}.cred-btn{position:relative;display:block;width:100%;margin-top:26px;padding:12px 0;border:1px solid rgba(0,229,255,.3);border-radius:4px;background:#00e5ff0f;color:#00e5ff;font-size:11px;font-weight:700;letter-spacing:.3em;text-transform:uppercase;font-family:inherit;transition:background .25s,box-shadow .25s,opacity .25s;overflow:hidden}.cred-btn-glow{position:absolute;top:0;left:-100%;width:60%;height:100%;background:linear-gradient(90deg,transparent,rgba(0,229,255,.08),transparent);pointer-events:none;animation:cred-btn-shine 3s ease-in-out infinite}.cred-footer{margin-top:20px;text-align:center;font-size:7px;letter-spacing:.2em;text-transform:uppercase;color:#c8dff02e;font-family:var(--aiops-font-family, "Courier New", monospace)}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{width:100%;height:100%;overflow:hidden}body{font-family:var(--aiops-font-family, "Segoe UI", system-ui, -apple-system, sans-serif);background-color:#020810;color:#c8dff0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#1e508c66;border-radius:2px}::-webkit-scrollbar-thumb:hover{background:#22d3ee4d}*{scrollbar-width:thin;scrollbar-color:rgba(30,80,140,.4) transparent}@keyframes holo-ring-pulse{0%,to{transform:scaleY(.22) scale(1)}50%{transform:scaleY(.22) scale(.965)}}@keyframes holo-beam-pulse{0%{opacity:.75;filter:brightness(1)}40%{opacity:1;filter:brightness(1.35)}70%{opacity:.55;filter:brightness(.7)}to{opacity:.75;filter:brightness(1)}}@keyframes holo-scan{0%{top:-3px;opacity:0}6%{opacity:1}90%{opacity:.6}to{top:110%;opacity:0}}@keyframes holo-led-blink{0%,88%,to{opacity:1}93%{opacity:.15}}@keyframes holo-float{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@keyframes holo-base-spin{0%{transform:translate(-50%) scaleY(.22) rotate(0)}to{transform:translate(-50%) scaleY(.22) rotate(360deg)}}@keyframes component-drill-pulse{0%,to{box-shadow:0 0 #f253}50%{box-shadow:0 0 12px 2px #ff225526}}@keyframes comp-dialog-border-glow{0%,to{border-color:var(--dialog-color, #00e5ff)33;box-shadow:0 0 30px var(--dialog-color, #00e5ff) 18,0 12px 60px #000000bf,inset 0 0 40px #0006}50%{border-color:var(--dialog-color, #00e5ff)66;box-shadow:0 0 50px var(--dialog-color, #00e5ff) 30,0 12px 60px #000000bf,inset 0 0 40px #0006}}@keyframes comp-dialog-icon-pop{0%{opacity:0;transform:scale(.3) translateY(14px)}60%{opacity:1;transform:scale(1.08) translateY(-3px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes comp-dialog-metric-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}