@i4ctime/q-ring 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@ import {
7
7
  listSecrets,
8
8
  queryAudit,
9
9
  tunnelList
10
- } from "./chunk-WQPJ2FTM.js";
10
+ } from "./chunk-3WTTWJYU.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
- --void:#050505;--mist:#1A1A2E;--ghost:#F8F9FA;--slate:#7A8490;
27
- --cyan:#00D1FF;--violet:#B266FF;--emerald:#00E676;--amber:#FFB800;--pink:#FF0055;
28
- --glass-bg:rgba(255,255,255,0.03);--glass-border:rgba(255,255,255,0.06);
29
- --shadow-q:0 0 20px rgba(0,209,255,0.12),0 0 40px rgba(178,102,255,0.08);
30
- --shadow-glass:0 4px 24px -1px rgba(0,0,0,0.3),0 0 1px rgba(255,255,255,0.04);
31
- --glow-sm:0 0 8px rgba(0,209,255,0.2);
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(--void);color:var(--ghost);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;line-height:1.5}
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:.18;pointer-events:none;z-index:0}
39
- .blob-1{width:600px;height:600px;top:-120px;left:-100px;background:radial-gradient(circle,var(--cyan),transparent 70%);animation:drift1 22s ease-in-out infinite}
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(--emerald),transparent 70%);animation:drift3 30s ease-in-out infinite;opacity:.08}
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.6rem;font-weight:700;letter-spacing:-.02em}
51
- .header h1 span{background:linear-gradient(135deg,var(--cyan),var(--violet));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
52
- .status-dot{width:8px;height:8px;border-radius:50%;background:var(--emerald);box-shadow:0 0 8px var(--emerald);display:inline-block;margin-right:8px;animation:pulse 2s ease-in-out infinite}
53
- .status-dot.disconnected{background:var(--pink);box-shadow:0 0 8px var(--pink)}
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:.75rem;color:var(--slate);display:flex;align-items:center;gap:6px}
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-glass);transition:border-color .25s,box-shadow .25s}
63
- .card:hover{border-color:rgba(255,255,255,0.1);box-shadow:var(--shadow-glass),var(--glow-sm)}
64
- .card-title{font-size:.7rem;text-transform:uppercase;letter-spacing:.08em;color:var(--slate);margin-bottom:12px;display:flex;align-items:center;gap:6px}
65
- .card-title .icon{font-size:.85rem}
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:.6rem;color:var(--slate);font-weight:400;margin-top:2px}
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:.8rem}
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:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:.78rem;min-width:120px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
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:.7rem;color:var(--slate);min-width:56px;text-align:right}
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:'SF Mono',SFMono-Regular,Consolas,monospace;font-size:.78rem;min-width:100px}
89
- .env-pill{font-size:.65rem;padding:2px 8px;border-radius:99px;font-weight:600;letter-spacing:.03em}
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(255,184,0,0.15);color:var(--amber);border:1px solid rgba(255,184,0,0.25)}
92
- .env-dev{background:rgba(0,230,118,0.15);color:var(--emerald);border:1px solid rgba(0,230,118,0.25)}
93
- .env-default{background:rgba(178,102,255,0.15);color:var(--violet);border:1px solid rgba(178,102,255,0.25)}
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:'SF Mono',SFMono-Regular,Consolas,monospace;font-size:.78rem}
98
- .entangle-arrow{color:var(--cyan)}
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(178,102,255,0.06);border:1px solid rgba(178,102,255,0.12);border-radius:8px;padding:10px 12px;font-size:.78rem;font-family:'SF Mono',SFMono-Regular,Consolas,monospace}
103
- .tunnel-meta{display:flex;gap:12px;margin-top:4px;font-size:.7rem;color:var(--slate)}
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:.75rem;font-family:'SF Mono',SFMono-Regular,Consolas,monospace}
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(--slate);min-width:70px;flex-shrink:0}
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(--cyan)}.audit-action.write{color:var(--emerald)}.audit-action.delete{color:var(--pink)}
111
- .audit-action.entangle{color:var(--violet)}.audit-action.tunnel{color:var(--violet)}.audit-action.teleport{color:var(--amber)}
112
- .audit-action.generate{color:var(--amber)}.audit-action.list{color:var(--slate)}.audit-action.export{color:var(--slate)}
113
- .audit-action.collapse{color:var(--cyan)}
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,0,85,0.06);border:1px solid rgba(255,0,85,0.15);border-radius:8px;padding:10px 14px;animation:anomaly-pulse 3s ease-in-out infinite}
119
- @keyframes anomaly-pulse{0%,100%{border-color:rgba(255,0,85,0.15)}50%{border-color:rgba(255,0,85,0.35)}}
120
- .anomaly-type{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;color:var(--pink);font-weight:700;margin-bottom:2px}
121
- .anomaly-desc{font-size:.8rem;color:var(--ghost)}
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:8px}
126
- .env-source{font-size:.75rem;color:var(--slate)}
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(--slate);font-size:.8rem;font-style:italic;padding:8px 0}
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>\u26A1 <span>q-ring</span> quantum status</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
- let connected = false;
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(--pink)';
171
- if(pct>=90) return 'var(--pink)';
172
- if(pct>=75) return 'var(--amber)';
173
- return 'var(--cyan)';
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
- const h=d.getHours().toString().padStart(2,'0');
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(--cyan)'},
189
- {v:h.stale,c:'var(--amber)'},
190
- {v:h.expired,c:'var(--pink)'},
191
- {v:h.noDecay,c:'var(--slate)'}
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(--cyan)"></span>Healthy \${h.healthy}</div>
208
- <div class="legend-item"><span class="legend-dot" style="background:var(--amber)"></span>Stale \${h.stale}</div>
209
- <div class="legend-item"><span class="legend-dot" style="background:var(--pink)"></span>Expired \${h.expired}</div>
210
- <div class="legend-item"><span class="legend-dot" style="background:var(--slate)"></span>No decay \${h.noDecay}</div>
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
- dash.innerHTML=\`
295
- <div class="card">
296
- <div class="card-title"><span class="icon">\u{1F6E1}\uFE0F</span> Health Summary</div>
297
- \${renderHealth(snap.health)}
298
- </div>
299
- <div class="card">
300
- <div class="card-title"><span class="icon">\u26A1</span> Environment</div>
301
- \${renderEnvironment(snap.environment)}
302
- </div>
303
- <div class="card">
304
- <div class="card-title"><span class="icon">\u23F0</span> Decay Timers</div>
305
- \${renderDecay(snap.secrets)}
306
- </div>
307
- <div class="card">
308
- <div class="card-title"><span class="icon">\u{1F30A}</span> Superposition States</div>
309
- \${renderSuperposition(snap.secrets)}
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-JTALLJM6.js.map
532
+ //# sourceMappingURL=dashboard-QQWKOOI5.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":[]}