@zhangferry-dev/tokendash 1.4.0 → 1.5.0

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TokenDash</title>
7
7
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text y='28' font-size='28'>⚡</text></svg>" />
8
- <script type="module" crossorigin src="/assets/index-B4YgU_cb.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-BPWY9q0y.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-iYDpTV63.css">
10
10
  </head>
11
11
  <body class="antialiased" style="background:#faf9f7">
@@ -177,9 +177,10 @@
177
177
  }
178
178
 
179
179
  .y-axis {
180
- display: grid;
181
- grid-template-rows: repeat(4, 1fr);
182
- justify-items: end;
180
+ display: flex;
181
+ flex-direction: column;
182
+ justify-content: space-between;
183
+ align-items: flex-end;
183
184
  width: max-content;
184
185
  padding-block: 6px 24px;
185
186
  color: var(--muted);
@@ -257,14 +258,14 @@
257
258
 
258
259
  .bar-tip {
259
260
  position: absolute;
260
- inset-block-end: calc(var(--height) + 10px);
261
+ inset-block-end: calc(var(--height) + 4px);
261
262
  font-size: 10px;
262
263
  color: var(--fg);
263
264
  font-family: var(--font-mono);
264
- background: rgba(255, 255, 255, 0.9);
265
- border: 1px solid var(--border);
266
- border-radius: 999px;
267
- padding: 4px 7px;
265
+ background: transparent;
266
+ border: none;
267
+ border-radius: 0;
268
+ padding: 0;
268
269
  white-space: nowrap;
269
270
  }
270
271
 
@@ -742,6 +743,7 @@
742
743
  var totalOutput = 0;
743
744
  var totalCacheRead = 0;
744
745
  var totalTokens = 0;
746
+ var totalCost = 0;
745
747
 
746
748
  (allDaily || []).forEach(function(daily) {
747
749
  if (!daily || !daily.daily) return;
@@ -751,6 +753,7 @@
751
753
  totalOutput += entry.outputTokens || 0;
752
754
  totalCacheRead += entry.cacheReadTokens || 0;
753
755
  totalTokens += entry.totalTokens || 0;
756
+ totalCost += entry.totalCost || 0;
754
757
  });
755
758
 
756
759
  var denominator = totalInput + totalCacheRead;
@@ -760,6 +763,14 @@
760
763
  setCardValue('input', formatNumber(totalInput), totalInput === 0);
761
764
  setCardValue('output', formatNumber(totalOutput), totalOutput === 0);
762
765
  setCardValue('cache-rate', formatPercent(cacheRate), cacheRate === 0);
766
+
767
+ return {
768
+ today: todayStr,
769
+ agentKey: selectedAgents.slice().sort().join(','),
770
+ totalTokens: totalTokens,
771
+ totalCost: totalCost,
772
+ totalCacheRead: totalCacheRead
773
+ };
763
774
  }
764
775
 
765
776
  function setCardValue(id, text, isEmpty) {
@@ -809,22 +820,34 @@
809
820
  return;
810
821
  }
811
822
 
823
+ var axisInfo = niceStep(maxValue);
812
824
  renderAxis(maxValue);
813
825
  var maxEntry = selected.reduce(function(max, entry) {
814
826
  return !max || entry.value > max.value ? entry : max;
815
827
  }, null);
816
- renderBars(selected, maxValue, maxEntry);
828
+ renderBars(selected, axisInfo.top, maxEntry);
817
829
  }
818
830
 
819
- function renderAxis(maxValue) {
831
+ function niceStep(value) {
832
+ // Always divide into 3 intervals, pick a step that gives round numbers
833
+ if (value <= 0) return { top: 3, step: 1 };
834
+ var rawStep = value / 3;
835
+ var exp = Math.floor(Math.log10(rawStep));
836
+ var base = Math.pow(10, exp);
837
+ var frac = rawStep / base;
838
+ // Round step up to nearest 1, 2, or 5 × 10^n
839
+ var nice = frac <= 1 ? base : frac <= 2 ? 2 * base : frac <= 5 ? 5 * base : 10 * base;
840
+ var top = nice * 3;
841
+ return { top: top, step: nice };
842
+ }
843
+
844
+ function renderAxis(axisMax) {
820
845
  var axis = document.getElementById('y-axis');
821
- var top = maxValue;
822
- var midHigh = top * (2 / 3);
823
- var midLow = top * (1 / 3);
846
+ var info = niceStep(axisMax);
824
847
  axis.innerHTML = [
825
- '<span>' + formatNumber(top) + '</span>',
826
- '<span>' + formatNumber(midHigh) + '</span>',
827
- '<span>' + formatNumber(midLow) + '</span>',
848
+ '<span>' + formatNumber(info.top) + '</span>',
849
+ '<span>' + formatNumber(info.top - info.step) + '</span>',
850
+ '<span>' + formatNumber(info.top - 2 * info.step) + '</span>',
828
851
  '<span>0</span>'
829
852
  ].join('');
830
853
  }
@@ -969,6 +992,7 @@
969
992
  selectedAgents = agents.slice(); // default: all selected
970
993
  }
971
994
  renderAgentDropdown();
995
+ saveSelectedAgents();
972
996
  }
973
997
 
974
998
  // --- Data fetching ---
@@ -1003,8 +1027,11 @@
1003
1027
  });
1004
1028
  })
1005
1029
  .then(function(data) {
1006
- renderMetrics(data.dailyResults);
1030
+ var traySnapshot = renderMetrics(data.dailyResults);
1007
1031
  renderChart(data.blockResults);
1032
+ if (window.electronAPI && window.electronAPI.updateTraySnapshot && traySnapshot.totalTokens > 0) {
1033
+ window.electronAPI.updateTraySnapshot(traySnapshot).catch(function() {});
1034
+ }
1008
1035
  hideError();
1009
1036
  })
1010
1037
  .catch(function(error) {
@@ -32,7 +32,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  createApp: () => createApp,
35
- main: () => main
35
+ main: () => main,
36
+ resolveStaticAssetBaseDir: () => resolveStaticAssetBaseDir
36
37
  });
37
38
  module.exports = __toCommonJS(index_exports);
38
39
  var import_express = __toESM(require("express"), 1);
@@ -260,6 +261,15 @@ var TokenCountPayloadSchema = import_zod2.z.object({
260
261
  type: import_zod2.z.literal("token_count"),
261
262
  info: TokenCountInfoSchema
262
263
  });
264
+ function tokenUsageKey(usage) {
265
+ return [
266
+ usage.input_tokens,
267
+ usage.cached_input_tokens,
268
+ usage.output_tokens,
269
+ usage.reasoning_output_tokens,
270
+ usage.total_tokens
271
+ ].join(":");
272
+ }
263
273
  function getSessionsDir() {
264
274
  return (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".codex", "sessions");
265
275
  }
@@ -304,6 +314,7 @@ function parseCodexSession(filepath) {
304
314
  let model = "";
305
315
  let createdAt = "";
306
316
  const tokenEvents = [];
317
+ const seenTotalUsageSnapshots = /* @__PURE__ */ new Set();
307
318
  for (const line of lines) {
308
319
  const trimmed = line.trim();
309
320
  if (!trimmed) continue;
@@ -337,6 +348,9 @@ function parseCodexSession(filepath) {
337
348
  }
338
349
  const info = parseResult.data.info;
339
350
  if (!info) continue;
351
+ const totalUsageKey = tokenUsageKey(info.total_token_usage);
352
+ if (seenTotalUsageSnapshots.has(totalUsageKey)) continue;
353
+ seenTotalUsageSnapshots.add(totalUsageKey);
340
354
  const last = info.last_token_usage;
341
355
  tokenEvents.push({
342
356
  timestamp,
@@ -402,8 +416,16 @@ function mergeAcc(a, b) {
402
416
  a.reasoningOutputTokens += b.reasoningOutputTokens;
403
417
  a.totalTokens += b.totalTokens;
404
418
  }
405
- function accToEntry(date, acc, models) {
406
- const cost = calculateCost(acc, models);
419
+ function addAccToBucket(bucket, ev, model) {
420
+ addAcc(bucket.acc, ev);
421
+ if (!model) return;
422
+ if (!bucket.models.has(model)) bucket.models.set(model, emptyAcc());
423
+ addAcc(bucket.models.get(model), ev);
424
+ }
425
+ function accToEntry(date, acc, modelAccs) {
426
+ const modelNames = [...modelAccs.keys()];
427
+ const modelBreakdowns = buildModelBreakdowns(modelAccs);
428
+ const totalCost = modelBreakdowns.reduce((sum, model) => sum + model.cost, 0);
407
429
  return {
408
430
  date,
409
431
  inputTokens: acc.inputTokens,
@@ -411,22 +433,19 @@ function accToEntry(date, acc, models) {
411
433
  cacheCreationTokens: 0,
412
434
  cacheReadTokens: acc.cachedInputTokens,
413
435
  totalTokens: acc.totalTokens,
414
- totalCost: cost,
415
- modelsUsed: [...models],
416
- modelBreakdowns: buildModelBreakdowns(acc, models, cost)
436
+ totalCost,
437
+ modelsUsed: modelNames,
438
+ modelBreakdowns
417
439
  };
418
440
  }
419
- function buildModelBreakdowns(acc, models, totalCost) {
420
- const modelList = [...models];
421
- if (modelList.length === 0) return [];
422
- const costPerModel = totalCost / modelList.length;
423
- return modelList.map((name) => ({
424
- modelName: name,
441
+ function buildModelBreakdowns(modelAccs) {
442
+ return [...modelAccs.entries()].map(([modelName, acc]) => ({
443
+ modelName,
425
444
  inputTokens: acc.inputTokens,
426
445
  outputTokens: acc.outputTokens,
427
446
  cacheCreationTokens: 0,
428
447
  cacheReadTokens: acc.cachedInputTokens,
429
- cost: costPerModel
448
+ cost: calculateCost(acc, /* @__PURE__ */ new Set([modelName]))
430
449
  }));
431
450
  }
432
451
  function groupSessions(sessions, options) {
@@ -457,28 +476,28 @@ function groupSessions(sessions, options) {
457
476
  break;
458
477
  }
459
478
  if (!grouped.has(key)) {
460
- grouped.set(key, { acc: emptyAcc(), models: /* @__PURE__ */ new Set() });
479
+ grouped.set(key, { acc: emptyAcc(), models: /* @__PURE__ */ new Map() });
461
480
  }
462
- const entry = grouped.get(key);
463
- addAcc(entry.acc, ev);
464
- if (session.model) entry.models.add(session.model);
481
+ addAccToBucket(grouped.get(key), ev, session.model);
465
482
  }
466
483
  }
467
484
  return grouped;
468
485
  }
469
- function getDailyResponse(options) {
470
- const sessions = parseAllSessions();
486
+ function buildDailyResponse(sessions, options) {
471
487
  const grouped = groupSessions(sessions, { groupBy: "day", ...options });
472
488
  const daily = [];
473
489
  const totalsAcc = emptyAcc();
474
- for (const [date, { acc, models: models2 }] of grouped) {
475
- daily.push(accToEntry(date, acc, models2));
490
+ const totalModels = /* @__PURE__ */ new Map();
491
+ for (const [date, { acc, models }] of grouped) {
492
+ daily.push(accToEntry(date, acc, models));
476
493
  mergeAcc(totalsAcc, acc);
494
+ for (const [model, modelAcc] of models) {
495
+ if (!totalModels.has(model)) totalModels.set(model, emptyAcc());
496
+ mergeAcc(totalModels.get(model), modelAcc);
497
+ }
477
498
  }
478
499
  daily.sort((a, b) => a.date.localeCompare(b.date));
479
- const models = /* @__PURE__ */ new Set();
480
- for (const s of sessions) if (s.model) models.add(s.model);
481
- const totalCost = calculateCost(totalsAcc, models);
500
+ const totalCost = buildModelBreakdowns(totalModels).reduce((sum, model) => sum + model.cost, 0);
482
501
  return {
483
502
  daily,
484
503
  totals: {
@@ -491,41 +510,37 @@ function getDailyResponse(options) {
491
510
  }
492
511
  };
493
512
  }
494
- function getProjectsResponse(options) {
495
- const sessions = parseAllSessions();
513
+ function buildProjectsResponse(sessions, options) {
496
514
  const tz = options?.timezone || "Asia/Shanghai";
497
- const projects = {};
515
+ const projectGroups = /* @__PURE__ */ new Map();
498
516
  for (const session of sessions) {
499
517
  const projectName = extractProjectName(session.cwd);
500
- const dailyMap = /* @__PURE__ */ new Map();
518
+ if (options?.project && projectName !== options.project) continue;
519
+ if (!projectGroups.has(projectName)) projectGroups.set(projectName, /* @__PURE__ */ new Map());
520
+ const dailyMap = projectGroups.get(projectName);
501
521
  for (const ev of session.tokenEvents) {
502
522
  const evDate = new Date(ev.timestamp);
503
523
  if (options?.since && evDate < options.since) continue;
504
524
  if (options?.until && evDate > options.until) continue;
505
525
  const dayKey = getDateKey(ev.timestamp, tz);
506
526
  if (!dailyMap.has(dayKey)) {
507
- dailyMap.set(dayKey, { acc: emptyAcc(), models: /* @__PURE__ */ new Set() });
527
+ dailyMap.set(dayKey, { acc: emptyAcc(), models: /* @__PURE__ */ new Map() });
508
528
  }
509
- addAcc(dailyMap.get(dayKey).acc, ev);
510
- if (session.model) dailyMap.get(dayKey).models.add(session.model);
511
- }
512
- if (!projects[projectName]) projects[projectName] = [];
513
- for (const [date, { acc, models }] of dailyMap) {
514
- projects[projectName].push(accToEntry(date, acc, models));
529
+ addAccToBucket(dailyMap.get(dayKey), ev, session.model);
515
530
  }
516
531
  }
517
- for (const key of Object.keys(projects)) {
518
- projects[key].sort((a, b) => a.date.localeCompare(b.date));
532
+ const projects = {};
533
+ for (const [projectName, dailyMap] of projectGroups) {
534
+ projects[projectName] = [...dailyMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([date, { acc, models }]) => accToEntry(date, acc, models));
519
535
  }
520
536
  return { projects };
521
537
  }
522
- function getBlocksResponse(options) {
523
- const sessions = parseAllSessions();
538
+ function buildBlocksResponse(sessions, options) {
524
539
  const grouped = groupSessions(sessions, { groupBy: "hour", ...options });
525
540
  const blocks = [];
526
541
  let idx = 0;
527
542
  for (const [hourKey, { acc, models }] of grouped) {
528
- const cost = calculateCost(acc, models);
543
+ const cost = buildModelBreakdowns(models).reduce((sum, model) => sum + model.cost, 0);
529
544
  const [datePart, timePart] = hourKey.split(" ");
530
545
  const hour = timePart.split(":")[0];
531
546
  blocks.push({
@@ -544,13 +559,22 @@ function getBlocksResponse(options) {
544
559
  },
545
560
  totalTokens: acc.totalTokens,
546
561
  costUSD: cost,
547
- models: [...models]
562
+ models: [...models.keys()]
548
563
  });
549
564
  idx++;
550
565
  }
551
566
  blocks.sort((a, b) => a.startTime.localeCompare(b.startTime));
552
567
  return { blocks };
553
568
  }
569
+ function getDailyResponse(options) {
570
+ return buildDailyResponse(parseAllSessions(), options);
571
+ }
572
+ function getProjectsResponse(options) {
573
+ return buildProjectsResponse(parseAllSessions(), options);
574
+ }
575
+ function getBlocksResponse(options) {
576
+ return buildBlocksResponse(parseAllSessions(), options);
577
+ }
554
578
 
555
579
  // src/server/openclawParser.ts
556
580
  var import_node_fs3 = require("node:fs");
@@ -1168,9 +1192,46 @@ function calculateCost2(inputTokens, cacheReadTokens, outputTokens, model) {
1168
1192
  }
1169
1193
  var CLAUDE_PROJECTS_DIR = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".claude", "projects");
1170
1194
  var fileCache = /* @__PURE__ */ new Map();
1195
+ var projectNameCache = /* @__PURE__ */ new Map();
1171
1196
  function extractProjectName2(dirName) {
1172
- const parts = dirName.replace(/^-/, "").split("-");
1173
- return parts[parts.length - 1] || dirName;
1197
+ if (!dirName.startsWith("-")) return dirName;
1198
+ const cached = projectNameCache.get(dirName);
1199
+ if (cached) return cached;
1200
+ const segments = dirName.replace(/^-/, "").split("-").filter(Boolean);
1201
+ if (segments.length === 0) {
1202
+ projectNameCache.set(dirName, dirName);
1203
+ return dirName;
1204
+ }
1205
+ if (segments.length === 1) {
1206
+ projectNameCache.set(dirName, segments[0]);
1207
+ return segments[0];
1208
+ }
1209
+ let bestName = segments[segments.length - 1];
1210
+ for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
1211
+ const parentSegments = segments.slice(0, splitAt);
1212
+ const candidateName = segments.slice(splitAt).join("-");
1213
+ let parentPath = "/";
1214
+ let valid = true;
1215
+ for (const seg of parentSegments) {
1216
+ const regular = (0, import_node_path5.join)(parentPath, seg);
1217
+ const hidden = (0, import_node_path5.join)(parentPath, "." + seg);
1218
+ if ((0, import_node_fs5.existsSync)(regular)) {
1219
+ parentPath = regular;
1220
+ } else if ((0, import_node_fs5.existsSync)(hidden)) {
1221
+ parentPath = hidden;
1222
+ } else {
1223
+ valid = false;
1224
+ break;
1225
+ }
1226
+ }
1227
+ if (!valid) continue;
1228
+ if ((0, import_node_fs5.existsSync)((0, import_node_path5.join)(parentPath, candidateName)) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(parentPath, "." + candidateName))) {
1229
+ bestName = candidateName;
1230
+ break;
1231
+ }
1232
+ }
1233
+ projectNameCache.set(dirName, bestName);
1234
+ return bestName;
1174
1235
  }
1175
1236
  function matchesProject(dirName, filter) {
1176
1237
  return extractProjectName2(dirName) === extractProjectName2(filter);
@@ -1345,7 +1406,7 @@ function getProjectsResponse4(tz = DEFAULT_TZ) {
1345
1406
  const projectMap = /* @__PURE__ */ new Map();
1346
1407
  for (const e of entries) {
1347
1408
  const date = getDateKey4(e.timestamp, tz);
1348
- const projectName = e.projectDir;
1409
+ const projectName = extractProjectName2(e.projectDir);
1349
1410
  if (!projectMap.has(projectName)) {
1350
1411
  projectMap.set(projectName, /* @__PURE__ */ new Map());
1351
1412
  }
@@ -1680,9 +1741,46 @@ function countLines(text) {
1680
1741
  return text.split("\n").length;
1681
1742
  }
1682
1743
  var CLAUDE_PROJECTS_DIR2 = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".claude", "projects");
1744
+ var projectNameCache2 = /* @__PURE__ */ new Map();
1683
1745
  function extractProjectName3(dirName) {
1684
- const parts = dirName.replace(/^-/, "").split("-");
1685
- return parts[parts.length - 1] || dirName;
1746
+ if (!dirName.startsWith("-")) return dirName;
1747
+ const cached = projectNameCache2.get(dirName);
1748
+ if (cached) return cached;
1749
+ const segments = dirName.replace(/^-/, "").split("-").filter(Boolean);
1750
+ if (segments.length === 0) {
1751
+ projectNameCache2.set(dirName, dirName);
1752
+ return dirName;
1753
+ }
1754
+ if (segments.length === 1) {
1755
+ projectNameCache2.set(dirName, segments[0]);
1756
+ return segments[0];
1757
+ }
1758
+ let bestName = segments[segments.length - 1];
1759
+ for (let splitAt = segments.length - 1; splitAt >= 1; splitAt--) {
1760
+ const parentSegments = segments.slice(0, splitAt);
1761
+ const candidateName = segments.slice(splitAt).join("-");
1762
+ let parentPath = "/";
1763
+ let valid = true;
1764
+ for (const seg of parentSegments) {
1765
+ const regular = (0, import_node_path6.join)(parentPath, seg);
1766
+ const hidden = (0, import_node_path6.join)(parentPath, "." + seg);
1767
+ if ((0, import_node_fs6.existsSync)(regular)) {
1768
+ parentPath = regular;
1769
+ } else if ((0, import_node_fs6.existsSync)(hidden)) {
1770
+ parentPath = hidden;
1771
+ } else {
1772
+ valid = false;
1773
+ break;
1774
+ }
1775
+ }
1776
+ if (!valid) continue;
1777
+ if ((0, import_node_fs6.existsSync)((0, import_node_path6.join)(parentPath, candidateName)) || (0, import_node_fs6.existsSync)((0, import_node_path6.join)(parentPath, "." + candidateName))) {
1778
+ bestName = candidateName;
1779
+ break;
1780
+ }
1781
+ }
1782
+ projectNameCache2.set(dirName, bestName);
1783
+ return bestName;
1686
1784
  }
1687
1785
  function matchesProject2(dirName, filter) {
1688
1786
  return extractProjectName3(dirName) === extractProjectName3(filter);
@@ -1867,6 +1965,21 @@ function computeAnalytics(toolCalls, timezone = "Asia/Shanghai") {
1867
1965
  toolCallTrend.push(entry);
1868
1966
  }
1869
1967
  toolCallTrend.sort((a, b) => a.date.localeCompare(b.date));
1968
+ if (toolCallTrend.length > 0) {
1969
+ const allTools = /* @__PURE__ */ new Set();
1970
+ for (const entry of toolCallTrend) {
1971
+ for (const key of Object.keys(entry)) {
1972
+ if (key !== "date") allTools.add(key);
1973
+ }
1974
+ }
1975
+ for (const entry of toolCallTrend) {
1976
+ for (const tool of allTools) {
1977
+ if (entry[tool] === void 0) {
1978
+ entry[tool] = 0;
1979
+ }
1980
+ }
1981
+ }
1982
+ }
1870
1983
  return { codeChangeTrend, toolUsageDistribution, productivityKPIs, toolCallTrend };
1871
1984
  }
1872
1985
 
@@ -2035,11 +2148,11 @@ function resolvePort(value) {
2035
2148
  return Number.isInteger(value) && value && value > 0 ? value : 3456;
2036
2149
  }
2037
2150
  function listen(app, port) {
2038
- return new Promise((resolve, reject) => {
2151
+ return new Promise((resolve2, reject) => {
2039
2152
  const server = app.listen(port);
2040
2153
  const handleListening = () => {
2041
2154
  cleanup();
2042
- resolve(server);
2155
+ resolve2(server);
2043
2156
  };
2044
2157
  const handleError = (error) => {
2045
2158
  cleanup();
@@ -2068,16 +2181,29 @@ async function listenWithPortFallback(app, preferredPort) {
2068
2181
  }
2069
2182
  throw new Error(`Could not find an available port starting from ${preferredPort}`);
2070
2183
  }
2184
+ function resolveStaticAssetBaseDir(moduleUrl = __esbuild_import_meta_url, baseDir) {
2185
+ if (baseDir) return { baseDir: (0, import_node_path8.resolve)(baseDir), isProduction: true };
2186
+ const moduleDir = (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(moduleUrl));
2187
+ const isProduction = moduleUrl.includes("/dist/");
2188
+ if (!isProduction) return { baseDir: (0, import_node_path8.resolve)(moduleDir), isProduction: false };
2189
+ if ((0, import_node_path8.basename)(moduleDir) === "server") {
2190
+ return { baseDir: (0, import_node_path8.resolve)((0, import_node_path8.dirname)(moduleDir)), isProduction: true };
2191
+ }
2192
+ return { baseDir: (0, import_node_path8.resolve)(moduleDir), isProduction: true };
2193
+ }
2071
2194
  function createApp(_port, baseDir) {
2072
2195
  const app = (0, import_express.default)();
2073
2196
  const router = import_express.default.Router();
2074
2197
  registerApiRoutes(router);
2075
2198
  app.use("/api", router);
2076
- const _baseDir = baseDir ?? (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(__esbuild_import_meta_url));
2077
- const isProduction = baseDir ? true : __esbuild_import_meta_url.includes("dist/");
2199
+ const { baseDir: _baseDir, isProduction } = resolveStaticAssetBaseDir(__esbuild_import_meta_url, baseDir);
2078
2200
  const popoverPath = isProduction ? (0, import_node_path8.join)(_baseDir, "client", "popover.html") : (0, import_node_path8.join)(_baseDir, "..", "..", "public", "popover.html");
2079
- app.get("/popover.html", (_req, res) => {
2080
- res.sendFile(popoverPath);
2201
+ app.get("/popover.html", (_req, res, next) => {
2202
+ if (!(0, import_node_fs8.existsSync)(popoverPath)) {
2203
+ next();
2204
+ return;
2205
+ }
2206
+ res.type("html").send((0, import_node_fs8.readFileSync)(popoverPath, "utf8"));
2081
2207
  });
2082
2208
  if (isProduction) {
2083
2209
  const clientPath = (0, import_node_path8.join)(_baseDir, "client");
@@ -2157,6 +2283,7 @@ async function main() {
2157
2283
  // Annotate the CommonJS export names for ESM import in node:
2158
2284
  0 && (module.exports = {
2159
2285
  createApp,
2160
- main
2286
+ main,
2287
+ resolveStaticAssetBaseDir
2161
2288
  });
2162
2289
  //# sourceMappingURL=electron-server.cjs.map