@enigmax/dashboard 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -14
- package/assets/index.html +136 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
# @enigmax/dashboard
|
|
2
2
|
|
|
3
3
|
The browser UI for [enigma](https://github.com/FJRG2007/enigma)'s local savings
|
|
4
|
-
dashboard: a single static page
|
|
5
|
-
(`assets/lib/chart.min.js`).
|
|
4
|
+
dashboard: a single static page plus a vendored charting library.
|
|
6
5
|
|
|
7
|
-
You do not install this directly. enigma
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
`enigma update`. enigma's loopback HTTP server serves these files and provides the
|
|
11
|
-
`/api/stats` and `/api/settings` endpoints the page talks to.
|
|
6
|
+
You do not install this directly. enigma fetches it on demand the first time you open the
|
|
7
|
+
dashboard (`enigma dashboard`) or enable it (`enigma config dashboard on`), and keeps it up
|
|
8
|
+
to date on `enigma update`.
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
dashboard never download it
|
|
15
|
-
|
|
16
|
-
The page is served only on `127.0.0.1` (never network-facing) and the settings write
|
|
17
|
-
endpoint is origin-guarded by enigma. The chart library retains its upstream Apache-2.0
|
|
18
|
-
license header as required; its attribution logo is suppressed in enigma's own CSS.
|
|
10
|
+
Shipping the UI separately keeps it out of the base `enigma-cli` package, so people who
|
|
11
|
+
never open the dashboard never download it. The dashboard runs only on your own machine
|
|
12
|
+
(`127.0.0.1`); it is never exposed to the network.
|
|
19
13
|
|
|
20
14
|
## License
|
|
21
15
|
|
|
22
|
-
Apache-2.0
|
|
16
|
+
Apache-2.0. The bundled charting library retains its upstream Apache-2.0 license header.
|
package/assets/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Enigma -
|
|
6
|
+
<title>Enigma - Dashboard</title>
|
|
7
7
|
<style>
|
|
8
8
|
:root {
|
|
9
9
|
--bg: #0b0e14; --surface: #151a23; --surface2: #1b2230; --border: #232a36;
|
|
@@ -13,13 +13,21 @@
|
|
|
13
13
|
body {
|
|
14
14
|
margin: 0; background: var(--bg); color: var(--text);
|
|
15
15
|
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
16
|
-
-webkit-font-smoothing: antialiased; padding: 24px; max-width:
|
|
16
|
+
-webkit-font-smoothing: antialiased; padding: 24px; max-width: 1040px; margin: 0 auto;
|
|
17
17
|
}
|
|
18
18
|
a { color: var(--accent2); }
|
|
19
|
-
header { display: flex; align-items: center; gap:
|
|
19
|
+
header { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; flex-wrap: wrap; }
|
|
20
|
+
.brand { display: flex; flex-direction: column; }
|
|
20
21
|
.wordmark { font-size: 20px; font-weight: 700; letter-spacing: 0.5px; }
|
|
21
22
|
.wordmark span { color: var(--accent); }
|
|
22
23
|
.sub { color: var(--muted); font-size: 13px; }
|
|
24
|
+
.tabs { display: flex; gap: 4px; }
|
|
25
|
+
.tabs .tab {
|
|
26
|
+
text-decoration: none; color: var(--muted); font-size: 13px; font-weight: 600;
|
|
27
|
+
padding: 7px 16px; border-radius: 8px; border: 1px solid transparent; transition: background .15s, color .15s, border-color .15s;
|
|
28
|
+
}
|
|
29
|
+
.tabs .tab:hover { color: var(--text); background: var(--surface); }
|
|
30
|
+
.tabs .tab.active { color: var(--accent); background: var(--surface); border-color: var(--border); }
|
|
23
31
|
.live { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 12px; }
|
|
24
32
|
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); box-shadow: 0 0 8px var(--good); }
|
|
25
33
|
.dot.stale { background: var(--muted); box-shadow: none; }
|
|
@@ -76,6 +84,9 @@
|
|
|
76
84
|
.stat-line b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
77
85
|
footer { color: var(--muted); font-size: 12px; line-height: 1.6; margin-top: 8px; }
|
|
78
86
|
code { background: var(--surface2); padding: 1px 6px; border-radius: 5px; color: var(--accent2); }
|
|
87
|
+
.page-head { margin-bottom: 18px; }
|
|
88
|
+
.page-head h1 { font-size: 18px; font-weight: 700; margin: 0 0 4px; }
|
|
89
|
+
.page-head p { color: var(--muted); font-size: 13px; margin: 0; max-width: 720px; line-height: 1.5; }
|
|
79
90
|
.set-cat { margin-bottom: 18px; }
|
|
80
91
|
.set-cat:last-child { margin-bottom: 0; }
|
|
81
92
|
.set-cat-h { color: var(--accent); font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 4px; }
|
|
@@ -97,15 +108,19 @@
|
|
|
97
108
|
border-radius: 7px; padding: 5px 10px; font-size: 12px; font-family: inherit; cursor: pointer;
|
|
98
109
|
}
|
|
99
110
|
.set-note { color: var(--accent); font-size: 12px; min-height: 16px; margin-top: 10px; }
|
|
100
|
-
@media (max-width: 720px) { .grid { grid-template-columns: repeat(2, 1fr); } }
|
|
111
|
+
@media (max-width: 720px) { .grid { grid-template-columns: repeat(2, 1fr); } header { gap: 12px; } }
|
|
101
112
|
</style>
|
|
102
113
|
</head>
|
|
103
114
|
<body>
|
|
104
115
|
<header>
|
|
105
|
-
<div>
|
|
116
|
+
<div class="brand">
|
|
106
117
|
<div class="wordmark">Enigma<span>.</span></div>
|
|
107
|
-
<div class="sub">
|
|
118
|
+
<div class="sub">Savings dashboard</div>
|
|
108
119
|
</div>
|
|
120
|
+
<nav class="tabs">
|
|
121
|
+
<a href="#/" data-view="savings" class="tab active">Savings</a>
|
|
122
|
+
<a href="#/settings" data-view="settings" class="tab">Settings</a>
|
|
123
|
+
</nav>
|
|
109
124
|
<div class="nav">
|
|
110
125
|
<span class="live"><span id="dot" class="dot stale"></span><span id="updated">Connecting...</span></span>
|
|
111
126
|
<nav class="links">
|
|
@@ -116,112 +131,118 @@
|
|
|
116
131
|
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 0 0-4.8851-1.5152.0741.0741 0 0 0-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 0 0-.0785-.037 19.7363 19.7363 0 0 0-4.8852 1.515.0699.0699 0 0 0-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 0 0 .0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 0 0 .0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 0 0-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 0 1-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 0 1 .0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 0 1 .0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 0 1-.0066.1276 12.2986 12.2986 0 0 1-1.873.8914.0766.0766 0 0 0-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 0 0 .0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 0 0 .0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 0 0-.0312-.0286ZM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189Zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9461 2.4189-2.1568 2.4189Z"/></svg>
|
|
117
132
|
</a>
|
|
118
133
|
<a href="https://ko-fi.com/fjrg2007" target="_blank" rel="noopener noreferrer" title="Support on Ko-fi" aria-label="Support on Ko-fi">
|
|
119
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 8h1a4 4 0 1 1 0 8h-1"/><path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/><line x1="6" y1="2" x2="6" y2="4"/><line x1="
|
|
134
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 8h1a4 4 0 1 1 0 8h-1"/><path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/><line x1="6" y1="2" x2="6" y2="4"/><line x1="14" y1="2" x2="14" y2="4"/></svg>
|
|
120
135
|
</a>
|
|
121
136
|
</nav>
|
|
122
137
|
</div>
|
|
123
138
|
</header>
|
|
124
139
|
|
|
125
|
-
<
|
|
126
|
-
<div class="
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<div class="panel
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
<div class="
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
<main id="view-savings">
|
|
141
|
+
<div class="grid">
|
|
142
|
+
<div class="card"><div class="label">Money Saved</div><div id="money" class="value good">-</div><div id="moneyFoot" class="foot">Estimated, by source</div></div>
|
|
143
|
+
<div class="card"><div class="label">Time Saved</div><div id="time" class="value accent">-</div><div id="timeFoot" class="foot">Estimated prefill time</div></div>
|
|
144
|
+
<div class="card"><div class="label">Tokens Saved</div><div id="saved" class="value">-</div><div id="savedFoot" class="foot">Across all compressions</div></div>
|
|
145
|
+
<div class="card"><div class="label">Savings Rate</div><div id="rate" class="value accent">-</div><div class="foot">Of input tokens removed</div></div>
|
|
146
|
+
<div class="card"><div class="label">Compressions</div><div id="calls" class="value">-</div><div class="foot">Reversible via CCR</div></div>
|
|
147
|
+
<div class="card"><div class="label">Avg / Compression</div><div id="avg" class="value">-</div><div class="foot">Tokens saved per call</div></div>
|
|
148
|
+
<div class="card"><div class="label">Best Compression</div><div id="best" class="value">-</div><div class="foot">Most saved in one call</div></div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div class="panel">
|
|
152
|
+
<div class="panel-head">
|
|
153
|
+
<h2>Savings Per Day <small id="rangeLabel"></small></h2>
|
|
154
|
+
<div class="ctrls">
|
|
155
|
+
<div class="ranges" id="metricToggle">
|
|
156
|
+
<button type="button" data-m="tokens" class="active">Tokens</button>
|
|
157
|
+
<button type="button" data-m="usd">$</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="ranges" id="modeToggle">
|
|
160
|
+
<button type="button" data-o="daily" class="active">Daily</button>
|
|
161
|
+
<button type="button" data-o="cumulative">Cumulative</button>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="ranges" id="ranges">
|
|
164
|
+
<button type="button" data-d="7">7D</button>
|
|
165
|
+
<button type="button" data-d="30" class="active">30D</button>
|
|
166
|
+
<button type="button" data-d="90">90D</button>
|
|
167
|
+
<button type="button" data-d="0">All</button>
|
|
168
|
+
</div>
|
|
152
169
|
</div>
|
|
153
170
|
</div>
|
|
171
|
+
<div id="chartWrap" class="chartwrap"></div>
|
|
172
|
+
<div id="chartEmpty" class="empty" style="display:none">No compression activity recorded yet. Run <code>enigma compress <file></code> or enable the compression MCP (<code>enigma config compress on</code>).</div>
|
|
154
173
|
</div>
|
|
155
|
-
|
|
156
|
-
<div
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<
|
|
167
|
-
<div
|
|
174
|
+
|
|
175
|
+
<div class="panel" id="usagePanel" style="display:none">
|
|
176
|
+
<h2 style="margin-bottom:4px">Real Tool Usage <small id="usageSub">Claude Code transcripts</small></h2>
|
|
177
|
+
<div class="sub" style="margin-bottom:14px">Measured token consumption and real prompt-cache savings, read-only from your own session logs. Deliberately not attributed to skills or output-style: a transcript has no counterfactual baseline, so any such number would be invented.</div>
|
|
178
|
+
<div class="grid" style="margin-bottom:16px">
|
|
179
|
+
<div class="card"><div class="label">Cache Saved</div><div id="uCacheMoney" class="value good">-</div><div class="foot">Est. from prompt caching</div></div>
|
|
180
|
+
<div class="card"><div class="label">Cache Reads</div><div id="uCacheRead" class="value accent">-</div><div class="foot">Tokens served from cache</div></div>
|
|
181
|
+
<div class="card"><div class="label">Input Tokens</div><div id="uInput" class="value">-</div><div class="foot">Consumed across sessions</div></div>
|
|
182
|
+
<div class="card"><div class="label">Output Tokens</div><div id="uOutput" class="value">-</div><div class="foot">Generated by the agent</div></div>
|
|
183
|
+
<div class="card"><div class="label">Sessions</div><div id="uSessions" class="value">-</div><div id="uSessionsFoot" class="foot">Transcripts scanned</div></div>
|
|
184
|
+
</div>
|
|
185
|
+
<h2 style="margin:0 0 10px">By Model</h2>
|
|
186
|
+
<div id="uByModel"></div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="panel" id="usageHint" style="display:none">
|
|
190
|
+
<h2 style="margin-bottom:8px">Real Tool Usage <small>opt-in</small></h2>
|
|
191
|
+
<div class="sub">Show real token consumption and prompt-cache savings from your Claude Code sessions in <a href="#/settings">Settings</a> (Real tool-usage stats). enigma reads only your local session transcripts; nothing is sent anywhere.</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div class="panel">
|
|
195
|
+
<h2 style="margin-bottom:14px">Savings By Source <small>which app/CLI compressed</small></h2>
|
|
196
|
+
<div id="sources"></div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div class="panel">
|
|
200
|
+
<h2 style="margin-bottom:14px">Savings By Content Type <small>what enigma compressed</small></h2>
|
|
201
|
+
<div id="types"></div>
|
|
168
202
|
</div>
|
|
169
|
-
|
|
170
|
-
<div
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div class="panel">
|
|
189
|
-
<h2 style="margin-bottom:14px">Before vs. After <small id="ratioLabel">cumulative tokens</small></h2>
|
|
190
|
-
<div class="bar-row"><span class="name">Before</span><div class="bar-track"><div id="barBefore" class="bar-fill" style="background:var(--accent2)"></div></div><span id="numBefore" class="num">-</span></div>
|
|
191
|
-
<div class="bar-row"><span class="name">After</span><div class="bar-track"><div id="barAfter" class="bar-fill" style="background:var(--good)"></div></div><span id="numAfter" class="num">-</span></div>
|
|
192
|
-
<div class="stat-line" style="margin:14px 0 0"><div>Removed <b id="removed">-</b></div><div>Reduction <b id="reduction">-</b></div><div>Compression ratio <b id="ratio">-</b></div></div>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<div class="panel">
|
|
196
|
-
<div class="panel-head">
|
|
197
|
-
<h2>Savings History <small id="histStats"></small></h2>
|
|
198
|
-
<div class="ctrls">
|
|
199
|
-
<div class="ranges" id="histToggle">
|
|
200
|
-
<button type="button" data-p="day" class="active">Daily</button>
|
|
201
|
-
<button type="button" data-p="week">Weekly</button>
|
|
202
|
-
<button type="button" data-p="month">Monthly</button>
|
|
203
|
+
|
|
204
|
+
<div class="panel">
|
|
205
|
+
<h2 style="margin-bottom:14px">Before vs. After <small id="ratioLabel">cumulative tokens</small></h2>
|
|
206
|
+
<div class="bar-row"><span class="name">Before</span><div class="bar-track"><div id="barBefore" class="bar-fill" style="background:var(--accent2)"></div></div><span id="numBefore" class="num">-</span></div>
|
|
207
|
+
<div class="bar-row"><span class="name">After</span><div class="bar-track"><div id="barAfter" class="bar-fill" style="background:var(--good)"></div></div><span id="numAfter" class="num">-</span></div>
|
|
208
|
+
<div class="stat-line" style="margin:14px 0 0"><div>Removed <b id="removed">-</b></div><div>Reduction <b id="reduction">-</b></div><div>Compression ratio <b id="ratio">-</b></div></div>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div class="panel">
|
|
212
|
+
<div class="panel-head">
|
|
213
|
+
<h2>Savings History <small id="histStats"></small></h2>
|
|
214
|
+
<div class="ctrls">
|
|
215
|
+
<div class="ranges" id="histToggle">
|
|
216
|
+
<button type="button" data-p="day" class="active">Daily</button>
|
|
217
|
+
<button type="button" data-p="week">Weekly</button>
|
|
218
|
+
<button type="button" data-p="month">Monthly</button>
|
|
219
|
+
</div>
|
|
203
220
|
</div>
|
|
204
221
|
</div>
|
|
222
|
+
<div class="tbl-wrap"><div id="histBody"></div></div>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div class="panel">
|
|
226
|
+
<h2 style="margin-bottom:14px">Recent Compressions <small>newest first</small></h2>
|
|
227
|
+
<div class="tbl-wrap"><div id="recent"></div></div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div class="panel">
|
|
231
|
+
<h2 style="margin-bottom:14px">Reversible Cache (CCR) <small>recoverable originals on disk</small></h2>
|
|
232
|
+
<div id="cache"></div>
|
|
233
|
+
</div>
|
|
234
|
+
</main>
|
|
235
|
+
|
|
236
|
+
<section id="view-settings" style="display:none">
|
|
237
|
+
<div class="page-head">
|
|
238
|
+
<h1>Settings</h1>
|
|
239
|
+
<p>Everything you can configure with <code>enigma config</code> or the terminal UI, editable here. Changes apply at global scope and take effect immediately; toggles that change agent memory need an agent restart.</p>
|
|
205
240
|
</div>
|
|
206
|
-
<div class="
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<div class="tbl-wrap"><div id="recent"></div></div>
|
|
212
|
-
</div>
|
|
213
|
-
|
|
214
|
-
<div class="panel">
|
|
215
|
-
<h2 style="margin-bottom:14px">Reversible Cache (CCR) <small>recoverable originals on disk</small></h2>
|
|
216
|
-
<div id="cache"></div>
|
|
217
|
-
</div>
|
|
218
|
-
|
|
219
|
-
<div class="panel">
|
|
220
|
-
<h2 style="margin-bottom:4px">Enigma Settings <small>same options as the terminal UI</small></h2>
|
|
221
|
-
<div class="sub" style="margin-bottom:14px">Everything configurable from <code>enigma config</code> / the TUI, editable here. Changes apply at global scope and take effect immediately; memory toggles need an agent restart. Numeric estimates use <code>enigma config token-price <usd></code> and <code>enigma config token-speed <tok/s></code>.</div>
|
|
222
|
-
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
223
|
-
<div id="settingsNote" class="set-note"></div>
|
|
224
|
-
</div>
|
|
241
|
+
<div class="panel">
|
|
242
|
+
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
243
|
+
<div id="settingsNote" class="set-note"></div>
|
|
244
|
+
</div>
|
|
245
|
+
</section>
|
|
225
246
|
|
|
226
247
|
<footer>
|
|
227
248
|
Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
|
|
@@ -579,7 +600,7 @@
|
|
|
579
600
|
}
|
|
580
601
|
}
|
|
581
602
|
|
|
582
|
-
// --- settings
|
|
603
|
+
// --- settings subpage (mirrors the TUI registry over /api/settings) ---
|
|
583
604
|
function esc(s) { return String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); }
|
|
584
605
|
|
|
585
606
|
function settingRow(s) {
|
|
@@ -627,6 +648,7 @@
|
|
|
627
648
|
});
|
|
628
649
|
}
|
|
629
650
|
|
|
651
|
+
let settingsLoaded = false;
|
|
630
652
|
async function loadSettings() {
|
|
631
653
|
try {
|
|
632
654
|
const res = await fetch("/api/settings", { cache: "no-store" });
|
|
@@ -634,12 +656,24 @@
|
|
|
634
656
|
} catch { $("settingsBody").innerHTML = '<div class="empty" style="padding:24px 0">Settings unavailable.</div>'; }
|
|
635
657
|
}
|
|
636
658
|
|
|
659
|
+
// --- hash routing between the Savings and Settings subpages ---
|
|
660
|
+
function route() {
|
|
661
|
+
const v = (location.hash || "").replace(/^#\/?/, "") === "settings" ? "settings" : "savings";
|
|
662
|
+
$("view-savings").style.display = v === "savings" ? "" : "none";
|
|
663
|
+
$("view-settings").style.display = v === "settings" ? "" : "none";
|
|
664
|
+
document.querySelectorAll(".tab").forEach((t) => t.classList.toggle("active", t.dataset.view === v));
|
|
665
|
+
if (v === "settings" && !settingsLoaded) { settingsLoaded = true; loadSettings(); }
|
|
666
|
+
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
667
|
+
if (v === "savings" && chart) { try { applyRange(); } catch { /* not ready */ } }
|
|
668
|
+
}
|
|
669
|
+
|
|
637
670
|
initChart();
|
|
638
671
|
poll();
|
|
639
672
|
setInterval(poll, POLL_MS);
|
|
640
673
|
document.addEventListener("visibilitychange", () => { if (!document.hidden) poll(); });
|
|
641
674
|
wireSettings();
|
|
642
|
-
|
|
675
|
+
window.addEventListener("hashchange", route);
|
|
676
|
+
route();
|
|
643
677
|
</script>
|
|
644
678
|
</body>
|
|
645
679
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Local browser dashboard UI for enigma: the static page and chart assets enigma serves on its loopback dashboard (savings, real tool usage, in-browser settings). Installed on demand by enigma-cli; not a runtime dependency.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|