codexmeter 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.
package/server/index.js CHANGED
@@ -2,12 +2,13 @@ import express from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { attachLiveSubscriber, createIngestState, detachLiveSubscriber, getLatestReplay, restartIngest, runIngest } from './ingest.js';
5
- import { createJobSummary, createVideoExportManager, getActiveVideoExportJob, getVideoExportJob, startOverviewVideoExport } from './export-video.js';
5
+ import { createJobSummary, createVideoExportManager, getActiveVideoExportJob, getVideoExportJob, getVideoExportSupport, startOverviewVideoExport } from './export-video.js';
6
6
 
7
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
8
 
9
9
  export function createServer(codexHome, opts = {}) {
10
10
  const app = express();
11
+ app.use(express.json());
11
12
  const state = createIngestState();
12
13
  const exportManager = createVideoExportManager();
13
14
  const distDir = path.join(__dirname, '..', 'dist');
@@ -80,13 +81,18 @@ export function createServer(codexHome, opts = {}) {
80
81
  app.get('/api/families', wrap('families'));
81
82
 
82
83
  app.post('/api/export/overview-video', async (req, res) => {
84
+ const activeJob = getActiveVideoExportJob(exportManager);
85
+ if (activeJob) {
86
+ res.status(409).json({ error: 'Another export job is already running.' });
87
+ return;
88
+ }
83
89
  const replay = getLatestReplay(state);
84
90
  if (!replay) {
85
91
  res.status(409).json({ error: 'No completed ingest replay is available yet.' });
86
92
  return;
87
93
  }
88
94
 
89
- const appBaseUrl = req.get('x-codexmeter-client-base') || opts.frontendBaseUrl || `${req.protocol}://${req.get('host')}`;
95
+ const appBaseUrl = opts.frontendBaseUrl || `${req.protocol}://${req.get('host')}`;
90
96
  const settledEnvelope = state.aggregates ? {
91
97
  overview: { data: state.aggregates.overview },
92
98
  repos: { data: state.aggregates.repos },
@@ -96,8 +102,13 @@ export function createServer(codexHome, opts = {}) {
96
102
  heatmap: { data: state.aggregates.heatmap },
97
103
  } : null;
98
104
  try {
99
- const job = await startOverviewVideoExport(exportManager, { replay, settledEnvelope, appBaseUrl });
100
- res.status(202).json(createJobSummary(job, `${req.protocol}://${req.get('host')}`));
105
+ const job = await startOverviewVideoExport(exportManager, {
106
+ replay,
107
+ settledEnvelope,
108
+ appBaseUrl,
109
+ installPortableBrowser: Boolean(req.body?.install_portable_browser),
110
+ });
111
+ res.status(202).json(createJobSummary(job));
101
112
  } catch (err) {
102
113
  res.status(err.statusCode || 500).json({ error: err.message || String(err) });
103
114
  }
@@ -109,7 +120,11 @@ export function createServer(codexHome, opts = {}) {
109
120
  res.json({ job: null });
110
121
  return;
111
122
  }
112
- res.json({ job: createJobSummary(job, `${req.protocol}://${req.get('host')}`) });
123
+ res.json({ job: createJobSummary(job) });
124
+ });
125
+
126
+ app.get('/api/export/support', async (_req, res) => {
127
+ res.json(await getVideoExportSupport());
113
128
  });
114
129
 
115
130
  app.get('/api/export/:jobId/status', (req, res) => {
@@ -118,7 +133,7 @@ export function createServer(codexHome, opts = {}) {
118
133
  res.status(404).json({ error: 'Export job not found.' });
119
134
  return;
120
135
  }
121
- res.json(createJobSummary(job, `${req.protocol}://${req.get('host')}`));
136
+ res.json(createJobSummary(job));
122
137
  });
123
138
 
124
139
  app.get('/api/export/:jobId/render-data', (req, res) => {
@@ -82,7 +82,7 @@ export function buildLiveBootstrap(live) {
82
82
  export function buildLivePatch(live, patch) {
83
83
  return {
84
84
  overview: Object.fromEntries(
85
- [...patch.overview].map((rangeKey) => [rangeKey, serializeOverviewBucket(live.overview[rangeKey])])
85
+ [...patch.overview].map((rangeKey) => [rangeKey, serializeOverviewBucket(live.overview[rangeKey], live.lowerBounds[rangeKey])])
86
86
  ),
87
87
  repos: serializePatchedTopRanges(live.repos, live.repoTopKeys, patch.repos, serializeRepoSummary),
88
88
  models: serializePatchedTopRanges(live.models, live.modelTopKeys, patch.models, serializeModelSummary),
@@ -352,14 +352,17 @@ function ensureHeatmapDay(dayMap, dayKey) {
352
352
 
353
353
  function serializeOverview(live) {
354
354
  return {
355
- total: serializeOverviewBucket(live.overview.total),
356
- d7: serializeOverviewBucket(live.overview.d7),
357
- d30: serializeOverviewBucket(live.overview.d30),
355
+ total: serializeOverviewBucket(live.overview.total, live.lowerBounds.total),
356
+ d7: serializeOverviewBucket(live.overview.d7, live.lowerBounds.d7),
357
+ d30: serializeOverviewBucket(live.overview.d30, live.lowerBounds.d30),
358
358
  cost_assumptions: CACHE_ASSUMPTIONS,
359
359
  };
360
360
  }
361
361
 
362
- function serializeOverviewBucket(bucket) {
362
+ function serializeOverviewBucket(bucket, lowerBound = 0) {
363
+ const boundedFrom = bucket.earliest === Infinity
364
+ ? null
365
+ : Math.max(bucket.earliest, lowerBound || 0);
363
366
  return {
364
367
  total_tokens: bucket.total_tokens,
365
368
  total_cost: bucket.total_cost,
@@ -368,7 +371,7 @@ function serializeOverviewBucket(bucket) {
368
371
  active_models: bucket.modelSet.size,
369
372
  total_elapsed_seconds: bucket.total_elapsed_seconds,
370
373
  date_range: {
371
- from: bucket.earliest === Infinity ? null : bucket.earliest,
374
+ from: boundedFrom,
372
375
  to: bucket.latest === -Infinity ? null : bucket.latest,
373
376
  },
374
377
  coverage: {
@@ -20,7 +20,7 @@ const AUTO = 'auto';
20
20
  export const OVERVIEW_INGEST_ANIMATION = {
21
21
  speed: 1,
22
22
  main: {
23
- presentationDurationMs: 190,
23
+ presentationDurationMs: 300,
24
24
  chartAppearDurationMs: 0,
25
25
  chartUpdateDurationMs: 200,
26
26
  easing: 'linear',
@@ -34,7 +34,7 @@ export const OVERVIEW_INGEST_ANIMATION = {
34
34
  // Fixed presentation duration in ms during the tail; use AUTO to derive it from main.durationScale.
35
35
  durationMs: AUTO,
36
36
  // Multiplier applied to the normal presentation duration when durationMs is AUTO.
37
- durationScale: 100,
37
+ durationScale: 20,
38
38
  // Shared easing applied by the Overview presentation animator during the tail.
39
39
  easing: 'cubicOut',
40
40
  // Backend Overview live-update cadence during the tail, in patches per second.
@@ -72,16 +72,52 @@ export const OVERVIEW_INGEST_ANIMATION = {
72
72
  nearMaxRatio: 0.985,
73
73
  },
74
74
  videoExport: {
75
+ // Video export settings
75
76
  width: 1080,
76
77
  height: 864,
77
78
  fps: 60,
78
- introDurationMs: 900,
79
- replayDurationMs: 8000,
79
+ frontloadSettledFrameCount: 1,
80
+
81
+ // Intro fade-in effect
82
+ introFadeEnabled: true,
83
+ introFadeStartOpacity: 0.95,
84
+ introContentStartOpacity: 0.05,
85
+ introFadeDurationMs: 400,
86
+ introFadeDelayMs: 0,
87
+ introFadeEasing: 'cubicOut',
88
+
89
+ // Start Delay for the animation
90
+ startHoldDurationMs: 100,
91
+
92
+ // Replay duration
93
+ replayDurationMs: 10000,
94
+
95
+ // Tail Settings
80
96
  tailDurationMs: 5000,
81
- tailReplayFraction: 0.72,
97
+ replayEasing: 'cubicIn',
98
+ tailSourceFraction: 0.55,
99
+ tailEasing: 'cubicOut',
100
+ finalHoldDurationMs: 5000,
101
+
102
+ // Final Flash Settings
103
+ finalFlashDurationMs: 1000,
104
+ finalFlashMaxOpacity: 0.35,
105
+
106
+ // Display Tween Settings
107
+ displayTweenStartMs: 200,
108
+ displayTweenBaseMs: 240,
109
+ displayTweenLateMs: 420,
110
+ displayTweenLateWindowMs: 2200,
111
+ barsChartUpdateDurationMs: 50,
112
+ barsChartTailUpdateDurationMs: 240,
113
+ donutsChartUpdateDurationMs: 50,
114
+ donutsChartTailUpdateDurationMs: 240,
115
+
116
+ // Render settings
117
+ supersampleScale: 2,
82
118
  captureFormat: 'png',
83
119
  jpegQuality: 92,
84
- crf: 10,
120
+ crf: 5,
85
121
  encoderPreset: 'veryfast',
86
122
  },
87
123
  };
@@ -1 +0,0 @@
1
- :root{--bg-base: #06080f;--bg-surface: #0d1117;--bg-card: #161b22;--bg-card-hover: #1c2333;--bg-elevated: #21262d;--border: #30363d;--border-accent: rgba(99, 102, 241, .25);--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #484f58;--accent: #6366f1;--accent-dim: rgba(99, 102, 241, .12);--green: #3fb950;--yellow: #d29922;--red: #f85149;--orange: #f97316;--cyan: #39d2f5;--pink: #f472b6;--font-sans: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;--font-mono: "JetBrains Mono", "Fira Code", monospace;--radius: 8px;--radius-lg: 12px}*{box-sizing:border-box;margin:0;padding:0}body{font-family:var(--font-sans);background:var(--bg-base);color:var(--text-primary);line-height:1.6;-webkit-font-smoothing:antialiased}.app{min-height:100vh;display:flex;flex-direction:column}.app-content{opacity:0;display:flex;flex-direction:column;min-height:100vh}.app-content-revealed{opacity:1;animation:contentReveal .5s ease forwards}@keyframes contentReveal{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.navbar{background:var(--bg-surface);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:100;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.navbar-inner{display:flex;align-items:center;gap:1.5rem;padding:0 2rem;height:48px;max-width:1400px;margin:0 auto;width:100%;box-sizing:border-box}.navbar-brand{font-weight:700;font-size:.95rem;letter-spacing:-.03em;background:linear-gradient(135deg,#818cf8,#6366f1);-webkit-background-clip:text;-webkit-text-fill-color:transparent}.navbar-tabs{display:flex;gap:2px}.navbar-tab{padding:.35rem .85rem;border-radius:6px;cursor:pointer;font-size:.8rem;font-weight:500;color:var(--text-secondary);background:none;border:none;transition:all .15s,opacity .35s ease}.navbar-tab:hover{color:var(--text-primary);background:var(--bg-card)}.navbar-tab.active{color:#fff;background:var(--accent)}.navbar-tab.navbar-tab-dimmed,.navbar-tab.navbar-tab-dimmed:hover{opacity:.4;color:var(--text-muted)}.navbar-meta{margin-left:auto;display:flex;align-items:center;gap:.75rem;flex:1;min-width:0}.navbar-ingest-wrap{display:flex;align-items:center;gap:.75rem;flex:1;min-width:0;transition:opacity .3s ease}.navbar-ingest-wrap.navbar-ingest-fade-out{opacity:0;pointer-events:none}.navbar-date-wrap{display:flex;align-items:center;gap:.75rem;margin-left:auto;flex-shrink:0;transition:opacity .3s ease}.navbar-date-wrap-dimmed{opacity:.5;pointer-events:none}.navbar-date{display:inline-block;width:22ch;min-width:22ch;font-variant-numeric:tabular-nums;text-align:right;overflow:hidden;text-overflow:ellipsis}.navbar-progress-wrap{flex:1;height:4px;background:var(--bg-elevated);border-radius:2px;overflow:hidden}.navbar-progress-bar{height:100%;background:linear-gradient(90deg,var(--accent),#818cf8);border-radius:2px;transition:width .4s ease}.navbar-status{font-size:.7rem;color:var(--text-muted);font-family:var(--font-mono)}.main-content{flex:1;padding:1.5rem 2rem 1.25rem;max-width:1400px;margin:0 auto;width:100%;min-width:0;container-type:inline-size;container-name:main}.app-footer{padding:.5rem 2rem .75rem;font-size:.7rem;color:var(--text-muted);text-align:center;max-width:1400px;margin:auto auto 0;width:100%;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.25rem}.section-title{font-size:1rem;font-weight:600;letter-spacing:-.01em}.stat-row{display:flex;flex-wrap:wrap;gap:1rem;align-items:stretch;margin-bottom:1.5rem}.stat-cards{display:grid;grid-template-columns:repeat(4,minmax(160px,1fr));gap:.75rem;flex:0 0 auto;min-width:0}@media(max-width:920px){.stat-cards{flex:1 1 auto;grid-template-columns:repeat(2,minmax(160px,1fr))}}@media(max-width:500px){.stat-cards{grid-template-columns:1fr}}.stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:1rem 1.1rem;transition:border-color .2s}.stat-card:hover{border-color:var(--border-accent)}.stat-label{font-size:.65rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin-bottom:.35rem}.stat-value{font-size:1.6rem;font-weight:700;font-family:var(--font-mono);letter-spacing:-.03em;line-height:1.1}.stat-sub{font-size:.72rem;color:var(--text-muted);margin-top:.2rem}.stat-per-day{font-size:.72rem;color:var(--text-muted);margin-top:.25rem;font-family:var(--font-mono)}.stat-per-day .stat-per-day-value{font-weight:600;color:var(--text-secondary)}.range-toggle{display:inline-flex;background:var(--bg-card);border:1px solid var(--border);border-radius:6px;overflow:hidden}.range-btn{padding:.3rem .7rem;font-size:.72rem;font-weight:600;color:var(--text-muted);background:none;border:none;cursor:pointer;transition:all .15s}.range-btn:hover{color:var(--text-secondary)}.range-btn.active{color:#fff;background:var(--accent)}.heatmap-wrap{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:1.25rem;margin-bottom:1.5rem;overflow-x:auto}.heatmap-grid{display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,1fr);gap:3px}.heatmap-cell{width:13px;height:13px;border-radius:2px;background:var(--bg-elevated);opacity:.4;transform:scale(.82);transition:background .18s ease,opacity .22s ease,transform .22s ease}.heatmap-cell.active{opacity:1;transform:scale(1)}.heatmap-cell:hover{outline:1px solid var(--text-muted)}.heatmap-cell.heatmap-cell-pop{animation:heatmap-cell-pop .38s cubic-bezier(.34,1.56,.64,1)}@keyframes heatmap-cell-pop{0%{transform:scale(1);box-shadow:none}40%{transform:scale(1.4);box-shadow:0 0 10px #6366f1b3,0 0 0 1.5px #0003}70%{transform:scale(1.1);box-shadow:0 0 4px #6366f159,0 0 0 .5px #0000001a}to{transform:scale(1);box-shadow:none}}.heatmap-cell.heatmap-cell-pop-dim{animation:heatmap-cell-pop-dim .32s cubic-bezier(.34,1.56,.64,1)}@keyframes heatmap-cell-pop-dim{0%{transform:scale(1)}50%{transform:scale(.85)}to{transform:scale(1)}}.heatmap-labels{display:flex;justify-content:space-between;margin-top:.5rem;font-size:.65rem;color:var(--text-muted)}.chart-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:1.25rem;margin-bottom:1.5rem;position:relative}.chart-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem}.chart-title{font-size:.85rem;font-weight:600}.chart-badge{font-size:.62rem;color:var(--yellow);background:#d299221a;padding:.15rem .5rem;border-radius:4px}.export-btn{position:absolute;top:.75rem;right:.75rem;padding:.25rem .5rem;border-radius:4px;border:1px solid var(--border);background:var(--bg-surface);color:var(--text-muted);font-size:.65rem;cursor:pointer;opacity:0;transition:all .15s;z-index:2}.chart-card:hover .export-btn{opacity:.7}.export-btn:hover{opacity:1!important;color:var(--text-primary);border-color:var(--accent)}.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin-bottom:1.5rem;min-width:0}.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1.25rem;margin-bottom:1.5rem;min-width:0}.grid-3>.chart-card{min-width:0;overflow:hidden}@media(max-width:900px){.grid-2,.grid-3{grid-template-columns:1fr}}.btn-group{display:flex;gap:2px}.btn{padding:.3rem .65rem;border-radius:5px;border:1px solid var(--border);background:var(--bg-surface);color:var(--text-muted);font-size:.72rem;font-weight:500;cursor:pointer;transition:all .12s}.btn:hover{color:var(--text-secondary);border-color:var(--text-muted)}.btn.active{color:#fff;background:var(--accent);border-color:var(--accent)}.icon-btn{width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-muted);font-size:.9rem;line-height:1;cursor:pointer;transition:color .12s,border-color .12s,background .12s,transform .12s}.icon-btn:hover:not(:disabled){color:var(--text-primary);border-color:var(--accent);background:var(--bg-elevated);transform:rotate(20deg)}.icon-btn:disabled{opacity:.55;cursor:wait}.overview-daily-spark{flex:1 1 140px;min-width:140px;min-height:0;position:relative;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:visible}.overview-daily-spark-title{position:absolute;top:1rem;left:1.1rem;font-size:.65rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);z-index:1;pointer-events:none}.overview-daily-spark-chart{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden}.overview-daily-spark-empty{display:flex;align-items:center;justify-content:center}.overview-daily-spark-empty-text{font-size:.7rem;color:var(--text-muted)}@media(max-width:920px){.overview-daily-spark{flex:1 1 100%;min-width:100%;min-height:140px}}.table-wrap{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}.model-detail-wrap{display:flex;flex-direction:column;gap:.85rem}.model-detail-note{color:var(--text-muted);font-size:.74rem;line-height:1.4}.model-detail-footer{display:flex;justify-content:flex-start;margin-top:.25rem}.model-detail-toggle{display:inline-flex;align-items:center;gap:.35rem;width:fit-content;padding:.35rem .6rem;font-size:.72rem;font-weight:500;color:var(--text-muted);background:#ffffff0a;border:1px solid var(--border);border-radius:6px;cursor:pointer;transition:color .2s,background .2s,border-color .2s}.model-detail-toggle:hover{color:var(--text-secondary);background:#ffffff0f;border-color:#6366f14d}.model-detail-toggle-text{font-family:var(--font-sans)}.model-detail-toggle-arrow{font-size:.6rem;opacity:.8;transition:transform .25s ease}.model-detail-toggle-arrow.expanded{transform:rotate(180deg)}.model-detail-charts{display:flex;gap:.25rem;flex-wrap:wrap;align-items:flex-start;width:100%;overflow:visible;position:relative;z-index:10}.model-detail-donut{flex:0 0 200px;width:200px;min-width:200px;height:200px;overflow:visible;z-index:10;display:flex;flex-direction:column}.model-detail-donut .chart-title{flex-shrink:0}.model-detail-donut .echarts-for-react{flex:1;min-height:0}.model-detail-wrap .model-detail-donut{flex:0 0 340px;width:340px;min-width:340px;height:220px}.model-detail-donut .echarts-for-react,.model-detail-donut .echarts-for-react>div,.model-detail-bar .echarts-for-react,.model-detail-bar .echarts-for-react>div{overflow:visible!important}.model-detail-bar{flex:1 1 300px;min-width:300px;height:200px;overflow:visible;display:flex;flex-direction:column}.model-detail-bar .chart-title{flex-shrink:0}.model-detail-bar .echarts-for-react{flex:1;min-height:0}.model-detail-summary-wrap{max-height:0;overflow:hidden;transition:max-height .2s ease-out}.model-detail-summary-wrap.expanded{max-height:400px}.model-detail-summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:.6rem;padding-top:.5rem}.model-detail-summary-card{background:#ffffff05;border:1px solid var(--border);border-radius:10px;padding:.65rem .75rem}.model-detail-summary-head{display:flex;align-items:center;gap:.45rem;margin-bottom:.45rem;font-size:.76rem;font-weight:600;color:var(--text-primary);text-transform:capitalize;min-width:0}.model-detail-summary-head .model-detail-summary-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.model-detail-swatch{width:9px;height:9px;border-radius:999px;flex:0 0 auto}.model-detail-summary-line{font-size:.72rem;color:var(--text-muted);line-height:1.45}.detail-summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.75rem;margin-bottom:1rem}.detail-summary-stat{background:#ffffff08;border:1px solid var(--border);border-radius:10px;padding:.8rem .9rem}.detail-summary-label{font-size:.68rem;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:.25rem}.detail-summary-value{font-size:1.1rem;font-family:var(--font-mono);color:var(--text-primary)}.table-search{padding:.75rem 1rem;border-bottom:1px solid var(--border)}.table-search input{width:100%;padding:.5rem .85rem;border-radius:6px;border:1px solid var(--border);background:var(--bg-surface);color:var(--text-primary);font-size:.82rem;font-family:var(--font-sans);outline:none}.table-search input:focus{border-color:var(--accent)}.table-search input::placeholder{color:var(--text-muted)}table{width:100%;border-collapse:collapse;font-size:.78rem}th{text-align:left;padding:.55rem .85rem;font-weight:600;color:var(--text-muted);border-bottom:1px solid var(--border);font-size:.68rem;text-transform:uppercase;letter-spacing:.05em;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap}td{padding:.5rem .85rem;border-bottom:1px solid rgba(48,54,61,.5);color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:240px}tr:hover td{color:var(--text-primary);background:#6366f108}.tag{display:inline-block;padding:.1rem .4rem;border-radius:4px;font-size:.66rem;font-family:var(--font-mono);font-weight:500}.tag-review{background:#06b6d41f;color:var(--cyan)}.tag-exploration{background:#f973161f;color:var(--orange)}.tag-planning{background:#eab3081f;color:var(--yellow)}.tag-memory{background:#ec48991f;color:var(--pink)}.tag-generic{background:#64748b1f;color:var(--text-secondary)}.loading-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#06080feb;-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:999;transition:opacity .6s ease}.loading-overlay.fading{opacity:0;pointer-events:none}.loading-logo{font-size:1.5rem;font-weight:700;background:linear-gradient(135deg,#818cf8,#6366f1);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:1.5rem}.loading-bar-wrap{width:260px;height:4px;background:var(--bg-elevated);border-radius:2px;overflow:hidden;margin-bottom:1rem}.loading-bar{height:100%;background:linear-gradient(90deg,var(--accent),#818cf8);border-radius:2px;transition:width .4s ease}.loading-phase{font-size:.82rem;font-weight:500;color:var(--text-secondary);margin-bottom:.2rem}.loading-detail{font-family:var(--font-mono);font-size:.72rem;color:var(--text-muted)}.loading-footer{position:absolute;bottom:1.5rem;font-size:.7rem;color:var(--text-muted)}.loading-heart{color:#f43f5e}.coverage-subtle{display:flex;align-items:center;gap:1.5rem;font-size:.68rem;color:var(--text-muted);padding:.25rem 0;margin-top:-.5rem;margin-bottom:.5rem}.coverage-item{display:flex;align-items:center;gap:.35rem;font-variant-numeric:tabular-nums}.coverage-nums{display:inline-block;min-width:9ch;text-align:right}.coverage-nums-single{min-width:4ch}.coverage-dot{width:6px;height:6px;border-radius:50%}html{scrollbar-width:none}::-webkit-scrollbar{display:none;width:0;height:0}@keyframes fadeUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.animate-in{animation:fadeUp .35s ease forwards}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.incomplete-badge{font-size:.65rem;color:var(--yellow);background:#d299221a;padding:.12rem .45rem;border-radius:3px;animation:pulse 2s infinite}.ingesting-badge .ingesting-pct{display:inline-block;min-width:4ch;text-align:right;font-variant-numeric:tabular-nums}.echarts-tooltip,[class*=ec-component-tooltip]{max-width:90vw!important;max-height:80vh!important;overflow:auto!important}