react-dashstream 0.0.5 → 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 +206 -7
- package/dist/AIOPsDashboard.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/package.json +56 -56
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 {
|
|
80
|
-
|
|
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
|
|
@@ -385,7 +584,7 @@ type ComponentStatus = "online" | "warning" | "critical" | "offline";
|
|
|
385
584
|
## Theme constants
|
|
386
585
|
|
|
387
586
|
```ts
|
|
388
|
-
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";
|
|
389
588
|
```
|
|
390
589
|
|
|
391
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;
|
|
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"}
|
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:
|
|
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)}}
|
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-dashstream",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"author": "Busaud",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"description": "Dashstream is a holographic 3D infrastructure monitoring dashboard for React. Pure CSS-3D — no WebGL, no canvas, no dependencies beyond React.",
|
|
8
|
-
"keywords": [
|
|
9
|
-
"react",
|
|
10
|
-
"aiops",
|
|
11
|
-
"dashboard",
|
|
12
|
-
"dashstream",
|
|
13
|
-
"toolkit",
|
|
14
|
-
"busaud"
|
|
15
|
-
],
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"README.md"
|
|
19
|
-
],
|
|
20
|
-
"main": "./dist/index.js",
|
|
21
|
-
"module": "./dist/index.js",
|
|
22
|
-
"types": "./dist/index.d.ts",
|
|
23
|
-
"exports": {
|
|
24
|
-
".": {
|
|
25
|
-
"types": "./dist/index.d.ts",
|
|
26
|
-
"import": "./dist/index.js"
|
|
27
|
-
},
|
|
28
|
-
"./dist/index.css": "./dist/index.css"
|
|
29
|
-
},
|
|
30
|
-
"sideEffects": [
|
|
31
|
-
"*.css"
|
|
32
|
-
],
|
|
33
|
-
"scripts": {
|
|
34
|
-
"dev": "vite",
|
|
35
|
-
"build:lib": "vite build",
|
|
36
|
-
"build": "tsc && vite build",
|
|
37
|
-
"preview": "vite preview",
|
|
38
|
-
"prepublishOnly": "npm run build:lib"
|
|
39
|
-
},
|
|
40
|
-
"peerDependencies": {
|
|
41
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
42
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/react": "^19.2.14",
|
|
46
|
-
"@types/react-dom": "^19.2.3",
|
|
47
|
-
"@vitejs/plugin-react": "^5.1.4",
|
|
48
|
-
"typescript": "~5.9.3",
|
|
49
|
-
"vite": "^7.3.1",
|
|
50
|
-
"vite-plugin-dts": "^4.5.4"
|
|
51
|
-
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"react": "^19.2.4",
|
|
54
|
-
"react-dom": "^19.2.4"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "react-dashstream",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "Busaud",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"description": "Dashstream is a holographic 3D infrastructure monitoring dashboard for React. Pure CSS-3D — no WebGL, no canvas, no dependencies beyond React.",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"react",
|
|
10
|
+
"aiops",
|
|
11
|
+
"dashboard",
|
|
12
|
+
"dashstream",
|
|
13
|
+
"toolkit",
|
|
14
|
+
"busaud"
|
|
15
|
+
],
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./dist/index.css": "./dist/index.css"
|
|
29
|
+
},
|
|
30
|
+
"sideEffects": [
|
|
31
|
+
"*.css"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "vite",
|
|
35
|
+
"build:lib": "vite build",
|
|
36
|
+
"build": "tsc && vite build",
|
|
37
|
+
"preview": "vite preview",
|
|
38
|
+
"prepublishOnly": "npm run build:lib"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
42
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/react": "^19.2.14",
|
|
46
|
+
"@types/react-dom": "^19.2.3",
|
|
47
|
+
"@vitejs/plugin-react": "^5.1.4",
|
|
48
|
+
"typescript": "~5.9.3",
|
|
49
|
+
"vite": "^7.3.1",
|
|
50
|
+
"vite-plugin-dts": "^4.5.4"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"react": "^19.2.4",
|
|
54
|
+
"react-dom": "^19.2.4"
|
|
55
|
+
}
|
|
56
|
+
}
|