@juliantanx/aiusage 1.0.2 → 1.0.4

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.
Files changed (52) hide show
  1. package/dist/{chunk-C6IKRE2P.js → chunk-2DX56FB6.js} +79 -15
  2. package/dist/{dist-XHYTXDVT.js → dist-4I33E46P.js} +1 -1
  3. package/dist/index.js +284 -58
  4. package/dist/web/_app/immutable/assets/0.DDbFsqNI.css +1 -0
  5. package/dist/web/_app/immutable/assets/2.fho1OPpm.css +1 -0
  6. package/dist/web/_app/immutable/assets/8.9sovMx-u.css +1 -0
  7. package/dist/web/_app/immutable/assets/9.Doxq00gF.css +1 -0
  8. package/dist/web/_app/immutable/assets/_layout.RgD-pTKj.css +1 -0
  9. package/dist/web/_app/immutable/assets/_page.9sovMx-u.css +1 -0
  10. package/dist/web/_app/immutable/assets/_page.DTuZozBR.css +1 -0
  11. package/dist/web/_app/immutable/assets/_page.Doxq00gF.css +1 -0
  12. package/dist/web/_app/immutable/chunks/ToolSelector.DIzQsA4_.js +2 -0
  13. package/dist/web/_app/immutable/chunks/api.D-M_rVlI.js +1 -0
  14. package/dist/web/_app/immutable/chunks/entry.-i5eOrF8.js +3 -0
  15. package/dist/web/_app/immutable/chunks/{index.Dm-3G3np.js → index.BjVwNvJa.js} +1 -1
  16. package/dist/web/_app/immutable/chunks/{index.CzqoKkoo.js → index.CUfK07C5.js} +1 -1
  17. package/dist/web/_app/immutable/chunks/scheduler.DukoKnjQ.js +1 -0
  18. package/dist/web/_app/immutable/chunks/{stores.BSVKBbO8.js → stores.BJHWQcKv.js} +1 -1
  19. package/dist/web/_app/immutable/entry/app.Pb7AfjR8.js +2 -0
  20. package/dist/web/_app/immutable/entry/start.DyuR4tFJ.js +1 -0
  21. package/dist/web/_app/immutable/nodes/0.BhkxfbMC.js +1 -0
  22. package/dist/web/_app/immutable/nodes/{1.xdMqApFe.js → 1.CNO4bJXv.js} +1 -1
  23. package/dist/web/_app/immutable/nodes/{8.BSeUhVCa.js → 10.1LgqVOtM.js} +1 -1
  24. package/dist/web/_app/immutable/nodes/{9.DJXBgSiY.js → 11.BOfGogcw.js} +1 -1
  25. package/dist/web/_app/immutable/nodes/2.C_8jrQsV.js +3 -0
  26. package/dist/web/_app/immutable/nodes/{3.Bqg88zQg.js → 3.Lz_pbSpS.js} +1 -1
  27. package/dist/web/_app/immutable/nodes/{4.B3zrG0ip.js → 4.BCB36ta_.js} +1 -1
  28. package/dist/web/_app/immutable/nodes/{2.CJyBxUXd.js → 5.CykeLKzi.js} +1 -1
  29. package/dist/web/_app/immutable/nodes/{5.C3h9wXyF.js → 6.De73932o.js} +1 -1
  30. package/dist/web/_app/immutable/nodes/{6.C8xhym7b.js → 7.6OEKXCWR.js} +1 -1
  31. package/dist/web/_app/immutable/nodes/8.niqPZkB9.js +1 -0
  32. package/dist/web/_app/immutable/nodes/9.DRZSbXAV.js +1 -0
  33. package/dist/web/_app/version.json +1 -1
  34. package/dist/web/index.html +9 -9
  35. package/package.json +4 -2
  36. package/dist/web/_app/immutable/assets/0.BTUc0PXb.css +0 -1
  37. package/dist/web/_app/immutable/assets/7.8R_5Laqi.css +0 -1
  38. package/dist/web/_app/immutable/assets/_layout.DLyrrG29.css +0 -1
  39. package/dist/web/_app/immutable/assets/_page.8R_5Laqi.css +0 -1
  40. package/dist/web/_app/immutable/chunks/ToolSelector.kb97TteP.js +0 -2
  41. package/dist/web/_app/immutable/chunks/api.DyoIo4nG.js +0 -1
  42. package/dist/web/_app/immutable/chunks/entry.DbT6jbyV.js +0 -3
  43. package/dist/web/_app/immutable/chunks/scheduler.Dx1bs0RQ.js +0 -1
  44. package/dist/web/_app/immutable/entry/app.BjD-8RWO.js +0 -2
  45. package/dist/web/_app/immutable/entry/start.Dg2jUJRH.js +0 -1
  46. package/dist/web/_app/immutable/nodes/0.DiQJRuoG.js +0 -1
  47. package/dist/web/_app/immutable/nodes/7.C950CTxa.js +0 -1
  48. /package/dist/web/_app/immutable/assets/{8.BcbNCOTS.css → 10.BcbNCOTS.css} +0 -0
  49. /package/dist/web/_app/immutable/assets/{9.QhlpTtkf.css → 11.QhlpTtkf.css} +0 -0
  50. /package/dist/web/_app/immutable/assets/{2.I4bgY8QS.css → 5.I4bgY8QS.css} +0 -0
  51. /package/dist/web/_app/immutable/assets/{5.BeDwa7mS.css → 6.BeDwa7mS.css} +0 -0
  52. /package/dist/web/_app/immutable/assets/{6.BYQidWiw.css → 7.BYQidWiw.css} +0 -0
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  removePriceOverride,
14
14
  resolvePrice,
15
15
  setPriceOverride
16
- } from "./chunk-C6IKRE2P.js";
16
+ } from "./chunk-2DX56FB6.js";
17
17
 
18
18
  // src/cli.ts
19
19
  import { Command } from "commander";
@@ -156,7 +156,7 @@ function looksMachineGenerated(name) {
156
156
  }
157
157
 
158
158
  // src/api/server.ts
159
- function getDateRangeFilter(range, from, to, prefix = "") {
159
+ function getDateRangeFilter(range, from, to, prefix = "", weekStart = 1) {
160
160
  const ts = prefix ? `${prefix}.ts` : "ts";
161
161
  if (from && to) {
162
162
  const startMs = new Date(from).getTime();
@@ -167,8 +167,9 @@ function getDateRangeFilter(range, from, to, prefix = "") {
167
167
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
168
168
  if (range === "week") {
169
169
  const dayOfWeek = today.getDay();
170
+ const diff = (dayOfWeek - weekStart + 7) % 7;
170
171
  const startOfWeek = new Date(today);
171
- startOfWeek.setDate(today.getDate() - dayOfWeek);
172
+ startOfWeek.setDate(today.getDate() - diff);
172
173
  return { where: `AND ${ts} >= @start`, params: { start: startOfWeek.getTime() } };
173
174
  }
174
175
  if (range === "month") {
@@ -218,6 +219,8 @@ function getDeviceFilter(device, currentDeviceInstanceId) {
218
219
  };
219
220
  }
220
221
  function createApiServer(db, options) {
222
+ const cfg = loadConfig();
223
+ let weekStart = cfg?.weekStart ?? 1;
221
224
  const server = http.createServer(async (req, res) => {
222
225
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
223
226
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -245,7 +248,7 @@ function createApiServer(db, options) {
245
248
  }
246
249
  const device = url.searchParams.get("device");
247
250
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
248
- const dr = getDateRangeFilter(range, from, to);
251
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
249
252
  const tool = url.searchParams.get("tool");
250
253
  const tf = getToolFilter(tool);
251
254
  let totals;
@@ -333,7 +336,7 @@ function createApiServer(db, options) {
333
336
  for (const row of byToolRows) {
334
337
  byTool[row.tool] = { tokens: row.tokens, cost: row.cost };
335
338
  }
336
- const drJoin = getDateRangeFilter(range, from, to, "r");
339
+ const drJoin = getDateRangeFilter(range, from, to, "r", weekStart);
337
340
  const dfJoin = df.where ? df.where.replace(/device_instance_id/g, "r.device_instance_id") : "";
338
341
  const tfJoin = getToolFilter(tool, "r");
339
342
  const topToolCalls = db.prepare(`
@@ -359,7 +362,7 @@ function createApiServer(db, options) {
359
362
  return;
360
363
  }
361
364
  if (url.pathname === "/api/tokens") {
362
- const dr = getDateRangeFilter(range, from, to);
365
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
363
366
  const device = url.searchParams.get("device");
364
367
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
365
368
  const tool = url.searchParams.get("tool");
@@ -409,7 +412,7 @@ function createApiServer(db, options) {
409
412
  return;
410
413
  }
411
414
  if (url.pathname === "/api/cost") {
412
- const dr = getDateRangeFilter(range, from, to);
415
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
413
416
  const device = url.searchParams.get("device");
414
417
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
415
418
  const tool = url.searchParams.get("tool");
@@ -485,7 +488,7 @@ function createApiServer(db, options) {
485
488
  return;
486
489
  }
487
490
  if (url.pathname === "/api/models") {
488
- const dr = getDateRangeFilter(range, from, to);
491
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
489
492
  const device = url.searchParams.get("device");
490
493
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
491
494
  const tool = url.searchParams.get("tool");
@@ -546,7 +549,7 @@ function createApiServer(db, options) {
546
549
  json(res, { toolCalls: [] });
547
550
  return;
548
551
  }
549
- const dr = getDateRangeFilter(range, from, to, "r");
552
+ const dr = getDateRangeFilter(range, from, to, "r", weekStart);
550
553
  const tool = url.searchParams.get("tool");
551
554
  const tf = getToolFilter(tool, "r");
552
555
  const totalRow = db.prepare(`
@@ -573,7 +576,7 @@ function createApiServer(db, options) {
573
576
  if (url.pathname === "/api/sessions") {
574
577
  const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
575
578
  const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get("pageSize") || "50", 10)));
576
- const dr = getDateRangeFilter(range, from, to);
579
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
577
580
  const device = url.searchParams.get("device");
578
581
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
579
582
  const tool = url.searchParams.get("tool");
@@ -617,7 +620,7 @@ function createApiServer(db, options) {
617
620
  return;
618
621
  }
619
622
  if (url.pathname === "/api/projects") {
620
- const dr = getDateRangeFilter(range, from, to);
623
+ const dr = getDateRangeFilter(range, from, to, "", weekStart);
621
624
  const device = url.searchParams.get("device");
622
625
  const df = getDeviceFilter(device, options?.currentDeviceInstanceId);
623
626
  const tool = url.searchParams.get("tool");
@@ -710,9 +713,9 @@ function createApiServer(db, options) {
710
713
  thinking: data.thinking
711
714
  };
712
715
  setPriceOverride(data.model, entry);
713
- const cfg = loadConfig() ?? {};
714
- cfg.priceOverrides = { ...cfg.priceOverrides, [data.model]: entry };
715
- saveConfig(cfg);
716
+ const cfg2 = loadConfig() ?? {};
717
+ cfg2.priceOverrides = { ...cfg2.priceOverrides, [data.model]: entry };
718
+ saveConfig(cfg2);
716
719
  json(res, { ok: true });
717
720
  } catch {
718
721
  json(res, { error: { code: "INVALID_JSON", message: "Invalid JSON body" } }, 400);
@@ -726,17 +729,17 @@ function createApiServer(db, options) {
726
729
  return;
727
730
  }
728
731
  removePriceOverride(model);
729
- const cfg = loadConfig() ?? {};
730
- if (cfg.priceOverrides) {
731
- delete cfg.priceOverrides[model];
732
- saveConfig(cfg);
732
+ const cfg2 = loadConfig() ?? {};
733
+ if (cfg2.priceOverrides) {
734
+ delete cfg2.priceOverrides[model];
735
+ saveConfig(cfg2);
733
736
  }
734
737
  json(res, { ok: true });
735
738
  return;
736
739
  }
737
740
  }
738
741
  if (url.pathname === "/api/pricing/recalc" && req.method === "POST") {
739
- const { resolvePrice: resolvePrice2 } = await import("./dist-XHYTXDVT.js");
742
+ const { resolvePrice: resolvePrice2 } = await import("./dist-4I33E46P.js");
740
743
  const BATCH_SIZE2 = 1e3;
741
744
  let updated = 0;
742
745
  let lastId = "";
@@ -844,6 +847,121 @@ function createApiServer(db, options) {
844
847
  json(res, { currentDeviceInstanceId: currentId, devices });
845
848
  return;
846
849
  }
850
+ if (url.pathname === "/api/config/credential" && req.method === "GET") {
851
+ const ref = url.searchParams.get("ref")?.trim();
852
+ if (!ref) {
853
+ json(res, { error: { code: "MISSING_CREDENTIAL_REF", message: "credential ref is required" } }, 400);
854
+ return;
855
+ }
856
+ const value = loadCredential(ref);
857
+ if (!value) {
858
+ json(res, { error: { code: "CREDENTIAL_NOT_FOUND", message: "Credential not found" } }, 404);
859
+ return;
860
+ }
861
+ json(res, { value });
862
+ return;
863
+ }
864
+ if (url.pathname === "/api/config") {
865
+ if (req.method === "GET") {
866
+ const currentCfg = loadConfig() ?? {};
867
+ const { credentials, priceOverrides, platform: platform4, ...rest } = currentCfg;
868
+ json(res, {
869
+ device: rest.device ?? null,
870
+ weekStart: rest.weekStart ?? 1,
871
+ dashboardPollInterval: rest.dashboardPollInterval ?? null,
872
+ parseInterval: rest.parseInterval ?? null,
873
+ retentionDays: rest.retentionDays ?? null,
874
+ sources: {
875
+ "claude-code": rest.sources?.["claude-code"] ?? null,
876
+ codex: rest.sources?.codex ?? null,
877
+ openclaw: rest.sources?.openclaw ?? null,
878
+ opencode: rest.sources?.opencode ?? null
879
+ },
880
+ sync: rest.sync ?? null,
881
+ credentialKeys: credentials ? Object.keys(credentials) : []
882
+ });
883
+ return;
884
+ }
885
+ if (req.method === "PUT") {
886
+ let body = "";
887
+ for await (const chunk of req) body += chunk;
888
+ try {
889
+ const update = JSON.parse(body);
890
+ const existing = loadConfig() ?? {};
891
+ if ("device" in update) {
892
+ if (!update.device) delete existing.device;
893
+ else existing.device = String(update.device);
894
+ }
895
+ if ("weekStart" in update) {
896
+ const ws = Number(update.weekStart);
897
+ if (ws === 0 || ws === 1) {
898
+ existing.weekStart = ws;
899
+ weekStart = ws;
900
+ } else {
901
+ delete existing.weekStart;
902
+ }
903
+ }
904
+ if ("dashboardPollInterval" in update) {
905
+ if (!update.dashboardPollInterval) delete existing.dashboardPollInterval;
906
+ else existing.dashboardPollInterval = Number(update.dashboardPollInterval);
907
+ }
908
+ if ("parseInterval" in update) {
909
+ if (!update.parseInterval) delete existing.parseInterval;
910
+ else existing.parseInterval = Number(update.parseInterval);
911
+ }
912
+ if ("retentionDays" in update) {
913
+ if (!update.retentionDays) delete existing.retentionDays;
914
+ else existing.retentionDays = Number(update.retentionDays);
915
+ }
916
+ if (update.sources && typeof update.sources === "object") {
917
+ const src = update.sources;
918
+ const s = existing.sources ?? {};
919
+ for (const key of ["claude-code", "codex", "openclaw", "opencode"]) {
920
+ if (key in src) {
921
+ if (!src[key]) delete s[key];
922
+ else s[key] = String(src[key]);
923
+ }
924
+ }
925
+ if (Object.keys(s).length) existing.sources = s;
926
+ else delete existing.sources;
927
+ }
928
+ if ("sync" in update) {
929
+ const syncUpdate = update.sync;
930
+ if (!syncUpdate?.backend) {
931
+ delete existing.sync;
932
+ } else {
933
+ const backendVal = String(syncUpdate.backend);
934
+ if (backendVal !== "github" && backendVal !== "s3") {
935
+ json(res, { error: { code: "INVALID_BACKEND", message: "sync.backend must be github or s3" } }, 400);
936
+ return;
937
+ }
938
+ const newSync = { backend: backendVal };
939
+ for (const f of ["repo", "bucket", "prefix", "endpoint", "region", "credentialRef"]) {
940
+ if (syncUpdate[f]) newSync[f] = String(syncUpdate[f]);
941
+ }
942
+ existing.sync = newSync;
943
+ }
944
+ }
945
+ if (update.credentials && typeof update.credentials === "object") {
946
+ const creds = update.credentials;
947
+ const c = existing.credentials ?? {};
948
+ for (const [key, val] of Object.entries(creds)) {
949
+ if (val !== "" && val !== null && val !== void 0) {
950
+ c[key] = String(val);
951
+ }
952
+ }
953
+ if (Object.keys(c).length) existing.credentials = c;
954
+ else delete existing.credentials;
955
+ }
956
+ saveConfig(existing);
957
+ options?.onConfigUpdated?.();
958
+ json(res, { ok: true });
959
+ } catch {
960
+ json(res, { error: { code: "INVALID_JSON", message: "Invalid JSON body" } }, 400);
961
+ }
962
+ return;
963
+ }
964
+ }
847
965
  json(res, { error: { code: "NOT_FOUND", message: "Endpoint not found" } }, 404);
848
966
  } catch (error) {
849
967
  console.error("API error:", error);
@@ -1272,8 +1390,10 @@ async function runParse(db, filterTool, options) {
1272
1390
  });
1273
1391
  const result = aggregator.parseLine(line, context);
1274
1392
  if (result) {
1275
- insertRecord(db, result.record);
1276
- parsedCount++;
1393
+ if (result.record) {
1394
+ insertRecord(db, result.record);
1395
+ parsedCount++;
1396
+ }
1277
1397
  for (const tc of result.toolCalls) {
1278
1398
  insertToolCall(db, tc);
1279
1399
  toolCallCount++;
@@ -1665,35 +1785,56 @@ var GitSyncBackend = class _GitSyncBackend {
1665
1785
  token;
1666
1786
  cacheDir;
1667
1787
  dataDir;
1788
+ branch;
1668
1789
  constructor(config) {
1669
1790
  this.repo = config.repo;
1670
1791
  this.token = config.token;
1671
1792
  this.cacheDir = config.cacheDir;
1672
1793
  this.dataDir = join4(config.cacheDir, "data");
1794
+ this.branch = config.branch ?? "main";
1673
1795
  }
1674
1796
  get remoteUrl() {
1675
1797
  return `https://x-access-token:${this.token}@github.com/${this.repo}.git`;
1676
1798
  }
1799
+ sanitizeError(error) {
1800
+ if (error instanceof Error) {
1801
+ const pattern = /https:\/\/[^@\s]+@github\.com/g;
1802
+ error.message = error.message.replace(pattern, "https://github.com");
1803
+ const err = error;
1804
+ if (typeof err.stderr === "string") {
1805
+ err.stderr = err.stderr.replace(pattern, "https://github.com");
1806
+ }
1807
+ }
1808
+ return error;
1809
+ }
1677
1810
  async git(args, cwd) {
1678
- const { stdout } = await exec("git", args, {
1679
- cwd: cwd ?? this.cacheDir,
1680
- timeout: 6e4,
1681
- env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
1682
- });
1683
- return stdout.trim();
1811
+ try {
1812
+ const { stdout } = await exec("git", args, {
1813
+ cwd: cwd ?? this.cacheDir,
1814
+ timeout: 6e4,
1815
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
1816
+ });
1817
+ return stdout.trim();
1818
+ } catch (error) {
1819
+ throw this.sanitizeError(error);
1820
+ }
1684
1821
  }
1685
1822
  /** Clone or pull the repo to get latest remote state */
1686
1823
  async prepare() {
1687
1824
  try {
1688
1825
  await stat(join4(this.cacheDir, ".git"));
1689
- await this.git(["fetch", "origin", "main", "--depth=1"]);
1690
- await this.git(["reset", "--hard", "origin/main"]);
1826
+ await this.git(["fetch", "origin", this.branch, "--depth=1"]);
1827
+ await this.git(["reset", "--hard", `origin/${this.branch}`]);
1691
1828
  } catch {
1692
1829
  await mkdir(this.cacheDir, { recursive: true });
1693
- await exec("git", ["clone", "--depth=1", this.remoteUrl, this.cacheDir], {
1694
- timeout: 12e4,
1695
- env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
1696
- });
1830
+ try {
1831
+ await exec("git", ["clone", "--depth=1", this.remoteUrl, this.cacheDir], {
1832
+ timeout: 12e4,
1833
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
1834
+ });
1835
+ } catch (error) {
1836
+ throw this.sanitizeError(error);
1837
+ }
1697
1838
  }
1698
1839
  }
1699
1840
  async readFile(path2) {
@@ -1725,11 +1866,11 @@ var GitSyncBackend = class _GitSyncBackend {
1725
1866
  await this.git(["commit", "-m", `sync ${(/* @__PURE__ */ new Date()).toISOString()}`]);
1726
1867
  for (let attempt = 1; attempt <= _GitSyncBackend.MAX_PUSH_RETRIES; attempt++) {
1727
1868
  try {
1728
- await this.git(["push", "origin", "main"]);
1869
+ await this.git(["push", "origin", this.branch]);
1729
1870
  return true;
1730
1871
  } catch (error) {
1731
1872
  if (attempt >= _GitSyncBackend.MAX_PUSH_RETRIES) throw error;
1732
- await this.git(["pull", "--rebase", "origin", "main"]);
1873
+ await this.git(["pull", "--rebase", "origin", this.branch]);
1733
1874
  }
1734
1875
  }
1735
1876
  return false;
@@ -1803,14 +1944,14 @@ var S3SyncBackend = class {
1803
1944
  do {
1804
1945
  const command = new ListObjectsV2Command({
1805
1946
  Bucket: this.bucket,
1806
- Prefix: `${this.prefix}data/`,
1947
+ Prefix: this.prefix,
1807
1948
  ContinuationToken: continuationToken
1808
1949
  });
1809
1950
  const response = await this.client.send(command);
1810
1951
  if (response.Contents) {
1811
1952
  for (const obj of response.Contents) {
1812
1953
  const key = obj.Key;
1813
- const relPath = key.slice(`${this.prefix}data/`.length);
1954
+ const relPath = key.slice(this.prefix.length);
1814
1955
  if (relPath.endsWith(".ndjson")) {
1815
1956
  files.push(relPath);
1816
1957
  }
@@ -1916,6 +2057,22 @@ async function runSync(db, options) {
1916
2057
  return result;
1917
2058
  }
1918
2059
 
2060
+ // src/commands/clean.ts
2061
+ function cleanOldData(db, days) {
2062
+ const cutoff = Date.now() - days * 864e5;
2063
+ const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
2064
+ const deletedCount = recordsResult.changes;
2065
+ const syncedResult = db.prepare("DELETE FROM synced_records WHERE ts < ?").run(cutoff);
2066
+ const deletedSyncedCount = syncedResult.changes;
2067
+ const orphanResult = db.prepare("DELETE FROM tool_calls WHERE record_id IS NULL AND ts < ?").run(cutoff);
2068
+ const deletedOrphanToolCalls = orphanResult.changes;
2069
+ return {
2070
+ deletedCount,
2071
+ deletedSyncedCount,
2072
+ deletedOrphanToolCalls
2073
+ };
2074
+ }
2075
+
1919
2076
  // src/sync/runtime.ts
1920
2077
  var SyncRuntimeController = class {
1921
2078
  runSyncFn;
@@ -1981,6 +2138,77 @@ var SyncRuntimeController = class {
1981
2138
  }
1982
2139
  };
1983
2140
 
2141
+ // src/runtime/settings-controller.ts
2142
+ var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1e3;
2143
+ var RuntimeSettingsController = class {
2144
+ db;
2145
+ loadConfigFn;
2146
+ runParseFn;
2147
+ runCleanupFn;
2148
+ cleanupIntervalMs;
2149
+ parseTimer = null;
2150
+ cleanupTimer = null;
2151
+ parseInFlight = false;
2152
+ started = false;
2153
+ constructor(options) {
2154
+ this.db = options.db;
2155
+ this.loadConfigFn = options.loadConfig;
2156
+ this.runParseFn = options.runParse;
2157
+ this.runCleanupFn = options.runCleanup;
2158
+ this.cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
2159
+ }
2160
+ start() {
2161
+ if (this.started) return;
2162
+ this.started = true;
2163
+ this.applyConfig();
2164
+ }
2165
+ reload() {
2166
+ if (!this.started) return;
2167
+ this.applyConfig();
2168
+ }
2169
+ stop() {
2170
+ this.started = false;
2171
+ if (this.parseTimer) clearInterval(this.parseTimer);
2172
+ if (this.cleanupTimer) clearInterval(this.cleanupTimer);
2173
+ this.parseTimer = null;
2174
+ this.cleanupTimer = null;
2175
+ }
2176
+ applyConfig() {
2177
+ if (this.parseTimer) clearInterval(this.parseTimer);
2178
+ if (this.cleanupTimer) clearInterval(this.cleanupTimer);
2179
+ this.parseTimer = null;
2180
+ this.cleanupTimer = null;
2181
+ const config = this.loadConfigFn();
2182
+ const parseInterval = Number(config?.parseInterval ?? 0);
2183
+ const retentionDays = Number(config?.retentionDays ?? 0);
2184
+ if (parseInterval > 0) {
2185
+ this.parseTimer = setInterval(() => {
2186
+ void this.runParseSafely();
2187
+ }, parseInterval);
2188
+ }
2189
+ if (retentionDays > 0) {
2190
+ this.cleanupTimer = setInterval(() => {
2191
+ try {
2192
+ this.runCleanupFn(this.db, retentionDays);
2193
+ } catch (err) {
2194
+ console.error("[settings-controller] cleanup failed:", err);
2195
+ }
2196
+ }, this.cleanupIntervalMs);
2197
+ }
2198
+ }
2199
+ async runParseSafely() {
2200
+ if (this.parseInFlight) return;
2201
+ this.parseInFlight = true;
2202
+ try {
2203
+ await this.runParseFn(this.db);
2204
+ } catch (err) {
2205
+ console.error("[settings-controller] parse failed:", err);
2206
+ } finally {
2207
+ this.parseInFlight = false;
2208
+ }
2209
+ }
2210
+ };
2211
+
1984
2212
  // src/commands/serve.ts
1985
2213
  var MIME_TYPES = {
1986
2214
  ".html": "text/html",
@@ -2005,13 +2233,25 @@ function serve(options) {
2005
2233
  runSync: (runtimeOptions) => runSync(options.db, runtimeOptions).then(() => void 0),
2006
2234
  getPersistedState: () => getState(AIUSAGE_DIR)
2007
2235
  });
2236
+ const runtimeSettings = new RuntimeSettingsController({
2237
+ db: options.db,
2238
+ loadConfig,
2239
+ runParse,
2240
+ runCleanup: cleanOldData
2241
+ });
2242
+ runtimeSettings.start();
2008
2243
  const apiServer = createApiServer(options.db, {
2009
2244
  currentDeviceInstanceId: getState(AIUSAGE_DIR)?.deviceInstanceId,
2010
2245
  onRefresh: () => runParse(options.db),
2011
2246
  onSyncStart: () => syncRuntime.start(),
2012
- getSyncStatus: () => syncRuntime.getStatus()
2247
+ getSyncStatus: () => syncRuntime.getStatus(),
2248
+ onConfigUpdated: () => runtimeSettings.reload()
2013
2249
  });
2014
- const webBuildDir = join6(dirname(fileURLToPath(import.meta.url)), "web");
2250
+ const webBuildDir = (() => {
2251
+ const prodDir = join6(dirname(fileURLToPath(import.meta.url)), "web");
2252
+ if (existsSync5(prodDir)) return prodDir;
2253
+ return join6(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "web", "build");
2254
+ })();
2015
2255
  const server = http2.createServer(async (req, res) => {
2016
2256
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
2017
2257
  if (url.pathname.startsWith("/api/")) {
@@ -2047,11 +2287,13 @@ function serve(options) {
2047
2287
  });
2048
2288
  process.on("SIGINT", () => {
2049
2289
  console.log("\nShutting down...");
2290
+ runtimeSettings.stop();
2050
2291
  server.close(() => {
2051
2292
  process.exit(0);
2052
2293
  });
2053
2294
  });
2054
2295
  process.on("SIGTERM", () => {
2296
+ runtimeSettings.stop();
2055
2297
  server.close(() => {
2056
2298
  process.exit(0);
2057
2299
  });
@@ -2085,7 +2327,7 @@ function runInit(options) {
2085
2327
  sync: {
2086
2328
  backend: "github",
2087
2329
  repo: options.repo,
2088
- credentialRef: `keychain://aiusage/github/${options.repo}`
2330
+ credentialRef: `github/${options.repo}/token`
2089
2331
  },
2090
2332
  device: options.device ?? existingConfig?.device,
2091
2333
  platform: existingConfig?.platform ?? platform3(),
@@ -2131,7 +2373,7 @@ Consent recorded for schema v1.`
2131
2373
  prefix,
2132
2374
  endpoint: options.endpoint,
2133
2375
  region: options.region ?? "auto",
2134
- credentialRef: `keychain://aiusage/s3/${options.bucket}`
2376
+ credentialRef: `s3/${options.bucket}/accessKeyId`
2135
2377
  },
2136
2378
  device: options.device ?? existingConfig?.device,
2137
2379
  platform: existingConfig?.platform ?? platform3(),
@@ -2352,22 +2594,6 @@ function exportData(db, format) {
2352
2594
  return lines.join("\n");
2353
2595
  }
2354
2596
 
2355
- // src/commands/clean.ts
2356
- function cleanOldData(db, days) {
2357
- const cutoff = Date.now() - days * 864e5;
2358
- const recordsResult = db.prepare("DELETE FROM records WHERE ts < ?").run(cutoff);
2359
- const deletedCount = recordsResult.changes;
2360
- const syncedResult = db.prepare("DELETE FROM synced_records WHERE ts < ?").run(cutoff);
2361
- const deletedSyncedCount = syncedResult.changes;
2362
- const orphanResult = db.prepare("DELETE FROM tool_calls WHERE record_id IS NULL AND ts < ?").run(cutoff);
2363
- const deletedOrphanToolCalls = orphanResult.changes;
2364
- return {
2365
- deletedCount,
2366
- deletedSyncedCount,
2367
- deletedOrphanToolCalls
2368
- };
2369
- }
2370
-
2371
2597
  // src/commands/recalc.ts
2372
2598
  var BATCH_SIZE = 1e3;
2373
2599
  function recalcPricing(db) {
@@ -2643,7 +2869,7 @@ function createDatabase(path2) {
2643
2869
  import { join as join8 } from "path";
2644
2870
  var DB_PATH = join8(AIUSAGE_DIR, "cache.db");
2645
2871
  var program = new Command();
2646
- program.name("aiusage").version("1.0.2").description("CLI tool for AI usage statistics");
2872
+ program.name("aiusage").version(true ? "1.0.4" : "dev").description("CLI tool for AI usage statistics");
2647
2873
  program.action(() => {
2648
2874
  const db = createDatabase(DB_PATH);
2649
2875
  const state = getState(AIUSAGE_DIR);
@@ -0,0 +1 @@
1
+ *{margin:0;padding:0;box-sizing:border-box}body{font-family:DM Sans,sans-serif;background:var(--bg-base);color:var(--text-primary);min-height:100vh;-webkit-font-smoothing:antialiased;transition:background .3s ease,color .3s ease}:root,:root[data-theme=dark]{--bg-base:#0b0e14;--bg-surface:#111620;--bg-raised:#171c28;--bg-hover:#1c2333;--border-subtle:rgba(255, 255, 255, .06);--border-medium:rgba(255, 255, 255, .1);--text-primary:#e6edf3;--text-secondary:#8b949e;--text-muted:#484f58;--accent:#f0b429;--accent-dim:rgba(240, 180, 41, .15);--accent-glow:rgba(240, 180, 41, .3);--green:#2ea66a;--green-dim:rgba(46, 166, 106, .15);--blue:#3b82f6;--blue-dim:rgba(59, 130, 246, .15);--purple:#a78bfa;--purple-dim:rgba(167, 139, 250, .15);--rose:#f43f5e;--rose-dim:rgba(244, 63, 94, .15);--mono:"JetBrains Mono", monospace;--grain-opacity:.03}:root[data-theme=light]{--bg-base:#f4f5f7;--bg-surface:#ffffff;--bg-raised:#f0f1f3;--bg-hover:#e8eaed;--border-subtle:rgba(0, 0, 0, .07);--border-medium:rgba(0, 0, 0, .12);--text-primary:#1a1d23;--text-secondary:#57606a;--text-muted:#8b949e;--accent:#c78c06;--accent-dim:rgba(199, 140, 6, .1);--accent-glow:rgba(199, 140, 6, .15);--green:#1a7f4b;--green-dim:rgba(26, 127, 75, .1);--blue:#2563eb;--blue-dim:rgba(37, 99, 235, .1);--purple:#7c3aed;--purple-dim:rgba(124, 58, 237, .1);--rose:#dc2626;--rose-dim:rgba(220, 38, 38, .1);--mono:"JetBrains Mono", monospace;--grain-opacity:.015}.grain.svelte-zqr7pm.svelte-zqr7pm{position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:9999;opacity:var(--grain-opacity);background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");background-repeat:repeat;background-size:128px}.app.svelte-zqr7pm.svelte-zqr7pm{display:flex;flex-direction:column;min-height:100vh}header.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:2rem;padding:0 2rem;height:56px;background:var(--bg-surface);border-bottom:1px solid var(--border-subtle);position:sticky;top:0;z-index:100;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);transition:background .3s ease,border-color .3s ease}.brand.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.5rem;flex-shrink:0;text-decoration:none}.logo.svelte-zqr7pm.svelte-zqr7pm{font-size:1.25rem;color:var(--accent);filter:drop-shadow(0 0 6px var(--accent-glow));transition:color .3s ease}.name.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-weight:700;font-size:.95rem;letter-spacing:-.02em;color:var(--text-primary);transition:color .3s ease}nav.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.25rem;flex:1}nav.svelte-zqr7pm a.svelte-zqr7pm{text-decoration:none;color:var(--text-secondary);padding:.4rem .75rem;border-radius:6px;font-size:.8rem;font-weight:500;line-height:1;transition:all .15s ease;white-space:nowrap}nav.svelte-zqr7pm a.svelte-zqr7pm:hover{color:var(--text-primary);background:var(--bg-hover)}nav.svelte-zqr7pm a.active.svelte-zqr7pm{color:var(--accent);background:var(--accent-dim)}.controls.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.35rem;padding:.35rem .6rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s ease}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm:disabled{opacity:.6;cursor:not-allowed}.sync-icon.svelte-zqr7pm.svelte-zqr7pm{font-size:.85rem;line-height:1}.sync-toggle.svelte-zqr7pm:disabled .sync-icon.svelte-zqr7pm{animation:svelte-zqr7pm-spin 1s linear infinite}@keyframes svelte-zqr7pm-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.sync-label.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-size:.7rem;font-weight:600;letter-spacing:.02em}.sync-label.ok.svelte-zqr7pm.svelte-zqr7pm{color:var(--green)}.sync-label.err.svelte-zqr7pm.svelte-zqr7pm{color:var(--rose)}.theme-toggle.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.4rem;padding:.35rem .65rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s ease}.theme-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}.theme-icon.svelte-zqr7pm.svelte-zqr7pm{font-size:.85rem;line-height:1}.theme-label.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-size:.7rem;font-weight:600;letter-spacing:.02em}.lang-toggle.svelte-zqr7pm.svelte-zqr7pm{padding:.35rem .75rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-family:var(--mono);font-size:.75rem;font-weight:600;cursor:pointer;transition:all .15s ease;letter-spacing:.02em}.lang-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}main.svelte-zqr7pm.svelte-zqr7pm{flex:1;padding:2rem;max-width:1200px;width:100%;margin:0 auto;animation:svelte-zqr7pm-fadeUp .4s ease}@keyframes svelte-zqr7pm-fadeUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.card{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:10px;padding:1.25rem;transition:border-color .2s ease,background .3s ease}.card:hover{border-color:var(--border-medium)}.section-title{font-family:var(--mono);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);margin-bottom:.75rem}table{width:100%;border-collapse:collapse}th{font-family:var(--mono);font-size:.65rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);text-align:left;padding:.6rem .75rem;border-bottom:1px solid var(--border-subtle)}td{padding:.6rem .75rem;border-bottom:1px solid var(--border-subtle);font-size:.85rem;color:var(--text-secondary);transition:color .3s ease}tbody tr{transition:background .15s ease}tbody tr:hover{background:var(--bg-hover)}.mono{font-family:var(--mono)}.accent{color:var(--accent)}.green{color:var(--green)}.blue{color:var(--blue)}.purple{color:var(--purple)}.state-msg{text-align:center;padding:4rem 2rem;color:var(--text-muted);font-size:.9rem}.state-msg h2{font-family:var(--mono);font-size:1rem;font-weight:600;color:var(--text-secondary);margin-bottom:.5rem}.state-msg.error{color:var(--rose)}button{font-family:DM Sans,sans-serif}@media (max-width: 768px){header.svelte-zqr7pm.svelte-zqr7pm{flex-wrap:wrap;height:auto;padding:.75rem 1rem;gap:.5rem}nav.svelte-zqr7pm.svelte-zqr7pm{order:3;width:100%;overflow-x:auto;padding-bottom:.25rem}.controls.svelte-zqr7pm.svelte-zqr7pm{margin-left:auto}main.svelte-zqr7pm.svelte-zqr7pm{padding:1rem}}
@@ -0,0 +1 @@
1
+ .top-bar.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;padding:.55rem .9rem;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:8px}.live-wrap.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:center;gap:.45rem;padding:.2rem .55rem;border:1px solid rgba(46,166,106,.3);border-radius:4px;background:#2ea66a12;flex-shrink:0}.live-dot.svelte-1i8v7x1.svelte-1i8v7x1{width:6px;height:6px;border-radius:50%;background:var(--green);animation:svelte-1i8v7x1-livePulse 2.4s ease-in-out infinite}.live-wrap.pulse.svelte-1i8v7x1 .live-dot.svelte-1i8v7x1{animation:svelte-1i8v7x1-liveBurst .5s ease}.live-label.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.58rem;font-weight:700;letter-spacing:.2em;color:var(--green)}@keyframes svelte-1i8v7x1-livePulse{0%,to{box-shadow:0 0 #2ea66a80}50%{box-shadow:0 0 0 4px #2ea66a00}}@keyframes svelte-1i8v7x1-liveBurst{0%{transform:scale(1)}40%{transform:scale(1.7);box-shadow:0 0 8px var(--green)}to{transform:scale(1)}}.range-badge.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.62rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--accent);border:1px solid var(--accent-dim);border-radius:4px;padding:.2rem .55rem;background:var(--accent-dim)}.clock-block.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;flex-direction:column;align-items:flex-start;margin-left:auto;gap:.05rem}.clock-time.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.88rem;font-weight:700;letter-spacing:.05em;color:var(--text-primary);font-variant-numeric:tabular-nums}.clock-date.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.54rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--text-muted)}.cfg-btn.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-muted);cursor:pointer;transition:border-color .15s,color .15s;flex-shrink:0}.cfg-btn.svelte-1i8v7x1.svelte-1i8v7x1:hover{border-color:var(--accent);color:var(--accent)}.cfg-icon.svelte-1i8v7x1.svelte-1i8v7x1{font-size:.85rem;line-height:1}.counter-section.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;display:flex;flex-direction:column;align-items:center;padding:3rem 2rem 1.75rem;margin-bottom:1rem;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:14px;overflow:hidden;transition:border-color .4s}.counter-section.pulsing.svelte-1i8v7x1.svelte-1i8v7x1{border-color:#f0b42938}.counter-section.svelte-1i8v7x1.svelte-1i8v7x1:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--accent) 50%,transparent);opacity:.65}.grid-bg.svelte-1i8v7x1.svelte-1i8v7x1{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;background-image:radial-gradient(circle,rgba(240,180,41,.12) 1px,transparent 1px);background-size:28px 28px;opacity:.45}.counter-glow.svelte-1i8v7x1.svelte-1i8v7x1{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:560px;height:260px;background:radial-gradient(ellipse,rgba(240,180,41,.07) 0%,transparent 65%);pointer-events:none}.scan-line.svelte-1i8v7x1.svelte-1i8v7x1{position:absolute;top:0;bottom:0;left:-50%;width:35%;background:linear-gradient(90deg,transparent,rgba(240,180,41,.04),transparent);animation:svelte-1i8v7x1-scanPass 6s linear infinite;pointer-events:none}@keyframes svelte-1i8v7x1-scanPass{0%{left:-40%}to{left:140%}}.counter-eyebrow.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;z-index:1;display:flex;align-items:center;gap:1rem;margin-bottom:1.1rem}.eyebrow-line.svelte-1i8v7x1.svelte-1i8v7x1{display:block;height:1px;width:60px;background:linear-gradient(90deg,transparent,var(--border-medium))}.eyebrow-line.svelte-1i8v7x1.svelte-1i8v7x1:last-child{transform:scaleX(-1)}.eyebrow-text.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.6rem;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:var(--text-muted)}.counter-number.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;z-index:1;font-family:var(--mono);font-size:clamp(2.8rem,6.5vw,5.5rem);font-weight:800;letter-spacing:-.035em;line-height:1;color:var(--accent);text-shadow:0 0 30px rgba(240,180,41,.38),0 0 90px rgba(240,180,41,.13);font-variant-numeric:tabular-nums;transition:opacity .3s,text-shadow .5s}.counter-number.refreshing.svelte-1i8v7x1.svelte-1i8v7x1{opacity:.5}.counter-section.pulsing.svelte-1i8v7x1 .counter-number.svelte-1i8v7x1{text-shadow:0 0 50px rgba(240,180,41,.55),0 0 130px rgba(240,180,41,.22)}.counter-sub.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;z-index:1;display:flex;align-items:center;gap:1.75rem;margin-top:1.4rem}.sub-chip.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;flex-direction:column;align-items:center;gap:.15rem}.sub-arrow.svelte-1i8v7x1.svelte-1i8v7x1{font-size:.65rem;color:var(--text-muted);line-height:1}.sub-lbl.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.5rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;color:var(--text-muted)}.sub-val.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.82rem;font-weight:600;color:var(--text-secondary);font-variant-numeric:tabular-nums}.sub-sep.svelte-1i8v7x1.svelte-1i8v7x1{width:1px;height:28px;background:var(--border-subtle);margin-top:.3rem}.refresh-bar-track.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;z-index:1;width:100%;height:2px;background:var(--border-subtle);border-radius:1px;margin-top:2rem;overflow:hidden}.refresh-bar-fill.svelte-1i8v7x1.svelte-1i8v7x1{height:100%;background:linear-gradient(90deg,var(--accent-dim),var(--accent));border-radius:1px;transition:width 1s linear;box-shadow:0 0 6px var(--accent-glow)}.refresh-meta.svelte-1i8v7x1.svelte-1i8v7x1{position:relative;z-index:1;display:flex;align-items:center;gap:.75rem;margin-top:.5rem;font-family:var(--mono);font-size:.56rem;font-weight:600;letter-spacing:.08em;color:var(--text-muted)}.refreshing-label.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--accent)}.now-btn.svelte-1i8v7x1.svelte-1i8v7x1{margin-left:auto;padding:.15rem .5rem;border:1px solid var(--border-medium);border-radius:4px;background:transparent;font-family:var(--mono);font-size:.55rem;font-weight:600;letter-spacing:.06em;color:var(--text-muted);cursor:pointer;transition:border-color .15s,color .15s}.now-btn.svelte-1i8v7x1.svelte-1i8v7x1:hover{border-color:var(--accent);color:var(--accent)}.stats-strip.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:stretch;background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:10px;margin-bottom:1rem;overflow:hidden}.stat-block.svelte-1i8v7x1.svelte-1i8v7x1{flex:1;display:flex;align-items:center;gap:.9rem;padding:1.1rem 1.4rem;position:relative;transition:background .15s}.stat-block.svelte-1i8v7x1.svelte-1i8v7x1:before{content:"";position:absolute;top:0;left:0;right:0;height:1px}.stat-block.svelte-1i8v7x1.svelte-1i8v7x1:hover{background:var(--bg-raised)}.stat-cost.svelte-1i8v7x1.svelte-1i8v7x1:before{background:var(--green)}.stat-sess.svelte-1i8v7x1.svelte-1i8v7x1:before{background:var(--blue)}.stat-days.svelte-1i8v7x1.svelte-1i8v7x1:before{background:var(--purple)}.strip-divider.svelte-1i8v7x1.svelte-1i8v7x1{width:1px;background:var(--border-subtle);margin:.75rem 0}.stat-icon-wrap.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:1.4rem;line-height:1;opacity:.22;flex-shrink:0}.stat-cost.svelte-1i8v7x1 .stat-icon-wrap.svelte-1i8v7x1{color:var(--green)}.stat-sess.svelte-1i8v7x1 .stat-icon-wrap.svelte-1i8v7x1{color:var(--blue)}.stat-days.svelte-1i8v7x1 .stat-icon-wrap.svelte-1i8v7x1{color:var(--purple)}.stat-info.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;flex-direction:column;gap:.22rem}.stat-lbl.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.56rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--text-muted)}.stat-val.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:1.75rem;font-weight:700;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums}.cost-val.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--green);text-shadow:0 0 18px rgba(46,166,106,.25)}.sess-val.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--blue);text-shadow:0 0 18px rgba(59,130,246,.25)}.days-val.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--purple);text-shadow:0 0 18px rgba(167,139,250,.25)}.comp-wrap.svelte-1i8v7x1.svelte-1i8v7x1{margin-bottom:.5rem}.comp-bar.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;height:26px;border-radius:6px;overflow:hidden;border:1px solid var(--border-subtle);background:var(--bg-raised)}.seg.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:center;justify-content:center;overflow:hidden;min-width:0;transition:width 1.5s cubic-bezier(.25,.46,.45,.94)}.seg-lbl.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.5rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;padding:0 4px}.seg-input.svelte-1i8v7x1.svelte-1i8v7x1{background:#f0b42933;border-right:1px solid var(--bg-base)}.seg-output.svelte-1i8v7x1.svelte-1i8v7x1{background:#3b82f62e;border-right:1px solid var(--bg-base)}.seg-cr.svelte-1i8v7x1.svelte-1i8v7x1{background:#a78bfa2b;border-right:1px solid var(--bg-base)}.seg-cw.svelte-1i8v7x1.svelte-1i8v7x1{background:#2ea66a2b}.seg-input.svelte-1i8v7x1 .seg-lbl.svelte-1i8v7x1{color:var(--accent)}.seg-output.svelte-1i8v7x1 .seg-lbl.svelte-1i8v7x1{color:var(--blue)}.seg-cr.svelte-1i8v7x1 .seg-lbl.svelte-1i8v7x1{color:var(--purple)}.seg-cw.svelte-1i8v7x1 .seg-lbl.svelte-1i8v7x1{color:var(--green)}.comp-legend.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;gap:1.25rem;margin-top:.45rem;padding:0 .2rem;flex-wrap:wrap}.leg.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.54rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.leg-i.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--accent)}.leg-o.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--blue)}.leg-cr.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--purple)}.leg-cw.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--green)}.overlay-backdrop.svelte-1i8v7x1.svelte-1i8v7x1{position:fixed;top:0;right:0;bottom:0;left:0;z-index:200;background:#070a1099;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);animation:svelte-1i8v7x1-fadeIn .15s ease}@keyframes svelte-1i8v7x1-fadeIn{0%{opacity:0}to{opacity:1}}.config-panel.svelte-1i8v7x1.svelte-1i8v7x1{position:fixed;z-index:201;top:50%;left:50%;transform:translate(-50%,-50%);width:min(400px,90vw);background:var(--bg-surface);border:1px solid var(--border-medium);border-radius:12px;padding:1.5rem;animation:svelte-1i8v7x1-panelIn .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 24px 80px #00000080}@keyframes svelte-1i8v7x1-panelIn{0%{opacity:0;transform:translate(-50%,-46%)}to{opacity:1;transform:translate(-50%,-50%)}}.cfg-header.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.25rem}.cfg-title.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.65rem;font-weight:700;letter-spacing:.2em;color:var(--text-muted)}.cfg-close.svelte-1i8v7x1.svelte-1i8v7x1{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--border-medium);border-radius:4px;background:transparent;color:var(--text-muted);font-size:.7rem;cursor:pointer;transition:border-color .15s,color .15s}.cfg-close.svelte-1i8v7x1.svelte-1i8v7x1:hover{border-color:var(--rose);color:var(--rose)}.cfg-section.svelte-1i8v7x1.svelte-1i8v7x1{margin-bottom:1.1rem}.cfg-label.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.58rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;color:var(--text-muted);margin-bottom:.5rem}.cfg-pills.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;gap:.4rem;flex-wrap:wrap}.pill.svelte-1i8v7x1.svelte-1i8v7x1{padding:.3rem .75rem;border:1px solid var(--border-medium);border-radius:5px;background:var(--bg-raised);font-family:var(--mono);font-size:.7rem;font-weight:600;color:var(--text-secondary);cursor:pointer;transition:border-color .15s,color .15s,background .15s;white-space:nowrap}.pill.svelte-1i8v7x1.svelte-1i8v7x1:hover{border-color:var(--accent);color:var(--accent)}.pill.active.svelte-1i8v7x1.svelte-1i8v7x1{border-color:var(--accent);color:var(--accent);background:var(--accent-dim)}.pill-eg.svelte-1i8v7x1.svelte-1i8v7x1{opacity:.5;font-size:.6rem;margin-left:.3rem}.cfg-refresh-info.svelte-1i8v7x1.svelte-1i8v7x1{margin-top:.75rem;padding:.6rem .75rem;border:1px solid var(--border-subtle);border-radius:6px;background:var(--bg-raised)}.refresh-info-text.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.6rem;font-weight:600;letter-spacing:.04em;color:var(--text-muted)}.settings-link.svelte-1i8v7x1.svelte-1i8v7x1{color:var(--accent);text-decoration:none}.settings-link.svelte-1i8v7x1.svelte-1i8v7x1:hover{text-decoration:underline}.splash-loading.svelte-1i8v7x1.svelte-1i8v7x1,.splash-error.svelte-1i8v7x1.svelte-1i8v7x1,.splash-empty.svelte-1i8v7x1.svelte-1i8v7x1{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;min-height:320px;text-align:center}.splash-spinner.svelte-1i8v7x1.svelte-1i8v7x1{width:32px;height:32px;border:2px solid var(--border-subtle);border-top-color:var(--accent);border-radius:50%;animation:svelte-1i8v7x1-spin .8s linear infinite}@keyframes svelte-1i8v7x1-spin{to{transform:rotate(360deg)}}.splash-text.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.65rem;font-weight:700;letter-spacing:.25em;color:var(--text-muted);animation:svelte-1i8v7x1-blink 1.2s ease infinite}@keyframes svelte-1i8v7x1-blink{0%,to{opacity:1}50%{opacity:.35}}.err-code.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:2rem;font-weight:800;color:var(--rose)}.err-msg.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.8rem;color:var(--text-muted)}.retry-btn.svelte-1i8v7x1.svelte-1i8v7x1{margin-top:.5rem;padding:.4rem 1.2rem;border:1px solid var(--rose);border-radius:6px;background:transparent;font-family:var(--mono);font-size:.7rem;font-weight:700;color:var(--rose);cursor:pointer;transition:background .15s}.retry-btn.svelte-1i8v7x1.svelte-1i8v7x1:hover{background:#f43f5e1a}.empty-icon.svelte-1i8v7x1.svelte-1i8v7x1{font-size:2.5rem;color:var(--text-muted)}.empty-title.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.9rem;font-weight:700;letter-spacing:.2em;color:var(--text-secondary)}.empty-hint.svelte-1i8v7x1.svelte-1i8v7x1{font-family:var(--mono);font-size:.72rem;color:var(--text-muted)}@media (max-width: 680px){.counter-section.svelte-1i8v7x1.svelte-1i8v7x1{padding:2.5rem 1rem 1.5rem}.counter-sub.svelte-1i8v7x1.svelte-1i8v7x1{gap:1rem}.stats-strip.svelte-1i8v7x1.svelte-1i8v7x1{flex-direction:column}.strip-divider.svelte-1i8v7x1.svelte-1i8v7x1{width:auto;height:1px;margin:0 1rem}.top-bar.svelte-1i8v7x1.svelte-1i8v7x1{flex-wrap:wrap;gap:.6rem}.clock-block.svelte-1i8v7x1.svelte-1i8v7x1{margin-left:0}}
@@ -0,0 +1 @@
1
+ .filter-bar.svelte-aa20kz.svelte-aa20kz{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;flex-wrap:wrap}.model.svelte-aa20kz.svelte-aa20kz{font-size:.8rem;color:var(--text-primary)}.pagination.svelte-aa20kz.svelte-aa20kz{display:flex;align-items:center;justify-content:center;gap:1rem;margin-top:1.5rem}.pagination.svelte-aa20kz button.svelte-aa20kz{padding:.4rem .85rem;border:1px solid var(--border-subtle);background:var(--bg-raised);color:var(--text-secondary);border-radius:6px;cursor:pointer;font-size:.8rem;font-weight:500;transition:all .15s ease}.pagination.svelte-aa20kz button.svelte-aa20kz:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}.pagination.svelte-aa20kz button.svelte-aa20kz:disabled{opacity:.35;cursor:not-allowed}.page-info.svelte-aa20kz.svelte-aa20kz{font-size:.75rem;color:var(--text-muted)}
@@ -0,0 +1 @@
1
+ .header-row.svelte-1iyktc5.svelte-1iyktc5{display:flex;align-items:center;margin-bottom:1.5rem}.page-title.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:1.1rem;font-weight:700;color:var(--text-primary)}.sections.svelte-1iyktc5.svelte-1iyktc5{display:flex;flex-direction:column;gap:1rem}.card.svelte-1iyktc5.svelte-1iyktc5{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:10px;padding:1.25rem;transition:border-color .2s}.card.svelte-1iyktc5.svelte-1iyktc5:hover{border-color:var(--border-medium)}.group-title.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);margin-bottom:1rem}.fields.svelte-1iyktc5.svelte-1iyktc5{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:1rem}.field.svelte-1iyktc5.svelte-1iyktc5{display:flex;flex-direction:column;gap:.25rem}.field.full.svelte-1iyktc5.svelte-1iyktc5{grid-column:1 / -1}.field-label.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:.65rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-secondary)}.field-hint.svelte-1iyktc5.svelte-1iyktc5{font-size:.72rem;color:var(--text-muted);margin-top:-.1rem}.field-input.svelte-1iyktc5.svelte-1iyktc5{font-family:DM Sans,sans-serif;font-size:.85rem;padding:.45rem .65rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-primary);transition:border-color .15s,box-shadow .15s;width:100%}.field-input.svelte-1iyktc5.svelte-1iyktc5:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-dim)}.field-input.mono.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:.8rem}select.field-input.svelte-1iyktc5.svelte-1iyktc5{cursor:pointer}.section-error.svelte-1iyktc5.svelte-1iyktc5{margin-top:.75rem;font-size:.8rem;color:var(--rose)}.section-footer.svelte-1iyktc5.svelte-1iyktc5{display:flex;justify-content:flex-end;margin-top:1rem;padding-top:.75rem;border-top:1px solid var(--border-subtle)}.btn-save.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:.75rem;font-weight:600;padding:.45rem 1.1rem;border:1px solid var(--accent);border-radius:6px;background:var(--accent-dim);color:var(--accent);cursor:pointer;transition:all .15s;min-width:72px}.btn-save.svelte-1iyktc5.svelte-1iyktc5:hover:not(:disabled){background:var(--accent);color:#000}.btn-save.svelte-1iyktc5.svelte-1iyktc5:disabled{opacity:.55;cursor:not-allowed}.btn-save.saved.svelte-1iyktc5.svelte-1iyktc5{border-color:var(--green);background:var(--green-dim);color:var(--green)}.state-msg.svelte-1iyktc5.svelte-1iyktc5{color:var(--text-muted);padding:2rem;text-align:center}.state-msg.error.svelte-1iyktc5.svelte-1iyktc5{color:var(--rose)}.credential-row.svelte-1iyktc5.svelte-1iyktc5{display:flex;gap:.5rem;align-items:center}.credential-row.svelte-1iyktc5 .field-input.svelte-1iyktc5{flex:1}.btn-secondary.svelte-1iyktc5.svelte-1iyktc5{font-family:var(--mono);font-size:.75rem;font-weight:600;padding:.45rem .9rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);cursor:pointer;white-space:nowrap}.btn-secondary.svelte-1iyktc5.svelte-1iyktc5:disabled{opacity:.55;cursor:not-allowed}
@@ -0,0 +1 @@
1
+ *{margin:0;padding:0;box-sizing:border-box}body{font-family:DM Sans,sans-serif;background:var(--bg-base);color:var(--text-primary);min-height:100vh;-webkit-font-smoothing:antialiased;transition:background .3s ease,color .3s ease}:root,:root[data-theme=dark]{--bg-base:#0b0e14;--bg-surface:#111620;--bg-raised:#171c28;--bg-hover:#1c2333;--border-subtle:rgba(255, 255, 255, .06);--border-medium:rgba(255, 255, 255, .1);--text-primary:#e6edf3;--text-secondary:#8b949e;--text-muted:#484f58;--accent:#f0b429;--accent-dim:rgba(240, 180, 41, .15);--accent-glow:rgba(240, 180, 41, .3);--green:#2ea66a;--green-dim:rgba(46, 166, 106, .15);--blue:#3b82f6;--blue-dim:rgba(59, 130, 246, .15);--purple:#a78bfa;--purple-dim:rgba(167, 139, 250, .15);--rose:#f43f5e;--rose-dim:rgba(244, 63, 94, .15);--mono:"JetBrains Mono", monospace;--grain-opacity:.03}:root[data-theme=light]{--bg-base:#f4f5f7;--bg-surface:#ffffff;--bg-raised:#f0f1f3;--bg-hover:#e8eaed;--border-subtle:rgba(0, 0, 0, .07);--border-medium:rgba(0, 0, 0, .12);--text-primary:#1a1d23;--text-secondary:#57606a;--text-muted:#8b949e;--accent:#c78c06;--accent-dim:rgba(199, 140, 6, .1);--accent-glow:rgba(199, 140, 6, .15);--green:#1a7f4b;--green-dim:rgba(26, 127, 75, .1);--blue:#2563eb;--blue-dim:rgba(37, 99, 235, .1);--purple:#7c3aed;--purple-dim:rgba(124, 58, 237, .1);--rose:#dc2626;--rose-dim:rgba(220, 38, 38, .1);--mono:"JetBrains Mono", monospace;--grain-opacity:.015}.grain.svelte-zqr7pm.svelte-zqr7pm{position:fixed;inset:0;pointer-events:none;z-index:9999;opacity:var(--grain-opacity);background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");background-repeat:repeat;background-size:128px}.app.svelte-zqr7pm.svelte-zqr7pm{display:flex;flex-direction:column;min-height:100vh}header.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:2rem;padding:0 2rem;height:56px;background:var(--bg-surface);border-bottom:1px solid var(--border-subtle);position:sticky;top:0;z-index:100;backdrop-filter:blur(12px);transition:background .3s ease,border-color .3s ease}.brand.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.5rem;flex-shrink:0;text-decoration:none}.logo.svelte-zqr7pm.svelte-zqr7pm{font-size:1.25rem;color:var(--accent);filter:drop-shadow(0 0 6px var(--accent-glow));transition:color .3s ease}.name.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-weight:700;font-size:.95rem;letter-spacing:-.02em;color:var(--text-primary);transition:color .3s ease}nav.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.25rem;flex:1}nav.svelte-zqr7pm a.svelte-zqr7pm{text-decoration:none;color:var(--text-secondary);padding:.4rem .75rem;border-radius:6px;font-size:.8rem;font-weight:500;line-height:1;transition:all .15s ease;white-space:nowrap}nav.svelte-zqr7pm a.svelte-zqr7pm:hover{color:var(--text-primary);background:var(--bg-hover)}nav.svelte-zqr7pm a.active.svelte-zqr7pm{color:var(--accent);background:var(--accent-dim)}.controls.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.35rem;padding:.35rem .6rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s ease}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}.sync-toggle.svelte-zqr7pm.svelte-zqr7pm:disabled{opacity:.6;cursor:not-allowed}.sync-icon.svelte-zqr7pm.svelte-zqr7pm{font-size:.85rem;line-height:1}.sync-toggle.svelte-zqr7pm:disabled .sync-icon.svelte-zqr7pm{animation:svelte-zqr7pm-spin 1s linear infinite}@keyframes svelte-zqr7pm-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.sync-label.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-size:.7rem;font-weight:600;letter-spacing:.02em}.sync-label.ok.svelte-zqr7pm.svelte-zqr7pm{color:var(--green)}.sync-label.err.svelte-zqr7pm.svelte-zqr7pm{color:var(--rose)}.theme-toggle.svelte-zqr7pm.svelte-zqr7pm{display:flex;align-items:center;gap:.4rem;padding:.35rem .65rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s ease}.theme-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}.theme-icon.svelte-zqr7pm.svelte-zqr7pm{font-size:.85rem;line-height:1}.theme-label.svelte-zqr7pm.svelte-zqr7pm{font-family:var(--mono);font-size:.7rem;font-weight:600;letter-spacing:.02em}.lang-toggle.svelte-zqr7pm.svelte-zqr7pm{padding:.35rem .75rem;border:1px solid var(--border-medium);border-radius:6px;background:var(--bg-raised);color:var(--text-secondary);font-family:var(--mono);font-size:.75rem;font-weight:600;cursor:pointer;transition:all .15s ease;letter-spacing:.02em}.lang-toggle.svelte-zqr7pm.svelte-zqr7pm:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px var(--accent-dim)}main.svelte-zqr7pm.svelte-zqr7pm{flex:1;padding:2rem;max-width:1200px;width:100%;margin:0 auto;animation:svelte-zqr7pm-fadeUp .4s ease}@keyframes svelte-zqr7pm-fadeUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.card{background:var(--bg-surface);border:1px solid var(--border-subtle);border-radius:10px;padding:1.25rem;transition:border-color .2s ease,background .3s ease}.card:hover{border-color:var(--border-medium)}.section-title{font-family:var(--mono);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);margin-bottom:.75rem}table{width:100%;border-collapse:collapse}th{font-family:var(--mono);font-size:.65rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);text-align:left;padding:.6rem .75rem;border-bottom:1px solid var(--border-subtle)}td{padding:.6rem .75rem;border-bottom:1px solid var(--border-subtle);font-size:.85rem;color:var(--text-secondary);transition:color .3s ease}tbody tr{transition:background .15s ease}tbody tr:hover{background:var(--bg-hover)}.mono{font-family:var(--mono)}.accent{color:var(--accent)}.green{color:var(--green)}.blue{color:var(--blue)}.purple{color:var(--purple)}.state-msg{text-align:center;padding:4rem 2rem;color:var(--text-muted);font-size:.9rem}.state-msg h2{font-family:var(--mono);font-size:1rem;font-weight:600;color:var(--text-secondary);margin-bottom:.5rem}.state-msg.error{color:var(--rose)}button{font-family:DM Sans,sans-serif}@media (max-width: 768px){header.svelte-zqr7pm.svelte-zqr7pm{flex-wrap:wrap;height:auto;padding:.75rem 1rem;gap:.5rem}nav.svelte-zqr7pm.svelte-zqr7pm{order:3;width:100%;overflow-x:auto;padding-bottom:.25rem}.controls.svelte-zqr7pm.svelte-zqr7pm{margin-left:auto}main.svelte-zqr7pm.svelte-zqr7pm{padding:1rem}}
@@ -0,0 +1 @@
1
+ .filter-bar.svelte-aa20kz.svelte-aa20kz{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;flex-wrap:wrap}.model.svelte-aa20kz.svelte-aa20kz{font-size:.8rem;color:var(--text-primary)}.pagination.svelte-aa20kz.svelte-aa20kz{display:flex;align-items:center;justify-content:center;gap:1rem;margin-top:1.5rem}.pagination.svelte-aa20kz button.svelte-aa20kz{padding:.4rem .85rem;border:1px solid var(--border-subtle);background:var(--bg-raised);color:var(--text-secondary);border-radius:6px;cursor:pointer;font-size:.8rem;font-weight:500;transition:all .15s ease}.pagination.svelte-aa20kz button.svelte-aa20kz:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}.pagination.svelte-aa20kz button.svelte-aa20kz:disabled{opacity:.35;cursor:not-allowed}.page-info.svelte-aa20kz.svelte-aa20kz{font-size:.75rem;color:var(--text-muted)}