@i4ctime/q-ring 0.3.0 → 0.3.2
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 +19 -1
- package/dist/{chunk-HOMNGA55.js → chunk-3WTTWJYU.js} +4 -2
- package/dist/chunk-3WTTWJYU.js.map +1 -0
- package/dist/{chunk-WQPJ2FTM.js → chunk-F4SPZ774.js} +4 -2
- package/dist/chunk-F4SPZ774.js.map +1 -0
- package/dist/{dashboard-JTALLJM6.js → dashboard-QQWKOOI5.js} +158 -108
- package/dist/dashboard-QQWKOOI5.js.map +1 -0
- package/dist/{dashboard-7GII7BS2.js → dashboard-X3ONQFLV.js} +158 -108
- package/dist/dashboard-X3ONQFLV.js.map +1 -0
- package/dist/index.js +66 -45
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-HOMNGA55.js.map +0 -1
- package/dist/chunk-WQPJ2FTM.js.map +0 -1
- package/dist/dashboard-7GII7BS2.js.map +0 -1
- package/dist/dashboard-JTALLJM6.js.map +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
listSecrets,
|
|
8
8
|
queryAudit,
|
|
9
9
|
tunnelList
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-F4SPZ774.js";
|
|
11
11
|
|
|
12
12
|
// src/core/dashboard.ts
|
|
13
13
|
import { createServer } from "http";
|
|
@@ -20,25 +20,57 @@ function getDashboardHtml() {
|
|
|
20
20
|
<meta charset="utf-8"/>
|
|
21
21
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
22
22
|
<title>q-ring \u2014 quantum status</title>
|
|
23
|
+
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
|
24
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
|
25
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"/>
|
|
23
26
|
<style>
|
|
24
27
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
25
28
|
:root{
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--glow
|
|
29
|
+
--bg-deep:#04080f;
|
|
30
|
+
--bg-section:#080e18;
|
|
31
|
+
--bg-card:#0f1a2e;
|
|
32
|
+
--bg-card-hover:#142240;
|
|
33
|
+
--border:#1a2d4a;
|
|
34
|
+
--border-glow:#0ea5e9;
|
|
35
|
+
|
|
36
|
+
--text-primary:#f8f8ff;
|
|
37
|
+
--text-secondary:#c8cfe0;
|
|
38
|
+
--text-dim:#8899b4;
|
|
39
|
+
|
|
40
|
+
--accent:#0ea5e9;
|
|
41
|
+
--accent-bright:#38bdf8;
|
|
42
|
+
--accent-dim:rgba(14,165,233,0.15);
|
|
43
|
+
--accent-glow:rgba(14,165,233,0.4);
|
|
44
|
+
|
|
45
|
+
--danger:#ff5e5b;
|
|
46
|
+
--warning:#fbbf24;
|
|
47
|
+
--green:#22c55e;
|
|
48
|
+
--violet:#a855f7;
|
|
49
|
+
--pink:#ff0055;
|
|
50
|
+
|
|
51
|
+
--font-display:'Outfit',sans-serif;
|
|
52
|
+
--font-mono:'JetBrains Mono',monospace;
|
|
53
|
+
|
|
32
54
|
--radius:12px;
|
|
55
|
+
--radius-sm:8px;
|
|
56
|
+
--radius-lg:20px;
|
|
57
|
+
|
|
58
|
+
--glass-bg:rgba(15,26,46,0.65);
|
|
59
|
+
--glass-border:rgba(26,45,74,0.8);
|
|
60
|
+
--shadow-card:0 4px 24px -1px rgba(0,0,0,0.4),0 0 1px rgba(255,255,255,0.04);
|
|
61
|
+
--shadow-glow:0 0 20px rgba(14,165,233,0.12),0 0 40px rgba(168,85,247,0.08);
|
|
33
62
|
}
|
|
34
|
-
html{background:var(--
|
|
63
|
+
html{background:var(--bg-deep);color:var(--text-primary);font-family:var(--font-display);font-size:16px;line-height:1.5}
|
|
35
64
|
body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
36
65
|
|
|
66
|
+
/* Hidden SVG defs for gradient icons */
|
|
67
|
+
.svg-defs{position:absolute;width:0;height:0;overflow:hidden}
|
|
68
|
+
|
|
37
69
|
/* Mesh blobs */
|
|
38
|
-
.blob{position:fixed;border-radius:50%;filter:blur(100px);opacity:.
|
|
39
|
-
.blob-1{width:600px;height:600px;top:-120px;left:-100px;background:radial-gradient(circle,var(--
|
|
70
|
+
.blob{position:fixed;border-radius:50%;filter:blur(100px);opacity:.12;pointer-events:none;z-index:0}
|
|
71
|
+
.blob-1{width:600px;height:600px;top:-120px;left:-100px;background:radial-gradient(circle,var(--accent),transparent 70%);animation:drift1 22s ease-in-out infinite}
|
|
40
72
|
.blob-2{width:500px;height:500px;bottom:-80px;right:-60px;background:radial-gradient(circle,var(--violet),transparent 70%);animation:drift2 26s ease-in-out infinite}
|
|
41
|
-
.blob-3{width:350px;height:350px;top:40%;left:50%;background:radial-gradient(circle,var(--
|
|
73
|
+
.blob-3{width:350px;height:350px;top:40%;left:50%;background:radial-gradient(circle,var(--green),transparent 70%);animation:drift3 30s ease-in-out infinite;opacity:.06}
|
|
42
74
|
@keyframes drift1{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(60px,-40px) scale(1.08)}66%{transform:translate(-30px,50px) scale(.95)}}
|
|
43
75
|
@keyframes drift2{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(-50px,30px) scale(1.05)}66%{transform:translate(40px,-60px) scale(.92)}}
|
|
44
76
|
@keyframes drift3{0%,100%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-40%,-60%) scale(1.1)}}
|
|
@@ -46,87 +78,89 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
46
78
|
.container{position:relative;z-index:1;max-width:1280px;margin:0 auto;padding:24px 20px 48px}
|
|
47
79
|
|
|
48
80
|
/* Header */
|
|
49
|
-
.header{display:flex;align-items:center;justify-content:space-between;margin-bottom:28px}
|
|
50
|
-
.header h1{font-size:1.
|
|
51
|
-
.header h1
|
|
52
|
-
.
|
|
53
|
-
.
|
|
81
|
+
.header{display:flex;align-items:center;justify-content:space-between;margin-bottom:28px;padding:16px 20px;background:rgba(4,8,15,0.75);backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);border:1px solid var(--border);border-radius:var(--radius)}
|
|
82
|
+
.header h1{font-family:var(--font-display);font-size:1.65rem;font-weight:700;letter-spacing:-.02em;display:flex;align-items:center;gap:10px}
|
|
83
|
+
.header h1 .q-icon{display:flex;filter:drop-shadow(0 0 6px rgba(14,165,233,0.6))}
|
|
84
|
+
.header h1 .brand{background:linear-gradient(135deg,#00D1FF,var(--violet));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
|
|
85
|
+
.header h1 .sub{color:var(--text-dim);font-weight:400;font-size:1.1rem;-webkit-text-fill-color:var(--text-dim)}
|
|
86
|
+
.status-dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);display:inline-block;animation:pulse 2s ease-in-out infinite}
|
|
87
|
+
.status-dot.disconnected{background:var(--danger);box-shadow:0 0 8px var(--danger)}
|
|
54
88
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
|
55
|
-
.conn-label{font-size:.
|
|
89
|
+
.conn-label{font-size:.85rem;color:var(--text-dim);display:flex;align-items:center;gap:6px;font-family:var(--font-mono)}
|
|
56
90
|
|
|
57
91
|
/* Grid */
|
|
58
92
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:16px}
|
|
59
93
|
.grid-wide{grid-column:1/-1}
|
|
60
94
|
|
|
61
|
-
/* Cards */
|
|
62
|
-
.card{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow-
|
|
63
|
-
.card:hover{border-color:
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
/* Cards \u2014 reveal animation */
|
|
96
|
+
.card{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow-card);transition:border-color .3s,box-shadow .3s,transform .3s;opacity:0;transform:translateY(16px);animation:cardReveal .5s cubic-bezier(0.16,1,0.3,1) forwards}
|
|
97
|
+
.card:hover{border-color:var(--border-glow);box-shadow:var(--shadow-card),var(--shadow-glow);transform:translateY(-2px)}
|
|
98
|
+
@keyframes cardReveal{to{opacity:1;transform:translateY(0)}}
|
|
99
|
+
|
|
100
|
+
.card-title{font-family:var(--font-display);font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;color:var(--text-dim);margin-bottom:14px;display:flex;align-items:center;gap:8px;font-weight:600}
|
|
101
|
+
.card-title svg{width:16px;height:16px;flex-shrink:0;filter:drop-shadow(0 0 4px rgba(14,165,233,0.4))}
|
|
66
102
|
|
|
67
103
|
/* Health donut */
|
|
68
104
|
.health-row{display:flex;align-items:center;gap:24px}
|
|
69
105
|
.donut-wrap{position:relative;width:100px;height:100px;flex-shrink:0}
|
|
70
106
|
.donut-wrap svg{transform:rotate(-90deg)}
|
|
71
|
-
.donut-wrap .donut-label{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;font-size:1.5rem;font-weight:700;line-height:1}
|
|
72
|
-
.donut-wrap .donut-label small{font-size:.
|
|
107
|
+
.donut-wrap .donut-label{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;font-size:1.5rem;font-weight:700;line-height:1;font-family:var(--font-display)}
|
|
108
|
+
.donut-wrap .donut-label small{font-size:.7rem;color:var(--text-dim);font-weight:400;margin-top:2px;letter-spacing:.04em}
|
|
73
109
|
.health-legend{display:flex;flex-direction:column;gap:6px}
|
|
74
|
-
.legend-item{display:flex;align-items:center;gap:8px;font-size:.
|
|
110
|
+
.legend-item{display:flex;align-items:center;gap:8px;font-size:.88rem}
|
|
75
111
|
.legend-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
76
112
|
|
|
77
113
|
/* Decay bars */
|
|
78
114
|
.decay-list{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow-y:auto}
|
|
79
115
|
.decay-item{display:flex;align-items:center;gap:10px}
|
|
80
|
-
.decay-key{font-family:
|
|
116
|
+
.decay-key{font-family:var(--font-mono);font-size:.85rem;min-width:120px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
81
117
|
.decay-bar{flex:1;height:6px;border-radius:3px;background:rgba(255,255,255,0.06);overflow:hidden;position:relative}
|
|
82
118
|
.decay-fill{height:100%;border-radius:3px;transition:width .6s ease}
|
|
83
|
-
.decay-time{font-size:.
|
|
119
|
+
.decay-time{font-size:.8rem;color:var(--text-dim);min-width:56px;text-align:right;font-family:var(--font-mono)}
|
|
84
120
|
|
|
85
121
|
/* Superposition pills */
|
|
86
122
|
.super-list{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow-y:auto}
|
|
87
123
|
.super-item{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
88
|
-
.super-key{font-family:
|
|
89
|
-
.env-pill{font-size:.
|
|
124
|
+
.super-key{font-family:var(--font-mono);font-size:.85rem;min-width:100px}
|
|
125
|
+
.env-pill{font-size:.75rem;padding:2px 8px;border-radius:99px;font-weight:600;letter-spacing:.03em;font-family:var(--font-mono)}
|
|
90
126
|
.env-prod{background:rgba(255,0,85,0.2);color:var(--pink);border:1px solid rgba(255,0,85,0.3)}
|
|
91
|
-
.env-staging{background:rgba(
|
|
92
|
-
.env-dev{background:rgba(
|
|
93
|
-
.env-default{background:rgba(
|
|
127
|
+
.env-staging{background:rgba(251,191,36,0.15);color:var(--warning);border:1px solid rgba(251,191,36,0.25)}
|
|
128
|
+
.env-dev{background:rgba(34,197,94,0.15);color:var(--green);border:1px solid rgba(34,197,94,0.25)}
|
|
129
|
+
.env-default{background:rgba(168,85,247,0.15);color:var(--violet);border:1px solid rgba(168,85,247,0.25)}
|
|
94
130
|
|
|
95
131
|
/* Entanglement */
|
|
96
132
|
.entangle-list{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow-y:auto}
|
|
97
|
-
.entangle-pair{display:flex;align-items:center;gap:8px;font-family:
|
|
98
|
-
.entangle-arrow{color:var(--
|
|
133
|
+
.entangle-pair{display:flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:.85rem}
|
|
134
|
+
.entangle-arrow{color:var(--accent-bright)}
|
|
99
135
|
|
|
100
136
|
/* Tunnels */
|
|
101
137
|
.tunnel-list{display:flex;flex-direction:column;gap:8px;max-height:240px;overflow-y:auto}
|
|
102
|
-
.tunnel-card{background:rgba(
|
|
103
|
-
.tunnel-meta{display:flex;gap:12px;margin-top:4px;font-size:.
|
|
138
|
+
.tunnel-card{background:rgba(168,85,247,0.06);border:1px solid rgba(168,85,247,0.15);border-radius:var(--radius-sm);padding:10px 12px;font-size:.85rem;font-family:var(--font-mono)}
|
|
139
|
+
.tunnel-meta{display:flex;gap:12px;margin-top:4px;font-size:.8rem;color:var(--text-dim)}
|
|
104
140
|
|
|
105
141
|
/* Audit feed */
|
|
106
|
-
.audit-feed{display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;font-size:.
|
|
142
|
+
.audit-feed{display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;font-size:.85rem;font-family:var(--font-mono)}
|
|
107
143
|
.audit-row{display:flex;gap:8px;padding:3px 0;border-bottom:1px solid rgba(255,255,255,0.03)}
|
|
108
|
-
.audit-ts{color:var(--
|
|
144
|
+
.audit-ts{color:var(--text-dim);min-width:70px;flex-shrink:0}
|
|
109
145
|
.audit-action{min-width:64px;font-weight:600}
|
|
110
|
-
.audit-action.read{color:var(--
|
|
111
|
-
.audit-action.entangle{color:var(--violet)}.audit-action.tunnel{color:var(--violet)}.audit-action.teleport{color:var(--
|
|
112
|
-
.audit-action.generate{color:var(--
|
|
113
|
-
.audit-action.collapse{color:var(--
|
|
114
|
-
.audit-key{color:var(--ghost)}
|
|
115
|
-
.audit-detail{color:var(--slate);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
146
|
+
.audit-action.read{color:var(--accent)}.audit-action.write{color:var(--green)}.audit-action.delete{color:var(--danger)}
|
|
147
|
+
.audit-action.entangle{color:var(--violet)}.audit-action.tunnel{color:var(--violet)}.audit-action.teleport{color:var(--warning)}
|
|
148
|
+
.audit-action.generate{color:var(--warning)}.audit-action.list{color:var(--text-dim)}.audit-action.export{color:var(--text-dim)}
|
|
149
|
+
.audit-action.collapse{color:var(--accent)}
|
|
116
150
|
|
|
117
151
|
/* Anomalies */
|
|
118
|
-
.anomaly-card{background:rgba(255,
|
|
119
|
-
@keyframes anomaly-pulse{0%,100%{border-color:rgba(255,
|
|
120
|
-
.anomaly-type{font-size:.
|
|
121
|
-
.anomaly-desc{font-size:.
|
|
152
|
+
.anomaly-card{background:rgba(255,94,91,0.06);border:1px solid rgba(255,94,91,0.15);border-radius:var(--radius-sm);padding:10px 14px;animation:anomaly-pulse 3s ease-in-out infinite}
|
|
153
|
+
@keyframes anomaly-pulse{0%,100%{border-color:rgba(255,94,91,0.15)}50%{border-color:rgba(255,94,91,0.4)}}
|
|
154
|
+
.anomaly-type{font-size:.8rem;text-transform:uppercase;letter-spacing:.06em;color:var(--danger);font-weight:700;margin-bottom:2px;font-family:var(--font-display)}
|
|
155
|
+
.anomaly-desc{font-size:.88rem;color:var(--text-primary)}
|
|
122
156
|
|
|
123
157
|
/* Environment badge */
|
|
124
158
|
.env-status{display:flex;align-items:center;gap:12px}
|
|
125
|
-
.env-big{font-size:1.1rem;font-weight:700;padding:4px 14px;border-radius:
|
|
126
|
-
.env-source{font-size:.
|
|
159
|
+
.env-big{font-size:1.1rem;font-weight:700;padding:4px 14px;border-radius:var(--radius-sm)}
|
|
160
|
+
.env-source{font-size:.85rem;color:var(--text-dim)}
|
|
127
161
|
|
|
128
162
|
/* Empty states */
|
|
129
|
-
.empty{color:var(--
|
|
163
|
+
.empty{color:var(--text-dim);font-size:.88rem;font-style:italic;padding:8px 0}
|
|
130
164
|
|
|
131
165
|
/* Scrollbar */
|
|
132
166
|
::-webkit-scrollbar{width:4px;height:4px}
|
|
@@ -136,12 +170,27 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
136
170
|
</style>
|
|
137
171
|
</head>
|
|
138
172
|
<body>
|
|
173
|
+
|
|
174
|
+
<!-- Shared SVG gradient definition -->
|
|
175
|
+
<svg class="svg-defs" aria-hidden="true" focusable="false">
|
|
176
|
+
<defs>
|
|
177
|
+
<linearGradient id="neon-grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
178
|
+
<stop offset="0%" stop-color="#00D1FF"/>
|
|
179
|
+
<stop offset="100%" stop-color="#a855f7"/>
|
|
180
|
+
</linearGradient>
|
|
181
|
+
</defs>
|
|
182
|
+
</svg>
|
|
183
|
+
|
|
139
184
|
<div class="blob blob-1"></div>
|
|
140
185
|
<div class="blob blob-2"></div>
|
|
141
186
|
<div class="blob blob-3"></div>
|
|
142
187
|
<div class="container">
|
|
143
188
|
<div class="header">
|
|
144
|
-
<h1
|
|
189
|
+
<h1>
|
|
190
|
+
<span class="q-icon"><img src="https://i4ctime.github.io/quantum_ring/assets/icon.png" alt="q-ring" width="28" height="28" style="display:block;border-radius:4px"/></span>
|
|
191
|
+
<span class="brand">q-ring</span>
|
|
192
|
+
<span class="sub">quantum status</span>
|
|
193
|
+
</h1>
|
|
145
194
|
<div class="conn-label"><span class="status-dot" id="connDot"></span><span id="connText">connecting\u2026</span></div>
|
|
146
195
|
</div>
|
|
147
196
|
<div class="grid" id="dashboard">
|
|
@@ -155,7 +204,18 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
155
204
|
const dash = $('dashboard');
|
|
156
205
|
const dot = $('connDot');
|
|
157
206
|
const connText = $('connText');
|
|
158
|
-
|
|
207
|
+
|
|
208
|
+
/* --- SVG icon library (matching gh-pages neon gradient) --- */
|
|
209
|
+
const icons = {
|
|
210
|
+
health: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>',
|
|
211
|
+
environment: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>',
|
|
212
|
+
decay: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/></svg>',
|
|
213
|
+
superposition: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>',
|
|
214
|
+
entangle: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>',
|
|
215
|
+
tunnel: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 10h.01"/><path d="M15 10h.01"/><path d="M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z"/></svg>',
|
|
216
|
+
anomaly: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
|
|
217
|
+
audit: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="url(#neon-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>'
|
|
218
|
+
};
|
|
159
219
|
|
|
160
220
|
function esc(s){ const d=document.createElement('div');d.textContent=s;return d.innerHTML; }
|
|
161
221
|
|
|
@@ -167,28 +227,25 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
167
227
|
}
|
|
168
228
|
|
|
169
229
|
function decayColor(pct,expired){
|
|
170
|
-
if(expired) return 'var(--
|
|
171
|
-
if(pct>=90) return 'var(--
|
|
172
|
-
if(pct>=75) return 'var(--
|
|
173
|
-
return 'var(--
|
|
230
|
+
if(expired) return 'var(--danger)';
|
|
231
|
+
if(pct>=90) return 'var(--danger)';
|
|
232
|
+
if(pct>=75) return 'var(--warning)';
|
|
233
|
+
return 'var(--accent)';
|
|
174
234
|
}
|
|
175
235
|
|
|
176
236
|
function fmtTime(ts){
|
|
177
237
|
const d=new Date(ts);
|
|
178
|
-
|
|
179
|
-
const m=d.getMinutes().toString().padStart(2,'0');
|
|
180
|
-
const s=d.getSeconds().toString().padStart(2,'0');
|
|
181
|
-
return h+':'+m+':'+s;
|
|
238
|
+
return d.getHours().toString().padStart(2,'0')+':'+d.getMinutes().toString().padStart(2,'0')+':'+d.getSeconds().toString().padStart(2,'0');
|
|
182
239
|
}
|
|
183
240
|
|
|
184
241
|
function renderHealth(h){
|
|
185
242
|
const total=h.total||1;
|
|
186
243
|
const r=42, circ=2*Math.PI*r;
|
|
187
244
|
const slices=[
|
|
188
|
-
{v:h.healthy,c:'var(--
|
|
189
|
-
{v:h.stale,c:'var(--
|
|
190
|
-
{v:h.expired,c:'var(--
|
|
191
|
-
{v:h.noDecay,c:'var(--
|
|
245
|
+
{v:h.healthy,c:'var(--accent)'},
|
|
246
|
+
{v:h.stale,c:'var(--warning)'},
|
|
247
|
+
{v:h.expired,c:'var(--danger)'},
|
|
248
|
+
{v:h.noDecay,c:'var(--text-dim)'}
|
|
192
249
|
];
|
|
193
250
|
let offset=0;
|
|
194
251
|
let rings='';
|
|
@@ -204,10 +261,10 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
204
261
|
<div class="donut-label">\${h.total}<small>secrets</small></div>
|
|
205
262
|
</div>
|
|
206
263
|
<div class="health-legend">
|
|
207
|
-
<div class="legend-item"><span class="legend-dot" style="background:var(--
|
|
208
|
-
<div class="legend-item"><span class="legend-dot" style="background:var(--
|
|
209
|
-
<div class="legend-item"><span class="legend-dot" style="background:var(--
|
|
210
|
-
<div class="legend-item"><span class="legend-dot" style="background:var(--
|
|
264
|
+
<div class="legend-item"><span class="legend-dot" style="background:var(--accent)"></span>Healthy \${h.healthy}</div>
|
|
265
|
+
<div class="legend-item"><span class="legend-dot" style="background:var(--warning)"></span>Stale \${h.stale}</div>
|
|
266
|
+
<div class="legend-item"><span class="legend-dot" style="background:var(--danger)"></span>Expired \${h.expired}</div>
|
|
267
|
+
<div class="legend-item"><span class="legend-dot" style="background:var(--text-dim)"></span>No decay \${h.noDecay}</div>
|
|
211
268
|
</div>
|
|
212
269
|
</div>\`;
|
|
213
270
|
}
|
|
@@ -290,47 +347,41 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
290
347
|
</div>\`;
|
|
291
348
|
}
|
|
292
349
|
|
|
350
|
+
const panels = [
|
|
351
|
+
{ id:'p-health', icon:icons.health, label:'Health Summary', wide:false, render:s=>renderHealth(s.health) },
|
|
352
|
+
{ id:'p-env', icon:icons.environment, label:'Environment', wide:false, render:s=>renderEnvironment(s.environment) },
|
|
353
|
+
{ id:'p-decay', icon:icons.decay, label:'Decay Timers', wide:false, render:s=>renderDecay(s.secrets) },
|
|
354
|
+
{ id:'p-super', icon:icons.superposition, label:'Superposition States', wide:false, render:s=>renderSuperposition(s.secrets) },
|
|
355
|
+
{ id:'p-ent', icon:icons.entangle, label:'Entanglement', wide:false, render:s=>renderEntanglements(s.entanglements) },
|
|
356
|
+
{ id:'p-tunnel', icon:icons.tunnel, label:'Quantum Tunnels', wide:false, render:s=>renderTunnels(s.tunnels) },
|
|
357
|
+
{ id:'p-anomaly', icon:icons.anomaly, label:'Anomaly Alerts', wide:true, render:s=>renderAnomalies(s.anomalies) },
|
|
358
|
+
{ id:'p-audit', icon:icons.audit, label:'Audit Log', wide:true, render:s=>renderAudit(s.audit) }
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
let initialised = false;
|
|
362
|
+
|
|
293
363
|
function render(snap){
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
\${
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
</div>
|
|
311
|
-
<div class="card">
|
|
312
|
-
<div class="card-title"><span class="icon">\u{1F517}</span> Entanglement</div>
|
|
313
|
-
\${renderEntanglements(snap.entanglements)}
|
|
314
|
-
</div>
|
|
315
|
-
<div class="card">
|
|
316
|
-
<div class="card-title"><span class="icon">\u{1F47B}</span> Quantum Tunnels</div>
|
|
317
|
-
\${renderTunnels(snap.tunnels)}
|
|
318
|
-
</div>
|
|
319
|
-
<div class="card grid-wide">
|
|
320
|
-
<div class="card-title"><span class="icon">\u26A0\uFE0F</span> Anomaly Alerts</div>
|
|
321
|
-
\${renderAnomalies(snap.anomalies)}
|
|
322
|
-
</div>
|
|
323
|
-
<div class="card grid-wide">
|
|
324
|
-
<div class="card-title"><span class="icon">\u{1F441}\uFE0F</span> Audit Log</div>
|
|
325
|
-
\${renderAudit(snap.audit)}
|
|
326
|
-
</div>
|
|
327
|
-
\`;
|
|
364
|
+
if(!initialised){
|
|
365
|
+
initialised = true;
|
|
366
|
+
dash.innerHTML = panels.map((p,i) =>
|
|
367
|
+
\`<div class="card\${p.wide?' grid-wide':''}" id="\${p.id}" style="animation-delay:\${i*60}ms">
|
|
368
|
+
<div class="card-title">\${p.icon} \${p.label}</div>
|
|
369
|
+
<div class="card-body" id="\${p.id}-body"></div>
|
|
370
|
+
</div>\`
|
|
371
|
+
).join('');
|
|
372
|
+
}
|
|
373
|
+
for(const p of panels){
|
|
374
|
+
const body = $(\`\${p.id}-body\`);
|
|
375
|
+
if(body){
|
|
376
|
+
const html = p.render(snap);
|
|
377
|
+
if(body._prev !== html){ body.innerHTML = html; body._prev = html; }
|
|
378
|
+
}
|
|
379
|
+
}
|
|
328
380
|
}
|
|
329
381
|
|
|
330
382
|
function connect(){
|
|
331
383
|
const es=new EventSource('/events');
|
|
332
384
|
es.onopen=()=>{
|
|
333
|
-
connected=true;
|
|
334
385
|
dot.classList.remove('disconnected');
|
|
335
386
|
connText.textContent='live';
|
|
336
387
|
};
|
|
@@ -338,7 +389,6 @@ body{min-height:100vh;overflow-x:hidden;position:relative}
|
|
|
338
389
|
try{ render(JSON.parse(e.data)); }catch(err){ console.error('render error',err); }
|
|
339
390
|
};
|
|
340
391
|
es.onerror=()=>{
|
|
341
|
-
connected=false;
|
|
342
392
|
dot.classList.add('disconnected');
|
|
343
393
|
connText.textContent='reconnecting\u2026';
|
|
344
394
|
};
|
|
@@ -378,7 +428,7 @@ function toSecretSnapshot(entry) {
|
|
|
378
428
|
};
|
|
379
429
|
}
|
|
380
430
|
function collectSnapshot() {
|
|
381
|
-
const entries = listSecrets({ source: "api" });
|
|
431
|
+
const entries = listSecrets({ source: "api", silent: true });
|
|
382
432
|
const secrets = entries.map(toSecretSnapshot);
|
|
383
433
|
let healthy = 0;
|
|
384
434
|
let stale = 0;
|
|
@@ -401,7 +451,7 @@ function collectSnapshot() {
|
|
|
401
451
|
health: { healthy, stale, expired, noDecay, total: secrets.length },
|
|
402
452
|
entanglements: listEntanglements(),
|
|
403
453
|
tunnels: tunnelList(),
|
|
404
|
-
audit: queryAudit({ limit: 50 }),
|
|
454
|
+
audit: queryAudit({ limit: 50 }).filter((e) => e.action !== "list"),
|
|
405
455
|
anomalies: detectAnomalies(),
|
|
406
456
|
environment: collapseEnvironment()
|
|
407
457
|
};
|
|
@@ -479,4 +529,4 @@ export {
|
|
|
479
529
|
collectSnapshot,
|
|
480
530
|
startDashboardServer
|
|
481
531
|
};
|
|
482
|
-
//# sourceMappingURL=dashboard-
|
|
532
|
+
//# sourceMappingURL=dashboard-X3ONQFLV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/dashboard.ts","../src/core/dashboard-html.ts"],"sourcesContent":["/**\n * Quantum Status Dashboard: local HTTP server with SSE live updates.\n *\n * Collects a full snapshot of all quantum state every few seconds and\n * pushes it to connected browsers. Never exposes secret values.\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse, type Server } from \"node:http\";\nimport { listSecrets } from \"./keyring.js\";\nimport { checkDecay, type DecayStatus, type QuantumEnvelope } from \"./envelope.js\";\nimport { listEntanglements, type EntanglementPair } from \"./entanglement.js\";\nimport { tunnelList } from \"./tunnel.js\";\nimport { queryAudit, detectAnomalies, type AuditEvent, type AccessAnomaly } from \"./observer.js\";\nimport { collapseEnvironment, type CollapseResult } from \"./collapse.js\";\nimport { getDashboardHtml } from \"./dashboard-html.js\";\n\nexport interface SecretSnapshot {\n key: string;\n scope: string;\n type: \"collapsed\" | \"superposition\";\n environments?: string[];\n defaultEnv?: string;\n decay: DecayStatus;\n accessCount: number;\n lastAccessedAt?: string;\n createdAt: string;\n updatedAt: string;\n description?: string;\n tags?: string[];\n entangled?: { service: string; key: string }[];\n}\n\nexport interface TunnelSnapshot {\n id: string;\n createdAt: number;\n expiresAt?: number;\n accessCount: number;\n maxReads?: number;\n}\n\nexport interface DashboardSnapshot {\n timestamp: string;\n secrets: SecretSnapshot[];\n health: { healthy: number; stale: number; expired: number; noDecay: number; total: number };\n entanglements: EntanglementPair[];\n tunnels: TunnelSnapshot[];\n audit: AuditEvent[];\n anomalies: AccessAnomaly[];\n environment: CollapseResult | null;\n}\n\nfunction toSecretSnapshot(entry: {\n key: string;\n scope: string;\n envelope?: QuantumEnvelope;\n decay?: DecayStatus;\n}): SecretSnapshot {\n const envelope = entry.envelope;\n const decay = envelope ? checkDecay(envelope) : {\n isExpired: false,\n isStale: false,\n lifetimePercent: 0,\n secondsRemaining: null,\n timeRemaining: null,\n };\n\n return {\n key: entry.key,\n scope: entry.scope,\n type: envelope?.states ? \"superposition\" : \"collapsed\",\n environments: envelope?.states ? Object.keys(envelope.states) : undefined,\n defaultEnv: envelope?.defaultEnv,\n decay,\n accessCount: envelope?.meta.accessCount ?? 0,\n lastAccessedAt: envelope?.meta.lastAccessedAt,\n createdAt: envelope?.meta.createdAt ?? \"\",\n updatedAt: envelope?.meta.updatedAt ?? \"\",\n description: envelope?.meta.description,\n tags: envelope?.meta.tags,\n entangled: envelope?.meta.entangled,\n };\n}\n\nexport function collectSnapshot(): DashboardSnapshot {\n const entries = listSecrets({ source: \"api\", silent: true });\n\n const secrets = entries.map(toSecretSnapshot);\n\n let healthy = 0;\n let stale = 0;\n let expired = 0;\n let noDecay = 0;\n\n for (const s of secrets) {\n if (!s.decay.timeRemaining) {\n noDecay++;\n } else if (s.decay.isExpired) {\n expired++;\n } else if (s.decay.isStale) {\n stale++;\n } else {\n healthy++;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n secrets,\n health: { healthy, stale, expired, noDecay, total: secrets.length },\n entanglements: listEntanglements(),\n tunnels: tunnelList(),\n audit: queryAudit({ limit: 50 }).filter(e => e.action !== \"list\"),\n anomalies: detectAnomalies(),\n environment: collapseEnvironment(),\n };\n}\n\nexport interface DashboardServerOptions {\n port?: number;\n}\n\nexport function startDashboardServer(\n options: DashboardServerOptions = {},\n): { port: number; close: () => void; server: Server } {\n const port = options.port ?? 9876;\n const clients = new Set<ServerResponse>();\n let intervalHandle: ReturnType<typeof setInterval> | null = null;\n\n const html = getDashboardHtml();\n\n function broadcast() {\n const snapshot = collectSnapshot();\n const data = `data: ${JSON.stringify(snapshot)}\\n\\n`;\n for (const res of clients) {\n try {\n res.write(data);\n } catch {\n clients.delete(res);\n }\n }\n }\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = req.url ?? \"/\";\n\n if (url === \"/events\") {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n // Send initial snapshot immediately\n const snapshot = collectSnapshot();\n res.write(`data: ${JSON.stringify(snapshot)}\\n\\n`);\n\n clients.add(res);\n req.on(\"close\", () => clients.delete(res));\n return;\n }\n\n if (url === \"/api/status\") {\n const snapshot = collectSnapshot();\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n res.end(JSON.stringify(snapshot, null, 2));\n return;\n }\n\n // Serve the dashboard HTML\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n });\n\n server.listen(port, \"127.0.0.1\", () => {\n intervalHandle = setInterval(broadcast, 5000);\n if (intervalHandle && typeof intervalHandle === \"object\" && \"unref\" in intervalHandle) {\n intervalHandle.unref();\n }\n });\n\n return {\n port,\n close: () => {\n if (intervalHandle) clearInterval(intervalHandle);\n for (const res of clients) {\n try { res.end(); } catch { /* noop */ }\n }\n clients.clear();\n server.close();\n },\n server,\n };\n}\n","/**\n * Self-contained HTML dashboard for q-ring quantum status.\n * Matches the gh-pages site design system: Outfit + JetBrains Mono fonts,\n * deep navy palette, neon cyan→violet SVG icons, glassmorphism cards.\n *\n * Zero dependencies — inline CSS + vanilla JS with EventSource for SSE.\n */\n\nexport function getDashboardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n<title>q-ring — quantum status</title>\n<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\"/>\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin/>\n<link href=\"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Outfit:wght@300;400;500;600;700;800&display=swap\" rel=\"stylesheet\"/>\n<style>\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\n:root{\n --bg-deep:#04080f;\n --bg-section:#080e18;\n --bg-card:#0f1a2e;\n --bg-card-hover:#142240;\n --border:#1a2d4a;\n --border-glow:#0ea5e9;\n\n --text-primary:#f8f8ff;\n --text-secondary:#c8cfe0;\n --text-dim:#8899b4;\n\n --accent:#0ea5e9;\n --accent-bright:#38bdf8;\n --accent-dim:rgba(14,165,233,0.15);\n --accent-glow:rgba(14,165,233,0.4);\n\n --danger:#ff5e5b;\n --warning:#fbbf24;\n --green:#22c55e;\n --violet:#a855f7;\n --pink:#ff0055;\n\n --font-display:'Outfit',sans-serif;\n --font-mono:'JetBrains Mono',monospace;\n\n --radius:12px;\n --radius-sm:8px;\n --radius-lg:20px;\n\n --glass-bg:rgba(15,26,46,0.65);\n --glass-border:rgba(26,45,74,0.8);\n --shadow-card:0 4px 24px -1px rgba(0,0,0,0.4),0 0 1px rgba(255,255,255,0.04);\n --shadow-glow:0 0 20px rgba(14,165,233,0.12),0 0 40px rgba(168,85,247,0.08);\n}\nhtml{background:var(--bg-deep);color:var(--text-primary);font-family:var(--font-display);font-size:16px;line-height:1.5}\nbody{min-height:100vh;overflow-x:hidden;position:relative}\n\n/* Hidden SVG defs for gradient icons */\n.svg-defs{position:absolute;width:0;height:0;overflow:hidden}\n\n/* Mesh blobs */\n.blob{position:fixed;border-radius:50%;filter:blur(100px);opacity:.12;pointer-events:none;z-index:0}\n.blob-1{width:600px;height:600px;top:-120px;left:-100px;background:radial-gradient(circle,var(--accent),transparent 70%);animation:drift1 22s ease-in-out infinite}\n.blob-2{width:500px;height:500px;bottom:-80px;right:-60px;background:radial-gradient(circle,var(--violet),transparent 70%);animation:drift2 26s ease-in-out infinite}\n.blob-3{width:350px;height:350px;top:40%;left:50%;background:radial-gradient(circle,var(--green),transparent 70%);animation:drift3 30s ease-in-out infinite;opacity:.06}\n@keyframes drift1{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(60px,-40px) scale(1.08)}66%{transform:translate(-30px,50px) scale(.95)}}\n@keyframes drift2{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(-50px,30px) scale(1.05)}66%{transform:translate(40px,-60px) scale(.92)}}\n@keyframes drift3{0%,100%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-40%,-60%) scale(1.1)}}\n\n.container{position:relative;z-index:1;max-width:1280px;margin:0 auto;padding:24px 20px 48px}\n\n/* Header */\n.header{display:flex;align-items:center;justify-content:space-between;margin-bottom:28px;padding:16px 20px;background:rgba(4,8,15,0.75);backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);border:1px solid var(--border);border-radius:var(--radius)}\n.header h1{font-family:var(--font-display);font-size:1.65rem;font-weight:700;letter-spacing:-.02em;display:flex;align-items:center;gap:10px}\n.header h1 .q-icon{display:flex;filter:drop-shadow(0 0 6px rgba(14,165,233,0.6))}\n.header h1 .brand{background:linear-gradient(135deg,#00D1FF,var(--violet));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}\n.header h1 .sub{color:var(--text-dim);font-weight:400;font-size:1.1rem;-webkit-text-fill-color:var(--text-dim)}\n.status-dot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);display:inline-block;animation:pulse 2s ease-in-out infinite}\n.status-dot.disconnected{background:var(--danger);box-shadow:0 0 8px var(--danger)}\n@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}\n.conn-label{font-size:.85rem;color:var(--text-dim);display:flex;align-items:center;gap:6px;font-family:var(--font-mono)}\n\n/* Grid */\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:16px}\n.grid-wide{grid-column:1/-1}\n\n/* Cards — reveal animation */\n.card{background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--glass-border);border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow-card);transition:border-color .3s,box-shadow .3s,transform .3s;opacity:0;transform:translateY(16px);animation:cardReveal .5s cubic-bezier(0.16,1,0.3,1) forwards}\n.card:hover{border-color:var(--border-glow);box-shadow:var(--shadow-card),var(--shadow-glow);transform:translateY(-2px)}\n@keyframes cardReveal{to{opacity:1;transform:translateY(0)}}\n\n.card-title{font-family:var(--font-display);font-size:.8rem;text-transform:uppercase;letter-spacing:.1em;color:var(--text-dim);margin-bottom:14px;display:flex;align-items:center;gap:8px;font-weight:600}\n.card-title svg{width:16px;height:16px;flex-shrink:0;filter:drop-shadow(0 0 4px rgba(14,165,233,0.4))}\n\n/* Health donut */\n.health-row{display:flex;align-items:center;gap:24px}\n.donut-wrap{position:relative;width:100px;height:100px;flex-shrink:0}\n.donut-wrap svg{transform:rotate(-90deg)}\n.donut-wrap .donut-label{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;font-size:1.5rem;font-weight:700;line-height:1;font-family:var(--font-display)}\n.donut-wrap .donut-label small{font-size:.7rem;color:var(--text-dim);font-weight:400;margin-top:2px;letter-spacing:.04em}\n.health-legend{display:flex;flex-direction:column;gap:6px}\n.legend-item{display:flex;align-items:center;gap:8px;font-size:.88rem}\n.legend-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n\n/* Decay bars */\n.decay-list{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow-y:auto}\n.decay-item{display:flex;align-items:center;gap:10px}\n.decay-key{font-family:var(--font-mono);font-size:.85rem;min-width:120px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.decay-bar{flex:1;height:6px;border-radius:3px;background:rgba(255,255,255,0.06);overflow:hidden;position:relative}\n.decay-fill{height:100%;border-radius:3px;transition:width .6s ease}\n.decay-time{font-size:.8rem;color:var(--text-dim);min-width:56px;text-align:right;font-family:var(--font-mono)}\n\n/* Superposition pills */\n.super-list{display:flex;flex-direction:column;gap:8px;max-height:280px;overflow-y:auto}\n.super-item{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.super-key{font-family:var(--font-mono);font-size:.85rem;min-width:100px}\n.env-pill{font-size:.75rem;padding:2px 8px;border-radius:99px;font-weight:600;letter-spacing:.03em;font-family:var(--font-mono)}\n.env-prod{background:rgba(255,0,85,0.2);color:var(--pink);border:1px solid rgba(255,0,85,0.3)}\n.env-staging{background:rgba(251,191,36,0.15);color:var(--warning);border:1px solid rgba(251,191,36,0.25)}\n.env-dev{background:rgba(34,197,94,0.15);color:var(--green);border:1px solid rgba(34,197,94,0.25)}\n.env-default{background:rgba(168,85,247,0.15);color:var(--violet);border:1px solid rgba(168,85,247,0.25)}\n\n/* Entanglement */\n.entangle-list{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow-y:auto}\n.entangle-pair{display:flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:.85rem}\n.entangle-arrow{color:var(--accent-bright)}\n\n/* Tunnels */\n.tunnel-list{display:flex;flex-direction:column;gap:8px;max-height:240px;overflow-y:auto}\n.tunnel-card{background:rgba(168,85,247,0.06);border:1px solid rgba(168,85,247,0.15);border-radius:var(--radius-sm);padding:10px 12px;font-size:.85rem;font-family:var(--font-mono)}\n.tunnel-meta{display:flex;gap:12px;margin-top:4px;font-size:.8rem;color:var(--text-dim)}\n\n/* Audit feed */\n.audit-feed{display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;font-size:.85rem;font-family:var(--font-mono)}\n.audit-row{display:flex;gap:8px;padding:3px 0;border-bottom:1px solid rgba(255,255,255,0.03)}\n.audit-ts{color:var(--text-dim);min-width:70px;flex-shrink:0}\n.audit-action{min-width:64px;font-weight:600}\n.audit-action.read{color:var(--accent)}.audit-action.write{color:var(--green)}.audit-action.delete{color:var(--danger)}\n.audit-action.entangle{color:var(--violet)}.audit-action.tunnel{color:var(--violet)}.audit-action.teleport{color:var(--warning)}\n.audit-action.generate{color:var(--warning)}.audit-action.list{color:var(--text-dim)}.audit-action.export{color:var(--text-dim)}\n.audit-action.collapse{color:var(--accent)}\n\n/* Anomalies */\n.anomaly-card{background:rgba(255,94,91,0.06);border:1px solid rgba(255,94,91,0.15);border-radius:var(--radius-sm);padding:10px 14px;animation:anomaly-pulse 3s ease-in-out infinite}\n@keyframes anomaly-pulse{0%,100%{border-color:rgba(255,94,91,0.15)}50%{border-color:rgba(255,94,91,0.4)}}\n.anomaly-type{font-size:.8rem;text-transform:uppercase;letter-spacing:.06em;color:var(--danger);font-weight:700;margin-bottom:2px;font-family:var(--font-display)}\n.anomaly-desc{font-size:.88rem;color:var(--text-primary)}\n\n/* Environment badge */\n.env-status{display:flex;align-items:center;gap:12px}\n.env-big{font-size:1.1rem;font-weight:700;padding:4px 14px;border-radius:var(--radius-sm)}\n.env-source{font-size:.85rem;color:var(--text-dim)}\n\n/* Empty states */\n.empty{color:var(--text-dim);font-size:.88rem;font-style:italic;padding:8px 0}\n\n/* Scrollbar */\n::-webkit-scrollbar{width:4px;height:4px}\n::-webkit-scrollbar-track{background:transparent}\n::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.08);border-radius:2px}\n::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.15)}\n</style>\n</head>\n<body>\n\n<!-- Shared SVG gradient definition -->\n<svg class=\"svg-defs\" aria-hidden=\"true\" focusable=\"false\">\n <defs>\n <linearGradient id=\"neon-grad\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stop-color=\"#00D1FF\"/>\n <stop offset=\"100%\" stop-color=\"#a855f7\"/>\n </linearGradient>\n </defs>\n</svg>\n\n<div class=\"blob blob-1\"></div>\n<div class=\"blob blob-2\"></div>\n<div class=\"blob blob-3\"></div>\n<div class=\"container\">\n <div class=\"header\">\n <h1>\n <span class=\"q-icon\"><img src=\"https://i4ctime.github.io/quantum_ring/assets/icon.png\" alt=\"q-ring\" width=\"28\" height=\"28\" style=\"display:block;border-radius:4px\"/></span>\n <span class=\"brand\">q-ring</span>\n <span class=\"sub\">quantum status</span>\n </h1>\n <div class=\"conn-label\"><span class=\"status-dot\" id=\"connDot\"></span><span id=\"connText\">connecting\\u2026</span></div>\n </div>\n <div class=\"grid\" id=\"dashboard\">\n <div class=\"card\"><div class=\"empty\">Connecting to q-ring\\u2026</div></div>\n </div>\n</div>\n\n<script>\n(function(){\n const $ = (id) => document.getElementById(id);\n const dash = $('dashboard');\n const dot = $('connDot');\n const connText = $('connText');\n\n /* --- SVG icon library (matching gh-pages neon gradient) --- */\n const icons = {\n health: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/></svg>',\n environment: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\"/></svg>',\n decay: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 22h14\"/><path d=\"M5 2h14\"/><path d=\"M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22\"/><path d=\"M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2\"/></svg>',\n superposition: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"12 2 2 7 12 12 22 7 12 2\"/><polyline points=\"2 17 12 22 22 17\"/><polyline points=\"2 12 12 17 22 12\"/></svg>',\n entangle: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/></svg>',\n tunnel: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 10h.01\"/><path d=\"M15 10h.01\"/><path d=\"M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z\"/></svg>',\n anomaly: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>',\n audit: '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"url(#neon-grad)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg>'\n };\n\n function esc(s){ const d=document.createElement('div');d.textContent=s;return d.innerHTML; }\n\n function envClass(e){\n if(e==='prod'||e==='production') return 'env-prod';\n if(e==='staging'||e==='stage') return 'env-staging';\n if(e==='dev'||e==='development') return 'env-dev';\n return 'env-default';\n }\n\n function decayColor(pct,expired){\n if(expired) return 'var(--danger)';\n if(pct>=90) return 'var(--danger)';\n if(pct>=75) return 'var(--warning)';\n return 'var(--accent)';\n }\n\n function fmtTime(ts){\n const d=new Date(ts);\n return d.getHours().toString().padStart(2,'0')+':'+d.getMinutes().toString().padStart(2,'0')+':'+d.getSeconds().toString().padStart(2,'0');\n }\n\n function renderHealth(h){\n const total=h.total||1;\n const r=42, circ=2*Math.PI*r;\n const slices=[\n {v:h.healthy,c:'var(--accent)'},\n {v:h.stale,c:'var(--warning)'},\n {v:h.expired,c:'var(--danger)'},\n {v:h.noDecay,c:'var(--text-dim)'}\n ];\n let offset=0;\n let rings='';\n for(const sl of slices){\n const pct=sl.v/total;\n const len=pct*circ;\n rings+=\\`<circle cx=\"50\" cy=\"50\" r=\"\\${r}\" fill=\"none\" stroke=\"\\${sl.c}\" stroke-width=\"12\" stroke-dasharray=\"\\${len} \\${circ-len}\" stroke-dashoffset=\"-\\${offset}\" stroke-linecap=\"round\" opacity=\"0.85\"/>\\`;\n offset+=len;\n }\n return \\`<div class=\"health-row\">\n <div class=\"donut-wrap\">\n <svg viewBox=\"0 0 100 100\" width=\"100\" height=\"100\">\\${rings}</svg>\n <div class=\"donut-label\">\\${h.total}<small>secrets</small></div>\n </div>\n <div class=\"health-legend\">\n <div class=\"legend-item\"><span class=\"legend-dot\" style=\"background:var(--accent)\"></span>Healthy \\${h.healthy}</div>\n <div class=\"legend-item\"><span class=\"legend-dot\" style=\"background:var(--warning)\"></span>Stale \\${h.stale}</div>\n <div class=\"legend-item\"><span class=\"legend-dot\" style=\"background:var(--danger)\"></span>Expired \\${h.expired}</div>\n <div class=\"legend-item\"><span class=\"legend-dot\" style=\"background:var(--text-dim)\"></span>No decay \\${h.noDecay}</div>\n </div>\n </div>\\`;\n }\n\n function renderDecay(secrets){\n const withDecay=secrets.filter(s=>s.decay&&s.decay.timeRemaining);\n if(!withDecay.length) return '<div class=\"empty\">No secrets with decay configured</div>';\n return '<div class=\"decay-list\">'+withDecay.map(s=>{\n const pct=Math.min(s.decay.lifetimePercent,100);\n const col=decayColor(pct,s.decay.isExpired);\n const label=s.decay.isExpired?'expired':s.decay.timeRemaining||'';\n return \\`<div class=\"decay-item\">\n <span class=\"decay-key\" title=\"\\${esc(s.key)}\">\\${esc(s.key)}</span>\n <div class=\"decay-bar\"><div class=\"decay-fill\" style=\"width:\\${pct}%;background:\\${col}\"></div></div>\n <span class=\"decay-time\" style=\"color:\\${col}\">\\${esc(label)}</span>\n </div>\\`;\n }).join('')+'</div>';\n }\n\n function renderSuperposition(secrets){\n const sup=secrets.filter(s=>s.type==='superposition'&&s.environments);\n if(!sup.length) return '<div class=\"empty\">No secrets in superposition</div>';\n return '<div class=\"super-list\">'+sup.map(s=>{\n const pills=(s.environments||[]).map(e=>{\n const isDefault=e===s.defaultEnv;\n return \\`<span class=\"env-pill \\${envClass(e)}\">\\${esc(e)}\\${isDefault?' \\u2713':''}</span>\\`;\n }).join('');\n return \\`<div class=\"super-item\"><span class=\"super-key\">\\${esc(s.key)}</span>\\${pills}</div>\\`;\n }).join('')+'</div>';\n }\n\n function renderEntanglements(pairs){\n if(!pairs.length) return '<div class=\"empty\">No entangled secrets</div>';\n const seen=new Set();\n const unique=pairs.filter(p=>{\n const id=[p.source.service,p.source.key,p.target.service,p.target.key].sort().join('|');\n if(seen.has(id)) return false;\n seen.add(id);return true;\n });\n return '<div class=\"entangle-list\">'+unique.map(p=>\n \\`<div class=\"entangle-pair\"><span>\\${esc(p.source.key)}</span><span class=\"entangle-arrow\">\\u2194</span><span>\\${esc(p.target.key)}</span></div>\\`\n ).join('')+'</div>';\n }\n\n function renderTunnels(tunnels){\n if(!tunnels.length) return '<div class=\"empty\">No active tunnels</div>';\n return '<div class=\"tunnel-list\">'+tunnels.map(t=>{\n const rem=t.expiresAt?Math.max(0,Math.floor((t.expiresAt-Date.now())/1000)):null;\n return \\`<div class=\"tunnel-card\">\\${esc(t.id)}<div class=\"tunnel-meta\">\n <span>reads: \\${t.accessCount}\\${t.maxReads?'/'+t.maxReads:''}</span>\n \\${rem!==null?'<span>expires: '+rem+'s</span>':'<span>no expiry</span>'}\n </div></div>\\`;\n }).join('')+'</div>';\n }\n\n function renderAudit(events){\n if(!events.length) return '<div class=\"empty\">No audit events</div>';\n return '<div class=\"audit-feed\">'+events.slice(0,40).map(e=>\n \\`<div class=\"audit-row\">\n <span class=\"audit-ts\">\\${fmtTime(e.timestamp)}</span>\n <span class=\"audit-action \\${e.action}\">\\${e.action}</span>\n <span class=\"audit-key\">\\${e.key?esc(e.key):''}</span>\n <span class=\"audit-detail\">\\${e.detail?esc(e.detail):''}</span>\n </div>\\`\n ).join('')+'</div>';\n }\n\n function renderAnomalies(anomalies){\n if(!anomalies.length) return '<div class=\"empty\">No anomalies detected \\u2014 all clear</div>';\n return anomalies.map(a=>\n \\`<div class=\"anomaly-card\"><div class=\"anomaly-type\">\\${esc(a.type)}</div><div class=\"anomaly-desc\">\\${esc(a.description)}</div></div>\\`\n ).join('');\n }\n\n function renderEnvironment(env){\n if(!env) return '<div class=\"empty\">No environment detected</div>';\n return \\`<div class=\"env-status\">\n <span class=\"env-big env-pill \\${envClass(env.env)}\">\\${esc(env.env)}</span>\n <span class=\"env-source\">detected via \\${esc(env.source)}</span>\n </div>\\`;\n }\n\n const panels = [\n { id:'p-health', icon:icons.health, label:'Health Summary', wide:false, render:s=>renderHealth(s.health) },\n { id:'p-env', icon:icons.environment, label:'Environment', wide:false, render:s=>renderEnvironment(s.environment) },\n { id:'p-decay', icon:icons.decay, label:'Decay Timers', wide:false, render:s=>renderDecay(s.secrets) },\n { id:'p-super', icon:icons.superposition, label:'Superposition States', wide:false, render:s=>renderSuperposition(s.secrets) },\n { id:'p-ent', icon:icons.entangle, label:'Entanglement', wide:false, render:s=>renderEntanglements(s.entanglements) },\n { id:'p-tunnel', icon:icons.tunnel, label:'Quantum Tunnels', wide:false, render:s=>renderTunnels(s.tunnels) },\n { id:'p-anomaly', icon:icons.anomaly, label:'Anomaly Alerts', wide:true, render:s=>renderAnomalies(s.anomalies) },\n { id:'p-audit', icon:icons.audit, label:'Audit Log', wide:true, render:s=>renderAudit(s.audit) }\n ];\n\n let initialised = false;\n\n function render(snap){\n if(!initialised){\n initialised = true;\n dash.innerHTML = panels.map((p,i) =>\n \\`<div class=\"card\\${p.wide?' grid-wide':''}\" id=\"\\${p.id}\" style=\"animation-delay:\\${i*60}ms\">\n <div class=\"card-title\">\\${p.icon} \\${p.label}</div>\n <div class=\"card-body\" id=\"\\${p.id}-body\"></div>\n </div>\\`\n ).join('');\n }\n for(const p of panels){\n const body = $(\\`\\${p.id}-body\\`);\n if(body){\n const html = p.render(snap);\n if(body._prev !== html){ body.innerHTML = html; body._prev = html; }\n }\n }\n }\n\n function connect(){\n const es=new EventSource('/events');\n es.onopen=()=>{\n dot.classList.remove('disconnected');\n connText.textContent='live';\n };\n es.onmessage=(e)=>{\n try{ render(JSON.parse(e.data)); }catch(err){ console.error('render error',err); }\n };\n es.onerror=()=>{\n dot.classList.add('disconnected');\n connText.textContent='reconnecting\\u2026';\n };\n }\n\n connect();\n})();\n</script>\n</body>\n</html>`;\n}\n"],"mappings":";;;;;;;;;;;;AAOA,SAAS,oBAA4E;;;ACC9E,SAAS,mBAA2B;AACzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":[]}
|