quotacap 0.0.26 → 0.0.28
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/dist/adapters/grok.js +62 -19
- package/dist/cli/advise.js +6 -1
- package/dist/cli/status.js +6 -1
- package/dist/cli/takeover.js +1 -1
- package/dist/runtime/updates.d.ts +4 -0
- package/dist/runtime/updates.js +11 -0
- package/dist/service/index.js +8 -2
- package/dist/service/macos.d.ts +1 -0
- package/dist/service/macos.js +10 -0
- package/dist/service/systemd.d.ts +1 -0
- package/dist/service/systemd.js +10 -0
- package/dist/version.js +1 -1
- package/dist/web/src/App.js +2 -2
- package/dist/web/src/components/PacingLegend.d.ts +3 -1
- package/dist/web/src/components/PacingLegend.js +3 -2
- package/dist/webAssets.js +1 -1
- package/dist/webHtml.js +1 -1
- package/package.json +1 -1
- package/web/dist/assets/{index-ClNhsr1-.js → index-hmABfkqB.js} +1 -1
- package/web/dist/index.html +1 -1
package/dist/adapters/grok.js
CHANGED
|
@@ -35,35 +35,73 @@ function parseGrokReset(raw, now) {
|
|
|
35
35
|
}
|
|
36
36
|
return dt.toISOString();
|
|
37
37
|
}
|
|
38
|
+
function validPct(v) {
|
|
39
|
+
return Number.isFinite(v) && v >= 0 && v <= 100;
|
|
40
|
+
}
|
|
38
41
|
export function parseGrokTui(text, now = new Date()) {
|
|
39
42
|
const cleaned = stripAnsi(text);
|
|
40
|
-
|
|
43
|
+
// The TUI writes "Weekly lim", repositions the cursor, then writes
|
|
44
|
+
// "t (Tier)", so after ANSI stripping the header reads "Weekly limt".
|
|
45
|
+
// The [i!l] class stays optional to keep tolerating past glitch variants.
|
|
46
|
+
const headerRe = /Weekly\s+lim[i!l]?t\s*\(([^)]+)\)/gi;
|
|
41
47
|
let plan = "unknown";
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
let lastHeaderIdx = -1;
|
|
49
|
+
for (const m of cleaned.matchAll(headerRe)) {
|
|
50
|
+
if (m.index === undefined)
|
|
51
|
+
continue;
|
|
52
|
+
plan = m[1].trim();
|
|
53
|
+
lastHeaderIdx = m.index;
|
|
44
54
|
}
|
|
45
|
-
|
|
55
|
+
if (lastHeaderIdx < 0) {
|
|
46
56
|
const tier = cleaned.match(/\b(SuperGrok|Grok\s+Pro|Grok\s+Enterprise|Grok\s+Basic)\b/i);
|
|
47
57
|
if (tier)
|
|
48
58
|
plan = tier[1].trim();
|
|
49
59
|
}
|
|
50
|
-
const pctRe = /Weekly lim[i!l]t[^\n%]*?(\d+)%/i;
|
|
51
|
-
const mPct = cleaned.match(pctRe);
|
|
52
60
|
let usedPct = null;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
// 1. Dialog header: first percent in a bounded window after the LAST
|
|
62
|
+
// header (later renders supersede earlier ones). The bar percent sits on
|
|
63
|
+
// the line below the header, so this must span newlines.
|
|
64
|
+
if (lastHeaderIdx >= 0) {
|
|
65
|
+
const after = cleaned.slice(lastHeaderIdx, lastHeaderIdx + 1200);
|
|
66
|
+
const m = after.match(/(\d{1,3})%/);
|
|
67
|
+
if (m) {
|
|
68
|
+
const v = parseInt(m[1], 10);
|
|
69
|
+
if (validPct(v))
|
|
70
|
+
usedPct = v;
|
|
71
|
+
}
|
|
57
72
|
}
|
|
73
|
+
// 2. Legacy same-line form ("Weekly limit (Tier) 26%"), excluding the
|
|
74
|
+
// startup status line "Weekly limit left: N%", which reports REMAINING
|
|
75
|
+
// quota with inverted semantics. Last valid match wins.
|
|
58
76
|
if (usedPct === null) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
const lineRe = /Weekly\s+lim[i!l]?t[^\n%]*?(\d{1,3})%/gi;
|
|
78
|
+
for (const m of cleaned.matchAll(lineRe)) {
|
|
79
|
+
if (/left/i.test(m[0]))
|
|
80
|
+
continue;
|
|
81
|
+
const v = parseInt(m[1], 10);
|
|
82
|
+
if (validPct(v))
|
|
64
83
|
usedPct = v;
|
|
65
84
|
}
|
|
66
85
|
}
|
|
86
|
+
// 3. Bare progress-bar percent. Last valid match wins.
|
|
87
|
+
if (usedPct === null) {
|
|
88
|
+
const barRe = /[█░▓▒]+\s*(\d{1,3})%/g;
|
|
89
|
+
for (const m of cleaned.matchAll(barRe)) {
|
|
90
|
+
const v = parseInt(m[1], 10);
|
|
91
|
+
if (validPct(v))
|
|
92
|
+
usedPct = v;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 4. Status-line remaining figure ("Weekly limit left: N%") converted to
|
|
96
|
+
// used. Last valid match wins.
|
|
97
|
+
if (usedPct === null) {
|
|
98
|
+
const leftRe = /Weekly\s+lim[i!l]?t\s+left\s*:?\s*(\d{1,3})%/gi;
|
|
99
|
+
for (const m of cleaned.matchAll(leftRe)) {
|
|
100
|
+
const v = parseInt(m[1], 10);
|
|
101
|
+
if (validPct(v))
|
|
102
|
+
usedPct = 100 - v;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
67
105
|
if (usedPct === null)
|
|
68
106
|
throw new Error("grok: weekly percent not found in TUI output");
|
|
69
107
|
let creditsUsd;
|
|
@@ -73,11 +111,13 @@ export function parseGrokTui(text, now = new Date()) {
|
|
|
73
111
|
if (Number.isFinite(v) && v >= 0)
|
|
74
112
|
creditsUsd = v;
|
|
75
113
|
}
|
|
76
|
-
const
|
|
114
|
+
const resetRe = /Resets:\s*([A-Za-z]+\s+\d+,\s*\d+:\d+)/gi;
|
|
115
|
+
let resetsRaw = null;
|
|
116
|
+
for (const m of cleaned.matchAll(resetRe))
|
|
117
|
+
resetsRaw = m[1].trim();
|
|
77
118
|
let resetsAt = null;
|
|
78
119
|
let estimated = false;
|
|
79
|
-
if (
|
|
80
|
-
const resetsRaw = mReset[1].trim();
|
|
120
|
+
if (resetsRaw) {
|
|
81
121
|
resetsAt = parseGrokReset(resetsRaw, now);
|
|
82
122
|
if (!resetsAt)
|
|
83
123
|
throw new Error(`grok: bad resets timestamp "${resetsRaw}"`);
|
|
@@ -115,7 +155,10 @@ export const grokAdapter = {
|
|
|
115
155
|
rows: 50,
|
|
116
156
|
settleDelayMs: 5000,
|
|
117
157
|
input: "/usage\r",
|
|
118
|
-
|
|
158
|
+
// Wait for the usage dialog (tier header or Resets line), not the
|
|
159
|
+
// startup status line "Weekly limit left: N%", which matches /Weekly lim/
|
|
160
|
+
// and would complete before the dialog renders.
|
|
161
|
+
completionRegex: /Weekly\s+lim[i!l]?t\s*\(|Resets:|Credits:/i,
|
|
119
162
|
timeoutMs: 14000,
|
|
120
163
|
maxBytes: 256 * 1024,
|
|
121
164
|
signal: adapterSignal("grok"),
|
package/dist/cli/advise.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ensureConfig, getDbPath } from "../config.js";
|
|
|
4
4
|
import { createServiceClient } from "../runtime/client.js";
|
|
5
5
|
import { VERSION } from "../version.js";
|
|
6
6
|
import { isServiceManaged, runServiceCommand } from "../service/index.js";
|
|
7
|
-
import { detectChannel, printUpdateFooter, readUpdateCache, refreshUpdateCache, updateFooter, } from "../runtime/updates.js";
|
|
7
|
+
import { detectChannel, printUpdateFooter, printVersionLine, readUpdateCache, refreshUpdateCache, updateFooter, } from "../runtime/updates.js";
|
|
8
8
|
import { ClientError, OFFLINE_LABEL, emptySnapshot, offlineOnlyClient, resolveSnapshot } from "./snapshot-source.js";
|
|
9
9
|
import { checkSkew, execSkewWarning, olderCliWarning, takeoverManaged, unmanagedSkewWarning, } from "./takeover.js";
|
|
10
10
|
export function registerAdviseCommand(program, deps) {
|
|
@@ -67,6 +67,9 @@ export function registerAdviseCommand(program, deps) {
|
|
|
67
67
|
...takeoverOpts,
|
|
68
68
|
});
|
|
69
69
|
console.error(r.message);
|
|
70
|
+
// The takeover verified the daemon now reports the CLI version;
|
|
71
|
+
// keep the pre-check health from mislabeling the version line.
|
|
72
|
+
health.version = VERSION;
|
|
70
73
|
}
|
|
71
74
|
catch (e) {
|
|
72
75
|
console.error(e instanceof Error ? e.message : String(e));
|
|
@@ -108,6 +111,7 @@ export function registerAdviseCommand(program, deps) {
|
|
|
108
111
|
console.log(JSON.stringify(rec, null, 2));
|
|
109
112
|
else {
|
|
110
113
|
console.log(`${rec.use}: ${rec.reason}`);
|
|
114
|
+
printVersionLine(o, VERSION, health?.version);
|
|
111
115
|
printUpdateFooter(o, footer);
|
|
112
116
|
}
|
|
113
117
|
return;
|
|
@@ -123,6 +127,7 @@ export function registerAdviseCommand(program, deps) {
|
|
|
123
127
|
console.log(JSON.stringify(rec, null, 2));
|
|
124
128
|
else {
|
|
125
129
|
console.log(`${rec.use}: ${rec.reason}`);
|
|
130
|
+
printVersionLine(o, VERSION, health?.version);
|
|
126
131
|
printUpdateFooter(o, footer);
|
|
127
132
|
}
|
|
128
133
|
});
|
package/dist/cli/status.js
CHANGED
|
@@ -6,7 +6,7 @@ import { assertValidSortKey } from "../format/rows.js";
|
|
|
6
6
|
import { createServiceClient } from "../runtime/client.js";
|
|
7
7
|
import { VERSION } from "../version.js";
|
|
8
8
|
import { isServiceManaged, runServiceCommand } from "../service/index.js";
|
|
9
|
-
import { detectChannel, printUpdateFooter, readUpdateCache, refreshUpdateCache, updateFooter, } from "../runtime/updates.js";
|
|
9
|
+
import { detectChannel, printUpdateFooter, printVersionLine, readUpdateCache, refreshUpdateCache, updateFooter, } from "../runtime/updates.js";
|
|
10
10
|
import { ClientError, OFFLINE_LABEL, offlineOnlyClient, resolveSnapshot } from "./snapshot-source.js";
|
|
11
11
|
import { checkSkew, execSkewWarning, olderCliWarning, takeoverManaged, unmanagedSkewWarning, } from "./takeover.js";
|
|
12
12
|
const WIDE_COLUMNS = 100;
|
|
@@ -76,6 +76,9 @@ export function registerStatusCommand(program, deps) {
|
|
|
76
76
|
...takeoverOpts,
|
|
77
77
|
});
|
|
78
78
|
console.error(r.message);
|
|
79
|
+
// The takeover verified the daemon now reports the CLI version;
|
|
80
|
+
// keep the pre-check health from mislabeling the version line.
|
|
81
|
+
health.version = VERSION;
|
|
79
82
|
}
|
|
80
83
|
catch (e) {
|
|
81
84
|
console.error(e instanceof Error ? e.message : String(e));
|
|
@@ -122,6 +125,7 @@ export function registerStatusCommand(program, deps) {
|
|
|
122
125
|
return;
|
|
123
126
|
}
|
|
124
127
|
console.log(e.message);
|
|
128
|
+
printVersionLine(o, VERSION, health?.version);
|
|
125
129
|
printUpdateFooter(o, footer);
|
|
126
130
|
return;
|
|
127
131
|
}
|
|
@@ -153,6 +157,7 @@ export function registerStatusCommand(program, deps) {
|
|
|
153
157
|
if (issues)
|
|
154
158
|
console.log("\n" + issues);
|
|
155
159
|
}
|
|
160
|
+
printVersionLine(o, VERSION, health?.version);
|
|
156
161
|
printUpdateFooter(o, footer);
|
|
157
162
|
});
|
|
158
163
|
}
|
package/dist/cli/takeover.js
CHANGED
|
@@ -119,7 +119,7 @@ export async function takeoverManaged(opts) {
|
|
|
119
119
|
: "unknown";
|
|
120
120
|
const execService = opts.execService ?? ((args, o) => runServiceCommand(args, o ?? {}, {}));
|
|
121
121
|
const verb = opts.refreshRegistration ? "install" : "restart";
|
|
122
|
-
const code = await execService([verb], opts.refreshRegistration ? { version: cliVersion } : undefined);
|
|
122
|
+
const code = await execService([verb], opts.refreshRegistration ? { version: cliVersion, quiet: true } : undefined);
|
|
123
123
|
if (code !== 0) {
|
|
124
124
|
throw new TakeoverError(`daemon upgrade failed: service ${verb} exited ${code}`);
|
|
125
125
|
}
|
|
@@ -57,6 +57,10 @@ export declare function updateFooter(current: string, latest: string | null): st
|
|
|
57
57
|
export declare function printUpdateFooter(o: {
|
|
58
58
|
json?: boolean;
|
|
59
59
|
}, footer: string | null): void;
|
|
60
|
+
export declare function printVersionLine(o: {
|
|
61
|
+
json?: boolean;
|
|
62
|
+
compact?: boolean;
|
|
63
|
+
}, cliVersion: string, daemonVersion: unknown): void;
|
|
60
64
|
export declare function platformAssetName(platform?: NodeJS.Platform, arch?: string): string;
|
|
61
65
|
export declare function verifyChecksum(data: Buffer, sumsText: string, name: string): boolean;
|
|
62
66
|
export declare function defaultExtractTar(tarball: string, destDir: string): void;
|
package/dist/runtime/updates.js
CHANGED
|
@@ -333,6 +333,17 @@ export function printUpdateFooter(o, footer) {
|
|
|
333
333
|
return;
|
|
334
334
|
console.error(footer);
|
|
335
335
|
}
|
|
336
|
+
// Version identity line, printed next to the update footer under the same
|
|
337
|
+
// stderr/TTY rules, plus never under --compact (prompt renderers must not
|
|
338
|
+
// see per-render noise). The daemon part appears only when the reachable
|
|
339
|
+
// daemon reports a version that differs from the CLI.
|
|
340
|
+
export function printVersionLine(o, cliVersion, daemonVersion) {
|
|
341
|
+
if (o.json || o.compact || !process.stderr.isTTY)
|
|
342
|
+
return;
|
|
343
|
+
const daemon = typeof daemonVersion === "string" && daemonVersion ? daemonVersion : null;
|
|
344
|
+
const suffix = daemon && daemon !== cliVersion ? ` · daemon ${daemon}` : "";
|
|
345
|
+
console.error(`quotacap ${cliVersion}${suffix}`);
|
|
346
|
+
}
|
|
336
347
|
export function platformAssetName(platform = process.platform, arch = process.arch) {
|
|
337
348
|
const os = platform === "darwin" ? "darwin" : platform === "linux" ? "linux" : null;
|
|
338
349
|
const a = arch === "arm64" ? "arm64" : arch === "x64" ? "x64" : null;
|
package/dist/service/index.js
CHANGED
|
@@ -75,8 +75,14 @@ export async function runServiceCommand(args, opts = {}, deps = {}) {
|
|
|
75
75
|
case "install":
|
|
76
76
|
// Internal: the post-update refresh passes the target version so
|
|
77
77
|
// install compares and records it instead of the old in-process
|
|
78
|
-
// VERSION
|
|
79
|
-
|
|
78
|
+
// VERSION, plus quiet so a good update prints its own three lines
|
|
79
|
+
// instead of the install chatter. The `service install` CLI never
|
|
80
|
+
// sets opts.version or opts.quiet.
|
|
81
|
+
await backend.install({
|
|
82
|
+
...deps,
|
|
83
|
+
version: opts.version ?? deps.version,
|
|
84
|
+
quiet: opts.quiet ?? deps.quiet,
|
|
85
|
+
});
|
|
80
86
|
break;
|
|
81
87
|
case "uninstall":
|
|
82
88
|
await backend.uninstall(deps);
|
package/dist/service/macos.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface ServiceDeps {
|
|
|
21
21
|
sleep?: (ms: number) => Promise<void>;
|
|
22
22
|
bootstrapTimeoutMs?: number;
|
|
23
23
|
version?: string;
|
|
24
|
+
quiet?: boolean;
|
|
24
25
|
}
|
|
25
26
|
export declare function foregroundGuidance(verb: string, platform: string): string;
|
|
26
27
|
export declare function defaultRunLaunchctl(args: string[]): string;
|
package/dist/service/macos.js
CHANGED
|
@@ -279,6 +279,8 @@ export async function install(deps = {}) {
|
|
|
279
279
|
(deps.print ?? console.log)(foregroundGuidance("install", platform));
|
|
280
280
|
return;
|
|
281
281
|
}
|
|
282
|
+
if (deps.quiet)
|
|
283
|
+
deps = { ...deps, print: () => { } };
|
|
282
284
|
const { home, uid, dataDir, print, run, lint, sleep, bootstrapTimeoutMs } = resolved(deps);
|
|
283
285
|
const version = deps.version ?? VERSION;
|
|
284
286
|
// Provision beside the data dir the service will use (identical to the
|
|
@@ -342,6 +344,14 @@ export async function install(deps = {}) {
|
|
|
342
344
|
installedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
343
345
|
}, dataDir);
|
|
344
346
|
bootoutQuiet(run, uid, print);
|
|
347
|
+
// bootout is asynchronous: without this the replacement races the dying
|
|
348
|
+
// process for the port, the same race restart() guards against.
|
|
349
|
+
// deps.port keeps tests off the real daemon's port.
|
|
350
|
+
const upgradePort = deps.port ?? (await readConfig()).port;
|
|
351
|
+
const waitReleased = deps.waitReleased ?? defaultWaitReleased;
|
|
352
|
+
if (!(await waitReleased(upgradePort))) {
|
|
353
|
+
print(`port ${upgradePort} is still in use after stopping; starting anyway`);
|
|
354
|
+
}
|
|
345
355
|
}
|
|
346
356
|
else {
|
|
347
357
|
writeServiceMetadata({
|
|
@@ -18,6 +18,7 @@ export interface SystemdDeps {
|
|
|
18
18
|
error?: (msg: string) => void;
|
|
19
19
|
now?: () => Date;
|
|
20
20
|
version?: string;
|
|
21
|
+
quiet?: boolean;
|
|
21
22
|
}
|
|
22
23
|
export declare function formatSystemctlError(args: string[], detail: string): Error;
|
|
23
24
|
export declare function defaultRunSystemctl(args: string[]): string;
|
package/dist/service/systemd.js
CHANGED
|
@@ -106,6 +106,8 @@ export async function install(deps = {}) {
|
|
|
106
106
|
(deps.print ?? console.log)(foregroundGuidance("install", platform));
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
+
if (deps.quiet)
|
|
110
|
+
deps = { ...deps, print: () => { } };
|
|
109
111
|
const { home, dataDir, print, run } = resolved(deps);
|
|
110
112
|
const version = deps.version ?? VERSION;
|
|
111
113
|
// Provision beside the data dir the service will use (identical to the
|
|
@@ -157,6 +159,14 @@ export async function install(deps = {}) {
|
|
|
157
159
|
installedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
158
160
|
}, dataDir);
|
|
159
161
|
stopQuiet(run, print);
|
|
162
|
+
// systemctl stop can return before the port is released; same race as
|
|
163
|
+
// launchd. deps.port mirrors start(): tests must never probe the real
|
|
164
|
+
// daemon's port.
|
|
165
|
+
const upgradePort = deps.port ?? (await readConfig()).port;
|
|
166
|
+
const waitReleased = deps.waitReleased ?? defaultWaitReleased;
|
|
167
|
+
if (!(await waitReleased(upgradePort))) {
|
|
168
|
+
print(`port ${upgradePort} is still in use after stopping; starting anyway`);
|
|
169
|
+
}
|
|
160
170
|
}
|
|
161
171
|
else {
|
|
162
172
|
writeServiceMetadata({
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// generated by scripts/build-embed.mjs — do not edit
|
|
2
|
-
export const VERSION = "0.0.
|
|
2
|
+
export const VERSION = "0.0.28";
|
package/dist/web/src/App.js
CHANGED
|
@@ -95,7 +95,7 @@ function App() {
|
|
|
95
95
|
: null;
|
|
96
96
|
const header = (_jsx(Header, { runtime: snapshot?.runtime ?? null, unreachable: error?.kind === "service-unavailable", refreshing: refreshing, onRefresh: () => void refresh(), onSettings: () => setSettingsOpen(true) }));
|
|
97
97
|
if (snapshot && setupMode) {
|
|
98
|
-
return (_jsxs(_Fragment, { children: [header, _jsx("main", { children: _jsx(Onboarding, { snapshot: snapshot, onFirstPoll: () => void refresh(), polling: refreshing, pollError: pollErrorText }) }), _jsx(SettingsDrawer, { open: settingsOpen, providers: snapshot.providers, onRefresh: () => void refresh(), refreshing: refreshing, onClose: () => setSettingsOpen(false) }), _jsx(SiteFooter, {})] }));
|
|
98
|
+
return (_jsxs(_Fragment, { children: [header, _jsx("main", { children: _jsx(Onboarding, { snapshot: snapshot, onFirstPoll: () => void refresh(), polling: refreshing, pollError: pollErrorText }) }), _jsx(SettingsDrawer, { open: settingsOpen, providers: snapshot.providers, onRefresh: () => void refresh(), refreshing: refreshing, onClose: () => setSettingsOpen(false) }), _jsx(SiteFooter, { version: snapshot.runtime.version })] }));
|
|
99
99
|
}
|
|
100
100
|
return (_jsxs(_Fragment, { children: [header, _jsxs("main", { children: [notice && (_jsx("div", { "data-testid": "refresh-notice", role: "status", children: notice })), loading && !snapshot && (_jsx("div", { "data-testid": "state-loading", "aria-busy": "true", "aria-label": "Loading quota state", children: [0, 1, 2].map((i) => (_jsx("div", { "aria-hidden": "true", className: "skel", style: {
|
|
101
101
|
height: 64,
|
|
@@ -109,7 +109,7 @@ function App() {
|
|
|
109
109
|
}, onViewAdvice: () => {
|
|
110
110
|
setSelectedProvider(null);
|
|
111
111
|
setAdviceOpen(true);
|
|
112
|
-
} }))] }), _jsx(ProviderDrawer, { provider: snapshot?.providers.find((p) => p.id === selectedProvider) ?? null, asOf: snapshot?.asOf ?? "", recommendation: snapshot?.recommendation ?? null, onClose: () => setSelectedProvider(null), onRenamed: () => void load() }), snapshot && (_jsx(AdviceDrawer, { open: adviceOpen, recommendation: snapshot.recommendation, providers: snapshot.providers, asOf: snapshot.asOf, onClose: () => setAdviceOpen(false) })), _jsx(SettingsDrawer, { open: settingsOpen, providers: snapshot?.providers ?? [], onRefresh: () => void refresh(), refreshing: refreshing, onClose: () => setSettingsOpen(false) }), _jsx(SiteFooter, {})] }));
|
|
112
|
+
} }))] }), _jsx(ProviderDrawer, { provider: snapshot?.providers.find((p) => p.id === selectedProvider) ?? null, asOf: snapshot?.asOf ?? "", recommendation: snapshot?.recommendation ?? null, onClose: () => setSelectedProvider(null), onRenamed: () => void load() }), snapshot && (_jsx(AdviceDrawer, { open: adviceOpen, recommendation: snapshot.recommendation, providers: snapshot.providers, asOf: snapshot.asOf, onClose: () => setAdviceOpen(false) })), _jsx(SettingsDrawer, { open: settingsOpen, providers: snapshot?.providers ?? [], onRefresh: () => void refresh(), refreshing: refreshing, onClose: () => setSettingsOpen(false) }), _jsx(SiteFooter, { version: snapshot?.runtime.version })] }));
|
|
113
113
|
}
|
|
114
114
|
const el = typeof document === "undefined" ? null : document.getElementById("app");
|
|
115
115
|
if (el)
|
|
@@ -38,6 +38,7 @@ function servingHost() {
|
|
|
38
38
|
return "127.0.0.1";
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
export function SiteFooter() {
|
|
42
|
-
|
|
41
|
+
export function SiteFooter({ version }) {
|
|
42
|
+
const name = version ? `QuotaCap v${version}` : "QuotaCap";
|
|
43
|
+
return (_jsxs("footer", { className: "sitefoot", children: [_jsxs("div", { className: "sitefoot-start", children: [_jsxs("span", { children: [name, " \u00B7 local quota tracker \u00B7 bound to ", _jsx("code", { children: servingHost() })] }), _jsx(PacingLegend, {})] }), _jsx("span", { children: "MIT licence \u00B7 no telemetry \u00B7 history stays on this machine" })] }));
|
|
43
44
|
}
|