copilot-usage-studio 0.1.0 → 0.2.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.
- package/CHANGELOG.md +14 -4
- package/README.md +177 -150
- package/dist/copilot-usage-studio/browser/chunk-CUKTUQ6W.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-EYAHOEVS.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-FXNLFSUB.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-HJNYITFH.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-J47KKACI.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-KDAJN6DF.js +4 -0
- package/dist/copilot-usage-studio/browser/chunk-NXH37FKU.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-QQOFM3HF.js +1 -0
- package/dist/copilot-usage-studio/browser/chunk-TVSYR63W.js +2 -0
- package/dist/copilot-usage-studio/browser/index.html +11 -11
- package/dist/copilot-usage-studio/browser/main-TL27ZSEQ.js +1 -0
- package/docs/copilot-memory.md +91 -0
- package/docs/local-deployment.md +58 -8
- package/docs/scanner-api.md +4 -3
- package/lib/local-runtime.mjs +77 -2
- package/package.json +79 -78
- package/scripts/scan-vscode-sessions.mjs +320 -14
- package/dist/copilot-usage-studio/browser/chunk-C6VWIY5S.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-DLWQO3VR.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-F6TIG2GE.js +0 -4
- package/dist/copilot-usage-studio/browser/chunk-JIP7ONRZ.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-RNKEPBEU.js +0 -1
- package/dist/copilot-usage-studio/browser/chunk-Z3XIAKMM.js +0 -1
- package/dist/copilot-usage-studio/browser/main-C6XOJRSH.js +0 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { homedir, platform } from 'node:os';
|
|
3
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from 'node:path';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
5
6
|
import {
|
|
6
7
|
costBreakdownUsdForTokens,
|
|
@@ -18,6 +19,8 @@ const pricingVersion = pricingData.version;
|
|
|
18
19
|
const pricingSourceUrl = pricingData.sourceUrl;
|
|
19
20
|
const fallbackPricingModel = pricingData.fallbackModel;
|
|
20
21
|
const traceEventLimit = 1000;
|
|
22
|
+
const memoryFileLimit = 5000;
|
|
23
|
+
const memoryFileSizeLimit = 1024 * 1024;
|
|
21
24
|
|
|
22
25
|
const pricing = pricingData.models;
|
|
23
26
|
|
|
@@ -36,6 +39,11 @@ function createDiagnostics() {
|
|
|
36
39
|
importedChatSnapshotSessions: 0,
|
|
37
40
|
debugLogSessionsWithTranscripts: 0,
|
|
38
41
|
transcriptEventsAvailable: 0,
|
|
42
|
+
scannedMemoryRoots: 0,
|
|
43
|
+
importedMemories: 0,
|
|
44
|
+
importedPlans: 0,
|
|
45
|
+
skippedOversizedMemories: 0,
|
|
46
|
+
skippedUnreadableMemories: 0,
|
|
39
47
|
skippedEmptyDebugLogs: 0,
|
|
40
48
|
skippedChatSnapshotsWithoutRequests: 0,
|
|
41
49
|
skippedDuplicateChatSnapshots: 0,
|
|
@@ -261,6 +269,262 @@ function listFiles(dir, suffix) {
|
|
|
261
269
|
.filter((path) => statSync(path).isFile() && path.endsWith(suffix));
|
|
262
270
|
}
|
|
263
271
|
|
|
272
|
+
function listDebugLogFiles(root) {
|
|
273
|
+
if (!existsSync(root)) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const files = [];
|
|
278
|
+
const pending = [root];
|
|
279
|
+
|
|
280
|
+
while (pending.length) {
|
|
281
|
+
const current = pending.pop();
|
|
282
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
283
|
+
const path = join(current, entry.name);
|
|
284
|
+
if (entry.isDirectory()) {
|
|
285
|
+
pending.push(path);
|
|
286
|
+
} else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
|
|
287
|
+
files.push(path);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return files.sort();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function listFilesRecursive(root, predicate, limit = memoryFileLimit) {
|
|
296
|
+
if (!existsSync(root)) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const files = [];
|
|
301
|
+
const pending = [root];
|
|
302
|
+
|
|
303
|
+
while (pending.length && files.length < limit) {
|
|
304
|
+
const current = pending.pop();
|
|
305
|
+
let entries = [];
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
309
|
+
} catch (error) {
|
|
310
|
+
diagnostics.skippedUnreadableMemories += 1;
|
|
311
|
+
diagnostics.warnings.push(`${current}: memory directory skipped: ${error.message}`);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for (const entry of entries) {
|
|
316
|
+
const path = join(current, entry.name);
|
|
317
|
+
if (entry.isDirectory()) {
|
|
318
|
+
pending.push(path);
|
|
319
|
+
} else if (entry.isFile() && predicate(path)) {
|
|
320
|
+
files.push(path);
|
|
321
|
+
if (files.length >= limit) {
|
|
322
|
+
diagnostics.warnings.push(`${root}: memory scan capped at ${limit} files.`);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return files;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function decodeMemorySessionId(value) {
|
|
333
|
+
try {
|
|
334
|
+
const decoded = Buffer.from(String(value), 'base64').toString('utf8');
|
|
335
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(decoded)
|
|
336
|
+
? decoded
|
|
337
|
+
: '';
|
|
338
|
+
} catch {
|
|
339
|
+
return '';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function memoryTitle(content, file) {
|
|
344
|
+
const heading = String(content)
|
|
345
|
+
.split(/\r?\n/)
|
|
346
|
+
.map((line) => line.match(/^#{1,3}\s+(.+?)\s*#*$/)?.[1]?.trim())
|
|
347
|
+
.find(Boolean);
|
|
348
|
+
|
|
349
|
+
if (heading) {
|
|
350
|
+
return heading.slice(0, 160);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return basename(file, extname(file))
|
|
354
|
+
.replace(/[-_]+/g, ' ')
|
|
355
|
+
.replace(/\b\w/g, (character) => character.toUpperCase())
|
|
356
|
+
.slice(0, 160);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function memoryExcerpt(content) {
|
|
360
|
+
return String(content)
|
|
361
|
+
.replace(/^#{1,6}\s+/gm, '')
|
|
362
|
+
.replace(/[`*_>~-]+/g, ' ')
|
|
363
|
+
.replace(/\s+/g, ' ')
|
|
364
|
+
.trim()
|
|
365
|
+
.slice(0, 280);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function memoryFromFile(file, root, source, workspace) {
|
|
369
|
+
try {
|
|
370
|
+
const stats = statSync(file);
|
|
371
|
+
if (stats.size > memoryFileSizeLimit) {
|
|
372
|
+
diagnostics.skippedOversizedMemories += 1;
|
|
373
|
+
diagnostics.warnings.push(`${file}: memory skipped because it exceeds 1 MiB.`);
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const content = readFileSync(file, 'utf8');
|
|
378
|
+
const relativePath = relative(root, file);
|
|
379
|
+
const segments = relativePath.split(sep).filter(Boolean);
|
|
380
|
+
const sessionId = source === 'workspace' ? decodeMemorySessionId(segments[0]) : '';
|
|
381
|
+
const kind = basename(file).toLowerCase() === 'plan.md' ? 'plan' : 'memory';
|
|
382
|
+
const scope = source === 'global'
|
|
383
|
+
? 'global'
|
|
384
|
+
: segments[0]?.toLowerCase() === 'repo'
|
|
385
|
+
? 'repository'
|
|
386
|
+
: sessionId
|
|
387
|
+
? 'session'
|
|
388
|
+
: 'workspace';
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
id: createHash('sha256').update(resolve(file)).digest('hex').slice(0, 24),
|
|
392
|
+
kind,
|
|
393
|
+
scope,
|
|
394
|
+
title: memoryTitle(content, file),
|
|
395
|
+
excerpt: memoryExcerpt(content),
|
|
396
|
+
content,
|
|
397
|
+
workspace: source === 'global' ? '' : workspace,
|
|
398
|
+
sessionId,
|
|
399
|
+
sourcePath: resolve(file),
|
|
400
|
+
relativePath,
|
|
401
|
+
createdAt: stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : '',
|
|
402
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
403
|
+
sizeBytes: stats.size,
|
|
404
|
+
characterCount: content.length,
|
|
405
|
+
lineCount: content ? content.split(/\r?\n/).length : 0,
|
|
406
|
+
};
|
|
407
|
+
} catch (error) {
|
|
408
|
+
diagnostics.skippedUnreadableMemories += 1;
|
|
409
|
+
diagnostics.warnings.push(`${file}: memory skipped: ${error.message}`);
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function memoriesFromRoot(root, source, workspace = '') {
|
|
415
|
+
if (!existsSync(root)) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
diagnostics.scannedMemoryRoots += 1;
|
|
420
|
+
const memories = listFilesRecursive(root, (file) => extname(file).toLowerCase() === '.md')
|
|
421
|
+
.map((file) => memoryFromFile(file, root, source, workspace))
|
|
422
|
+
.filter(Boolean);
|
|
423
|
+
|
|
424
|
+
diagnostics.importedMemories += memories.length;
|
|
425
|
+
diagnostics.importedPlans += memories.filter((memory) => memory.kind === 'plan').length;
|
|
426
|
+
return memories;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function normalizeMemoryVirtualPath(value) {
|
|
430
|
+
const path = String(value ?? '').trim().replace(/\\/g, '/');
|
|
431
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function virtualPathForMemory(memory) {
|
|
435
|
+
const segments = String(memory.relativePath ?? '').split(/[\\/]+/).filter(Boolean);
|
|
436
|
+
|
|
437
|
+
if (memory.scope === 'session') {
|
|
438
|
+
return normalizeMemoryVirtualPath(`/memories/session/${segments.slice(1).join('/')}`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return normalizeMemoryVirtualPath(`/memories/${segments.join('/')}`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function memoryRecallsFromDebugLog(sessionDir, workspace = '') {
|
|
445
|
+
const sessionId = basename(sessionDir);
|
|
446
|
+
const recalls = [];
|
|
447
|
+
|
|
448
|
+
for (const file of listDebugLogFiles(sessionDir)) {
|
|
449
|
+
const events = readJsonl(file);
|
|
450
|
+
let modelCallNumber = 0;
|
|
451
|
+
const modelCallNumbers = new Map();
|
|
452
|
+
|
|
453
|
+
events.forEach((event, index) => {
|
|
454
|
+
if (event.type === 'llm_request') {
|
|
455
|
+
modelCallNumber += 1;
|
|
456
|
+
modelCallNumbers.set(index, modelCallNumber);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
events.forEach((event, index) => {
|
|
461
|
+
if (event.type !== 'tool_call' || event.name !== 'memory') {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const args = safeJson(event.attrs?.args) ?? {};
|
|
466
|
+
if (args.command !== 'view' || !args.path) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const nextModelIndex = events.findIndex(
|
|
471
|
+
(candidate, candidateIndex) => candidateIndex > index && candidate.type === 'llm_request',
|
|
472
|
+
);
|
|
473
|
+
const nextModel = nextModelIndex >= 0 ? events[nextModelIndex] : null;
|
|
474
|
+
const tokenFields = nextModel ? llmTokenFields(nextModel) : null;
|
|
475
|
+
const sourceLog = basename(file);
|
|
476
|
+
|
|
477
|
+
recalls.push({
|
|
478
|
+
id: createHash('sha256')
|
|
479
|
+
.update(`${resolve(file)}:${index}:${args.path}`)
|
|
480
|
+
.digest('hex')
|
|
481
|
+
.slice(0, 24),
|
|
482
|
+
sessionId,
|
|
483
|
+
workspace,
|
|
484
|
+
virtualPath: normalizeMemoryVirtualPath(args.path),
|
|
485
|
+
timestamp: timestampForEvent(event),
|
|
486
|
+
sourceLog,
|
|
487
|
+
returnedCharacterCount: String(event.attrs?.result ?? '').length,
|
|
488
|
+
...(nextModel
|
|
489
|
+
? {
|
|
490
|
+
followingModelCall: {
|
|
491
|
+
number: modelCallNumbers.get(nextModelIndex) ?? 0,
|
|
492
|
+
model: normalizeModel(nextModel.attrs?.model, pricing),
|
|
493
|
+
inputTokens: tokenFields.inputTokens,
|
|
494
|
+
cachedInputTokens: tokenFields.cachedInputTokens,
|
|
495
|
+
outputTokens: tokenFields.outputTokens,
|
|
496
|
+
},
|
|
497
|
+
}
|
|
498
|
+
: {}),
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return recalls.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export function attachMemoryRecalls(memories, sessions) {
|
|
507
|
+
const recalls = sessions.flatMap((session) => session.memoryRecalls ?? []);
|
|
508
|
+
|
|
509
|
+
return memories.map((memory) => {
|
|
510
|
+
const virtualPath = virtualPathForMemory(memory);
|
|
511
|
+
const matchingRecalls = recalls.filter((recall) => {
|
|
512
|
+
if (recall.virtualPath !== virtualPath) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
if (memory.scope === 'session') {
|
|
516
|
+
return memory.sessionId === recall.sessionId;
|
|
517
|
+
}
|
|
518
|
+
if (memory.scope === 'repository' || memory.scope === 'workspace') {
|
|
519
|
+
return memory.workspace === recall.workspace;
|
|
520
|
+
}
|
|
521
|
+
return memory.scope === 'global';
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
return matchingRecalls.length ? { ...memory, recalls: matchingRecalls } : memory;
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
264
528
|
function costUsd(model, tokens) {
|
|
265
529
|
return costUsdForTokens(model, tokens, pricing, fallbackPricingModel);
|
|
266
530
|
}
|
|
@@ -1152,6 +1416,7 @@ export function sessionFromDebugLog(sessionDir, workspaceDir) {
|
|
|
1152
1416
|
const sourceUsage = sourceUsageSummary(llmRequests);
|
|
1153
1417
|
const setupPayloadForEvent = modelCallSetupPayloadFactory(sessionDir);
|
|
1154
1418
|
const transcript = transcriptAvailability(workspaceDir, sid);
|
|
1419
|
+
const memoryRecalls = memoryRecallsFromDebugLog(sessionDir, workspaceName(workspaceDir));
|
|
1155
1420
|
|
|
1156
1421
|
if (transcript.available) {
|
|
1157
1422
|
diagnostics.debugLogSessionsWithTranscripts += 1;
|
|
@@ -1229,6 +1494,7 @@ export function sessionFromDebugLog(sessionDir, workspaceDir) {
|
|
|
1229
1494
|
advancedSignals: evidence,
|
|
1230
1495
|
requestPayload: payload,
|
|
1231
1496
|
modelLimits,
|
|
1497
|
+
...(memoryRecalls.length ? { memoryRecalls } : {}),
|
|
1232
1498
|
traceEvents: capTraceEvents(
|
|
1233
1499
|
main.map((event, index) => {
|
|
1234
1500
|
const tokenFields =
|
|
@@ -1504,6 +1770,7 @@ function enrichSessionFromWorkspaceState(session, stateBySessionId) {
|
|
|
1504
1770
|
function parseWorkspace(workspaceDir) {
|
|
1505
1771
|
diagnostics.scannedWorkspaces += 1;
|
|
1506
1772
|
const stateBySessionId = readWorkspaceState(workspaceDir);
|
|
1773
|
+
const workspace = workspaceName(workspaceDir);
|
|
1507
1774
|
const debugRoot = join(workspaceDir, 'GitHub.copilot-chat', 'debug-logs');
|
|
1508
1775
|
const debugSessions = listDirs(debugRoot)
|
|
1509
1776
|
.map((sessionDir) => sessionFromDebugLog(sessionDir, workspaceDir))
|
|
@@ -1524,7 +1791,14 @@ function parseWorkspace(workspaceDir) {
|
|
|
1524
1791
|
.filter(Boolean);
|
|
1525
1792
|
diagnostics.importedChatSnapshotSessions += chatSessions.length;
|
|
1526
1793
|
|
|
1527
|
-
return
|
|
1794
|
+
return {
|
|
1795
|
+
sessions: [...debugSessions, ...chatSessions],
|
|
1796
|
+
memories: memoriesFromRoot(
|
|
1797
|
+
join(workspaceDir, 'GitHub.copilot-chat', 'memory-tool', 'memories'),
|
|
1798
|
+
'workspace',
|
|
1799
|
+
workspace,
|
|
1800
|
+
),
|
|
1801
|
+
};
|
|
1528
1802
|
}
|
|
1529
1803
|
|
|
1530
1804
|
function workspaceDirsFromUserDir(userDir) {
|
|
@@ -1532,6 +1806,29 @@ function workspaceDirsFromUserDir(userDir) {
|
|
|
1532
1806
|
return listDirs(workspaceStorage);
|
|
1533
1807
|
}
|
|
1534
1808
|
|
|
1809
|
+
function workspaceDirsForRoot(root) {
|
|
1810
|
+
if (existsSync(join(root, 'workspace.json'))) {
|
|
1811
|
+
return [root];
|
|
1812
|
+
}
|
|
1813
|
+
if (basename(root) === 'workspaceStorage') {
|
|
1814
|
+
return listDirs(root);
|
|
1815
|
+
}
|
|
1816
|
+
return workspaceDirsFromUserDir(root);
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
function userDirForRoot(root) {
|
|
1820
|
+
if (existsSync(join(root, 'workspaceStorage'))) {
|
|
1821
|
+
return root;
|
|
1822
|
+
}
|
|
1823
|
+
if (basename(root) === 'workspaceStorage') {
|
|
1824
|
+
return dirname(root);
|
|
1825
|
+
}
|
|
1826
|
+
if (basename(dirname(root)) === 'workspaceStorage') {
|
|
1827
|
+
return dirname(dirname(root));
|
|
1828
|
+
}
|
|
1829
|
+
return null;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1535
1832
|
/**
|
|
1536
1833
|
* Scan local VS Code Copilot storage and return the normalized app data model.
|
|
1537
1834
|
* This function does not write files, which lets CLI, desktop, extension, and
|
|
@@ -1566,19 +1863,27 @@ export async function scanVsCodeSessions(options = {}) {
|
|
|
1566
1863
|
|
|
1567
1864
|
try {
|
|
1568
1865
|
DatabaseSync = options.sqlite === false ? null : await loadSqliteSupport();
|
|
1569
|
-
const workspaceDirs = roots.flatMap(
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
) {
|
|
1574
|
-
return [root];
|
|
1575
|
-
}
|
|
1576
|
-
return workspaceDirsFromUserDir(root);
|
|
1577
|
-
});
|
|
1578
|
-
|
|
1579
|
-
const sessions = workspaceDirs
|
|
1580
|
-
.flatMap(parseWorkspace)
|
|
1866
|
+
const workspaceDirs = [...new Set(roots.flatMap(workspaceDirsForRoot))];
|
|
1867
|
+
const workspaceResults = workspaceDirs.map(parseWorkspace);
|
|
1868
|
+
const sessions = workspaceResults
|
|
1869
|
+
.flatMap((result) => result.sessions)
|
|
1581
1870
|
.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
1871
|
+
const globalMemoryRoots = [...new Set(
|
|
1872
|
+
roots
|
|
1873
|
+
.map(userDirForRoot)
|
|
1874
|
+
.filter(Boolean)
|
|
1875
|
+
.map((userDir) => join(userDir, 'globalStorage', 'github.copilot-chat', 'memory-tool', 'memories')),
|
|
1876
|
+
)];
|
|
1877
|
+
const memoryMap = new Map();
|
|
1878
|
+
for (const memory of [
|
|
1879
|
+
...workspaceResults.flatMap((result) => result.memories),
|
|
1880
|
+
...globalMemoryRoots.flatMap((root) => memoriesFromRoot(root, 'global')),
|
|
1881
|
+
]) {
|
|
1882
|
+
memoryMap.set(memory.id, memory);
|
|
1883
|
+
}
|
|
1884
|
+
const memories = attachMemoryRecalls([...memoryMap.values()], sessions).sort((a, b) =>
|
|
1885
|
+
b.modifiedAt.localeCompare(a.modifiedAt),
|
|
1886
|
+
);
|
|
1582
1887
|
const seenIds = new Set();
|
|
1583
1888
|
for (const session of sessions) {
|
|
1584
1889
|
if (seenIds.has(session.id)) {
|
|
@@ -1600,6 +1905,7 @@ export async function scanVsCodeSessions(options = {}) {
|
|
|
1600
1905
|
sessions.map((session) => session.cacheTokenAudit).filter(Boolean),
|
|
1601
1906
|
),
|
|
1602
1907
|
},
|
|
1908
|
+
memories,
|
|
1603
1909
|
sessions,
|
|
1604
1910
|
};
|
|
1605
1911
|
} finally {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as Y,b as Z,c as ee,d as te,e as ne,g as V}from"./chunk-JIP7ONRZ.js";import{$a as y,Ca as u,Da as x,Ea as R,Fa as M,Ga as P,Ha as g,Hb as A,Ia as i,Ib as E,Ja as n,Jb as U,Ka as w,Na as O,O as b,P as f,Pa as v,Pb as H,Ra as p,Rb as j,Sa as D,Sb as q,Tb as J,U as z,Ua as a,Ub as K,Va as _,Vb as Q,Wa as m,Wb as X,Xa as N,Ya as T,Z as S,Za as c,_a as C,cb as k,ka as l,nb as W,sa as G,wb as L}from"./chunk-F6TIG2GE.js";var ie=(o,t)=>t.value,me=(o,t)=>t.model+t.pricingModel,ge=(o,t)=>t.size,_e=(o,t)=>t.key,ue=(o,t)=>t.label,xe=(o,t)=>t.session.id;function Ce(o,t){if(o&1&&a(0),o&2){let e=p();m(" ",e.analyticsExcludedCount," excluded by these filters. ")}}function ye(o,t){if(o&1){let e=O();i(0,"button",11),v("click",function(){b(e);let r=p(2);return f(r.resetAnalyticsFilters())}),a(1,"Reset filters"),n()}}function he(o,t){if(o&1&&(i(0,"option",8),a(1),n()),o&2){let e=t.$implicit;g("value",e.value),l(),_(e.label)}}function be(o,t){if(o&1&&(i(0,"option",8),a(1),n()),o&2){let e=t.$implicit;g("value",e),l(),_(e==="all"?"All workspaces":e)}}function fe(o,t){if(o&1&&(i(0,"option",8),a(1),n()),o&2){let e=t.$implicit;g("value",e),l(),_(e==="all"?"All models":e)}}function ve(o,t){if(o&1&&(i(0,"option",8),a(1),n()),o&2){let e=t.$implicit;g("value",e.value),l(),_(e.label)}}function Me(o,t){if(o&1){let e=O();i(0,"button",11),v("click",function(){b(e);let r=p(3);return f(r.resetAnalyticsFilters())}),a(1,"Reset filters"),n()}}function Pe(o,t){if(o&1&&(i(0,"section",10)(1,"h3"),a(2),n(),i(3,"p"),a(4),n(),u(5,Me,2,0,"button",5),n()),o&2){let e=p();l(2),_(e.emptyTitle),l(2),_(e.emptyDetail),l(),x(e.analyticsFiltersActive?5:-1)}}function Oe(o,t){o&1&&(i(0,"em",27),a(1,"fallback pricing"),n())}function Se(o,t){if(o&1&&(a(0),c(1,"number")),o&2){let e=p().$implicit;m(" / ",C(1,1,e.cachedInput)," cached in ")}}function we(o,t){if(o&1&&(a(0),c(1,"number")),o&2){let e=p().$implicit;m(" / ",C(1,1,e.cacheWrite)," cache write ")}}function ke(o,t){if(o&1&&(i(0,"div",17)(1,"strong"),a(2),i(3,"em"),a(4),n(),u(5,Oe,2,0,"em",27),n(),i(6,"span"),a(7),c(8,"number"),n(),i(9,"span"),a(10),c(11,"number"),i(12,"em"),a(13),c(14,"number"),u(15,Se,2,3),u(16,we,2,3),a(17),c(18,"number"),n()(),i(19,"span"),a(20),c(21,"number"),i(22,"em"),a(23),c(24,"number"),n()(),i(25,"span"),a(26),c(27,"number"),n(),i(28,"span",28),a(29),c(30,"number"),w(31,"i"),n()()),o&2){let e=t.$implicit,s=p(3);l(2),m(" ",e.model," "),l(2),_(e.pricingModel),l(),x(e.usesFallbackPrice?5:-1),l(2),_(C(8,15,e.sessionCount)),l(3),m(" ",C(11,17,e.tokens)," "),l(3),m(" ",C(14,19,e.input)," normal in "),l(2),x(e.cachedInput?15:-1),l(),x(e.cacheWrite?16:-1),l(),m(" / ",C(18,21,e.output)," out "),l(3),m(" $",y(21,23,e.cost,"1.2-4")," "),l(3),m("",y(24,26,s.aiCredits(e.cost),"1.0-1")," credits"),l(3),m("$",y(27,29,e.costPer1k,"1.4-4")),l(3),m(" ",y(30,32,e.share,"1.0-0"),"% "),l(2),D("width",e.share,"%")}}function Ee(o,t){o&1&&(i(0,"p",18),a(1,"No model rows for the current Insights cohort."),n())}function Te(o,t){if(o&1&&(i(0,"small"),a(1),n(),i(2,"span",30),a(3,"Open run"),n()),o&2){let e=p().$implicit;l(),m("Top run: ",e.topSession.title)}}function Ae(o,t){if(o&1){let e=O();i(0,"button",29),v("click",function(){let r=b(e).$implicit,d=p(3);return f(d.emitOpenSession(r.topSession))}),i(1,"span"),a(2),n(),i(3,"strong"),a(4),c(5,"number"),n(),i(6,"em"),a(7),c(8,"number"),c(9,"number"),c(10,"number"),n(),u(11,Te,4,1),n()}if(o&2){let e=t.$implicit;g("disabled",!e.topSession),l(2),_(e.size),l(2),m("",C(5,7,e.count)," runs"),l(3),T(" ",y(8,9,e.credits,"1.0-1")," credits \xB7 $",y(9,12,e.cost,"1.2-4")," \xB7 ",y(10,15,e.share,"1.0-0"),"% of cohort "),l(4),x(e.topSession?11:-1)}}function Ie(o,t){if(o&1&&(i(0,"small"),a(1),n(),i(2,"span",30),a(3,"Open run"),n()),o&2){let e=p().$implicit;l(),m("Top run: ",e.topSession.title)}}function Fe(o,t){if(o&1){let e=O();i(0,"button",29),v("click",function(){let r=b(e).$implicit,d=p(3);return f(d.emitOpenSession(r.topSession))}),i(1,"span"),a(2),n(),i(3,"strong"),a(4),c(5,"number"),n(),i(6,"em"),a(7),c(8,"number"),c(9,"number"),c(10,"number"),n(),u(11,Ie,4,1),n()}if(o&2){let e=t.$implicit;g("disabled",!e.topSession),l(2),_(e.label),l(2),m("",C(5,7,e.count)," runs"),l(3),T(" ",y(8,9,e.credits,"1.0-1")," credits \xB7 $",y(9,12,e.cost,"1.2-4")," \xB7 ",C(10,15,e.tokens)," tokens "),l(4),x(e.topSession?11:-1)}}function Be(o,t){o&1&&(i(0,"p",18),a(1,"No trend rows for the current Insights cohort."),n())}function $e(o,t){if(o&1&&(i(0,"em"),a(1),n(),i(2,"span",30),a(3,"Open run"),n()),o&2){let e=p().$implicit;l(),_(e.session.title)}}function Re(o,t){if(o&1){let e=O();i(0,"button",29),v("click",function(){let r=b(e).$implicit,d=p(3);return f(d.emitOpenSession(r.session))}),i(1,"span",31),a(2),w(3,"app-help-popover",32),n(),i(4,"strong"),a(5),n(),u(6,$e,4,1),n()}if(o&2){let e=t.$implicit;g("disabled",!e.session),l(2),m(" ",e.label," "),l(),g("text",e.help)("label","Explain "+e.label)("interactive",!1),l(2),_(e.value),l(),x(e.session?6:-1)}}function Ve(o,t){if(o&1){let e=O();i(0,"button",33),v("click",function(){let r=b(e).$implicit,d=p(3);return f(d.emitOpenSession(r.session))}),i(1,"span"),a(2),c(3,"number"),c(4,"number"),c(5,"number"),n(),i(6,"strong"),a(7),n(),i(8,"em"),a(9),n(),i(10,"span",30),a(11,"Open run"),n()()}if(o&2){let e=t.$implicit,s=p(3);l(2),T(" ",C(3,5,e.tokens)," tokens \xB7 $",y(4,7,s.usageUsd(e.session),"1.2-4")," \xB7 ",y(5,10,s.usageCredits(e.session),"1.0-1")," credits "),l(5),_(e.session.title),l(2),_(e.reason)}}function ze(o,t){o&1&&(i(0,"p",18),a(1,"No outliers in the current Insights cohort."),n())}function Ge(o,t){if(o&1&&(i(0,"section",12)(1,"div",13)(2,"div",14)(3,"div")(4,"h3"),a(5,"Model breakdown"),n(),i(6,"p"),a(7,"Model mix and priced token shape for this cohort."),n()(),i(8,"span"),a(9),c(10,"number"),n()(),i(11,"div",15)(12,"div",16)(13,"span"),a(14,"Model"),n(),i(15,"span"),a(16,"Sessions"),n(),i(17,"span"),a(18,"Tokens"),n(),i(19,"span"),a(20,"Cost"),n(),i(21,"span"),a(22,"Cost / 1k"),n(),i(23,"span"),a(24,"Share"),n()(),M(25,ke,32,35,"div",17,me,!1,Ee,2,0,"p",18),n()(),i(28,"aside",19)(29,"div",20)(30,"div",21)(31,"h3"),a(32,"Run size mix"),n(),i(33,"p"),a(34,"How runs are distributed by size."),n()(),i(35,"div",22),M(36,Ae,12,18,"button",23,ge),n()(),i(38,"div",20)(39,"div",21)(40,"h3"),a(41,"Usage trend"),n(),i(42,"p"),a(43),n()(),i(44,"div",22),M(45,Fe,12,17,"button",23,_e,!1,Be,2,0,"p",18),n()()()(),i(48,"section",24)(49,"div",20)(50,"div",21)(51,"h3"),a(52,"Runs to inspect"),n(),i(53,"p"),a(54,"Open the largest runs in this cohort."),n()(),i(55,"div",25),M(56,Re,7,7,"button",23,ue),n()(),i(58,"div",20)(59,"div",21)(60,"h3"),a(61,"Outlier signals"),n(),i(62,"p"),a(63,"Runs that stand apart from this cohort."),n()(),i(64,"div",25),M(65,Ve,12,13,"button",26,xe,!1,ze,2,0,"p",18),n()()()),o&2){let e=p();l(9),m("",C(10,5,e.modelRows.length)," rows"),l(16),P(e.modelRows),l(11),P(e.distribution),l(7),m("",e.groupingLabel,"."),l(2),P(e.trendRows),l(11),P(e.highlights),l(9),P(e.outliers)}}function De(o,t){if(o&1){let e=O();i(0,"section",0)(1,"div",1)(2,"div")(3,"p",2),a(4,"Cohort insights"),n(),i(5,"h2"),a(6,"Run patterns"),n(),i(7,"p",3),a(8),u(9,Ce,1,1),w(10,"app-help-popover",4),n()(),u(11,ye,2,0,"button",5),n(),i(12,"section",6)(13,"label")(14,"span"),a(15," Time range "),n(),i(16,"select",7),v("ngModelChange",function(r){b(e);let d=p();return f(d.setAnalyticsTimeRange(r))}),M(17,he,2,2,"option",8,ie),n()(),i(19,"label")(20,"span"),a(21,"Workspace"),n(),i(22,"select",7),v("ngModelChange",function(r){b(e);let d=p();return f(d.setAnalyticsWorkspaceFilter(r))}),M(23,be,2,2,"option",8,R),n()(),i(25,"label")(26,"span"),a(27,"Model"),n(),i(28,"select",7),v("ngModelChange",function(r){b(e);let d=p();return f(d.setAnalyticsModelFilter(r))}),M(29,fe,2,2,"option",8,R),n()(),i(31,"label")(32,"span"),a(33," Group trend by "),w(34,"app-help-popover",9),n(),i(35,"select",7),v("ngModelChange",function(r){b(e);let d=p();return f(d.setAnalyticsGrouping(r))}),M(36,ve,2,2,"option",8,ie),n()()(),u(38,Pe,6,3,"section",10)(39,Ge,68,7),n()}if(o&2){let e=t,s=p();l(8),N(" ",e.count," of ",e.importedCount," imported sessions included. "),l(),x(e.analyticsExcludedCount>0?9:-1),l(),g("text",s.help.analyticsScope),l(),x(e.analyticsFiltersActive?11:-1),l(5),g("ngModel",s.analyticsTimeRange()),l(),P(s.analyticsTimeOptions),l(5),g("ngModel",s.analyticsWorkspaceFilter()),l(),P(s.analyticsWorkspaceOptions()),l(5),g("ngModel",s.analyticsModelFilter()),l(),P(s.analyticsModelOptions()),l(5),g("text",s.help.trendGrouping),l(),g("ngModel",s.analyticsGrouping()),l(),P(s.analyticsGroupingOptions),l(2),x(e.count===0?38:39)}}var oe=class o{sessionsInput=S([]);openSession=new z;set sessions(t){this.sessionsInput.set(t??[])}analyticsTimeRange=S("all");analyticsWorkspaceFilter=S("all");analyticsModelFilter=S("all");analyticsGrouping=S("day");help={analyticsScope:"Insights use imported sessions and the time, workspace, and model filters on this page.",trendGrouping:"Time range chooses which sessions are included. Grouping only changes how those sessions are bucketed in the trend panel."};sizeOptions=["Small","Medium","Large","Very large"];analyticsTimeOptions=[{value:"all",label:"All time"},{value:"current-month",label:"Current month"},{value:"previous-month",label:"Previous month"},{value:"7d",label:"Last 7 days"},{value:"30d",label:"Last 30 days"},{value:"90d",label:"Last 90 days"}];analyticsGroupingOptions=[{value:"day",label:"By day"},{value:"week",label:"By week"},{value:"month",label:"By month"}];analyticsWorkspaceOptions=k(()=>{let t=new Set(this.sessionsInput().map(s=>s.workspace).filter(Boolean)),e=this.analyticsWorkspaceFilter();return e!=="all"&&t.add(e),["all",...[...t].sort()]});analyticsModelOptions=k(()=>{let t=new Set;for(let s of this.sessionsInput())for(let r of s.modelBreakdown)t.add(r.pricingModel||r.model);let e=this.analyticsModelFilter();return e!=="all"&&t.add(e),["all",...[...t].sort()]});analyticsSessions=k(()=>Y(this.sessionsInput(),this.analyticsTimeRange(),this.analyticsWorkspaceFilter(),this.analyticsModelFilter()));analytics=k(()=>{let t=this.analyticsSessions(),e=t.length,s=this.sessionsInput().length,r=t.reduce((h,$)=>h+A($),0),d=t.reduce((h,$)=>h+E($),0),ae=e?r/e:0,le=e?d/e:0,I=V(t,h=>A(h)),F=V(t,h=>E(h)),se=Z(t,d),re=ee(t,this.analyticsGrouping()),ce=te(t,d),pe=ne(t,le,ae),de=this.analyticsTimeRange()!=="all"||this.analyticsWorkspaceFilter()!=="all"||this.analyticsModelFilter()!=="all"||this.analyticsGrouping()!=="day",B=Math.max(s-e,0);return{count:e,importedCount:s,analyticsFiltersActive:de,analyticsExcludedCount:B,emptyTitle:s===0?"No imported sessions":"No sessions in this Insights cohort",emptyDetail:s===0?"Import VS Code sessions to explore model mix, run sizes, trends, and outliers.":`${B.toLocaleString()} imported session${B===1?"":"s"} excluded by the Insights controls. Reset the filters to return to all imported sessions.`,timeRangeLabel:this.analyticsTimeOptions.find(h=>h.value===this.analyticsTimeRange())?.label??"All time",workspaceLabel:this.analyticsWorkspaceFilter()==="all"?"All workspaces":this.analyticsWorkspaceFilter(),modelLabel:this.analyticsModelFilter()==="all"?"All models":this.analyticsModelFilter(),groupingLabel:this.analyticsGroupingOptions.find(h=>h.value===this.analyticsGrouping())?.label??"By day",highlights:[{label:"Highest-token run",session:I,value:I?`${A(I).toLocaleString()} tokens`:"n/a",help:"The included session with the largest imported token total."},{label:"Most expensive run",session:F,value:F?`$${E(F).toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:4})}`:"n/a",help:"The included session with the highest GitHub source usage, falling back to token estimate when source usage is absent."}],modelRows:se,trendRows:re,distribution:ce,outliers:pe}});setAnalyticsTimeRange(t){this.analyticsTimeRange.set(t)}setAnalyticsWorkspaceFilter(t){this.analyticsWorkspaceFilter.set(t)}setAnalyticsModelFilter(t){this.analyticsModelFilter.set(t)}setAnalyticsGrouping(t){this.analyticsGrouping.set(t)}resetAnalyticsFilters(){this.analyticsTimeRange.set("all"),this.analyticsWorkspaceFilter.set("all"),this.analyticsModelFilter.set("all"),this.analyticsGrouping.set("day")}emitOpenSession(t){t&&this.openSession.emit(t)}aiCredits(t){return t/L}usageUsd(t){return E(t)}usageCredits(t){return U(t)}static \u0275fac=function(e){return new(e||o)};static \u0275cmp=G({type:o,selectors:[["app-analytics-page"]],inputs:{sessions:"sessions"},outputs:{openSession:"openSession"},decls:1,vars:1,consts:[[1,"pricing-page","analytics-page"],[1,"pricing-header"],[1,"eyebrow"],[1,"analytics-cohort"],["label","Explain analytics scope",3,"text"],["type","button",1,"ghost-button"],["aria-label","Analytics cohort controls",1,"analytics-controls"],[3,"ngModelChange","ngModel"],[3,"value"],["label","Explain trend grouping",3,"text"],[1,"analytics-empty"],["type","button",1,"ghost-button",3,"click"],[1,"analytics-primary"],[1,"analytics-panel","model-panel"],[1,"panel-heading"],["role","table","aria-label","Model breakdown",1,"cost-table","compact-table"],[1,"cost-row","analytics-model-row","cost-row-heading"],[1,"cost-row","analytics-model-row"],[1,"empty"],[1,"analytics-side"],[1,"analytics-panel"],[1,"panel-heading","compact-heading"],[1,"stat-list","compact"],["type","button",3,"disabled"],[1,"analytics-split"],[1,"highlight-list"],["type","button"],[1,"warning-text"],[1,"share-cell"],["type","button",3,"click","disabled"],[1,"open-cue"],[1,"row-kicker"],[3,"text","label","interactive"],["type","button",3,"click"]],template:function(e,s){if(e&1&&u(0,De,40,11,"section",0),e&2){let r;x((r=s.analytics())?0:-1,r)}},dependencies:[X,K,Q,J,j,q,H,W],styles:[".analytics-page[_ngcontent-%COMP%]{display:grid;gap:10px}.pricing-header[_ngcontent-%COMP%]{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.pricing-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%]{margin:3px 0 0;color:var(--text-strong);font-size:21px;font-weight:760;letter-spacing:0}.pricing-header[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;max-width:820px;margin:6px 0 0;color:var(--muted);font-size:12px;line-height:1.45}.analytics-cohort[_ngcontent-%COMP%]{opacity:.86}.ghost-button[_ngcontent-%COMP%]{border:1px solid var(--line);border-radius:9px;padding:7px 10px;color:var(--text);background:var(--surface-soft);font-size:13px;font-weight:720;cursor:pointer}.ghost-button[_ngcontent-%COMP%]:hover{border-color:var(--line-strong)}.analytics-controls[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,minmax(140px,1fr));gap:7px;border:1px solid var(--line);border-radius:12px;padding:8px;background:linear-gradient(135deg,#0c9f830e,#6f5cff09),var(--surface);box-shadow:var(--shadow-soft, 0 12px 30px rgba(15, 23, 42, .08))}.analytics-controls[_ngcontent-%COMP%] label[_ngcontent-%COMP%]{display:grid;gap:5px;min-width:0;color:var(--muted);font-size:10px;font-weight:720}.analytics-controls[_ngcontent-%COMP%] label[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:4px}.analytics-controls[_ngcontent-%COMP%] select[_ngcontent-%COMP%], .analytics-controls[_ngcontent-%COMP%] input[_ngcontent-%COMP%]{width:100%;min-height:30px;border:1px solid var(--line);border-radius:7px;padding:6px 9px;background:var(--surface-soft);color:var(--text);font:inherit;font-size:12px;font-weight:720}.analytics-empty[_ngcontent-%COMP%], .analytics-panel[_ngcontent-%COMP%]{border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:var(--shadow-soft, 0 12px 30px rgba(15, 23, 42, .08))}.analytics-empty[_ngcontent-%COMP%]{display:grid;gap:8px;justify-items:start;padding:18px}.analytics-empty[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{max-width:720px;margin:0;color:var(--muted);font-size:12px}.analytics-primary[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr;gap:10px;align-items:start}.analytics-side[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.analytics-split[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;align-items:start}.analytics-panel[_ngcontent-%COMP%]{display:grid;gap:8px;min-width:0;padding:11px}.analytics-panel[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], .analytics-empty[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{margin:0;color:var(--text-strong);font-size:15px;font-weight:760}.panel-heading[_ngcontent-%COMP%]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.compact-heading[_ngcontent-%COMP%]{align-items:baseline}.panel-heading[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{margin:3px 0 0;color:var(--muted);font-size:12px}.compact-heading[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{margin:0;text-align:right}.panel-note[_ngcontent-%COMP%]{margin:-3px 0 0;color:var(--muted);font-size:12px}.panel-heading[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]{flex:0 0 auto;border:1px solid var(--line);border-radius:999px;padding:3px 7px;color:var(--muted);background:var(--surface-soft);font-size:11px;font-weight:720}.highlight-list[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%]{display:grid;gap:6px}.highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{display:grid;gap:5px;width:100%;min-width:0;border:1px solid var(--line);border-radius:9px;padding:8px 9px;background:var(--surface-soft);color:var(--text);text-align:left}.highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]{cursor:pointer}.highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:hover, .stat-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:hover{border-color:var(--line-strong)}.highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:disabled, .stat-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:disabled{cursor:default;opacity:.65}.highlight-list[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .highlight-list[_ngcontent-%COMP%] em[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] small[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] em[_ngcontent-%COMP%], .cost-row[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .cost-row[_ngcontent-%COMP%] em[_ngcontent-%COMP%], .empty[_ngcontent-%COMP%]{color:var(--muted);font-size:12px;font-style:normal;line-height:1.35}.stat-list[_ngcontent-%COMP%] small[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row-kicker[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:5px}.highlight-list[_ngcontent-%COMP%] strong[_ngcontent-%COMP%], .stat-list[_ngcontent-%COMP%] strong[_ngcontent-%COMP%], .cost-row[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-strong);font-size:14px;font-weight:760;line-height:1.25}.open-cue[_ngcontent-%COMP%]{justify-self:start;border:1px solid var(--line);border-radius:999px;padding:3px 7px;background:#0c9f8314;color:var(--accent-strong, var(--accent))!important;font-size:10px!important;font-weight:760;text-transform:uppercase;letter-spacing:.04em}.highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:hover .open-cue[_ngcontent-%COMP%], .highlight-list[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:focus-visible .open-cue[_ngcontent-%COMP%]{border-color:color-mix(in srgb,var(--accent) 45%,var(--line));background:#0c9f8324}.stat-list.compact[_ngcontent-%COMP%] div[_ngcontent-%COMP%], .stat-list.compact[_ngcontent-%COMP%] button[_ngcontent-%COMP%]{grid-template-columns:minmax(110px,.35fr) minmax(0,1fr) auto;gap:4px 10px;align-items:center;padding:8px 9px}.stat-list.compact[_ngcontent-%COMP%] small[_ngcontent-%COMP%], .stat-list.compact[_ngcontent-%COMP%] .open-cue[_ngcontent-%COMP%]{grid-column:2 / -1}.cost-table[_ngcontent-%COMP%]{overflow-x:auto;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.cost-row[_ngcontent-%COMP%]{display:grid;align-items:start;gap:8px;min-width:650px;border-bottom:1px solid var(--line);padding:8px 10px}.cost-row[_ngcontent-%COMP%]:last-child{border-bottom:0}.cost-row-heading[_ngcontent-%COMP%]{color:var(--muted);background:var(--surface-soft);font-size:11px;font-weight:760;letter-spacing:.04em;text-transform:uppercase}.analytics-model-row[_ngcontent-%COMP%]{grid-template-columns:minmax(170px,1.25fr) minmax(62px,.36fr) minmax(180px,1fr) minmax(86px,.48fr) minmax(84px,.46fr) minmax(80px,.45fr)}.analytics-model-row[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{display:grid;gap:3px}.analytics-model-row[_ngcontent-%COMP%] em[_ngcontent-%COMP%]{display:block;margin-top:2px}.warning-text[_ngcontent-%COMP%]{color:var(--warning-text, #ffcf91)!important}.share-cell[_ngcontent-%COMP%]{display:grid;gap:5px}.share-cell[_ngcontent-%COMP%] i[_ngcontent-%COMP%]{display:block;width:0;max-width:100%;height:4px;border-radius:999px;background:linear-gradient(90deg,var(--accent),var(--accent-2))}@media(max-width:1200px){.analytics-side[_ngcontent-%COMP%], .analytics-split[_ngcontent-%COMP%]{grid-template-columns:1fr}}@media(max-width:960px){.analytics-primary[_ngcontent-%COMP%]{grid-template-columns:1fr}}@media(max-width:900px){.pricing-header[_ngcontent-%COMP%], .analytics-controls[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr}}@media(max-width:600px){.analytics-panel[_ngcontent-%COMP%]{padding:11px}.cost-row[_ngcontent-%COMP%]{min-width:620px}}"]})};export{oe as AnalyticsPageComponent};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{$a as _,Ca as f,Da as w,Db as Q,Fa as M,Ga as v,Ha as c,Ia as t,Ib as X,Ja as n,Jb as Y,Ka as x,Na as F,O as U,P as D,Pa as E,Pb as Z,Ra as C,Rb as ee,Sa as L,Sb as te,Ta as z,Tb as ne,U as N,Ua as i,Ub as ie,Va as s,Vb as ae,Wa as d,Wb as oe,Xa as T,Z as S,Za as p,_a as O,a as b,b as k,cb as P,ja as A,ka as o,mb as G,nb as B,rb as H,sa as R,sb as $,tb as W,ub as V,vb as j,wb as q,xb as J,yb as K,zb as y}from"./chunk-F6TIG2GE.js";var re=(l,a)=>a.id,ce=(l,a)=>a.value,se=(l,a)=>a.model+":"+a.tier;function pe(l,a){if(l&1&&(t(0,"option",10),i(1),n()),l&2){let e=a.$implicit;c("value",e.id),o(),s(e.label)}}function de(l,a){if(l&1&&(t(0,"option",10),i(1),n()),l&2){let e=a.$implicit;c("value",e.value),o(),s(e.label)}}function ge(l,a){if(l&1){let e=F();t(0,"button",24),E("click",function(){let g=U(e).$implicit,m=C();return D(m.setSelectedAllowancePlan(g.id))}),t(1,"span"),i(2),n(),t(3,"strong"),i(4),p(5,"number"),n(),t(6,"em"),i(7),n()()}if(l&2){let e=a.$implicit,r=C();z("active",r.selectedAllowance().id===e.id),o(2),s(e.period),o(2),s(O(5,5,e.creditsPerUserMonthly)),o(3),d("",e.label," credits/user/month")}}function me(l,a){if(l&1&&(t(0,"em"),i(1),n()),l&2){let e=C().$implicit;o(),s(e.note)}}function ue(l,a){if(l&1&&(i(0),p(1,"number")),l&2){let e=C().$implicit;d(" $",_(1,1,e.cacheWrite,"1.0-3")," ")}}function he(l,a){l&1&&i(0," - ")}function xe(l,a){if(l&1&&(t(0,"span",26),i(1," Direct + fallback "),x(2,"app-help-popover",28),n()),l&2){let e=C(3);o(2),c("text",e.help.pricingFallback)}}function _e(l,a){if(l&1&&(t(0,"span",26),i(1," Fallback row "),x(2,"app-help-popover",28),n()),l&2){let e=C(3);o(2),c("text",e.help.pricingFallback)}}function Ce(l,a){l&1&&(t(0,"span",27),i(1,"Direct match"),n())}function Pe(l,a){if(l&1&&f(0,xe,3,1,"span",26)(1,_e,3,1,"span",26)(2,Ce,2,0,"span",27),l&2){let e=C().$implicit;w(e.usedDirectly&&e.usedAsFallback?0:e.usedAsFallback?1:2)}}function be(l,a){l&1&&(t(0,"span",25),i(1,"Not yet"),n())}function fe(l,a){if(l&1&&(t(0,"div",23)(1,"strong"),i(2),f(3,me,2,1,"em"),n(),t(4,"span"),i(5),n(),t(6,"span"),i(7),n(),t(8,"span"),i(9),n(),t(10,"span"),i(11),t(12,"em"),i(13),n()(),t(14,"span"),i(15),p(16,"number"),n(),t(17,"span"),i(18),p(19,"number"),n(),t(20,"span"),f(21,ue,2,4)(22,he,1,0),n(),t(23,"span"),i(24),p(25,"number"),n(),t(26,"span"),f(27,Pe,3,1)(28,be,2,0,"span",25),n()()),l&2){let e=a.$implicit;o(2),d(" ",e.model," "),o(),w(e.note?3:-1),o(2),s(e.provider),o(2),s(e.releaseStatus),o(2),s(e.category),o(2),d(" ",e.tier," "),o(2),s(e.threshold),o(2),d("$",_(16,12,e.input,"1.0-3")),o(3),d("$",_(19,15,e.cachedInput,"1.0-3")),o(3),w(e.cacheWrite?21:22),o(3),d("$",_(25,18,e.output,"1.0-3")),o(3),w(e.usedByImportedSessions?27:28)}}var le=class l{sessionsInput=S([]);selectedAllowancePlanInput=S("business-standard");usageTimeRange=S("all");set sessions(a){this.sessionsInput.set(a??[])}set selectedAllowancePlan(a){this.selectedAllowancePlanInput.set(a??"business-standard")}selectedAllowancePlanChange=new N;pricingVersion=H;pricingSourceLabel=W;pricingSourceUrl=$;pricingSnapshotDate=V;pricingImportedAt=j;allowanceSourceUrl=J;creditUsd=q;allowancePlans=y;usageTimeOptions=[{value:"all",label:"All imported sessions"},{value:"7d",label:"Last 7 days"},{value:"30d",label:"Last 30 days"},{value:"90d",label:"Last 90 days"}];help={inputTokens:"Normal, non-cached input/context tokens priced at the GitHub input rate. Raw VS Code inputTokens can be higher when cachedTokens are present.",outputTokens:"Generated model response tokens.",cachedInput:"Input/context tokens VS Code reported as cachedTokens. They are part of raw input, but priced with GitHub cached-input rates instead of normal input rates.",cacheWrite:"Provider cache creation tokens when the billing source exposes them. GitHub lists this mainly for Anthropic pricing rows.",pricingFallback:"The raw model name from VS Code did not match a GitHub price row in the local pricing table, so the estimate uses the displayed fallback price row. Treat this as an explicit estimate assumption.",allowance:"Included AI credits for Copilot Business and Enterprise are monthly per assigned license, but GitHub pools them at the organization or enterprise billing entity level.",credit:"GitHub states that 1 AI credit equals $0.01 USD. When VS Code logs GitHub source usage, the app uses that first; otherwise it converts the local token estimate into credits.",usageWindow:"This only filters the imported sessions used by the allowance meter. It does not change the GitHub price table below."};selectedAllowance=P(()=>y.find(a=>a.id===this.selectedAllowancePlanInput())??y[0]);allowanceSessions=P(()=>{let a=this.usageCutoff(this.sessionsInput(),this.usageTimeRange());return this.sessionsInput().filter(e=>!a||new Date(e.startedAt).getTime()>=a)});totalEstimateUsd=P(()=>this.allowanceSessions().reduce((a,e)=>a+X(e),0));totalEstimateCredits=P(()=>this.allowanceSessions().reduce((a,e)=>a+Y(e),0));selectedAllowanceUsage=P(()=>{let a=this.selectedAllowance(),e=this.totalEstimateCredits(),r=a.creditsPerUserMonthly>0?e/a.creditsPerUserMonthly*100:0;return{credits:e,share:r,perUserCredits:a.creditsPerUserMonthly,sessions:this.allowanceSessions().length,totalSessions:this.sessionsInput().length,windowLabel:this.usageTimeOptions.find(g=>g.value===this.usageTimeRange())?.label??"All imported sessions"}});pricingRows=P(()=>Object.entries(K).flatMap(([a,e])=>{let r=u=>{let I=this.sessionsInput().flatMap(h=>h.modelBreakdown).filter(h=>h.pricingModel===a&&(h.pricingTiers?.includes(u)??u===(e.tierLabel??"Default")));return{usedByImportedSessions:I.length>0,usedDirectly:I.some(h=>!this.usesPricingFallback(h.model,h.pricingModel)),usedAsFallback:I.some(h=>this.usesPricingFallback(h.model,h.pricingModel))}},g={model:a,provider:e.provider,releaseStatus:e.releaseStatus,category:e.category,note:e.note},m=e.tierLabel??"Default";return[k(b(b({},g),r(m)),{tier:m,threshold:e.tierThresholdLabel??"All request sizes",input:e.input,cachedInput:e.cachedInput,cacheWrite:e.cacheWrite??0,output:e.output}),...(e.tiers??[]).map(u=>k(b(b({},g),r(u.label)),{tier:u.label,threshold:u.thresholdLabel,input:u.input,cachedInput:u.cachedInput,cacheWrite:u.cacheWrite??e.cacheWrite??0,output:u.output}))]}));setSelectedAllowancePlan(a){if(!y.some(r=>r.id===a))return;let e=a;this.selectedAllowancePlanInput.set(e),this.selectedAllowancePlanChange.emit(e)}setUsageTimeRange(a){this.usageTimeOptions.some(e=>e.value===a)&&this.usageTimeRange.set(a)}usesPricingFallback=Q;usageCutoff(a,e){if(e==="all"||!a.length)return null;let r=e==="7d"?7:e==="30d"?30:90,g=Math.max(...a.map(m=>new Date(m.startedAt).getTime()).filter(Number.isFinite));return Number.isFinite(g)?g-r*24*60*60*1e3:null}static \u0275fac=function(e){return new(e||l)};static \u0275cmp=R({type:l,selectors:[["app-pricing-page"]],inputs:{sessions:"sessions",selectedAllowancePlan:"selectedAllowancePlan"},outputs:{selectedAllowancePlanChange:"selectedAllowancePlanChange"},decls:129,vars:46,consts:[[1,"pricing-page"],[1,"pricing-header"],[1,"eyebrow"],["target","_blank","rel","noreferrer",3,"href"],[1,"pricing-source"],["aria-label","GitHub Copilot AI credit allowance",1,"allowance-panel"],[1,"allowance-copy"],[1,"allowance-control"],["label","Explain AI credit allowance",3,"text"],[3,"ngModelChange","ngModel"],[3,"value"],["label","Explain usage window",3,"text"],[1,"allowance-meter"],["aria-hidden","true",1,"allowance-bar"],["aria-label","Available Copilot allowance plans",1,"allowance-grid"],["type","button",1,"allowance-card",3,"active"],[1,"pricing-note"],[1,"pricing-table"],[1,"pricing-row","pricing-row-heading"],["label","Explain normal input",3,"text"],["label","Explain cached input",3,"text"],["label","Explain cache write",3,"text"],["label","Explain output tokens",3,"text"],[1,"pricing-row"],["type","button",1,"allowance-card",3,"click"],[1,"muted"],[1,"used-pill","fallback-used"],[1,"used-pill"],["label","Explain fallback pricing",3,"text"]],template:function(e,r){e&1&&(t(0,"section",0)(1,"div",1)(2,"div")(3,"p",2),i(4,"Cost inputs"),n(),t(5,"h2"),i(6,"GitHub Copilot prices used by this app"),n(),t(7,"p"),i(8," Evidence page for the rate card, AI-credit conversion, source usage, and token-estimate fallback. "),n()(),t(9,"a",3),i(10,"Open GitHub source"),n()(),t(11,"section",4)(12,"div")(13,"span"),i(14,"Source"),n(),t(15,"strong"),i(16),n()(),t(17,"div")(18,"span"),i(19,"Pricing version"),n(),t(20,"strong"),i(21),n()(),t(22,"div")(23,"span"),i(24,"Source checked"),n(),t(25,"strong"),i(26),p(27,"date"),n()(),t(28,"div")(29,"span"),i(30,"Imported into app"),n(),t(31,"strong"),i(32),p(33,"date"),n()()(),t(34,"section",5)(35,"div",6)(36,"p",2),i(37,"License allowance"),n(),t(38,"h3"),i(39,"AI credits context"),n(),t(40,"p"),i(41," GitHub converts model usage into AI credits at "),t(42,"strong"),i(43),p(44,"number"),n(),i(45,". The meter below compares imported source usage, with token estimates as fallback, against the selected per-license allowance. "),n(),t(46,"a",3),i(47,"Open allowance source"),n()(),t(48,"div",7)(49,"label")(50,"span"),i(51," Selected plan "),x(52,"app-help-popover",8),n(),t(53,"select",9),E("ngModelChange",function(m){return r.setSelectedAllowancePlan(m)}),M(54,pe,2,2,"option",10,re),n()(),t(56,"label")(57,"span"),i(58," Usage window "),x(59,"app-help-popover",11),n(),t(60,"select",9),E("ngModelChange",function(m){return r.setUsageTimeRange(m)}),M(61,de,2,2,"option",10,ce),n()(),t(63,"div",12)(64,"div")(65,"span"),i(66),n(),t(67,"strong"),i(68),p(69,"number"),n(),t(70,"em"),i(71),p(72,"number"),p(73,"number"),n()(),t(74,"div")(75,"span"),i(76,"Monthly allowance"),n(),t(77,"strong"),i(78),p(79,"number"),n()(),t(80,"div")(81,"span"),i(82,"Allowance used"),n(),t(83,"strong"),i(84),p(85,"number"),n()()(),t(86,"div",13),x(87,"span"),n(),t(88,"p"),i(89),n()()(),t(90,"section",14),M(91,ge,8,7,"button",15,re),n(),t(93,"div",16)(94,"strong"),i(95,"Calculation rule"),n(),t(96,"p"),i(97," Per-1M-token GitHub rates are multiplied by imported VS Code token buckets. Normal input is "),t(98,"code"),i(99,"inputTokens - cachedTokens"),n(),i(100," when cached tokens are present. Estimates stay in USD because GitHub prices and AI credits are USD-native; cache write is only priced when a numeric cache-write field is imported. Long-context rates are selected independently for each model call from that call's normal plus cached input. "),n()(),t(101,"div",17)(102,"div",18)(103,"span"),i(104,"Model"),n(),t(105,"span"),i(106,"Provider"),n(),t(107,"span"),i(108,"Status"),n(),t(109,"span"),i(110,"Category"),n(),t(111,"span"),i(112,"Tier"),n(),t(113,"span"),i(114,"Normal input "),x(115,"app-help-popover",19),n(),t(116,"span"),i(117,"Cached input "),x(118,"app-help-popover",20),n(),t(119,"span"),i(120,"Cache write "),x(121,"app-help-popover",21),n(),t(122,"span"),i(123,"Output "),x(124,"app-help-popover",22),n(),t(125,"span"),i(126,"Used"),n()(),M(127,fe,29,21,"div",23,se),n()()),e&2&&(o(9),c("href",r.pricingSourceUrl,A),o(7),s(r.pricingSourceLabel),o(5),s(r.pricingVersion),o(5),s(_(27,25,r.pricingSnapshotDate,"MMM d, y")),o(6),s(_(33,28,r.pricingImportedAt,"MMM d, y")),o(11),d("1 credit = $",_(44,31,r.creditUsd,"1.2-2")," USD"),o(3),c("href",r.allowanceSourceUrl,A),o(6),c("text",r.help.allowance),o(),c("ngModel",r.selectedAllowance().id),o(),v(r.allowancePlans),o(5),c("text",r.help.usageWindow),o(),c("ngModel",r.usageTimeRange()),o(),v(r.usageTimeOptions),o(5),s(r.selectedAllowanceUsage().windowLabel),o(2),d("",_(69,34,r.selectedAllowanceUsage().credits,"1.0-1")," credits"),o(3),T("",O(72,37,r.selectedAllowanceUsage().sessions)," of ",O(73,39,r.selectedAllowanceUsage().totalSessions)," sessions"),o(7),d("",O(79,41,r.selectedAllowance().creditsPerUserMonthly)," credits/user"),o(6),d("",_(85,43,r.selectedAllowanceUsage().share,"1.0-1"),"%"),o(3),L("width",r.selectedAllowanceUsage().share>100?100:r.selectedAllowanceUsage().share,"%"),o(2),T("",r.selectedAllowance().period,". ",r.selectedAllowance().note),o(2),v(r.allowancePlans),o(24),c("text",r.help.inputTokens),o(3),c("text",r.help.cachedInput),o(3),c("text",r.help.cacheWrite),o(3),c("text",r.help.outputTokens),o(3),v(r.pricingRows()))},dependencies:[oe,ie,ae,ne,ee,te,Z,G,B],styles:["[_nghost-%COMP%]{display:block}.pricing-page[_ngcontent-%COMP%]{display:grid;gap:16px}.pricing-header[_ngcontent-%COMP%]{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.pricing-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%]{margin:0;color:var(--text-strong);font-size:22px;font-weight:760;letter-spacing:0}.pricing-header[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{margin:0}.pricing-header[_ngcontent-%COMP%] p[_ngcontent-%COMP%]:not(.eyebrow){max-width:720px;margin-top:6px;color:var(--muted);font-size:12px;line-height:1.45}.pricing-header[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{border:1px solid var(--line-strong);border-radius:999px;padding:7px 10px;color:var(--text);font-size:12px;font-weight:720;text-decoration:none;white-space:nowrap}.pricing-source[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));overflow:hidden;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:var(--shadow, 0 18px 50px rgba(0, 0, 0, .22))}.pricing-source[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{padding:12px;border-right:1px solid var(--line)}.pricing-source[_ngcontent-%COMP%] div[_ngcontent-%COMP%]:last-child{border-right:0}.pricing-source[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .pricing-row[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .pricing-note[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{color:var(--muted);font-size:12px}.pricing-source[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{display:block;margin-top:5px;color:var(--text);font-weight:760}.pricing-note[_ngcontent-%COMP%]{border:1px solid var(--line);border-left:4px solid var(--accent-2);border-radius:12px;padding:12px 14px;background:var(--surface-soft)}.pricing-note[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-strong)}.pricing-note[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{max-width:940px;margin:4px 0 0;line-height:1.45}.allowance-panel[_ngcontent-%COMP%]{display:grid;grid-template-columns:minmax(260px,.85fr) minmax(320px,1.15fr);gap:14px;border:1px solid var(--line);border-radius:14px;padding:14px;background:var(--header-gradient, linear-gradient(135deg, rgba(124, 92, 255, .16), rgba(31, 209, 165, .07)), var(--surface))}.allowance-copy[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{margin:0;color:var(--text-strong);font-size:18px;font-weight:760}.allowance-copy[_ngcontent-%COMP%] p[_ngcontent-%COMP%], .allowance-control[_ngcontent-%COMP%] p[_ngcontent-%COMP%], .allowance-card[_ngcontent-%COMP%] em[_ngcontent-%COMP%], .allowance-card[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{color:var(--muted);font-size:12px;line-height:1.45}.allowance-copy[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{display:inline-flex;margin-top:8px;color:var(--accent-2);font-size:12px;font-weight:760;text-decoration:none}.allowance-control[_ngcontent-%COMP%]{display:grid;align-content:start;gap:12px}.allowance-control[_ngcontent-%COMP%] label[_ngcontent-%COMP%]{display:grid;gap:6px}.allowance-control[_ngcontent-%COMP%] label[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:12px;font-weight:760}.allowance-control[_ngcontent-%COMP%] select[_ngcontent-%COMP%]{width:100%;border:1px solid var(--line-strong);border-radius:10px;padding:8px 10px;background:var(--control-bg, rgba(5, 12, 22, .55));color:var(--text);font:inherit;font-weight:720}.allowance-meter[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.allowance-meter[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{border:1px solid var(--line);border-radius:10px;padding:10px;background:var(--surface-soft)}.allowance-meter[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{display:block;color:var(--muted);font-size:11px}.allowance-meter[_ngcontent-%COMP%] em[_ngcontent-%COMP%]{display:block;margin-top:3px;color:var(--muted);font-size:11px;font-style:normal}.allowance-meter[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{display:block;margin-top:3px;color:var(--text-strong);font-size:16px;font-weight:760}.allowance-bar[_ngcontent-%COMP%]{height:7px;overflow:hidden;border-radius:999px;background:#6170852e}.allowance-bar[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--accent-2),#ffba4a)}.allowance-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.allowance-card[_ngcontent-%COMP%]{display:grid;gap:3px;border:1px solid var(--line);border-radius:12px;padding:10px;background:var(--surface);color:inherit;font:inherit;text-align:left;cursor:pointer}.allowance-card.active[_ngcontent-%COMP%]{border-color:#1fd1a570;background:#1fd1a517;box-shadow:0 0 0 1px #1fd1a51f inset}.allowance-card[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-strong);font-size:20px;font-weight:760}.pricing-table[_ngcontent-%COMP%]{overflow-x:auto;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.pricing-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:minmax(210px,1.35fr) repeat(3,minmax(100px,.65fr)) minmax(150px,.9fr) repeat(4,minmax(100px,.58fr)) minmax(124px,.7fr);min-width:1240px;border-bottom:1px solid var(--line)}.pricing-row[_ngcontent-%COMP%]:last-child{border-bottom:0}.pricing-row[_ngcontent-%COMP%] > *[_ngcontent-%COMP%]{padding:10px 12px}.pricing-row-heading[_ngcontent-%COMP%]{background:var(--surface-soft);color:var(--muted);font-size:11px;font-weight:720;text-transform:uppercase}.pricing-row[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text);font-size:14px;font-weight:760}.pricing-row[_ngcontent-%COMP%] em[_ngcontent-%COMP%]{display:block;margin-top:4px;color:var(--text);font-size:12px;font-style:normal;font-weight:600}.used-pill[_ngcontent-%COMP%]{display:inline-flex;border:1px solid rgba(31,209,165,.35);border-radius:999px;padding:4px 8px;background:#1fd1a51f;color:var(--success-text, #bbfff0)!important;font-size:12px;font-weight:900;white-space:nowrap}.used-pill.fallback-used[_ngcontent-%COMP%]{border-color:#ffba4a73;background:#ffba4a1f;color:var(--warning-text, #ffe0a0)!important}.muted[_ngcontent-%COMP%]{color:var(--muted)}.eyebrow[_ngcontent-%COMP%]{color:var(--accent-2);font-size:10px;font-weight:760;letter-spacing:.08em;text-transform:uppercase}@media(max-width:900px){.pricing-header[_ngcontent-%COMP%], .pricing-source[_ngcontent-%COMP%]{display:grid}.pricing-source[_ngcontent-%COMP%], .allowance-panel[_ngcontent-%COMP%], .allowance-grid[_ngcontent-%COMP%], .allowance-meter[_ngcontent-%COMP%]{grid-template-columns:1fr}.pricing-source[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{border-right:0;border-bottom:1px solid var(--line)}.pricing-source[_ngcontent-%COMP%] div[_ngcontent-%COMP%]:last-child{border-bottom:0}}.used-pill[_ngcontent-%COMP%]{padding:3px 7px;font-size:11px;font-weight:760}@media(max-width:1100px){.allowance-grid[_ngcontent-%COMP%]{grid-template-columns:repeat(2,minmax(0,1fr))}}.theme-light[_nghost-%COMP%] .pricing-source[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-source[_ngcontent-%COMP%], .theme-light[_nghost-%COMP%] .pricing-table[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-table[_ngcontent-%COMP%], .theme-light[_nghost-%COMP%] .pricing-note[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-note[_ngcontent-%COMP%], .theme-light[_nghost-%COMP%] .allowance-panel[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .allowance-panel[_ngcontent-%COMP%]{box-shadow:var(--shadow-soft)}.theme-light[_nghost-%COMP%] .pricing-source[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-source[_ngcontent-%COMP%]{background:linear-gradient(180deg,#ffffffc2,#ffffff75),var(--surface)}.theme-light[_nghost-%COMP%] .pricing-note[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-note[_ngcontent-%COMP%]{background:linear-gradient(135deg,#0c9f830b,#6f5cff05),var(--surface)}.theme-light[_nghost-%COMP%] .allowance-panel[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .allowance-panel[_ngcontent-%COMP%]{border-color:#bdcbdae6}.theme-light[_nghost-%COMP%] .allowance-card[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .allowance-card[_ngcontent-%COMP%], .theme-light[_nghost-%COMP%] .allowance-meter[_ngcontent-%COMP%] div[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .allowance-meter[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{background:#ffffffad}.theme-light[_nghost-%COMP%] .pricing-table[_ngcontent-%COMP%], .theme-light [_nghost-%COMP%] .pricing-table[_ngcontent-%COMP%]{background:var(--surface)}"]})};export{le as PricingPageComponent};
|