@specship/specship-win32-arm64 0.11.7 → 0.11.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/lib/dist/bin/specship.js +139 -13
  2. package/lib/dist/bin/specship.js.map +1 -1
  3. package/lib/dist/commands/{ss-check.md → specship/check.md} +5 -0
  4. package/lib/dist/commands/specship/spec.md +338 -0
  5. package/lib/dist/db/migrations.d.ts.map +1 -1
  6. package/lib/dist/db/migrations.js +1 -0
  7. package/lib/dist/db/migrations.js.map +1 -1
  8. package/lib/dist/enforce/enforce.d.ts +15 -0
  9. package/lib/dist/enforce/enforce.d.ts.map +1 -1
  10. package/lib/dist/enforce/enforce.js +43 -1
  11. package/lib/dist/enforce/enforce.js.map +1 -1
  12. package/lib/dist/extraction/index.d.ts +6 -0
  13. package/lib/dist/extraction/index.d.ts.map +1 -1
  14. package/lib/dist/extraction/index.js.map +1 -1
  15. package/lib/dist/index.d.ts +10 -2
  16. package/lib/dist/index.d.ts.map +1 -1
  17. package/lib/dist/index.js +54 -10
  18. package/lib/dist/index.js.map +1 -1
  19. package/lib/dist/installer/index.d.ts.map +1 -1
  20. package/lib/dist/installer/index.js +35 -0
  21. package/lib/dist/installer/index.js.map +1 -1
  22. package/lib/dist/installer/instructions-template.d.ts +2 -2
  23. package/lib/dist/installer/instructions-template.js +5 -5
  24. package/lib/dist/installer/instructions-template.js.map +1 -1
  25. package/lib/dist/installer/targets/claude.d.ts +16 -4
  26. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  27. package/lib/dist/installer/targets/claude.js +99 -21
  28. package/lib/dist/installer/targets/claude.js.map +1 -1
  29. package/lib/dist/mcp/designer-tools.js +1 -1
  30. package/lib/dist/mcp/designer-tools.js.map +1 -1
  31. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  32. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  33. package/lib/dist/mcp/server-instructions.js +1 -1
  34. package/lib/dist/mcp/spec-tools.d.ts +10 -0
  35. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  36. package/lib/dist/mcp/spec-tools.js +231 -5
  37. package/lib/dist/mcp/spec-tools.js.map +1 -1
  38. package/lib/dist/mcp/tools.d.ts +12 -0
  39. package/lib/dist/mcp/tools.d.ts.map +1 -1
  40. package/lib/dist/mcp/tools.js +34 -1
  41. package/lib/dist/mcp/tools.js.map +1 -1
  42. package/lib/dist/resolution/brief-link-resolver.d.ts +20 -0
  43. package/lib/dist/resolution/brief-link-resolver.d.ts.map +1 -1
  44. package/lib/dist/resolution/brief-link-resolver.js +65 -1
  45. package/lib/dist/resolution/brief-link-resolver.js.map +1 -1
  46. package/lib/dist/resolution/domain-gap-seed.d.ts +1 -1
  47. package/lib/dist/resolution/domain-gap-seed.js +1 -1
  48. package/lib/dist/resolution/spec-link-resolver.d.ts +16 -2
  49. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  50. package/lib/dist/resolution/spec-link-resolver.js +19 -1
  51. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  52. package/lib/dist/server/chat/classify.js +28 -8
  53. package/lib/dist/server/ingest/ingestor.js +17 -17
  54. package/lib/dist/server/ingest/pricing-backfill.js +55 -0
  55. package/lib/dist/server/ingest/pricing.js +3 -0
  56. package/lib/dist/server/ingest/project-paths.js +152 -0
  57. package/lib/dist/server/project-registry.js +15 -2
  58. package/lib/dist/server/routes/chat-answer.js +11 -0
  59. package/lib/dist/server/routes/claude.js +34 -13
  60. package/lib/dist/server/routes/events.js +20 -1
  61. package/lib/dist/server/routes/projects.js +6 -3
  62. package/lib/dist/server/routes/spec.js +22 -2
  63. package/lib/dist/server/server.js +21 -2
  64. package/lib/dist/web/chunk-AHI5XEVX.js +1 -0
  65. package/lib/dist/web/chunk-AS2GV63A.js +1 -0
  66. package/lib/dist/web/chunk-CGVC666W.js +1 -0
  67. package/lib/dist/web/chunk-KYSTCHAH.js +1 -0
  68. package/lib/dist/web/{chunk-E27QAKAB.js → chunk-M42QOYAE.js} +1 -1
  69. package/lib/dist/web/chunk-ML5ULGHY.js +1 -0
  70. package/lib/dist/web/{chunk-HPKNOV45.js → chunk-OTQR5VFM.js} +1 -1
  71. package/lib/dist/web/chunk-PS6HRYQQ.js +3 -0
  72. package/lib/dist/web/chunk-S5J5WQXB.js +6 -0
  73. package/lib/dist/web/chunk-W5TEIONI.js +2 -0
  74. package/lib/dist/web/chunk-XKG5X37F.js +1 -0
  75. package/lib/dist/web/index.html +2 -2
  76. package/lib/dist/web/main-JT2RR3JS.js +1 -0
  77. package/lib/dist/web/styles-ATJUD7TT.css +1 -0
  78. package/lib/dist/workflows/defaults/claude-design-implement.yaml +6 -6
  79. package/lib/dist/workflows/defaults/spec-author.yaml +1 -1
  80. package/lib/dist/workflows/defaults/spec-implement.yaml +41 -9
  81. package/lib/node_modules/.package-lock.json +1 -1
  82. package/lib/package.json +1 -1
  83. package/package.json +1 -1
  84. package/lib/dist/commands/ss-behaviour.md +0 -116
  85. package/lib/dist/commands/ss-design-implement.md +0 -84
  86. package/lib/dist/commands/ss-design-loop.md +0 -125
  87. package/lib/dist/commands/ss-spec.md +0 -118
  88. package/lib/dist/web/chunk-7MUNFW56.js +0 -1
  89. package/lib/dist/web/chunk-CYXQILZI.js +0 -1
  90. package/lib/dist/web/chunk-F5TBUYDH.js +0 -1
  91. package/lib/dist/web/chunk-JPSYO24K.js +0 -1
  92. package/lib/dist/web/chunk-LIFS4XH2.js +0 -6
  93. package/lib/dist/web/chunk-MADMRWIK.js +0 -3
  94. package/lib/dist/web/chunk-N5WSFE4J.js +0 -2
  95. package/lib/dist/web/chunk-UYOKO7C5.js +0 -1
  96. package/lib/dist/web/chunk-YNYVOPWF.js +0 -1
  97. package/lib/dist/web/chunk-YXYPTZ6E.js +0 -1
  98. package/lib/dist/web/main-5B4RR3BC.js +0 -1
  99. package/lib/dist/web/styles-KSOPUVDA.css +0 -1
  100. /package/lib/dist/commands/{ss-explore.md → specship/explore.md} +0 -0
@@ -14,16 +14,19 @@
14
14
  * "primary" instance instead, since their data (JSONL ingest, CLAUDE.md
15
15
  * hierarchy) is global to the user, not scoped to one project.
16
16
  */
17
- import { decodeProjectSlug } from './ingest/ingestor.js';
17
+ import { createSlugResolver } from './ingest/project-paths.js';
18
18
  export class ProjectRegistry {
19
19
  cache = new Map();
20
+ pinned = new Set();
20
21
  maxOpen;
21
22
  verbose;
22
23
  openImpl;
24
+ resolveSlug;
23
25
  constructor(opts, defaultOpenImpl) {
24
26
  this.maxOpen = opts.maxOpen ?? 5;
25
27
  this.verbose = opts.verbose ?? false;
26
28
  this.openImpl = opts.openImpl ?? defaultOpenImpl;
29
+ this.resolveSlug = opts.resolveSlug ?? createSlugResolver();
27
30
  }
28
31
  /**
29
32
  * Return the cached SpecShip for `projectPath`, opening it on demand.
@@ -55,7 +58,7 @@ export class ProjectRegistry {
55
58
  * (`-Users-alice-foo` → `/Users/alice/foo`).
56
59
  */
57
60
  async getBySlug(slug) {
58
- return this.get(decodeProjectSlug(slug));
61
+ return this.get(this.resolveSlug(slug));
59
62
  }
60
63
  /** Currently-cached project paths (most-recent-access first). */
61
64
  openPaths() {
@@ -64,6 +67,14 @@ export class ProjectRegistry {
64
67
  .map(([k]) => k);
65
68
  }
66
69
  has(projectPath) { return this.cache.has(projectPath); }
70
+ /**
71
+ * Exempt `projectPath` from LRU eviction. The server pins its primary:
72
+ * routes capture the primary instance by reference (`app.primaryCg`), so
73
+ * an eviction CLOSES a handle they still use and every request 500s with
74
+ * "database connection is not open". Surfaced when the /api/events sweep
75
+ * started opening many projects through this cache.
76
+ */
77
+ pin(projectPath) { this.pinned.add(projectPath); }
67
78
  /** Close + drop every cached instance. Called on server shutdown. */
68
79
  closeAll() {
69
80
  for (const [, v] of this.cache) {
@@ -80,6 +91,8 @@ export class ProjectRegistry {
80
91
  let oldestKey = null;
81
92
  let oldestTime = Infinity;
82
93
  for (const [k, v] of this.cache) {
94
+ if (this.pinned.has(k))
95
+ continue; // never close a pinned instance
83
96
  if (v.lastAccess < oldestTime) {
84
97
  oldestTime = v.lastAccess;
85
98
  oldestKey = k;
@@ -315,6 +315,17 @@ export function chunkAnswer(answer) {
315
315
  * `ChatSource` (A1) and every result set is stable-sorted (A3).
316
316
  */
317
317
  export function answerForIntent(cg, classified, detailCap = DETAIL_CAP) {
318
+ const base = dispatchIntent(cg, classified, detailCap);
319
+ // Deprecation nudge (CMD-NS-DOC, REQ-CMD-NS-005): a legacy `/ss-*` form still
320
+ // routes, but the answer names the `/specship:*` command it was renamed to.
321
+ if (classified.deprecatedAlias) {
322
+ const { from, to } = classified.deprecatedAlias;
323
+ return { ...base, answer: `Note: \`${from}\` was renamed to \`${to}\`.\n\n${base.answer}` };
324
+ }
325
+ return base;
326
+ }
327
+ /** The intent→query dispatch (see `answerForIntent`, which wraps this). */
328
+ function dispatchIntent(cg, classified, detailCap = DETAIL_CAP) {
318
329
  const query = classified.query.trim();
319
330
  switch (classified.intent) {
320
331
  case 'callers':
@@ -69,6 +69,20 @@ function priorWindow(key) {
69
69
  * Claude-specific aggregate queries directly. SpecShip exposes this via
70
70
  * `getDb()`-style accessors. Falls back to digging via the queries property.
71
71
  */
72
+ /**
73
+ * SQL expression extracting `path` from a tool call's input JSON. Prefers
74
+ * the full v7 `input_json` column and falls back to `input_summary`; BOTH
75
+ * are guarded with json_valid() because input_summary is truncated to 400
76
+ * chars by the ingestor — long inputs are malformed JSON and an unguarded
77
+ * json_extract throws SQLITE_ERROR, 500ing the route (REQ-DASHINT-003).
78
+ * `col` is the optional table alias prefix (e.g. 't.').
79
+ */
80
+ function jsonInputExtract(path, col = '') {
81
+ return `COALESCE(
82
+ CASE WHEN json_valid(${col}input_json) THEN json_extract(${col}input_json, '${path}') END,
83
+ CASE WHEN json_valid(${col}input_summary) THEN json_extract(${col}input_summary, '${path}') END
84
+ )`;
85
+ }
72
86
  function getDb(cg) {
73
87
  // SpecShip exposes the underlying DB via its DatabaseConnection. Look it
74
88
  // up via the private field as a fallback — works because it's the same
@@ -309,13 +323,20 @@ export async function registerClaudeRoutes(app) {
309
323
  ORDER BY ops DESC
310
324
  LIMIT 30
311
325
  `).all(sessionId);
312
- // Skills invoked via the Skill tool. The ingestor stashes the
313
- // JSON-serialized input under input_summary, so json_extract pulls the
314
- // skill name straight out without needing the v7 input_json column to
315
- // be backfilled.
326
+ // Skills invoked via the Skill tool. Read the skill name from the full
327
+ // v7 `input_json` column first — `input_summary` is truncated to 400
328
+ // chars by the ingestor, so long inputs are INVALID JSON and a bare
329
+ // json_extract on them throws SQLITE_ERROR ("malformed JSON"), 500ing
330
+ // the whole summary (REQ-DASHINT-003). Every extraction is guarded by
331
+ // json_valid(); rows malformed in both columns degrade to 'unknown'.
316
332
  const skills = db.prepare(`
317
333
  SELECT
318
- COALESCE(json_extract(input_summary, '$.skill'), json_extract(input_summary, '$.skill_name'), 'unknown') as name,
334
+ COALESCE(
335
+ CASE WHEN json_valid(input_json)
336
+ THEN COALESCE(json_extract(input_json, '$.skill'), json_extract(input_json, '$.skill_name')) END,
337
+ CASE WHEN json_valid(input_summary)
338
+ THEN COALESCE(json_extract(input_summary, '$.skill'), json_extract(input_summary, '$.skill_name')) END,
339
+ 'unknown') as name,
319
340
  COUNT(*) as count
320
341
  FROM claude_tool_calls
321
342
  WHERE session_id = ? AND tool_name = 'Skill'
@@ -421,12 +442,12 @@ export async function registerClaudeRoutes(app) {
421
442
  WHERE p.ts >= ?
422
443
  GROUP BY type
423
444
  `).all(since);
424
- // Subagent breakdown by name — Task tool calls grouped by subagent_type.
425
- // input_summary is the JSON-serialized tool input; json_extract pulls
426
- // out subagent_type (defaults to 'general-purpose' when unset).
445
+ // Subagent breakdown by name — Task tool calls grouped by subagent_type
446
+ // (defaults to 'general-purpose' when unset). Guarded extraction: Task
447
+ // inputs carry long prompts, so input_summary is almost always truncated.
427
448
  const subagentByName = db.prepare(`
428
449
  SELECT
429
- COALESCE(NULLIF(json_extract(input_summary, '$.subagent_type'), ''), 'general-purpose') as name,
450
+ COALESCE(NULLIF(${jsonInputExtract('$.subagent_type')}, ''), 'general-purpose') as name,
430
451
  COUNT(*) as calls,
431
452
  MIN(ts) as firstSeen,
432
453
  MAX(ts) as lastSeen
@@ -529,19 +550,19 @@ export async function registerClaudeRoutes(app) {
529
550
  SELECT COUNT(*) as calls, COUNT(DISTINCT session_id) as sessions
530
551
  FROM claude_tool_calls
531
552
  WHERE tool_name = 'Task' AND ts >= ?
532
- AND COALESCE(NULLIF(json_extract(input_summary, '$.subagent_type'), ''), 'general-purpose') = ?
553
+ AND COALESCE(NULLIF(${jsonInputExtract('$.subagent_type')}, ''), 'general-purpose') = ?
533
554
  `).get(since, type);
534
555
  const invocations = db.prepare(`
535
556
  SELECT
536
557
  t.session_id,
537
558
  t.ts,
538
- COALESCE(json_extract(t.input_summary, '$.description'), '') as description,
539
- COALESCE(json_extract(t.input_summary, '$.prompt'), '') as prompt,
559
+ COALESCE(${jsonInputExtract('$.description', 't.')}, '') as description,
560
+ COALESCE(${jsonInputExtract('$.prompt', 't.')}, '') as prompt,
540
561
  s.last_model
541
562
  FROM claude_tool_calls t
542
563
  LEFT JOIN claude_sessions s ON s.id = t.session_id
543
564
  WHERE t.tool_name = 'Task' AND t.ts >= ?
544
- AND COALESCE(NULLIF(json_extract(t.input_summary, '$.subagent_type'), ''), 'general-purpose') = ?
565
+ AND COALESCE(NULLIF(${jsonInputExtract('$.subagent_type', 't.')}, ''), 'general-purpose') = ?
545
566
  ORDER BY t.ts DESC
546
567
  LIMIT 50
547
568
  `).all(since, type);
@@ -32,6 +32,7 @@ export async function registerEventsRoutes(app) {
32
32
  const lastStatus = new Map(); // `${slug}:${runId}` -> status
33
33
  const seenDrift = new Set(); // `${slug}:${linkId}`
34
34
  const lastSweep = new Map(); // slug -> last reflection sweep ms
35
+ const lastIndexSig = new Map(); // slug -> lastIndexed signature
35
36
  let primed = false; // first pass seeds state silently (no burst on connect)
36
37
  let closed = false;
37
38
  const send = (ev) => {
@@ -50,8 +51,13 @@ export async function registerEventsRoutes(app) {
50
51
  catch {
51
52
  return;
52
53
  }
54
+ // Bound the sweep to the most-recently-active initialized projects
55
+ // (enumerate() sorts by recency): every project here is opened via the
56
+ // registry, and an unbounded sweep churns the LRU open/close cycle on
57
+ // every poll once slugs resolve to real paths.
58
+ projects = projects.filter((p) => p.initialized).slice(0, 10);
53
59
  for (const p of projects) {
54
- if (!p.initialized || closed)
60
+ if (closed)
55
61
  continue;
56
62
  let cg;
57
63
  try {
@@ -83,6 +89,19 @@ export async function registerEventsRoutes(app) {
83
89
  send({ kind: 'runDone', project: p.slug, projectPath: p.path, id: r.id, title: `Run ${r.status}`, detail: r.workflowName, status: r.status });
84
90
  }
85
91
  }
92
+ // Index/ingest activity (REQ-DASHUX-004): emit when the project's
93
+ // index freshness changes so open pages can refetch instead of
94
+ // going stale between manual refreshes.
95
+ try {
96
+ const sig = String(cg.getLastIndexedAt() ?? '');
97
+ if (lastIndexSig.get(p.slug) !== sig) {
98
+ lastIndexSig.set(p.slug, sig);
99
+ if (primed) {
100
+ send({ kind: 'activity', project: p.slug, projectPath: p.path, id: sig, title: 'Index updated' });
101
+ }
102
+ }
103
+ }
104
+ catch { /* ignore — activity detection must not break the stream */ }
86
105
  // Newly-drifted links.
87
106
  let links = [];
88
107
  try {
@@ -16,10 +16,10 @@ import { writeSseHead } from './sse.js';
16
16
  import { promises as fs, watch } from 'node:fs';
17
17
  import os from 'node:os';
18
18
  import path from 'node:path';
19
- import { decodeProjectSlug } from '../ingest/ingestor.js';
19
+ import { createSlugResolver } from '../ingest/project-paths.js';
20
20
  const projectsBus = new EventEmitter();
21
21
  projectsBus.setMaxListeners(0); // many SSE subscribers + internal
22
- export async function enumerate(claudeRoot) {
22
+ export async function enumerate(claudeRoot, resolverOpts) {
23
23
  let entries;
24
24
  try {
25
25
  entries = await fs.readdir(claudeRoot, { withFileTypes: true });
@@ -27,12 +27,15 @@ export async function enumerate(claudeRoot) {
27
27
  catch {
28
28
  return [];
29
29
  }
30
+ // Slug decoding is lossy ('-'/'.'/'_' → '-'); resolve real paths from
31
+ // ~/.claude.json + transcript cwds instead (REQ-SLUGRES-002).
32
+ const resolveSlug = createSlugResolver(resolverOpts);
30
33
  const out = [];
31
34
  for (const ent of entries) {
32
35
  if (!ent.isDirectory())
33
36
  continue;
34
37
  const slug = ent.name;
35
- const decoded = decodeProjectSlug(slug);
38
+ const decoded = resolveSlug(slug);
36
39
  const projDir = path.join(claudeRoot, slug);
37
40
  let sessionCount = 0;
38
41
  let lastModifiedMs = 0;
@@ -98,8 +98,28 @@ export async function registerSpecRoutes(app) {
98
98
  const cg = await resolveCg(app, req, reply);
99
99
  if (!cg)
100
100
  return;
101
- const docs = cg.getSpecQueries().getAllSpecs();
102
- return { specs: docs };
101
+ const sq = cg.getSpecQueries();
102
+ const docs = sq.getAllSpecs();
103
+ // Rolled-up link state per spec id (REQ-DASHUX-003): worst state wins
104
+ // (broken > orphaned > drifted), else verified only when every link is,
105
+ // else implemented. One bulk pass — the tree colors 500+ rows from this
106
+ // map without per-spec detail calls.
107
+ const statesById = new Map();
108
+ for (const l of sq.getAllLinks()) {
109
+ const arr = statesById.get(l.specId) ?? [];
110
+ arr.push(l.state);
111
+ statesById.set(l.specId, arr);
112
+ }
113
+ const linkStates = {};
114
+ for (const [id, states] of statesById) {
115
+ linkStates[id] =
116
+ states.includes('broken') ? 'broken'
117
+ : states.includes('orphaned') ? 'orphaned'
118
+ : states.includes('drifted') ? 'drifted'
119
+ : states.every((s) => s === 'verified') ? 'verified'
120
+ : 'implemented';
121
+ }
122
+ return { specs: docs, linkStates };
103
123
  });
104
124
  // Spec lifecycle funnel: idea → spec → implemented (REQ-FUNNEL-006). Computed
105
125
  // on the dynamically-loaded SpecShip instance — no runtime import of the
@@ -18,6 +18,7 @@ import Fastify from 'fastify';
18
18
  import cors from '@fastify/cors';
19
19
  import { startWatcher, primaryProjectMatcher } from './ingest/index.js';
20
20
  import { backfillDisplaced } from './ingest/impact-backfill.js';
21
+ import { recostUnpricedPrompts } from './ingest/pricing-backfill.js';
21
22
  import { ProjectRegistry } from './project-registry.js';
22
23
  import { makeStaticHandler } from './static-handler.js';
23
24
  import { registerGraphRoutes } from './routes/graph.js';
@@ -86,14 +87,22 @@ export async function createServer(options) {
86
87
  // Lazy-load specship. Used as the open() impl for the registry, and
87
88
  // (only when a primary path is set) to open the primary instance below.
88
89
  const cgMod = await loadSpecShip();
89
- const registry = new ProjectRegistry({ verbose }, (p) => cgMod.SpecShip.open(p));
90
+ // maxOpen must exceed the /api/events cross-project sweep size (10) plus
91
+ // the primary and a user-picked project, or the sweep churns open/close
92
+ // cycles through the LRU on every poll.
93
+ const registry = new ProjectRegistry({ verbose, maxOpen: 16 }, (p) => cgMod.SpecShip.open(p));
90
94
  // Primary project (optional). When set, specship-scoped routes default to
91
95
  // it when no `?project=<slug>` is provided, and the analytics ingest
92
96
  // hooks into its SQLite handle.
93
97
  let primaryCg = null;
94
98
  if (options.projectRoot) {
95
99
  primaryCg = await registry.get(options.projectRoot);
96
- if (!primaryCg && verbose) {
100
+ if (primaryCg) {
101
+ // Routes capture this instance by reference — it must never be LRU
102
+ // evicted (eviction closes the SQLite handle and every request 500s).
103
+ registry.pin(options.projectRoot);
104
+ }
105
+ else if (verbose) {
97
106
  console.error(`[specship-server] primary project ${options.projectRoot} not initialized — booting projectless`);
98
107
  }
99
108
  }
@@ -134,6 +143,16 @@ export async function createServer(options) {
134
143
  catch (err) {
135
144
  console.error('[specship-server] specship-impact backfill failed (non-fatal):', err instanceof Error ? err.message : String(err));
136
145
  }
146
+ // Re-cost prompts ingested while their model family was unpriced,
147
+ // e.g. fable sessions stuck at $0 (REQ-DASHINT-001.A2). Idempotent.
148
+ try {
149
+ const recosted = recostUnpricedPrompts(dbHandle);
150
+ if (verbose && recosted > 0)
151
+ console.error(`[specship-server] re-costed ${recosted} unpriced prompts`);
152
+ }
153
+ catch (err) {
154
+ console.error('[specship-server] pricing backfill failed (non-fatal):', err instanceof Error ? err.message : String(err));
155
+ }
137
156
  }
138
157
  }
139
158
  else if (!primaryCg && options.ingest !== false && verbose) {
@@ -0,0 +1 @@
1
+ import{Aa as p,Ta as c,Va as f,Xa as m,ea as i,ka as s,lb as a,pb as o,xa as d,ya as u,za as l,zb as v}from"./chunk-SYGIPC2P.js";var g=class r{value=o.required();suffix=o("");invert=o(!1);up=a(()=>this.value()>=0);good=a(()=>this.invert()?!this.up():this.up());text=a(()=>{let t=this.value();if(!Number.isFinite(t))return"\u2014";let e=Math.abs(t),n=e>=10?"\xD7"+Math.round(e):Math.round(e*100)+"%";return(t<0?"-":"+")+n+this.suffix()});static \u0275fac=function(e){return new(e||r)};static \u0275cmp=s({type:r,selectors:[["app-delta"]],inputs:{value:[1,"value"],suffix:[1,"suffix"],invert:[1,"invert"]},decls:3,vars:5,consts:[[1,"row","gap-2","tabular",2,"font-size","var(--fs-xs)","font-weight","600"],[3,"name","size"]],template:function(e,n){e&1&&(u(0,"span",0),p(1,"app-icon",1),f(2),l()),e&2&&(c("color",n.good()?"var(--success)":"var(--error)"),i(),d("name",n.up()?"trendUp":"trendDown")("size",12),i(),m(" ",n.text()," "))},dependencies:[v],styles:["[_nghost-%COMP%]{display:inline-flex}"],changeDetection:0})};export{g as a};
@@ -0,0 +1 @@
1
+ import{a as $}from"./chunk-JLOJCDIY.js";import{a as A}from"./chunk-5NTGUSXJ.js";import{a as L}from"./chunk-PZSU53SS.js";import{c as R}from"./chunk-DTWIJ3GJ.js";import"./chunk-JGDK6YKG.js";import{a as F}from"./chunk-TWVVIWKO.js";import{Aa as v,Ea as N,Fa as V,Ga as w,I as k,Ia as f,Ka as l,M as y,N as b,Ta as E,Ua as B,Va as s,W as u,Wa as m,Xa as _,bb as T,ea as o,ka as z,lb as M,qa as I,ra as g,sa as x,ua as O,va as C,wa as h,xa as d,ya as a,za as r,zb as D}from"./chunk-SYGIPC2P.js";import{a as P,b as S}from"./chunk-Q7L6LLAK.js";var q=()=>[0,1,2,3],G=(i,t)=>t.id;function J(i,t){i&1&&v(0,"div",8)}function K(i,t){i&1&&C(0,J,1,0,"div",8,O),i&2&&h(T(0,q))}function Q(i,t){i&1&&(a(0,"div",6),s(1,"No tips."),r())}function X(i,t){if(i&1&&v(0,"app-icon",15),i&2){let e=l().$implicit;d("name",e.icon)("size",15)}}function Y(i,t){i&1&&v(0,"app-icon",16),i&2&&d("size",15)}function Z(i,t){if(i&1){let e=w();a(0,"div",39),f("click",function(){y(e);let p=l(2).$implicit,c=l(2);return b(c.dismiss(p.id))}),s(1),r()}if(i&2){let e=t.$implicit;o(),_("Snooze ",e)}}function ee(i,t){if(i&1&&(a(0,"div",37),C(1,Z,2,1,"div",38,O),r()),i&2){let e=l(3);o(),h(e.snoozeOpts)}}function te(i,t){if(i&1){let e=w();a(0,"div",10),v(1,"div",11),a(2,"div",12)(3,"div",13)(4,"div",14),g(5,X,1,2,"app-icon",15)(6,Y,1,1,"app-icon",16),r(),a(7,"div",17)(8,"div",18),s(9),r()(),a(10,"app-pill",5),s(11),r()(),a(12,"div",19),s(13),r(),a(14,"div",20)(15,"div",21)(16,"div",22),s(17,"Evidence"),r(),a(18,"div",23),v(19,"app-icon",24),a(20,"span",25),s(21),r()(),a(22,"div",26),s(23),r()(),a(24,"div",21)(25,"div",22),s(26,"Impact"),r(),a(27,"div",27),s(28),r()()(),a(29,"div",28)(30,"div",29)(31,"span",30),s(32,"fix"),r(),a(33,"code",31),s(34),r(),v(35,"app-copy-btn",32),r(),a(36,"button",33),s(37,"Apply"),r(),a(38,"div",34)(39,"button",35),f("click",function(){let p=y(e).$implicit,c=l(2);return b(c.toggleSnooze(p.id))}),s(40," Dismiss "),v(41,"app-icon",36),r(),g(42,ee,3,0,"div",37),r()()()()}if(i&2){let e=t.$implicit,n=l(2);B("dismissed",n.isDismissed(e.id)),I("data-sev",e.severity),o(4),E("background",n.sevBg(e.severity))("color",n.sevColor(e.severity)),o(),x(e.icon?5:6),o(4),m(e.title),o(),d("color",n.sevColor(e.severity))("bg",n.sevBg(e.severity)),o(),m(e.severity),o(2),m(e.why),o(6),d("size",11),o(2),m(e.evidence.session),o(2),m(e.evidence.detail),o(5),m(e.saving),o(6),m(e.fix),o(),d("text",e.fix),o(6),d("size",12),o(),x(n.snoozeOpen(e.id)?42:-1)}}function ne(i,t){if(i&1&&(a(0,"div",7),C(1,te,43,21,"div",9,G),r()),i&2){let e=l();o(),h(e.ordered())}}var H=class i{api=k(F);resource=R(this.api,()=>"/api/claude/tips");dismissedIds=u(new Set);snoozeOpenIds=u(new Set);snoozeOpts=["1 day","1 week","Forever"];ordered=M(()=>{let t=this.resource.state().data?.tips??[],e=this.dismissedIds(),n={error:0,warn:1,info:2};return[...t].filter(p=>!e.has(p.id)).sort((p,c)=>(n[p.severity]??9)-(n[c.severity]??9))});counts=M(()=>{let t=this.resource.state().data?.tips??[];return{error:t.filter(e=>e.severity==="error").length,warn:t.filter(e=>e.severity==="warn").length,info:t.filter(e=>e.severity==="info").length}});isDismissed(t){return this.dismissedIds().has(t)}snoozeOpen(t){return this.snoozeOpenIds().has(t)}toggleSnooze(t){this.snoozeOpenIds.update(e=>{let n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n})}dismiss(t){this.dismissedIds.update(e=>new Set([...e,t])),this.snoozeOpenIds.update(e=>{let n=new Set(e);return n.delete(t),n})}sevColor(t){return t==="error"?"var(--error)":t==="warn"?"var(--warn)":"var(--info)"}sevBg(t){return t==="error"?"color-mix(in srgb, var(--error) 16%, transparent)":t==="warn"?"color-mix(in srgb, var(--warn) 16%, transparent)":"color-mix(in srgb, var(--info) 16%, transparent)"}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=z({type:i,selectors:[["app-tips-section"]],decls:17,vars:11,consts:[[1,"row","gap-8",2,"margin","18px 0 10px"],["name","tips",2,"color","var(--warn)",3,"size"],[2,"font-weight","600","font-size","12.5px"],[1,"muted",2,"font-size","11px"],[1,"grow"],[3,"color","bg"],[1,"empty"],[1,"tips-grid"],[1,"skel","card-skel"],[1,"tip-card","card",3,"dismissed"],[1,"tip-card","card"],[1,"sev-bar"],[1,"tip-body"],[1,"row","gap-10",2,"margin-bottom","10px"],[1,"icon-tile"],[3,"name","size"],["name","tips",3,"size"],[1,"grow",2,"min-width","0"],[1,"tip-title"],[1,"tip-why"],[1,"tip-grid"],[1,"kv-block"],[1,"eyebrow"],[1,"row","gap-6",2,"font-size","11.5px","margin-top","2px"],["name","sessions",2,"color","var(--text-muted)",3,"size"],[1,"mono",2,"color","var(--accent)"],[1,"mono","secondary",2,"font-size","11px","margin-top","4px"],[2,"font-size","16px","font-weight","700","color","var(--success)","font-variant-numeric","tabular-nums","margin-top","2px"],[1,"actions"],[1,"fix-row","grow"],[1,"muted","fix-label"],[1,"mono","fix-code"],[3,"text"],["type","button",1,"btn","btn-primary","btn-sm"],[1,"dismiss-wrap",2,"position","relative"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","chevronDown",3,"size"],[1,"snooze-menu"],[1,"snooze-opt"],[1,"snooze-opt",3,"click"]],template:function(e,n){e&1&&(a(0,"section")(1,"div",0),v(2,"app-icon",1),a(3,"span",2),s(4,"Tips"),r(),a(5,"span",3),s(6,"\xB7 a senior teammate reviewed your transcripts"),r(),v(7,"span",4),a(8,"app-pill",5),s(9),r(),a(10,"app-pill",5),s(11),r(),a(12,"app-pill",5),s(13),r()(),g(14,K,2,1)(15,Q,2,0,"div",6)(16,ne,3,0,"div",7),r()),e&2&&(o(2),d("size",14),o(6),d("color","var(--error)")("bg","var(--error-soft)"),o(),_("",n.counts().error," urgent"),o(),d("color","var(--warn)")("bg","var(--warn-soft)"),o(),_("",n.counts().warn," warn"),o(),d("color","var(--info)")("bg","var(--info-soft)"),o(),_("",n.counts().info," info"),o(),x(n.resource.state().loading?14:n.ordered().length===0?15:16))},dependencies:[$,L,D],styles:["[_nghost-%COMP%]{display:contents}.page[_ngcontent-%COMP%]{flex:1;padding:18px}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.secondary[_ngcontent-%COMP%]{color:var(--text-secondary)}.grow[_ngcontent-%COMP%]{flex:1}.tips-grid[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:12px;max-width:860px}.tip-card[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:var(--r-lg);overflow:hidden;display:flex}.tip-card.dismissed[_ngcontent-%COMP%]{display:none}.tip-card[data-sev=error][_ngcontent-%COMP%] .sev-bar[_ngcontent-%COMP%]{background:var(--error)}.tip-card[data-sev=warn][_ngcontent-%COMP%] .sev-bar[_ngcontent-%COMP%]{background:var(--warn)}.tip-card[data-sev=info][_ngcontent-%COMP%] .sev-bar[_ngcontent-%COMP%]{background:var(--info)}.sev-bar[_ngcontent-%COMP%]{width:3px;flex-shrink:0}.tip-body[_ngcontent-%COMP%]{padding:14px 16px;flex:1;min-width:0}.icon-tile[_ngcontent-%COMP%]{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;flex-shrink:0}.tip-title[_ngcontent-%COMP%]{font-size:14px;font-weight:600;letter-spacing:-.01em;text-wrap:pretty}.tip-why[_ngcontent-%COMP%]{color:var(--text-secondary);font-size:12.5px;line-height:1.6;margin-bottom:12px}.tip-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px}.kv-block[_ngcontent-%COMP%]{background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:var(--r-md);padding:10px}.eyebrow[_ngcontent-%COMP%]{font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--text-muted);margin-bottom:5px}.actions[_ngcontent-%COMP%]{display:flex;gap:8px;align-items:center}.fix-row[_ngcontent-%COMP%]{min-width:0;display:flex;align-items:center;gap:8px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:7px;padding:6px 10px}.fix-label[_ngcontent-%COMP%]{color:var(--text-muted);font-size:10.5px;flex-shrink:0}.fix-code[_ngcontent-%COMP%]{flex:1;min-width:0;color:var(--text-primary);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dismiss-wrap[_ngcontent-%COMP%]{position:relative}.snooze-menu[_ngcontent-%COMP%]{position:absolute;top:100%;right:0;margin-top:4px;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:8px;box-shadow:0 4px 16px #00000059;padding:5px;width:130px;z-index:10}.snooze-opt[_ngcontent-%COMP%]{padding:6px 9px;border-radius:5px;cursor:pointer;font-size:12px;color:var(--text-primary)}.snooze-opt[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.skel[_ngcontent-%COMP%]{background:var(--bg-elevated);border-radius:5px;animation:_ngcontent-%COMP%_skeleton 1.4s ease-in-out infinite}.card-skel[_ngcontent-%COMP%]{height:200px;margin-bottom:12px}.empty[_ngcontent-%COMP%]{color:var(--text-muted);font-size:13px;padding:40px;text-align:center}@keyframes _ngcontent-%COMP%_skeleton{0%,to{opacity:.5}50%{opacity:.9}}"],changeDetection:0})};var ie=()=>[0,1,2],oe=(i,t)=>t.contentHash;function re(i,t){if(i&1&&(a(0,"div",6),s(1),r()),i&2){let e=l();o(),m(e.errorMsg())}}function ae(i,t){i&1&&v(0,"div",9)}function se(i,t){i&1&&C(0,ae,1,0,"div",9,O),i&2&&h(T(0,ie))}function pe(i,t){i&1&&(a(0,"div",7),v(1,"app-icon",5),a(2,"div",10),s(3,"No proposals yet"),r(),a(4,"div",11),s(5,"Run "),a(6,"strong"),s(7,"Analyze"),r(),s(8," to mine your ingested transcripts for recurring patterns worth turning into a durable rule, skill, or hook."),r()()),i&2&&(o(),d("size",28))}function le(i,t){if(i&1&&(a(0,"span",19),s(1),r()),i&2){let e=l().$implicit,n=l(2);o(),m(n.outcomeOf(e.contentHash))}}function de(i,t){i&1&&(a(0,"div",30),s(1,"Loading preview\u2026"),r())}function ce(i,t){i&1&&(a(0,"div",31),s(1,"A non-SpecShip file already exists at this path \u2014 apply is blocked to avoid clobbering it."),r())}function me(i,t){if(i&1&&(g(0,ce,2,0,"div",31),a(1,"div",24),s(2,"Preview diff"),r(),a(3,"pre",32),s(4),r()),i&2){let e,n,p=l(2).$implicit,c=l(2);x((e=c.previewOf(p.contentHash))!=null&&e.conflict?0:-1),o(4),m((n=c.previewOf(p.contentHash))==null?null:n.diff)}}function ve(i,t){if(i&1&&(a(0,"div",26),g(1,de,2,0,"div",30)(2,me,5,2),r()),i&2){let e=l().$implicit,n=l(2);o(),x(n.previewOf(e.contentHash)===null?1:2)}}function ge(i,t){if(i&1){let e=w();a(0,"span",33),s(1,"applied"),r(),a(2,"button",34),f("click",function(){y(e);let p=l().$implicit,c=l(2);return b(c.undoProposal(p.contentHash))}),s(3,"Undo"),r()}if(i&2){let e=l().$implicit,n=l(2);o(2),d("disabled",n.isBusy(e.contentHash))}}function xe(i,t){if(i&1){let e=w();a(0,"button",34),f("click",function(){y(e);let p=l().$implicit,c=l(2);return b(c.dismissProposal(p.contentHash))}),s(1,"Dismiss"),r(),a(2,"button",4),f("click",function(){y(e);let p=l().$implicit,c=l(2);return b(c.applyProposal(p.contentHash))}),s(3),r()}if(i&2){let e,n=l().$implicit,p=l(2);d("disabled",p.isBusy(n.contentHash)),o(2),d("disabled",p.isBusy(n.contentHash)||((e=p.previewOf(n.contentHash))==null?null:e.conflict)),o(),_(" ",p.isBusy(n.contentHash)?"Applying\u2026":"Apply"," ")}}function _e(i,t){if(i&1){let e=w();a(0,"div",12),v(1,"div",13),a(2,"div",14)(3,"div",15)(4,"div",16)(5,"div",17)(6,"span",18),s(7),r(),a(8,"app-pill",3),s(9),r(),g(10,le,2,1,"span",19),r(),a(11,"div",20),s(12),r()()(),a(13,"div",21),s(14),r(),a(15,"div",22)(16,"div",23)(17,"div",24),s(18,"Evidence"),r(),a(19,"div",25),s(20),r()(),a(21,"div",23)(22,"div",24),s(23,"Writes to"),r(),a(24,"div",25),s(25),r()()(),g(26,ve,3,1,"div",26),a(27,"div",27)(28,"button",28),f("click",function(){let p=y(e).$implicit,c=l(2);return b(c.togglePreview(p.contentHash))}),s(29),r(),v(30,"div",29),g(31,ge,4,1)(32,xe,4,3),r()()()}if(i&2){let e=t.$implicit,n=l(2);I("data-sev",e.severity),o(),E("background",n.sevColor(e.severity)),o(6),m(n.typeLabel(e.type)),o(),d("color",n.sevColor(e.severity))("bg","color-mix(in srgb, "+n.sevColor(e.severity)+" 16%, transparent)"),o(),m(e.severity),o(),x(n.outcomeOf(e.contentHash)?10:-1),o(2),m(e.title),o(2),m(e.body),o(6),m(e.evidence.detail),o(5),m(e.targetPath),o(),x(n.previewOf(e.contentHash)!==void 0?26:-1),o(3),_(" ",n.previewOf(e.contentHash)!==void 0?"Hide preview":"Preview"," "),o(2),x(e.state==="applied"?31:32)}}function ue(i,t){if(i&1&&(a(0,"div",8),C(1,_e,33,15,"div",12,oe),r()),i&2){let e=l();o(),h(e.proposals())}}var j=class i{api=k(F);resource=R(this.api,()=>"/api/reflect?state=open");analyzing=u(!1);previews=u({});busy=u(new Set);outcomes=u({});errorMsg=u(null);proposals=M(()=>{let t=this.resource.state().data?.proposals??[],e={high:0,warn:1,info:2};return[...t].sort((n,p)=>(e[n.severity]??9)-(e[p.severity]??9))});counts=M(()=>{let t=this.resource.state().data?.proposals??[];return{high:t.filter(e=>e.severity==="high").length,warn:t.filter(e=>e.severity==="warn").length,info:t.filter(e=>e.severity==="info").length}});typeLabel(t){return t==="memory_rule"?"memory / rule":t==="skill"?"skill":"hook"}sevColor(t){return t==="high"?"var(--error)":t==="warn"?"var(--warn)":"var(--info)"}isBusy(t){return this.busy().has(t)}previewOf(t){return this.previews()[t]}outcomeOf(t){return this.outcomes()[t]}async analyze(){this.analyzing.set(!0),this.errorMsg.set(null);try{await this.api.post("/api/reflect/analyze",{}),this.resource.refetch()}catch(t){this.errorMsg.set(this.msg(t))}finally{this.analyzing.set(!1)}}async togglePreview(t){let e=this.previews();if(t in e){let n=P({},e);delete n[t],this.previews.set(n);return}this.previews.set(S(P({},e),{[t]:null}));try{let n=await this.api.get(`/api/reflect/${t}/preview`);this.previews.set(S(P({},this.previews()),{[t]:n}))}catch(n){this.errorMsg.set(this.msg(n));let p=P({},this.previews());delete p[t],this.previews.set(p)}}applyProposal(t){return this.mutate(t,`/api/reflect/${t}/apply`,"apply")}undoProposal(t){return this.mutate(t,`/api/reflect/${t}/undo`,"undo")}dismissProposal(t){return this.mutate(t,`/api/reflect/${t}/dismiss`,"dismiss")}async mutate(t,e,n){this.setBusy(t,!0),this.errorMsg.set(null);try{let p=await this.api.post(e,{}),c=p.outcome??(p.ok?"dismissed":"done");this.outcomes.set(S(P({},this.outcomes()),{[t]:c})),this.resource.refetch()}catch(p){this.errorMsg.set(`${n} failed: ${this.msg(p)}`)}finally{this.setBusy(t,!1)}}setBusy(t,e){this.busy.update(n=>{let p=new Set(n);return e?p.add(t):p.delete(t),p})}msg(t){return t instanceof Error?t.message:String(t)}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=z({type:i,selectors:[["app-improvements"]],decls:17,vars:14,consts:[[1,"page","scroll-y"],["icon","sparkles","title","Improvements","sub","Durable fixes mined from your transcripts \u2014 preview, then apply"],["actions",""],[3,"color","bg"],["type","button",1,"btn","btn-primary","btn-sm",3,"click","disabled"],["name","sparkles",3,"size"],[1,"banner-error"],[1,"empty"],[1,"prop-list"],[1,"skel","card-skel"],[1,"empty-title"],[1,"empty-sub"],[1,"prop-card","card"],[1,"sev-bar"],[1,"prop-body"],[1,"row","gap-10","head-row"],[1,"grow",2,"min-width","0"],[1,"row","gap-6","chips"],[1,"type-chip"],[1,"outcome"],[1,"prop-title"],[1,"prop-why"],[1,"prop-meta"],[1,"kv-block"],[1,"eyebrow"],[1,"mono","secondary","detail"],[1,"preview"],[1,"actions"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],[1,"grow"],[1,"muted"],[1,"banner-error","small"],[1,"diff","mono"],[1,"applied-tag"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click","disabled"]],template:function(e,n){e&1&&(a(0,"div",0)(1,"app-page-head",1),N(2,2),a(3,"app-pill",3),s(4),r(),a(5,"app-pill",3),s(6),r(),a(7,"app-pill",3),s(8),r(),a(9,"button",4),f("click",function(){return n.analyze()}),v(10,"app-icon",5),s(11),r(),V(),r(),g(12,re,2,1,"div",6),g(13,se,2,1)(14,pe,9,1,"div",7)(15,ue,3,0,"div",8),v(16,"app-tips-section"),r()),e&2&&(o(3),d("color","var(--error)")("bg","var(--error-soft)"),o(),_("",n.counts().high," high"),o(),d("color","var(--warn)")("bg","var(--warn-soft)"),o(),_("",n.counts().warn," warn"),o(),d("color","var(--info)")("bg","var(--info-soft)"),o(),_("",n.counts().info," info"),o(),d("disabled",n.analyzing()),o(),d("size",13),o(),_(" ",n.analyzing()?"Analyzing\u2026":"Analyze"," "),o(),x(n.errorMsg()?12:-1),o(),x(n.resource.state().loading?13:n.proposals().length===0?14:15))},dependencies:[A,$,D,H],styles:[".page[_ngcontent-%COMP%]{padding:20px 24px 48px}.banner-error[_ngcontent-%COMP%]{background:var(--error-soft);color:var(--error);border:1px solid color-mix(in srgb,var(--error) 30%,transparent);border-radius:8px;padding:10px 12px;font-size:12.5px;margin-bottom:14px}.banner-error.small[_ngcontent-%COMP%]{margin:8px 0;padding:8px 10px;font-size:11.5px}.empty[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;padding:64px 24px;color:var(--text-muted)}.empty[_ngcontent-%COMP%] .empty-title[_ngcontent-%COMP%]{font-size:15px;font-weight:600;color:var(--text)}.empty[_ngcontent-%COMP%] .empty-sub[_ngcontent-%COMP%]{font-size:12.5px;max-width:460px;line-height:1.5}.card-skel[_ngcontent-%COMP%]{height:132px;border-radius:12px;margin-bottom:12px}.prop-list[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:12px}.prop-card[_ngcontent-%COMP%]{position:relative;display:flex;overflow:hidden;padding:0}.prop-card[_ngcontent-%COMP%] .sev-bar[_ngcontent-%COMP%]{width:3px;flex:0 0 3px}.prop-card[_ngcontent-%COMP%] .prop-body[_ngcontent-%COMP%]{flex:1;min-width:0;padding:14px 16px}.head-row[_ngcontent-%COMP%]{margin-bottom:8px}.chips[_ngcontent-%COMP%]{margin-bottom:6px}.type-chip[_ngcontent-%COMP%]{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;font-weight:600;color:var(--text-muted);background:var(--surface-2, color-mix(in srgb, var(--text) 7%, transparent));border-radius:5px;padding:2px 7px}.outcome[_ngcontent-%COMP%]{font-size:10.5px;color:var(--success);font-weight:600;text-transform:lowercase}.prop-title[_ngcontent-%COMP%]{font-size:14px;font-weight:600;color:var(--text);line-height:1.35}.prop-why[_ngcontent-%COMP%]{font-size:12.5px;color:var(--text-secondary, var(--text-muted));line-height:1.5;margin-bottom:10px}.prop-meta[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:10px}.prop-meta[_ngcontent-%COMP%] .detail[_ngcontent-%COMP%]{font-size:11px;margin-top:2px;word-break:break-all}.preview[_ngcontent-%COMP%]{margin:8px 0 12px}.preview[_ngcontent-%COMP%] .diff[_ngcontent-%COMP%]{background:var(--surface-2, color-mix(in srgb, var(--text) 5%, transparent));border:1px solid var(--border, color-mix(in srgb, var(--text) 12%, transparent));border-radius:8px;padding:10px 12px;font-size:11.5px;line-height:1.5;max-height:280px;overflow:auto;white-space:pre;margin-top:4px}.actions[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;margin-top:4px}.applied-tag[_ngcontent-%COMP%]{font-size:11px;color:var(--success);font-weight:600}"],changeDetection:0})};export{j as Improvements};
@@ -0,0 +1 @@
1
+ import{a as xe}from"./chunk-PZSU53SS.js";import{b as he}from"./chunk-WBJ5VEB5.js";import{a as _e}from"./chunk-SQPYSOV6.js";import{a as ge}from"./chunk-QIZ65CGB.js";import{a as ue}from"./chunk-IOB4JJT4.js";import{c as se}from"./chunk-DOKZOFKM.js";import{c as B}from"./chunk-DTWIJ3GJ.js";import{a as ce}from"./chunk-JGDK6YKG.js";import{a as pe}from"./chunk-TWVVIWKO.js";import{Aa as p,Ga as O,I as W,Ia as M,Ka as m,M as w,N as S,O as P,P as G,Ta as X,Ua as Y,Va as l,W as N,Wa as _,Xa as j,Ya as re,Za as le,bb as de,ea as o,ka as oe,lb as T,qa as q,ra as b,sa as C,ua as U,va as D,wa as K,xa as k,ya as i,za as a,zb as me}from"./chunk-SYGIPC2P.js";import{a as ie,b as ae}from"./chunk-Q7L6LLAK.js";function fe(t,n,e){if(n.length===0)return new Map;switch(t){case"force":return te(n,e);case"anchored":return ye(n,e);default:return Ce(n,e)}}function Ce(t,n){let e=new Set(t.map(c=>c.id)),r=n.filter(c=>e.has(c.from)&&e.has(c.to)),d=new Map,s=new Map;for(let c of t)d.set(c.id,[]),s.set(c.id,0);for(let c of r)d.get(c.from).push(c.to),s.set(c.to,(s.get(c.to)??0)+1);let g=new Map;for(let c of t)g.set(c.id,0);let E=new Map(s),L=t.filter(c=>(s.get(c.id)??0)===0).map(c=>c.id),I=new Set;for(;L.length;){let c=L.shift();if(I.has(c))continue;I.add(c);let v=g.get(c);for(let f of d.get(c))g.set(f,Math.max(g.get(f),v+1)),E.set(f,(E.get(f)??1)-1),(E.get(f)??0)<=0&&L.push(f)}let u=14,z=new Map;for(let c of t){let v=Math.min(g.get(c.id),u),f=z.get(v)??[];f.push(c.id),z.set(v,f)}let h=210,y=48,x=new Map;for(let[c,v]of z)v.forEach((f,V)=>{x.set(f,{x:c*h,y:(V-(v.length-1)/2)*y})});return x}function te(t,n,e,r){let d=t.length,s=Math.max(220,d*9),g=new Map;t.forEach((h,y)=>{let x=r?.get(h.id);if(x){g.set(h.id,{x:x.x,y:x.y});return}let c=y/d*Math.PI*2,v=s*(.35+.65*(y*.6180339887%1));g.set(h.id,{x:Math.cos(c)*v,y:Math.sin(c)*v})});let E=new Set(t.map(h=>h.id)),L=n.filter(h=>E.has(h.from)&&E.has(h.to)),I=s/Math.sqrt(d),u=s/2,z=120;for(let h=0;h<z;h++){let y=new Map;for(let x of t)y.set(x.id,{x:0,y:0});for(let x=0;x<d;x++){let c=g.get(t[x].id),v=y.get(t[x].id);for(let f=x+1;f<d;f++){let V=g.get(t[f].id),R=c.x-V.x,F=c.y-V.y,$=Math.hypot(R,F)||.01,H=I*I/$,A=R/$,Q=F/$;v.x+=A*H,v.y+=Q*H;let ne=y.get(t[f].id);ne.x-=A*H,ne.y-=Q*H}}for(let x of L){let c=g.get(x.from),v=g.get(x.to),f=c.x-v.x,V=c.y-v.y,R=Math.hypot(f,V)||.01,F=R*R/I,$=f/R,H=V/R,A=y.get(x.from),Q=y.get(x.to);A.x-=$*F,A.y-=H*F,Q.x+=$*F,Q.y+=H*F}for(let x of t){if(e?.has(x.id))continue;let c=y.get(x.id),v=Math.hypot(c.x,c.y)||.01,f=g.get(x.id);f.x+=c.x/v*Math.min(v,u),f.y+=c.y/v*Math.min(v,u)}u*=.95}return g}function ye(t,n){let e=t.filter(g=>g.kind==="spec");if(e.length===0)return te(t,n);let r=-Math.max(340,t.length*5),d=new Map;e.forEach((g,E)=>d.set(g.id,{x:r,y:(E-(e.length-1)/2)*64}));let s=new Set(e.map(g=>g.id));return te(t,n,s,d)}var ke=()=>[1,2,3,4],we=(t,n)=>n.node.id,ee=(t,n)=>n.id;function Se(t,n){t&1&&p(0,"app-pick-project-empty",0)}function Me(t,n){if(t&1){let e=O();i(0,"button",19),M("click",function(){let d=w(e).$implicit,s=m(2);return S(s.toggleFilter(d))}),p(1,"span",20),l(2),i(3,"span",21),l(4),a()()}if(t&2){let e=n.$implicit,r=m(2);X("--chip-color",r.chipColor(e)),Y("is-active",r.isFilterActive(e)),o(),q("data-kind",e),o(),j(" ",r.kindLabel(e)," "),o(2),_(r.countFor(e))}}function Ee(t,n){t&1&&p(0,"span",12)}function Pe(t,n){if(t&1){let e=O();i(0,"button",23),M("click",function(){let d=w(e).$implicit,s=m(3);return S(s.pickResult(d.node))}),p(1,"span",24),i(2,"span",25),l(3),a(),i(4,"span",26),l(5),a(),i(6,"span",27),l(7),a()()}if(t&2){let e=n.$implicit,r=m(3);o(),Y("is-spec",e.node.kind==="spec")("is-route",e.node.kind==="route")("is-test",e.node.kind==="test"),o(2),_(e.node.name),o(2),_(e.node.kind),o(2),_(r.shortPath(e.node.filePath))}}function Ge(t,n){if(t&1&&(i(0,"div",13),D(1,Pe,8,9,"button",22,we),a()),t&2){let e=m(2);o(),K(e.searchResults.state().data.results)}}function Oe(t,n){if(t&1){let e=O();i(0,"app-graph-canvas",28),M("nodeClick",function(d){w(e);let s=m(3);return S(s.pickFromCanvas(d))}),a(),i(1,"div",29),l(2),i(3,"button",30),M("click",function(){w(e);let d=m(3);return S(d.toggleOverviewScope())}),l(4),a()()}if(t&2){let e=m(3);k("nodes",e.visibleNodes())("edges",e.visibleEdges())("selectedId","")("fitKey",e.fitKey())("hiddenIds",e.hiddenIds()),o(2),re(" Showing the ",e.fullGraphCounts().shown," most-connected of ",e.fullGraphCounts().total.toLocaleString()," nodes \xB7 search or click a node to drill in \xB7 "),o(2),j(" ",e.overviewScope()==="full"?"Show fewer":"Show full graph"," ")}}function Te(t,n){t&1&&(i(0,"div",17)(1,"p"),l(2,"Loading graph\u2026"),a()())}function Ie(t,n){if(t&1&&(i(0,"p",31),l(1),a()),t&2){let e=n;o(),le("",e.nodeCount.toLocaleString()," nodes \xB7 ",e.edgeCount.toLocaleString()," edges \xB7 ",e.fileCount.toLocaleString()," files")}}function Le(t,n){if(t&1&&(i(0,"div",17)(1,"h3"),l(2,"Explore the graph"),a(),i(3,"p"),l(4,"Search for a function, method or class to start. The graph renders the symbol with its callers, callees and any linked specs."),a(),b(5,Ie,2,3,"p",31),a()),t&2){let e,r=m(3);o(5),C((e=r.status.state().data)?5:-1,e)}}function ze(t,n){if(t&1&&b(0,Oe,5,8)(1,Te,3,0,"div",17)(2,Le,6,1,"div",17),t&2){let e=m(2);C(e.fullGraphActive()?0:e.fullGraphLoading()?1:2)}}function De(t,n){t&1&&(i(0,"div",17)(1,"p"),l(2,"Loading\u2026"),a()())}function Ke(t,n){if(t&1&&(i(0,"div",17)(1,"h3"),l(2,"Couldn't load that symbol"),a(),i(3,"p"),l(4),a()()),t&2){let e=m(2);o(4),_(e.nodeDetail.state().error)}}function Ne(t,n){if(t&1&&(i(0,"div",17)(1,"p"),l(2),a()()),t&2){let e=m(2);o(2),j('No match for "',e.selectedSymbol(),'".')}}function Ve(t,n){if(t&1&&(i(0,"div",34),p(1,"span",44),i(2,"span",45),l(3),a()()),t&2){let e=n.$implicit;o(),q("data-kind",e),o(2),_(e)}}function Re(t,n){if(t&1){let e=O();i(0,"app-graph-canvas",28),M("nodeClick",function(d){w(e);let s=m(2);return S(s.pickFromCanvas(d))}),a(),i(1,"div",32)(2,"div",33),D(3,Ve,4,2,"div",34,U),a(),p(5,"div",35),i(6,"div",36)(7,"div",37),P(),i(8,"svg",38),p(9,"line",39),a(),G(),i(10,"span",40),l(11,"implements"),a()(),i(12,"div",37),P(),i(13,"svg",38),p(14,"line",41),a(),G(),i(15,"span",40),l(16,"tests"),a()(),i(17,"div",37),P(),i(18,"svg",38),p(19,"line",42),a(),G(),i(20,"span",40),l(21,"calls"),a()(),i(22,"div",37),P(),i(23,"svg",38),p(24,"line",43),a(),G(),i(25,"span",40),l(26,"synth"),a()()()()}if(t&2){let e=m(2);k("nodes",e.visibleNodes())("edges",e.visibleEdges())("selectedId",e.selectedDetail().id)("fitKey",e.fitKey())("hiddenIds",e.hiddenIds()),o(3),K(e.kindKeys)}}function Fe(t,n){if(t&1&&(i(0,"span",52),l(1),a()),t&2){let e=m();o(),_(e.startLine?"L"+e.startLine:"")}}function He(t,n){if(t&1&&(i(0,"div",60)(1,"div",62),l(2,"Signature"),a(),i(3,"pre",63),l(4),a()()),t&2){let e=m();o(4),_(e.signature)}}function $e(t,n){if(t&1&&(i(0,"div",60)(1,"div",62),l(2,"Docstring"),a(),i(3,"p",64),l(4),a()()),t&2){let e=m();o(4),_(e.docstring)}}function je(t,n){if(t&1&&(i(0,"span",69),l(1),a()),t&2){let e=m().$implicit;o(),_(e.specTitle)}}function Be(t,n){if(t&1&&(i(0,"div",66),p(1,"app-state-pill",67),i(2,"span",68),l(3),a(),b(4,je,2,1,"span",69),a()),t&2){let e=n.$implicit;o(),k("state",e.state),o(2),_(e.specId),o(),C(e.specTitle?4:-1)}}function Ae(t,n){if(t&1&&(i(0,"div",60)(1,"div",62),l(2,"Linked specs "),i(3,"span",65),l(4),a()(),D(5,Be,5,3,"div",66,ee),a()),t&2){let e=m();o(4),_(e.linkedSpecs.length),o(),K(e.linkedSpecs)}}function Qe(t,n){if(t&1){let e=O();i(0,"button",72),M("click",function(){let d=w(e).$implicit,s=m(5);return S(s.pickResult(d))}),p(1,"app-icon",73),i(2,"span",68),l(3),a(),i(4,"span",74),l(5),a()()}if(t&2){let e=n.$implicit,r=m(5);o(),k("size",12),o(2),_(e.name),o(2),_(r.fileName(e.filePath))}}function qe(t,n){if(t&1&&D(0,Qe,6,3,"button",71,ee),t&2){let e=m(2);K(e.callers)}}function Ue(t,n){t&1&&(i(0,"div",70),l(1,"No callers"),a())}function Xe(t,n){if(t&1){let e=O();i(0,"button",72),M("click",function(){let d=w(e).$implicit,s=m(5);return S(s.pickResult(d))}),p(1,"app-icon",73),i(2,"span",68),l(3),a(),i(4,"span",74),l(5),a()()}if(t&2){let e=n.$implicit,r=m(5);o(),k("size",12),o(2),_(e.name),o(2),_(r.fileName(e.filePath))}}function We(t,n){if(t&1&&D(0,Xe,6,3,"button",71,ee),t&2){let e=m(2);K(e.callees)}}function Ye(t,n){t&1&&(i(0,"div",70),l(1,"No callees"),a())}function Je(t,n){if(t&1&&(i(0,"div",60)(1,"div",62),l(2,"Callers "),i(3,"span",65),l(4),a()(),b(5,qe,2,0)(6,Ue,2,0,"div",70),a(),i(7,"div",60)(8,"div",62),l(9,"Callees "),i(10,"span",65),l(11),a()(),b(12,We,2,0)(13,Ye,2,0,"div",70),a()),t&2){let e=m();o(4),_(e.callers.length),o(),C(e.callers.length?5:6),o(6),_(e.callees.length),o(),C(e.callees.length?12:13)}}function Ze(t,n){if(t&1){let e=O();i(0,"button",75),p(1,"app-icon",76),l(2," Implement "),a(),i(3,"button",14),M("click",function(){w(e);let d=m(3);return S(d.navigateToDrift())}),l(4,"Fix"),a(),i(5,"button",77),l(6,"Relink"),a()}t&2&&(o(),k("size",13))}function et(t,n){if(t&1){let e=O();i(0,"button",75),p(1,"app-icon",78),l(2," Reveal in editor "),a(),i(3,"button",14),M("click",function(){w(e);let d=m(3);return S(d.navigateToSpecs())}),l(4,"Open Specs"),a()}t&2&&(o(),k("size",13))}function tt(t,n){if(t&1){let e=O();i(0,"div",46)(1,"div",47),p(2,"span",48),i(3,"div",49)(4,"div",50),l(5),a(),i(6,"div",51)(7,"span",52),l(8),a(),b(9,Fe,2,1,"span",52),a()(),i(10,"button",53),M("click",function(){w(e);let d=m(2);return S(d.clearSelection())}),p(11,"app-icon",54),a()(),i(12,"div",55),p(13,"app-icon",56),i(14,"span",57),l(15),a(),p(16,"app-copy-btn",58),a()(),i(17,"div",59),b(18,He,5,1,"div",60),b(19,$e,5,1,"div",60),b(20,Ae,7,1,"div",60),b(21,Je,14,4),a(),i(22,"div",61),b(23,Ze,7,1)(24,et,5,1),a()}if(t&2){let e=n,r=e.kind==="spec",d=m(2).detailColor(e);o(2),X("background",d)("box-shadow","0 0 8px "+d),Y("is-rect",r),o(3),_(e.name),o(2),X("color",d)("background","color-mix(in srgb, "+d+" 14%, transparent)"),o(),_(e.kind),o(),C(e.signature?9:-1),o(2),k("size",14),o(2),k("size",12),o(2),_(e.filePath),o(),k("text",e.filePath),o(2),C(e.signature?18:-1),o(),C(e.docstring?19:-1),o(),C(e.linkedSpecs&&e.linkedSpecs.length?20:-1),o(),C(r?-1:21),o(2),C(r?23:24)}}function nt(t,n){t&1&&(i(0,"span",83),l(1),a()),t&2&&(o(),j("",n.nodeCount.toLocaleString()," shown"))}function it(t,n){if(t&1&&(i(0,"div",86),p(1,"span",88),i(2,"span",89),l(3),a(),i(4,"span",90),l(5),a()()),t&2){let e=n.$implicit,r=m(3);o(),q("data-kind",e),o(2),_(e),o(2),_(r.overviewKindCounts()[e])}}function at(t,n){t&1&&(i(0,"div",87),p(1,"app-state-pill",91)(2,"span",92)(3,"app-state-pill",93)(4,"span",92)(5,"app-state-pill",94)(6,"span",92),a())}function ot(t,n){if(t&1&&(i(0,"div",87),p(1,"app-state-pill",91),i(2,"span",95),l(3),a(),p(4,"app-state-pill",93),i(5,"span",95),l(6),a(),p(7,"app-state-pill",94),i(8,"span",95),l(9),a()()),t&2){let e,r,d,s=m(3);o(3),_(((e=s.health.state().data)==null||e.linkHealth==null?null:e.linkHealth.verified)??0),o(3),_(((r=s.health.state().data)==null||r.linkHealth==null?null:r.linkHealth.drifted)??0),o(3),_((((d=s.health.state().data)==null||d.linkHealth==null?null:d.linkHealth.broken)??0)+(((d=s.health.state().data)==null||d.linkHealth==null?null:d.linkHealth.orphaned)??0))}}function rt(t,n){t&1&&(i(0,"div",96),P(),i(1,"svg",97),p(2,"line",98),a(),G(),p(3,"span",99),a(),i(4,"div",96),P(),i(5,"svg",97),p(6,"line",100),a(),G(),p(7,"span",99),a(),i(8,"div",96),P(),i(9,"svg",97),p(10,"line",101),a(),G(),p(11,"span",99),a(),i(12,"div",96),P(),i(13,"svg",97),p(14,"line",102),a(),G(),p(15,"span",99),a())}function lt(t,n){if(t&1&&(i(0,"div",96),P(),i(1,"svg",97),p(2,"line",98),a(),G(),i(3,"span",103),l(4,"calls"),a(),i(5,"span",104),l(6),a()(),i(7,"div",96),P(),i(8,"svg",97),p(9,"line",100),a(),G(),i(10,"span",103),l(11,"implements / documents"),a(),i(12,"span",104),l(13),a()(),i(14,"div",96),P(),i(15,"svg",97),p(16,"line",101),a(),G(),i(17,"span",103),l(18,"tests"),a(),i(19,"span",104),l(20),a()(),i(21,"div",96),P(),i(22,"svg",97),p(23,"line",102),a(),G(),i(24,"span",103),l(25,"synthesized (heuristic)"),a(),i(26,"span",104),l(27),a()()),t&2){let e,r,d,s,g=m(3);o(6),_(((e=g.health.state().data)==null||e.edgeKinds==null?null:e.edgeKinds.calls)??0),o(7),_(((r=g.health.state().data)==null||r.edgeKinds==null?null:r.edgeKinds.implements)??0),o(7),_(((d=g.health.state().data)==null||d.edgeKinds==null?null:d.edgeKinds.tests)??0),o(7),_(((s=g.health.state().data)==null||s.edgeKinds==null?null:s.edgeKinds.synth)??0)}}function dt(t,n){t&1&&(i(0,"div",105),p(1,"span",106)(2,"span",107),a())}function st(t,n){t&1&&(i(0,"div",85)(1,"div",62),l(2,"Most connected"),a(),D(3,dt,3,0,"div",105,U),a()),t&2&&(o(3),K(de(0,ke)))}function pt(t,n){if(t&1){let e=O();i(0,"button",109),M("click",function(){let d=w(e).$implicit,s=m(4);return S(s.pickHub(d))}),p(1,"span",110),i(2,"span",111),l(3),a(),i(4,"span",112),l(5),a(),p(6,"app-icon",113),a()}if(t&2){let e=n.$implicit,r=m(4);q("aria-label",e.name+", "+e.degree+" edges"),o(),X("background",r.nodeColor(e.kind))("border-radius",e.kind==="spec"?"2px":"50%"),o(2),_(e.name),o(2),j("",e.degree," edges"),o(),k("size",11)}}function ct(t,n){if(t&1&&(i(0,"div",85)(1,"div",62),l(2,"Most connected"),a(),D(3,pt,7,8,"button",108,ee),a()),t&2){let e=m(3);o(3),K(e.health.state().data.hubs.slice(0,8))}}function mt(t,n){if(t&1&&(i(0,"div",79),p(1,"app-icon",80),i(2,"span",81),l(3,"Graph overview"),a(),p(4,"div",82),b(5,nt,2,1,"span",83),a(),i(6,"div",59)(7,"p",84),l(8," Click any node to inspect its signature, links, callers and callees \u2014 without leaving this view. "),a(),i(9,"div",85)(10,"div",62),l(11,"Nodes by kind"),a(),D(12,it,6,3,"div",86,U),a(),i(14,"div",85)(15,"div",62),l(16,"Spec link health"),a(),b(17,at,7,0,"div",87)(18,ot,10,3,"div",87),a(),i(19,"div",85)(20,"div",62),l(21,"Edge types"),a(),b(22,rt,16,0)(23,lt,28,4),a(),b(24,st,5,1,"div",85)(25,ct,5,0,"div",85),a()),t&2){let e,r,d,s=m(2);o(),k("size",15),o(4),C((e=s.status.state().data)?5:-1,e),o(7),K(s.kindKeys),o(5),C(s.health.state().loading?17:18),o(5),C(s.health.state().loading?22:(r=s.health.state().data)!=null&&r.edgeKinds?23:-1),o(2),C(s.health.state().loading?24:!((d=s.health.state().data)==null||d.hubs==null)&&d.hubs.length?25:-1)}}function ut(t,n){if(t&1){let e=O();i(0,"div",1)(1,"div",2)(2,"div",3)(3,"app-segmented",4),M("change",function(d){w(e);let s=m();return S(s.onLayoutChange(d))}),a(),p(4,"div",5),D(5,Me,5,7,"button",6,U),p(7,"div",7),i(8,"div",8)(9,"div",9),p(10,"app-icon",10),i(11,"input",11),M("input",function(d){w(e);let s=m();return S(s.onSearchInput(d.target.value))}),a(),b(12,Ee,1,0,"span",12),a(),b(13,Ge,3,0,"div",13),a(),i(14,"button",14),M("click",function(){w(e);let d=m();return S(d.recenter())}),p(15,"app-icon",15),l(16," Recenter "),a()(),i(17,"div",16),b(18,ze,3,1)(19,De,3,0,"div",17)(20,Ke,5,1,"div",17)(21,Ne,3,1,"div",17)(22,Re,27,5),a()(),i(23,"aside",18),b(24,tt,25,22)(25,mt,26,5),a()()}if(t&2){let e,r=m();o(3),k("options",r.layoutOptions)("value",r.layoutMode()),o(2),K(r.kindKeys),o(5),k("size",13),o(),k("value",r.searchInput()),o(),C(r.searchResults.state().loading?12:-1),o(),C(r.searchResults.state().data&&r.searchResults.state().data.results.length>0?13:-1),o(2),k("size",13),o(3),C(r.selectedSymbol()?r.nodeDetail.state().loading?19:r.nodeDetail.state().error?20:r.selectedDetail()?22:21:18),o(6),C((e=r.selectedDetail())?24:25,e)}}var ve=class t{api=W(pe);projects=W(ce);router=W(se);status=B(this.api,()=>`/api/status${this.projects.projectQuery()}`);health=B(this.api,()=>`/api/graph/health${this.projects.projectQuery()}`);overviewScope=N("neighborhood");overviewLimit(){return this.overviewScope()==="full"?250:60}fullGraph=B(this.api,()=>`/api/graph/full?limit=${this.overviewLimit()}${this.projects.projectQuery("&")}`);layoutMode=N("hierarchical");layoutOptions=[{value:"hierarchical",label:"Hierarchical"},{value:"force",label:"Force"},{value:"anchored",label:"Anchored"}];searchInput=N("");searchQuery=N("");searchDebounce=null;searchResults=B(this.api,()=>{let n=this.searchQuery().trim();return n.length>=2?`/api/graph/search?q=${encodeURIComponent(n)}&limit=12${this.projects.projectQuery("&")}`:null});selectedSymbol=N(null);nodeDetail=B(this.api,()=>{let n=this.selectedSymbol();return n?`/api/graph/node?symbol=${encodeURIComponent(n)}${this.projects.projectQuery("&")}`:null});selectedDetail=T(()=>this.nodeDetail.state().data?.matches?.[0]??null);filters=N({code:!0,spec:!0,route:!0,test:!0});fitKey=N(0);canvasData=T(()=>{let n=this.selectedDetail();if(!n)return this.fullGraphData();let e=this.filters(),r=h=>{let y=J(h);return e[y]},d={id:n.id,label:n.name,sub:Z(n.filePath),kind:n.kind,x:0,y:0},s=(n.callers??[]).filter(h=>r(h.kind)),g=(n.callees??[]).filter(h=>r(h.kind)),E=(n.linkedSpecs??[]).filter(()=>e.spec),L=280,I=56,u=[d],z=[];return s.forEach((h,y)=>{let x=(y-(s.length-1)/2)*I;u.push(be(h,-L,x)),z.push({from:h.id,to:n.id,kind:"calls"})}),g.forEach((h,y)=>{let x=(y-(g.length-1)/2)*I;u.push(be(h,L,x)),z.push({from:n.id,to:h.id,kind:"calls"})}),E.forEach((h,y)=>{let x=(y-(E.length-1)/2)*220,c="spec:"+h.specId;u.push({id:c,label:h.specId,sub:h.specTitle??void 0,kind:"spec",state:h.state,x,y:-160}),z.push({from:c,to:n.id,kind:"implements"})}),{nodes:u,edges:z}});fullGraphData=T(()=>{let n=this.fullGraph.state().data;if(!n?.nodes?.length)return{nodes:[],edges:[]};let e=this.filters(),r=n.nodes.filter(u=>e[J(u.kind)]),d=new Set(r.map(u=>u.id)),s=n.edges.filter(u=>d.has(u.from)&&d.has(u.to)),g=new Map(r.map(u=>[u.id,u.kind])),E=fe(this.layoutMode(),r.map(u=>({id:u.id,kind:u.kind})),s.map(u=>({from:u.from,to:u.to}))),L=r.map(u=>({id:u.id,label:u.name,sub:Z(u.filePath),kind:u.kind,x:E.get(u.id)?.x??0,y:E.get(u.id)?.y??0})),I=s.map(u=>({from:u.from,to:u.to,kind:u.provenance==="heuristic"?"synth":g.get(u.from)==="spec"||g.get(u.to)==="spec"?"implements":"calls"}));return{nodes:L,edges:I}});toggleOverviewScope(){this.overviewScope.update(n=>n==="full"?"neighborhood":"full"),this.fitKey.update(n=>n+1)}fullGraphActive=T(()=>!this.selectedSymbol()&&this.fullGraphData().nodes.length>0);fullGraphLoading=T(()=>!this.selectedSymbol()&&this.fullGraph.state().loading);fullGraphCounts=T(()=>{let n=this.fullGraph.state().data;return n?{shown:n.shown,total:n.total}:{shown:0,total:0}});visibleNodes=T(()=>this.canvasData().nodes);visibleEdges=T(()=>this.canvasData().edges);hiddenIds=T(()=>{let n=this.filters(),e=new Set;for(let r of this.canvasData().nodes)n[J(r.kind)]||e.add(r.id);return e});kindCounts=T(()=>{let n={code:0,spec:0,route:0,test:0};for(let e of this.canvasData().nodes)n[J(e.kind)]++;return n});overviewKindCounts=T(()=>{let n=this.status.state().data;if(!n?.nodesByKind)return{code:0,spec:0,route:0,test:0};let e=n.nodesByKind;return{code:(e.function??0)+(e.method??0)+(e.class??0)+(e.variable??0)+(e.constant??0)+(e.type_alias??0)+(e.interface??0)+(e.struct??0)+(e.enum??0)+(e.property??0)+(e.field??0)+(e.file??0)+(e.module??0)+(e.namespace??0)+(e.component??0),spec:e.spec??0,route:e.route??0,test:e.test??0}});kindKeys=["code","spec","route","test"];isFilterActive(n){return this.filters()[n]}countFor(n){return this.kindCounts()[n]}chipColor(n){switch(n){case"spec":return"var(--node-spec)";case"route":return"var(--node-route)";case"test":return"var(--node-test)";default:return"var(--node-code)"}}onSearchInput(n){this.searchInput.set(n),this.searchDebounce&&clearTimeout(this.searchDebounce),this.searchDebounce=setTimeout(()=>this.searchQuery.set(n),180)}pickResult(n){this.searchInput.set(n.name),this.searchQuery.set(""),this.selectedSymbol.set(n.name)}pickFromCanvas(n){let e=this.selectedDetail();if(!e){let s=this.fullGraph.state().data?.nodes.find(g=>g.id===n);s&&(this.selectedSymbol.set(s.name),this.fitKey.update(g=>g+1));return}if(n===e.id)return;let d=[...e.callers??[],...e.callees??[]].find(s=>s.id===n);d&&(this.selectedSymbol.set(d.name),this.fitKey.update(s=>s+1))}toggleFilter(n){this.filters.update(e=>ae(ie({},e),{[n]:!e[n]})),this.fitKey.update(e=>e+1)}recenter(){this.fitKey.update(n=>n+1)}clearSelection(){this.selectedSymbol.set(null),this.searchInput.set("")}onLayoutChange(n){this.layoutMode.set(n),this.fitKey.update(e=>e+1)}shortPath(n){return Z(n)}fileName(n){return n?n.split("/").pop()??"":""}kindLabel(n){return n.charAt(0).toUpperCase()+n.slice(1)}navigateToSpecs(){this.router.navigate(["/specs"])}navigateToDrift(){this.router.navigate(["/drift"])}nodeColor(n){return n==="spec"?"var(--node-spec)":n==="route"?"var(--node-route)":n==="test"?"var(--node-test)":"var(--node-code)"}pickHub(n){this.selectedSymbol.set(n.name),this.fitKey.update(e=>e+1)}detailColor(n){if(n.kind==="spec"){let e=n.linkedSpecs??[],r=e.some(s=>s.state==="broken"||s.state==="orphaned"),d=!r&&e.some(s=>s.state==="drifted");return r?"var(--error)":d?"var(--warn)":"var(--node-spec)"}return n.kind==="test"?"var(--node-test)":n.kind==="route"?"var(--node-route)":"var(--node-code)"}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=oe({type:t,selectors:[["app-graph"]],decls:2,vars:1,consts:[["surface","The graph"],[1,"gp-root"],[1,"gp-main"],[1,"gp-toolbar"],["size","sm",3,"change","options","value"],[1,"gp-divider"],["type","button",1,"gp-chip",3,"is-active","--chip-color"],[1,"gp-spacer"],[1,"gp-search"],[1,"gp-search-inner"],["name","search",1,"gp-search-icon",3,"size"],["type","text","placeholder","Fuzzy symbol search\u2026","aria-label","Symbol search",3,"input","value"],["aria-hidden","true",1,"gp-spinner"],["role","listbox",1,"gp-results"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","recenter",3,"size"],[1,"gp-canvas-wrap"],[1,"gp-empty"],[1,"gp-panel"],["type","button",1,"gp-chip",3,"click"],[1,"gp-chip-dot"],[1,"gp-chip-count"],["type","button",1,"gp-result"],["type","button",1,"gp-result",3,"click"],[1,"gp-result-dot"],[1,"gp-result-name","mono"],[1,"gp-result-kind"],[1,"gp-result-path","mono"],[3,"nodeClick","nodes","edges","selectedId","fitKey","hiddenIds"],[1,"gp-overview-note"],["type","button",1,"btn","btn-ghost","btn-xs",3,"click"],[1,"gp-stats"],[1,"gp-legend"],[1,"gp-legend-kinds"],[1,"gp-legend-item"],[1,"gp-legend-sep"],[1,"gp-legend-edges"],[1,"gp-legend-edge-row"],["width","16","height","8"],["x1","0","y1","4","x2","16","y2","4","stroke","var(--node-spec)","stroke-width","1.6"],[1,"secondary"],["x1","0","y1","4","x2","16","y2","4","stroke","var(--node-test)","stroke-width","1.6"],["x1","0","y1","4","x2","16","y2","4","stroke","var(--text-muted)","stroke-width","1.6"],["x1","0","y1","4","x2","16","y2","4","stroke","var(--text-muted)","stroke-width","1.6","stroke-dasharray","4 3"],[1,"gp-legend-dot"],[1,"secondary",2,"text-transform","capitalize"],[1,"gp-panel-header"],[1,"gp-panel-header-inner"],[1,"gp-node-dot"],[1,"grow",2,"min-width","0"],[1,"mono","gp-panel-name"],[1,"row","gap-6",2,"margin-top","3px"],[1,"pill"],["type","button","aria-label","Close panel",1,"btn","btn-ghost","btn-xs",3,"click"],["name","x",3,"size"],[1,"gp-panel-path","row","gap-6"],["name","folder",2,"color","var(--text-muted)","flex-shrink","0",3,"size"],[1,"mono","secondary","gp-path-text"],[3,"text"],[1,"gp-panel-body","scroll-y"],[1,"gp-section"],[1,"gp-panel-footer"],[1,"eyebrow","gp-eyebrow"],[1,"gp-code","mono"],[1,"secondary",2,"font-size","12px","line-height","1.6","margin","0"],[1,"muted",2,"font-weight","400","margin-left","5px"],[1,"gp-link-row"],[3,"state"],[1,"mono","grow","gp-link-text"],[1,"muted",2,"font-size","10px","flex-shrink","0","overflow","hidden","text-overflow","ellipsis","white-space","nowrap","max-width","120px"],[1,"muted",2,"font-size","12px","padding","4px 8px"],["type","button",1,"gp-node-link"],["type","button",1,"gp-node-link",3,"click"],["name","box",2,"color","var(--node-code)","flex-shrink","0",3,"size"],[1,"mono","muted","gp-link-file"],["type","button",1,"btn","btn-primary","btn-sm","grow",2,"justify-content","center"],["name","play",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm"],["name","reveal",3,"size"],[1,"gp-panel-header","gp-overview-head"],["name","graph",2,"color","var(--accent)",3,"size"],[2,"font-weight","600","font-size","13.5px"],[1,"grow"],[1,"mono","muted",2,"font-size","11px"],[1,"secondary",2,"font-size","12px","line-height","1.55","margin","0 0 18px"],[1,"gp-section-plain"],[1,"gp-kind-row"],[1,"row","gap-6",2,"flex-wrap","wrap","align-items","center"],[1,"gp-kind-dot"],[1,"grow",2,"font-size","12.5px","text-transform","capitalize"],[1,"mono","tabular",2,"font-size","12.5px","font-weight","600"],["state","verified"],["aria-hidden","true",1,"gp-skel","gp-skel-sm"],["state","drifted"],["state","broken"],[1,"mono","tabular","muted",2,"font-size","11px","align-self","center"],[1,"gp-edge-row"],["width","20","height","8"],["x1","0","y1","4","x2","20","y2","4","stroke","var(--text-muted)","stroke-width","1.8"],["aria-hidden","true",1,"gp-skel","gp-skel-lg"],["x1","0","y1","4","x2","20","y2","4","stroke","var(--node-spec)","stroke-width","1.8"],["x1","0","y1","4","x2","20","y2","4","stroke","var(--node-test)","stroke-width","1.8"],["x1","0","y1","4","x2","20","y2","4","stroke","var(--text-muted)","stroke-width","1.8","stroke-dasharray","4 3"],[1,"grow","secondary",2,"font-size","12px"],[1,"mono","tabular","muted",2,"font-size","11px"],["aria-hidden","true",1,"gp-hub-row"],[1,"gp-hub-dot","gp-skel-dot"],["aria-hidden","true",1,"gp-skel","gp-skel-hub"],["type","button",1,"gp-hub-row"],["type","button",1,"gp-hub-row",3,"click"],[1,"gp-hub-dot"],[1,"mono","grow","gp-hub-name"],[1,"muted","gp-hub-degree"],["name","chevron-right",2,"color","var(--text-muted)","flex-shrink","0",3,"size"]],template:function(e,r){e&1&&b(0,Se,1,0,"app-pick-project-empty",0)(1,ut,26,9,"div",1),e&2&&C(r.status.state().noProject?0:1)},dependencies:[ge,_e,me,ue,he,xe],styles:["[_nghost-%COMP%]{display:contents}.gp-root[_ngcontent-%COMP%]{flex:1;display:flex;min-height:0;min-width:0}.gp-main[_ngcontent-%COMP%]{flex:1;min-width:0;display:flex;flex-direction:column}.gp-toolbar[_ngcontent-%COMP%]{display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid var(--border-subtle);background:var(--bg-panel-2, var(--bg-panel));flex-wrap:wrap;flex-shrink:0}.gp-divider[_ngcontent-%COMP%]{width:1px;height:20px;background:var(--border-subtle);flex-shrink:0}.gp-chip[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:999px;font-size:11.5px;font-weight:500;cursor:pointer;border:1px solid var(--border-subtle);background:var(--bg-panel);color:var(--text-secondary);transition:all .1s}.gp-chip.is-active[_ngcontent-%COMP%]{border-color:transparent;background:color-mix(in srgb,var(--chip-color, var(--accent)) 22%,var(--bg-panel));color:var(--chip-color, var(--accent))}.gp-chip-dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%}.gp-chip-dot[data-kind=code][_ngcontent-%COMP%]{background:var(--node-code)}.gp-chip-dot[data-kind=spec][_ngcontent-%COMP%]{background:var(--node-spec);border-radius:2px}.gp-chip-dot[data-kind=route][_ngcontent-%COMP%]{background:var(--node-route)}.gp-chip-dot[data-kind=test][_ngcontent-%COMP%]{background:var(--node-test)}.gp-chip-count[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted);font-variant-numeric:tabular-nums}.gp-spacer[_ngcontent-%COMP%]{flex:1}.gp-search[_ngcontent-%COMP%]{position:relative;flex-shrink:0}.gp-search-inner[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px;height:28px;padding:0 10px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:7px;width:220px}.gp-search-inner[_ngcontent-%COMP%] input[_ngcontent-%COMP%]{flex:1;background:transparent;border:none;outline:none;color:var(--text-primary);font-size:12px;font-family:var(--font-mono)}.gp-search-icon[_ngcontent-%COMP%]{color:var(--text-muted);flex-shrink:0}.gp-spinner[_ngcontent-%COMP%]{width:12px;height:12px;border:2px solid var(--border-subtle);border-top-color:var(--accent);border-radius:50%;animation:_ngcontent-%COMP%_gp-spin .7s linear infinite;flex-shrink:0}@keyframes _ngcontent-%COMP%_gp-spin{to{transform:rotate(360deg)}}.gp-results[_ngcontent-%COMP%]{position:absolute;top:32px;left:0;right:0;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:8px;box-shadow:var(--shadow-pop, 0 12px 28px rgba(0, 0, 0, .5));padding:5px;z-index:30;max-height:320px;overflow-y:auto}.gp-result[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border-radius:5px;background:transparent;border:none;cursor:pointer;font:inherit;color:var(--text-primary);text-align:left}.gp-result[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.gp-result-dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%;background:var(--node-code);flex-shrink:0}.gp-result-dot.is-spec[_ngcontent-%COMP%]{background:var(--node-spec);border-radius:2px}.gp-result-dot.is-route[_ngcontent-%COMP%]{background:var(--node-route)}.gp-result-dot.is-test[_ngcontent-%COMP%]{background:var(--node-test)}.gp-result-name[_ngcontent-%COMP%]{font-size:12px;flex-shrink:0}.gp-result-kind[_ngcontent-%COMP%]{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}.gp-result-path[_ngcontent-%COMP%]{flex:1;text-align:right;font-size:10px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gp-canvas-wrap[_ngcontent-%COMP%]{flex:1;min-height:0;position:relative;display:flex}.gp-canvas-wrap[_ngcontent-%COMP%] app-graph-canvas[_ngcontent-%COMP%]{flex:1;min-height:0}.gp-empty[_ngcontent-%COMP%]{flex:1;display:grid;place-items:center;text-align:center;padding:60px 24px;background:var(--bg-canvas-2, var(--bg-canvas))}.gp-empty[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{font-size:18px;font-weight:600;margin:0 0 8px}.gp-empty[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{color:var(--text-secondary);font-size:13.5px;line-height:1.6;margin:0;max-width:520px}.gp-stats[_ngcontent-%COMP%]{margin-top:18px!important;font-family:var(--font-mono);font-size:11.5px!important;color:var(--text-muted)!important}.gp-legend[_ngcontent-%COMP%]{position:absolute;top:14px;left:14px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;max-width:70%;background:color-mix(in srgb,var(--bg-panel) 86%,transparent);border:1px solid var(--border-subtle);border-radius:8px;padding:6px 10px;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-size:11px;pointer-events:none;z-index:3}.gp-legend-kinds[_ngcontent-%COMP%]{display:flex;align-items:center;gap:12px}.gp-legend-item[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px}.gp-legend-dot[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;flex-shrink:0}.gp-legend-dot[data-kind=code][_ngcontent-%COMP%]{background:var(--node-code)}.gp-legend-dot[data-kind=spec][_ngcontent-%COMP%]{background:var(--node-spec);border-radius:2px}.gp-legend-dot[data-kind=route][_ngcontent-%COMP%]{background:var(--node-route)}.gp-legend-dot[data-kind=test][_ngcontent-%COMP%]{background:var(--node-test)}.gp-legend-sep[_ngcontent-%COMP%]{width:1px;height:16px;background:var(--border-subtle);flex-shrink:0}.gp-legend-edges[_ngcontent-%COMP%]{display:flex;align-items:center;gap:10px}.gp-legend-edge-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px}.gp-panel[_ngcontent-%COMP%]{width:360px;flex-shrink:0;border-left:1px solid var(--border-subtle);background:var(--bg-panel);display:flex;flex-direction:column;overflow:hidden}.gp-panel-header[_ngcontent-%COMP%]{padding:12px 14px;border-bottom:1px solid var(--border-subtle);flex-shrink:0}.gp-panel-header-inner[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:8px}.gp-overview-head[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px}.gp-node-dot[_ngcontent-%COMP%]{width:9px;height:9px;border-radius:50%;flex-shrink:0;margin-top:5px}.gp-node-dot.is-rect[_ngcontent-%COMP%]{border-radius:2px}.gp-panel-name[_ngcontent-%COMP%]{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gp-panel-path[_ngcontent-%COMP%]{margin-top:8px;font-size:11.5px;color:var(--text-secondary)}.gp-path-text[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.gp-panel-body[_ngcontent-%COMP%]{flex:1;padding:14px;overflow-y:auto;display:flex;flex-direction:column;gap:18px}.gp-section[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:4px}.gp-section-plain[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:2px}.gp-eyebrow[_ngcontent-%COMP%]{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:6px}.gp-code[_ngcontent-%COMP%]{margin:0;font-size:11.5px;line-height:1.6;color:var(--text-secondary);background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:7px;padding:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word}.gp-link-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;border:1px solid var(--border-subtle);margin-bottom:5px;font-size:11.5px}.gp-link-text[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.gp-node-link[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;width:100%;padding:5px 8px;border-radius:6px;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;text-align:left;font-size:12px}.gp-node-link[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.gp-link-file[_ngcontent-%COMP%]{font-size:10px;flex-shrink:0}.gp-edge-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:4px 0}.gp-kind-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:5px 0}.gp-kind-dot[_ngcontent-%COMP%]{width:9px;height:9px;border-radius:50%;flex-shrink:0}.gp-kind-dot[data-kind=code][_ngcontent-%COMP%]{background:var(--node-code);box-shadow:0 0 6px var(--node-code)}.gp-kind-dot[data-kind=spec][_ngcontent-%COMP%]{background:var(--node-spec);border-radius:2px;box-shadow:0 0 6px var(--node-spec)}.gp-kind-dot[data-kind=route][_ngcontent-%COMP%]{background:var(--node-route);box-shadow:0 0 6px var(--node-route)}.gp-kind-dot[data-kind=test][_ngcontent-%COMP%]{background:var(--node-test);box-shadow:0 0 6px var(--node-test)}.gp-panel-footer[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid var(--border-subtle);flex-shrink:0}.gp-hub-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;width:100%;padding:5px 8px;border-radius:6px;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;text-align:left}.gp-hub-row[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.gp-hub-dot[_ngcontent-%COMP%]{width:8px;height:8px;flex-shrink:0}.gp-hub-name[_ngcontent-%COMP%]{font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gp-hub-degree[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted);flex-shrink:0;font-variant-numeric:tabular-nums}@keyframes _ngcontent-%COMP%_gp-skel-pulse{0%,to{opacity:.35}50%{opacity:.7}}.gp-skel[_ngcontent-%COMP%]{display:inline-block;background:var(--border-subtle);border-radius:4px;animation:_ngcontent-%COMP%_gp-skel-pulse 1.4s ease-in-out infinite}.gp-skel-sm[_ngcontent-%COMP%]{width:24px;height:12px}.gp-skel-lg[_ngcontent-%COMP%]{width:80px;height:12px;flex:1}.gp-skel-hub[_ngcontent-%COMP%]{width:120px;height:12px;flex:1}.gp-skel-dot[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--border-subtle);animation:_ngcontent-%COMP%_gp-skel-pulse 1.4s ease-in-out infinite}.grow[_ngcontent-%COMP%]{flex:1}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.gp-overview-note[_ngcontent-%COMP%]{position:absolute;bottom:12px;left:50%;transform:translate(-50%);padding:5px 12px;border-radius:999px;background:var(--bg-elevated);border:1px solid var(--border-subtle);color:var(--text-secondary);font-size:11px;white-space:nowrap;pointer-events:none;box-shadow:var(--shadow-pop)}"],changeDetection:0})};function be(t,n,e){return{id:t.id,label:t.name,sub:Z(t.filePath),kind:t.kind,x:n,y:e}}function Z(t){return t?t.split("/").slice(-2).join("/"):""}function J(t){return t==="spec"?"spec":t==="route"?"route":t==="test"?"test":"code"}export{ve as Graph};
@@ -0,0 +1 @@
1
+ import{a as U}from"./chunk-NKPK3BR3.js";import{a as G}from"./chunk-G7VZT5KB.js";import{a as q}from"./chunk-YWIRU745.js";import{a as Q}from"./chunk-JLOJCDIY.js";import{a as B}from"./chunk-PZSU53SS.js";import{a as h,b as j}from"./chunk-WBJ5VEB5.js";import{a as N,c as $,d as V}from"./chunk-DOKZOFKM.js";import{c as A}from"./chunk-DTWIJ3GJ.js";import{a as F}from"./chunk-JGDK6YKG.js";import{a as R}from"./chunk-TWVVIWKO.js";import{Aa as s,Ga as P,I as v,Ia as C,Ka as c,M as f,N as b,Ta as M,Ua as w,Va as l,W as E,Wa as _,Xa as y,Ya as O,aa as T,ea as i,ka as z,lb as m,qa as I,ra as g,sa as u,va as k,wa as S,xa as p,ya as a,yb as L,za as r,zb as H}from"./chunk-SYGIPC2P.js";import"./chunk-Q7L6LLAK.js";var D=(n,e)=>e.id;function W(n,e){n&1&&(a(0,"div",1),s(1,"div",3)(2,"div",4)(3,"div",5)(4,"div",6)(5,"div",7),r())}function X(n,e){if(n&1&&(a(0,"app-empty",2)(1,"a",8),s(2,"app-icon",9),l(3," Back to Specs "),r()()),n&2){let t=c();p("body","No spec matches "+t.specId()+"."),i(2),p("size",12)}}function Y(n,e){if(n&1&&(a(0,"app-pill",20),l(1),r()),n&2){let t=c();p("color","var(--node-code)")("bg","var(--node-code-soft)"),i(),_(t.priority)}}function Z(n,e){if(n&1&&(a(0,"span",22),l(1,"owned by "),a(2,"span",37),l(3),r()()),n&2){let t=c();i(3),_(t.owner)}}function ee(n,e){if(n&1&&(a(0,"span",22),l(1),r()),n&2){let t=c(2);i(),y("verified ",t.relTime(e))}}function te(n,e){if(n&1&&s(0,"span",43),n&2){let t=e.$implicit,o=c(3);M("background",o.critColor(t.state)),w("pending",t.state==="pending"),p("title",t.id+" \xB7 "+o.stateLabel(t.state))}}function ne(n,e){n&1&&s(0,"app-icon",45),n&2&&p("name",e)("size",12)}function ie(n,e){if(n&1&&s(0,"span",49),n&2){let t=c().$implicit,o=c(3);M("border-color",o.critColor(t.state))}}function oe(n,e){if(n&1&&(a(0,"div",42)(1,"span",44),g(2,ne,1,2,"app-icon",45)(3,ie,1,2,"span",46),r(),a(4,"span",47),l(5),r(),a(6,"span",48),l(7),r()()),n&2){let t,o=e.$implicit,d=e.$index,x=c(3);i(),M("background",x.critBg(o.state))("color",x.critColor(o.state)),p("title",x.stateLabel(o.state)),i(),u((t=x.critIcon(o.state))?2:3,t),i(3),y("A",d+1),i(2),_(o.title)}}function re(n,e){if(n&1&&(a(0,"section",23)(1,"div",26)(2,"div",24),l(3,"Acceptance criteria"),r(),a(4,"div",38),l(5),r()(),a(6,"div",39),k(7,te,1,5,"span",40,D),r(),a(9,"div",41),k(10,oe,8,8,"div",42,D),r()()),n&2){let t=c(2);i(4),w("all-met",t.metCount()===t.criteria().length),i(),O(" ",t.metCount()," / ",t.criteria().length," met "),i(2),S(t.criteria()),i(3),S(t.criteria())}}function ae(n,e){if(n&1&&(a(0,"div",27),l(1),r()),n&2){let t=c(2);i(),O("",t.links().length," ",t.links().length===1?"symbol":"symbols")}}function de(n,e){if(n&1&&(a(0,"div",28),l(1,"No linked code yet \u2014 run "),a(2,"span",50),l(3),r(),l(4," to link it."),r()),n&2){let t,o=c(2);i(3),y("/specship:spec implement ",(t=o.spec())==null?null:t.id)}}function le(n,e){if(n&1&&(a(0,"app-pill"),l(1),r()),n&2){let t=c().$implicit;i(),_(t.provenance)}}function ce(n,e){if(n&1){let t=P();a(0,"div",51),s(1,"app-state-pill",52),a(2,"span",53),l(3),r(),s(4,"span",54),g(5,le,2,1,"app-pill"),a(6,"button",55),C("click",function(){let d=f(t).$implicit,x=c(3);return b(x.revealLink(d))}),s(7,"app-icon",56),l(8," Reveal "),r()()}if(n&2){let t=e.$implicit,o=c(3);i(),p("state",t.state),i(2),_(o.linkTarget(t)),i(2),u(t.provenance?5:-1),i(),I("aria-label","Reveal "+o.linkTarget(t)),i(),p("size",12)}}function pe(n,e){if(n&1&&(a(0,"div",29),k(1,ce,9,5,"div",51,D),r()),n&2){let t=c(2);i(),S(t.links())}}function se(n,e){if(n&1){let t=P();a(0,"div",10)(1,"a",11),s(2,"app-icon",12),l(3," Specs "),r(),a(4,"span",13),l(5,"/"),r(),a(6,"span",14),l(7),r(),a(8,"span",13),l(9,"\xB7"),r(),a(10,"span",15),l(11),r(),s(12,"app-copy-btn",16),r(),a(13,"h1",17),l(14),r(),a(15,"div",18),s(16,"app-state-pill",19),g(17,Y,2,3,"app-pill",20),a(18,"span",21),l(19),r(),g(20,Z,4,1,"span",22),g(21,ee,2,1,"span",22),r(),a(22,"section",23)(23,"div",24),l(24,"Requirement"),r(),s(25,"div",25),r(),g(26,re,12,4,"section",23),a(27,"section",23)(28,"div",26)(29,"div",24),l(30,"Linked code"),r(),g(31,ae,2,2,"div",27),r(),g(32,de,5,1,"div",28)(33,pe,3,0,"div",29),r(),a(34,"div",30)(35,"button",31),C("click",function(){f(t);let d=c();return b(d.onImplement())}),s(36,"app-icon",32),l(37),r(),a(38,"button",33),C("click",function(){f(t);let d=c();return b(d.onVerify())}),s(39,"app-icon",34),l(40),r(),a(41,"button",33),C("click",function(){f(t);let d=c();return b(d.onEdit())}),s(42,"app-icon",35),l(43," Edit spec "),r(),a(44,"button",33),C("click",function(){f(t);let d=c();return b(d.onShowInGraph())}),s(45,"app-icon",36),l(46," Show in graph "),r()()}if(n&2){let t,o=e,d=c();i(2),p("size",13),i(5),_(o.sourcePath),i(4),_(o.id),i(),p("text",o.id),i(2),_(o.title),i(2),p("state",d.overallState())("withDot",!0),i(),u(o.priority?17:-1),i(2),_(o.kind),i(),u(o.owner?20:-1),i(),u((t=d.verifiedAt())?21:-1,t),i(4),p("innerHTML",d.bodyHtml(),T),i(),u(d.criteria().length>0?26:-1),i(5),u(d.links().length>0?31:-1),i(),u(d.links().length===0?32:33),i(4),p("size",12),i(),y(" ",d.copied()==="implement"?"Copied!":"Implement"," "),i(2),p("size",12),i(),y(" ",d.copied()==="verify"?"Copied!":"Verify"," "),i(2),p("size",12),i(3),p("size",12)}}function K(n){return n.replace(/<!--[\s\S]*?-->/g,"")}var me=["broken","orphaned","drifted","drafted","implementing","implemented","verified"],xe={verified:"check",implemented:"check",completed:"check",drifted:"drift",broken:"cancel",orphaned:"cancel",failed:"cancel"};function ge(n){return n.length?n.some(e=>e.state==="broken")?"broken":n.some(e=>e.state==="orphaned")?"orphaned":n.some(e=>e.state==="drifted")?"drifted":n.every(e=>e.state==="verified")?"verified":"implemented":"pending"}var J=class n{api=v(R);projects=v(F);router=v($);route=v(N);sanitizer=v(L);paramMap=U(this.route.paramMap,{initialValue:this.route.snapshot.paramMap});specId=m(()=>this.paramMap().get("id")??"");detail=A(this.api,()=>{let e=this.specId();return e?`/api/spec/${encodeURIComponent(e)}${this.projects.projectQuery()}`:null});loading=m(()=>this.detail.state().loading);spec=m(()=>this.detail.state().data?.spec??null);links=m(()=>this.detail.state().data?.links??[]);children=m(()=>this.detail.state().data?.children??[]);childLinks=m(()=>this.detail.state().data?.childLinks??{});notFound=m(()=>!this.loading()&&!!this.specId()&&!this.spec());overallState=m(()=>{let e=this.links();if(!e.length)return"drafted";for(let t of me)if(e.some(o=>o.state===t))return t;return"implemented"});verifiedAt=m(()=>{let e=this.links().filter(t=>t.state==="verified").map(t=>t.updatedAt);return e.length?Math.max(...e):null});criteria=m(()=>{let e=this.childLinks();return this.children().filter(t=>t.kind==="acceptance").map(t=>{let o=e[t.id]??[],d=ge(o);return{id:t.id,title:K(t.title).trim(),state:d,hasLinks:o.length>0,met:d==="verified"}})});metCount=m(()=>this.criteria().filter(e=>e.met).length);critIcon(e){return xe[e]??null}critColor(e){return h[e]?.color??"var(--text-muted)"}critBg(e){return h[e]?.bg??"rgba(255,255,255,0.05)"}bodyHtml=m(()=>this.sanitizer.bypassSecurityTrustHtml(G(K(this.spec()?.body??""))));stateColor(e){return h[e]?.color??"var(--node-spec)"}stateLabel(e){return h[e]?.label??e}relTime(e){let t=Date.now()-e,o=Math.max(0,Math.round(t/1e3));if(o<60)return`${o}s ago`;let d=Math.round(o/60);if(d<60)return`${d}m ago`;let x=Math.round(d/60);return x<24?`${x}h ago`:`${Math.round(x/24)}d ago`}linkTarget(e){return`${e.targetFilePath}:${e.targetQualifiedName}`}copied=E(null);copyCmd(e,t){try{navigator.clipboard?.writeText(e)}catch{}this.copied.set(t),setTimeout(()=>{this.copied()===t&&this.copied.set(null)},1800)}onImplement(){let e=this.spec()?.id;e&&this.copyCmd(`/specship:spec implement ${e}`,"implement")}onVerify(){this.copyCmd("/specship:check","verify")}onEdit(){this.router.navigate(["/specs"])}onShowInGraph(){let e=this.spec()?.id;this.router.navigate(["/graph"],{queryParams:e?{focus:"spec:"+e}:{}})}revealLink(e){this.router.navigate(["/graph"],{queryParams:{focus:this.linkTarget(e)}})}static \u0275fac=function(t){return new(t||n)};static \u0275cmp=z({type:n,selectors:[["app-spec-detail"]],decls:4,vars:1,consts:[[1,"scroll-y","spec-detail"],[1,"sd-skel"],["icon","book","title","Spec not found",3,"body"],[1,"skel",2,"height","16px","width","220px","border-radius","5px"],[1,"skel",2,"height","34px","width","60%","border-radius","7px","margin-top","14px"],[1,"skel",2,"height","20px","width","340px","border-radius","6px","margin-top","12px"],[1,"skel",2,"height","120px","border-radius","10px","margin-top","22px"],[1,"skel",2,"height","160px","border-radius","10px","margin-top","14px"],["action","","routerLink","/specs",1,"btn","btn-secondary","btn-sm"],["name","arrowRight",3,"size"],[1,"sd-crumb"],["routerLink","/specs",1,"sd-back"],["name","chevronLeft",3,"size"],[1,"sd-sep"],[1,"mono","sd-path"],[1,"mono","sd-id"],["label","Copy spec id",3,"text"],[1,"sd-title"],[1,"sd-meta"],[3,"state","withDot"],[3,"color","bg"],[1,"sd-meta-kind"],[1,"sd-meta-dim"],[1,"sd-section"],[1,"sd-eyebrow"],[1,"sd-prose",3,"innerHTML"],[1,"sd-section-head"],[1,"sd-count"],[1,"sd-empty-row"],[1,"sd-links"],[1,"sd-actions"],["type","button",1,"btn","btn-primary","btn-sm",3,"click"],["name","play",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","check",3,"size"],["name","book",3,"size"],["name","graph",3,"size"],[1,"sd-meta-owner"],[1,"sd-rollup"],[1,"sd-crit-bars"],[1,"sd-crit-seg",3,"pending","background","title"],[1,"sd-criteria"],[1,"sd-crit"],[1,"sd-crit-seg",3,"title"],[1,"sd-crit-mark",3,"title"],[3,"name","size"],[1,"sd-crit-dot",3,"border-color"],[1,"sd-crit-id","mono"],[1,"sd-crit-text"],[1,"sd-crit-dot"],[1,"mono"],[1,"sd-link"],[3,"state"],[1,"mono","sd-link-target"],[1,"grow"],["type","button",1,"btn","btn-ghost","btn-xs",3,"click"],["name","reveal",3,"size"]],template:function(t,o){if(t&1&&(a(0,"div",0),g(1,W,6,0,"div",1)(2,X,4,2,"app-empty",2)(3,se,47,21),r()),t&2){let d;i(),u(o.loading()?1:o.notFound()?2:(d=o.spec())?3:-1,d)}},dependencies:[V,H,j,Q,B,q],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.spec-detail[_ngcontent-%COMP%]{flex:1;padding:22px 26px 40px;color:var(--text-primary);background:var(--bg-canvas);max-width:920px}.sd-skel[_ngcontent-%COMP%]{display:flex;flex-direction:column}.sd-crumb[_ngcontent-%COMP%]{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--text-muted);margin-bottom:12px}.sd-back[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:3px;color:var(--text-secondary);text-decoration:none;cursor:pointer}.sd-back[_ngcontent-%COMP%]:hover{color:var(--text-primary)}.sd-sep[_ngcontent-%COMP%]{color:var(--text-faint)}.sd-path[_ngcontent-%COMP%]{color:var(--text-secondary)}.sd-id[_ngcontent-%COMP%]{color:var(--node-spec)}.sd-title[_ngcontent-%COMP%]{font-size:24px;font-weight:650;letter-spacing:-.02em;line-height:1.18;margin:0 0 12px;color:var(--text-primary)}.sd-meta[_ngcontent-%COMP%]{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:26px;font-size:12px}.sd-meta-kind[_ngcontent-%COMP%]{font-size:11px;color:var(--text-secondary);background:var(--bg-hover);border:1px solid var(--border-subtle);padding:2px 8px;border-radius:5px}.sd-meta-dim[_ngcontent-%COMP%]{color:var(--text-muted)}.sd-meta-owner[_ngcontent-%COMP%]{color:var(--text-secondary)}.sd-section[_ngcontent-%COMP%]{margin-bottom:24px}.sd-section-head[_ngcontent-%COMP%]{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:10px}.sd-eyebrow[_ngcontent-%COMP%]{font-size:10.5px;font-weight:650;letter-spacing:.09em;text-transform:uppercase;color:var(--text-muted);margin-bottom:10px}.sd-section-head[_ngcontent-%COMP%] .sd-eyebrow[_ngcontent-%COMP%]{margin-bottom:0}.sd-prose[_ngcontent-%COMP%]{font-size:14px;line-height:1.62;color:var(--text-secondary)}.sd-prose[_ngcontent-%COMP%] [_ngcontent-%COMP%]:global(strong){color:var(--text-primary);font-weight:600}.sd-prose[_ngcontent-%COMP%] [_ngcontent-%COMP%]:global(code){font-family:ui-monospace,Geist Mono,monospace;font-size:.86em;color:var(--node-spec);background:var(--node-spec-soft);padding:1px 5px;border-radius:4px}.sd-prose[_ngcontent-%COMP%] [_ngcontent-%COMP%]:global(p){margin:0 0 10px}.sd-rollup[_ngcontent-%COMP%]{font-size:11px;font-weight:600;color:var(--text-muted);font-family:ui-monospace,monospace}.sd-rollup.all-met[_ngcontent-%COMP%]{color:var(--success)}.sd-crit-bars[_ngcontent-%COMP%]{display:flex;gap:4px;margin-bottom:12px}.sd-crit-seg[_ngcontent-%COMP%]{flex:1;height:4px;border-radius:999px;opacity:.9}.sd-crit-seg.pending[_ngcontent-%COMP%]{opacity:.25}.sd-criteria[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:6px}.sd-crit[_ngcontent-%COMP%]{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:8px;background:var(--bg-panel);border:1px solid var(--border-subtle)}.sd-crit-mark[_ngcontent-%COMP%]{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:5px;flex-shrink:0}.sd-crit-dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%;border:1.6px solid currentColor;box-sizing:border-box}.sd-crit-id[_ngcontent-%COMP%]{font-size:10px;color:var(--text-muted);flex-shrink:0;width:22px}.sd-crit-text[_ngcontent-%COMP%]{font-size:13px;color:var(--text-secondary);line-height:1.45}.sd-count[_ngcontent-%COMP%]{font-size:11px;color:var(--text-muted);font-family:ui-monospace,monospace}.sd-empty-row[_ngcontent-%COMP%]{font-size:12.5px;color:var(--text-muted);padding:12px;border-radius:8px;background:var(--bg-panel);border:1px dashed var(--border-subtle)}.sd-empty-row[_ngcontent-%COMP%] .mono[_ngcontent-%COMP%]{color:var(--text-secondary)}.sd-links[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:5px}.sd-link[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:8px;background:var(--bg-panel);border:1px solid var(--border-subtle);font-size:12.5px}.sd-link-state[_ngcontent-%COMP%]{display:inline-flex;align-items:center;flex-shrink:0}.sd-link-target[_ngcontent-%COMP%]{color:var(--text-primary)}.grow[_ngcontent-%COMP%]{flex:1}.sd-prov[_ngcontent-%COMP%]{font-size:10px;color:var(--text-muted);background:var(--bg-hover);border:1px solid var(--border-subtle);padding:1px 6px;border-radius:4px}.sd-link-statelabel[_ngcontent-%COMP%]{font-size:11px;font-weight:600}.sd-actions[_ngcontent-%COMP%]{display:flex;flex-wrap:wrap;gap:8px;margin-top:28px;padding-top:18px;border-top:1px solid var(--border-subtle)}.mono[_ngcontent-%COMP%]{font-family:ui-monospace,Geist Mono,monospace}'],changeDetection:0})};export{J as SpecDetail};
@@ -1 +1 @@
1
- import{a as te}from"./chunk-QPA2REUE.js";import{a as ee}from"./chunk-YNYVOPWF.js";import{a as Q}from"./chunk-5NTGUSXJ.js";import{a as X}from"./chunk-IOB4JJT4.js";import{c as q}from"./chunk-DTWIJ3GJ.js";import"./chunk-JGDK6YKG.js";import{a as Z}from"./chunk-TWVVIWKO.js";import{Aa as m,Ba as P,Ca as w,Da as A,Ea as H,Fa as j,I as N,Ia as I,Ka as u,O as z,P as R,Ta as y,Ua as G,Va as c,W as $,Wa as d,Xa as S,Ya as K,bb as C,ea as i,fb as V,gb as Y,ib as U,ka as O,lb as p,pb as k,qa as T,ra as _,sa as h,ua as B,va as v,vb as W,wa as b,xa as g,ya as r,za as s,zb as J}from"./chunk-SYGIPC2P.js";import"./chunk-Q7L6LLAK.js";var ge=(o,e)=>e.label;function fe(o,e){if(o&1&&(z(),A(0,"path")),o&2){let t=e.$implicit;T("d",t.d)("fill",t.color)}}function xe(o,e){if(o&1&&(P(0,"div",3),c(1),w()),o&2){let t=u();i(),d(t.centerSub())}}var E=class o{data=k([]);size=k(150);centerLabel=k("");centerSub=k("");arcs=p(()=>{let e=this.data(),t=e.reduce((x,D)=>x+D.cost,0);if(t<=0)return[];let a=this.size()/2,l=a*.6,f=-Math.PI/2,F=[];for(let x of e){let D=x.cost/t*Math.PI*2,M=f+D,L=D>Math.PI?1:0,ie=a+Math.cos(f)*a,ae=a+Math.sin(f)*a,re=a+Math.cos(M)*a,se=a+Math.sin(M)*a,ce=a+Math.cos(M)*l,de=a+Math.sin(M)*l,le=a+Math.cos(f)*l,pe=a+Math.sin(f)*l;F.push({d:`M ${ie} ${ae} A ${a} ${a} 0 ${L} 1 ${re} ${se} L ${ce} ${de} A ${l} ${l} 0 ${L} 0 ${le} ${pe} Z`,color:x.color,label:x.short||x.model||""}),f=M}return F});static \u0275fac=function(t){return new(t||o)};static \u0275cmp=O({type:o,selectors:[["app-donut"]],inputs:{data:[1,"data"],size:[1,"size"],centerLabel:[1,"centerLabel"],centerSub:[1,"centerSub"]},decls:8,vars:9,consts:[[1,"donut"],[1,"donut-center"],[1,"center-label"],[1,"center-sub"]],template:function(t,n){t&1&&(P(0,"div",0),z(),P(1,"svg"),v(2,fe,1,2,":svg:path",null,ge),w(),R(),P(4,"div",1)(5,"div",2),c(6),w(),_(7,xe,2,1,"div",3),w()()),t&2&&(y("width",n.size(),"px")("height",n.size(),"px"),i(),T("width",n.size())("height",n.size())("viewBox","0 0 "+n.size()+" "+n.size()),i(),b(n.arcs()),i(4),d(n.centerLabel()),i(),h(n.centerSub()?7:-1))},styles:["[_nghost-%COMP%]{display:inline-block}.donut[_ngcontent-%COMP%]{position:relative}.donut-center[_ngcontent-%COMP%]{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.center-label[_ngcontent-%COMP%]{font-variant-numeric:tabular-nums;font-size:18px;font-weight:700;color:var(--text-primary)}.center-sub[_ngcontent-%COMP%]{color:var(--text-muted);font-size:10.5px;margin-top:2px}"],changeDetection:0})};var _e=()=>({value:"today",label:"Today"}),he=()=>({value:"week",label:"This week"}),ve=()=>({value:"month",label:"This month"}),be=()=>({value:"all",label:"All time"}),ye=(o,e,t,n)=>[o,e,t,n],Ce=()=>[0,1,2,3],Me=(o,e)=>e.model,Pe=(o,e)=>e.id;function we(o,e){if(o&1&&m(0,"app-delta",10),o&2){let t=u();g("value",t.wowDelta())("invert",!0)}}function Se(o,e){if(o&1&&(r(0,"div",11)(1,"div",24),c(2),s(),r(3,"div",25),c(4),s()()),o&2){let t=e,n=u();i(2),d(n.fmt$(t.cost)),i(2),K("",t.prompts," prompts \xB7 day ",t.day)}}function ke(o,e){if(o&1&&(r(0,"div",19),m(1,"span",26),r(2,"span",27),c(3),s(),r(4,"span",28),c(5),s(),r(6,"span",29),c(7),s()()),o&2){let t=e.$implicit,n=u();i(),y("background",t.color),i(2),d(t.short),i(2),d(n.fmt$(t.cost)),i(2),S("",n.percentOf(t.cost,n.total()),"%")}}function De(o,e){o&1&&m(0,"div",30)}function Oe(o,e){o&1&&v(0,De,1,0,"div",30,B),o&2&&b(C(0,Ce))}function Ee(o,e){if(o&1&&(r(0,"div",32)(1,"span",33),c(2),s(),r(3,"div",34)(4,"div",35),c(5),s(),r(6,"div",36),m(7,"span",37),s()(),r(8,"span",38),c(9),s(),r(10,"span",39),c(11),s(),r(12,"span",40),c(13),Y(14,"number"),s(),r(15,"span",41),c(16),s()()),o&2){let t=e.$implicit,n=e.$index,a=u(2);G("first",n===0),i(2),d(n+1),i(3),d(t.text),i(2),y("width",t.cost_usd/a.maxPrompt()*100,"%")("background",t.cost_usd/a.maxPrompt()>.66?"var(--error)":"var(--accent)"),i(2),d(a.modelShort(t.model)),i(2),d(a.fmtK(a.promptTokens(t))),i(),y("color",a.cacheColor(a.cacheRate(t))),i(),S("",U(14,14,a.cacheRate(t)*100,"1.0-0"),"%"),i(3),d(a.fmt$(t.cost_usd))}}function ze(o,e){if(o&1&&v(0,Ee,17,17,"div",31,Pe),o&2){let t=u();b(t.topPrompts())}}var $e={"claude-opus-4-7":"#A586F5","claude-opus-4":"#A586F5","claude-sonnet-4-6":"#5B93F2","claude-sonnet-4-7":"#5B93F2","claude-sonnet-4":"#5B93F2","claude-haiku-4-5":"#29D2BE","claude-haiku-4":"#29D2BE"},ne={"claude-opus-4-7":"Opus 4.7","claude-opus-4":"Opus 4","claude-sonnet-4-6":"Sonnet 4.6","claude-sonnet-4-7":"Sonnet 4.7","claude-sonnet-4":"Sonnet 4","claude-haiku-4-5":"Haiku 4.5","claude-haiku-4":"Haiku 4"},oe=class o{api=N(Z);range=$("month");ranges=["today","week","month","all"];hovered=$(null);resource=q(this.api,()=>`/api/claude/costs?range=${this.range()}`);series=p(()=>this.resource.state().data?.series??[]);topPrompts=p(()=>(this.resource.state().data?.topPrompts??[]).slice(0,12));maxPrompt=p(()=>{let e=this.topPrompts();return Math.max(.01,...e.map(t=>t.cost_usd??0))});byModel=p(()=>(this.resource.state().data?.byModel??[]).map(t=>({model:t.model,short:ne[t.model]||t.model,cost:t.cost||0,color:$e[t.model]||"#5C6573"})));total=p(()=>this.resource.state().data?.total??0);wowDelta=p(()=>this.resource.state().data?.wowDelta??0);setRange(e){this.range.set(e)}onHover(e){this.hovered.set(e)}fmt$(e){return"$"+e.toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2})}fmtK(e){return e>=1e3?(e/1e3).toFixed(e>=1e4?0:1)+"k":String(e)}promptTokens(e){return(e.input_tokens??0)+(e.output_tokens??0)+(e.cache_creation_tokens??0)+(e.cache_read_tokens??0)}cacheRate(e){let t=(e.input_tokens??0)+(e.cache_creation_tokens??0)+(e.cache_read_tokens??0);return t>0?(e.cache_read_tokens??0)/t:0}cacheColor(e){return e>=.7?"var(--success)":e>=.5?"var(--warn)":"var(--error)"}modelShort(e){return e?ne[e]||e.replace(/^claude-/,""):"?"}percentOf(e,t){return t>0?Math.round(e/t*100):0}static \u0275fac=function(t){return new(t||o)};static \u0275cmp=O({type:o,selectors:[["app-costs"]],decls:34,vars:25,consts:[[1,"page","scroll-y"],["icon","dollar","title","Costs","sub","Where the money goes"],["actions",""],["size","sm",3,"change","value","options"],[1,"top-grid"],[1,"card","card-pad","chart-card"],[1,"chart-header"],[1,"big-number","mono"],[1,"row","gap-8",2,"margin-top","2px"],[1,"muted",2,"font-size","11.5px"],[3,"value","invert"],[2,"text-align","right"],["color","var(--accent)",3,"hover","series","height"],[1,"card","card-pad","donut-card"],[1,"row","gap-8",2,"align-self","flex-start","margin-bottom","8px"],["name","layers",2,"color","var(--node-code)",3,"size"],[2,"font-weight","600","font-size","12.5px"],[3,"data","size","centerLabel","centerSub"],[1,"model-legend"],[1,"legend-row"],[1,"card","card-pad"],[1,"row","gap-8",2,"margin-bottom","12px"],["name","sortAsc",2,"color","var(--accent)",3,"size"],[1,"muted",2,"font-size","11px"],[1,"mono",2,"font-size","13px","font-weight","600"],[1,"muted",2,"font-size","10.5px"],[1,"swatch"],[1,"mono","grow",2,"font-size","11.5px"],[1,"mono",2,"font-size","11.5px"],[1,"mono","muted",2,"font-size","10.5px","width","38px","text-align","right"],[1,"skel","row-skel"],[1,"prompt-row",3,"first"],[1,"prompt-row"],[1,"mono","muted","tabular","rank"],[1,"prompt-grow"],[1,"prompt-text"],[1,"bar-track",2,"margin-top","4px","max-width","280px"],[1,"bar-fill"],[1,"mono","muted","prompt-model"],[1,"mono","muted","tabular","prompt-toks"],[1,"mono","tabular","cache-cell"],[1,"mono","tabular","cost-cell"]],template:function(t,n){if(t&1&&(r(0,"div",0)(1,"app-page-head",1),H(2,2),r(3,"app-segmented",3),I("change",function(l){return n.setRange(l)}),s(),j(),s(),r(4,"div",4)(5,"div",5)(6,"div",6)(7,"div")(8,"div",7),c(9),s(),r(10,"div",8)(11,"span",9),c(12),s(),_(13,we,1,2,"app-delta",10),s()(),_(14,Se,5,3,"div",11),s(),r(15,"app-line-chart",12),I("hover",function(l){return n.onHover(l)}),s()(),r(16,"div",13)(17,"div",14),m(18,"app-icon",15),r(19,"span",16),c(20,"By model"),s()(),m(21,"app-donut",17),r(22,"div",18),v(23,ke,8,5,"div",19,Me),s()()(),r(25,"div",20)(26,"div",21),m(27,"app-icon",22),r(28,"span",16),c(29,"Most expensive prompts"),s(),r(30,"span",23),c(31),s()(),_(32,Oe,2,1)(33,ze,2,0),s()()),t&2){let a;i(3),g("value",n.range())("options",V(20,ye,C(16,_e),C(17,he),C(18,ve),C(19,be))),i(6),d(n.fmt$(n.total())),i(3),S("total \xB7 ",n.range()),i(),h(n.range()!=="all"&&n.wowDelta()!==0?13:-1),i(),h((a=n.hovered())?14:-1,a),i(),g("series",n.series())("height",200),i(3),g("size",14),i(3),g("data",n.byModel())("size",150)("centerLabel","$"+n.total().toFixed(0))("centerSub",n.range()+" window"),i(2),b(n.byModel()),i(4),g("size",14),i(4),S("\xB7 top ",n.topPrompts().length),i(),h(n.resource.state().loading?32:33)}},dependencies:[te,E,Q,X,ee,J,W],styles:["[_nghost-%COMP%]{display:contents}.page[_ngcontent-%COMP%]{flex:1;padding:18px}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1}.card[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:var(--r-lg)}.card-pad[_ngcontent-%COMP%]{padding:14px}.top-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1.3fr 1fr;gap:14px;margin-bottom:14px}.chart-card[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:10px}.chart-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:flex-start}.big-number[_ngcontent-%COMP%]{font-size:34px;font-weight:700;letter-spacing:-.02em}.donut-card[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:10px}.model-legend[_ngcontent-%COMP%]{width:100%;display:flex;flex-direction:column;gap:6px;margin-top:14px}.legend-row[_ngcontent-%COMP%]{display:flex;gap:8px;align-items:center}.swatch[_ngcontent-%COMP%]{width:9px;height:9px;border-radius:3px;flex-shrink:0}.prompt-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:18px 1fr 70px 54px 44px 56px;gap:12px;align-items:center;padding:9px 0;border-top:1px solid var(--border-subtle)}.prompt-row.first[_ngcontent-%COMP%]{border-top:none}.rank[_ngcontent-%COMP%]{width:16px;font-size:11px}.prompt-grow[_ngcontent-%COMP%]{min-width:0}.prompt-text[_ngcontent-%COMP%]{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bar-track[_ngcontent-%COMP%]{height:4px;background:#ffffff0f;border-radius:999px;overflow:hidden}.bar-fill[_ngcontent-%COMP%]{display:block;height:100%;border-radius:999px}.prompt-model[_ngcontent-%COMP%]{font-size:10.5px;text-align:right}.prompt-toks[_ngcontent-%COMP%], .cache-cell[_ngcontent-%COMP%]{font-size:11px;text-align:right}.cost-cell[_ngcontent-%COMP%]{font-size:13px;font-weight:600;text-align:right}.skel[_ngcontent-%COMP%]{background:var(--bg-elevated);border-radius:5px;animation:_ngcontent-%COMP%_skeleton 1.4s ease-in-out infinite}.row-skel[_ngcontent-%COMP%]{height:30px;margin:6px 0}@keyframes _ngcontent-%COMP%_skeleton{0%,to{opacity:.5}50%{opacity:.9}}"],changeDetection:0})};export{oe as Costs};
1
+ import{a as te}from"./chunk-QPA2REUE.js";import{a as ee}from"./chunk-AHI5XEVX.js";import{a as Q}from"./chunk-5NTGUSXJ.js";import{a as X}from"./chunk-IOB4JJT4.js";import{c as q}from"./chunk-DTWIJ3GJ.js";import"./chunk-JGDK6YKG.js";import{a as Z}from"./chunk-TWVVIWKO.js";import{Aa as m,Ba as P,Ca as w,Da as A,Ea as H,Fa as j,I as N,Ia as I,Ka as u,O as z,P as R,Ta as y,Ua as G,Va as c,W as $,Wa as d,Xa as S,Ya as K,bb as C,ea as i,fb as V,gb as Y,ib as U,ka as O,lb as p,pb as k,qa as T,ra as _,sa as h,ua as B,va as v,vb as W,wa as b,xa as g,ya as r,za as s,zb as J}from"./chunk-SYGIPC2P.js";import"./chunk-Q7L6LLAK.js";var ge=(o,e)=>e.label;function fe(o,e){if(o&1&&(z(),A(0,"path")),o&2){let t=e.$implicit;T("d",t.d)("fill",t.color)}}function xe(o,e){if(o&1&&(P(0,"div",3),c(1),w()),o&2){let t=u();i(),d(t.centerSub())}}var E=class o{data=k([]);size=k(150);centerLabel=k("");centerSub=k("");arcs=p(()=>{let e=this.data(),t=e.reduce((x,D)=>x+D.cost,0);if(t<=0)return[];let a=this.size()/2,l=a*.6,f=-Math.PI/2,F=[];for(let x of e){let D=x.cost/t*Math.PI*2,M=f+D,L=D>Math.PI?1:0,ie=a+Math.cos(f)*a,ae=a+Math.sin(f)*a,re=a+Math.cos(M)*a,se=a+Math.sin(M)*a,ce=a+Math.cos(M)*l,de=a+Math.sin(M)*l,le=a+Math.cos(f)*l,pe=a+Math.sin(f)*l;F.push({d:`M ${ie} ${ae} A ${a} ${a} 0 ${L} 1 ${re} ${se} L ${ce} ${de} A ${l} ${l} 0 ${L} 0 ${le} ${pe} Z`,color:x.color,label:x.short||x.model||""}),f=M}return F});static \u0275fac=function(t){return new(t||o)};static \u0275cmp=O({type:o,selectors:[["app-donut"]],inputs:{data:[1,"data"],size:[1,"size"],centerLabel:[1,"centerLabel"],centerSub:[1,"centerSub"]},decls:8,vars:9,consts:[[1,"donut"],[1,"donut-center"],[1,"center-label"],[1,"center-sub"]],template:function(t,n){t&1&&(P(0,"div",0),z(),P(1,"svg"),v(2,fe,1,2,":svg:path",null,ge),w(),R(),P(4,"div",1)(5,"div",2),c(6),w(),_(7,xe,2,1,"div",3),w()()),t&2&&(y("width",n.size(),"px")("height",n.size(),"px"),i(),T("width",n.size())("height",n.size())("viewBox","0 0 "+n.size()+" "+n.size()),i(),b(n.arcs()),i(4),d(n.centerLabel()),i(),h(n.centerSub()?7:-1))},styles:["[_nghost-%COMP%]{display:inline-block}.donut[_ngcontent-%COMP%]{position:relative}.donut-center[_ngcontent-%COMP%]{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.center-label[_ngcontent-%COMP%]{font-variant-numeric:tabular-nums;font-size:18px;font-weight:700;color:var(--text-primary)}.center-sub[_ngcontent-%COMP%]{color:var(--text-muted);font-size:10.5px;margin-top:2px}"],changeDetection:0})};var _e=()=>({value:"today",label:"Today"}),he=()=>({value:"week",label:"This week"}),ve=()=>({value:"month",label:"This month"}),be=()=>({value:"all",label:"All time"}),ye=(o,e,t,n)=>[o,e,t,n],Ce=()=>[0,1,2,3],Me=(o,e)=>e.model,Pe=(o,e)=>e.id;function we(o,e){if(o&1&&m(0,"app-delta",10),o&2){let t=u();g("value",t.wowDelta())("invert",!0)}}function Se(o,e){if(o&1&&(r(0,"div",11)(1,"div",24),c(2),s(),r(3,"div",25),c(4),s()()),o&2){let t=e,n=u();i(2),d(n.fmt$(t.cost)),i(2),K("",t.prompts," prompts \xB7 day ",t.day)}}function ke(o,e){if(o&1&&(r(0,"div",19),m(1,"span",26),r(2,"span",27),c(3),s(),r(4,"span",28),c(5),s(),r(6,"span",29),c(7),s()()),o&2){let t=e.$implicit,n=u();i(),y("background",t.color),i(2),d(t.short),i(2),d(n.fmt$(t.cost)),i(2),S("",n.percentOf(t.cost,n.total()),"%")}}function De(o,e){o&1&&m(0,"div",30)}function Oe(o,e){o&1&&v(0,De,1,0,"div",30,B),o&2&&b(C(0,Ce))}function Ee(o,e){if(o&1&&(r(0,"div",32)(1,"span",33),c(2),s(),r(3,"div",34)(4,"div",35),c(5),s(),r(6,"div",36),m(7,"span",37),s()(),r(8,"span",38),c(9),s(),r(10,"span",39),c(11),s(),r(12,"span",40),c(13),Y(14,"number"),s(),r(15,"span",41),c(16),s()()),o&2){let t=e.$implicit,n=e.$index,a=u(2);G("first",n===0),i(2),d(n+1),i(3),d(t.text),i(2),y("width",t.cost_usd/a.maxPrompt()*100,"%")("background",t.cost_usd/a.maxPrompt()>.66?"var(--error)":"var(--accent)"),i(2),d(a.modelShort(t.model)),i(2),d(a.fmtK(a.promptTokens(t))),i(),y("color",a.cacheColor(a.cacheRate(t))),i(),S("",U(14,14,a.cacheRate(t)*100,"1.0-0"),"%"),i(3),d(a.fmt$(t.cost_usd))}}function ze(o,e){if(o&1&&v(0,Ee,17,17,"div",31,Pe),o&2){let t=u();b(t.topPrompts())}}var $e={"claude-opus-4-7":"#A586F5","claude-opus-4":"#A586F5","claude-sonnet-4-6":"#5B93F2","claude-sonnet-4-7":"#5B93F2","claude-sonnet-4":"#5B93F2","claude-haiku-4-5":"#29D2BE","claude-haiku-4":"#29D2BE"},ne={"claude-opus-4-7":"Opus 4.7","claude-opus-4":"Opus 4","claude-sonnet-4-6":"Sonnet 4.6","claude-sonnet-4-7":"Sonnet 4.7","claude-sonnet-4":"Sonnet 4","claude-haiku-4-5":"Haiku 4.5","claude-haiku-4":"Haiku 4"},oe=class o{api=N(Z);range=$("month");ranges=["today","week","month","all"];hovered=$(null);resource=q(this.api,()=>`/api/claude/costs?range=${this.range()}`);series=p(()=>this.resource.state().data?.series??[]);topPrompts=p(()=>(this.resource.state().data?.topPrompts??[]).slice(0,12));maxPrompt=p(()=>{let e=this.topPrompts();return Math.max(.01,...e.map(t=>t.cost_usd??0))});byModel=p(()=>(this.resource.state().data?.byModel??[]).map(t=>({model:t.model,short:ne[t.model]||t.model,cost:t.cost||0,color:$e[t.model]||"#5C6573"})));total=p(()=>this.resource.state().data?.total??0);wowDelta=p(()=>this.resource.state().data?.wowDelta??0);setRange(e){this.range.set(e)}onHover(e){this.hovered.set(e)}fmt$(e){return"$"+e.toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2})}fmtK(e){return e>=1e3?(e/1e3).toFixed(e>=1e4?0:1)+"k":String(e)}promptTokens(e){return(e.input_tokens??0)+(e.output_tokens??0)+(e.cache_creation_tokens??0)+(e.cache_read_tokens??0)}cacheRate(e){let t=(e.input_tokens??0)+(e.cache_creation_tokens??0)+(e.cache_read_tokens??0);return t>0?(e.cache_read_tokens??0)/t:0}cacheColor(e){return e>=.7?"var(--success)":e>=.5?"var(--warn)":"var(--error)"}modelShort(e){return e?ne[e]||e.replace(/^claude-/,""):"?"}percentOf(e,t){return t>0?Math.round(e/t*100):0}static \u0275fac=function(t){return new(t||o)};static \u0275cmp=O({type:o,selectors:[["app-costs"]],decls:34,vars:25,consts:[[1,"page","scroll-y"],["icon","dollar","title","Costs","sub","Where the money goes"],["actions",""],["size","sm",3,"change","value","options"],[1,"top-grid"],[1,"card","card-pad","chart-card"],[1,"chart-header"],[1,"big-number","mono"],[1,"row","gap-8",2,"margin-top","2px"],[1,"muted",2,"font-size","11.5px"],[3,"value","invert"],[2,"text-align","right"],["color","var(--accent)",3,"hover","series","height"],[1,"card","card-pad","donut-card"],[1,"row","gap-8",2,"align-self","flex-start","margin-bottom","8px"],["name","layers",2,"color","var(--node-code)",3,"size"],[2,"font-weight","600","font-size","12.5px"],[3,"data","size","centerLabel","centerSub"],[1,"model-legend"],[1,"legend-row"],[1,"card","card-pad"],[1,"row","gap-8",2,"margin-bottom","12px"],["name","sortAsc",2,"color","var(--accent)",3,"size"],[1,"muted",2,"font-size","11px"],[1,"mono",2,"font-size","13px","font-weight","600"],[1,"muted",2,"font-size","10.5px"],[1,"swatch"],[1,"mono","grow",2,"font-size","11.5px"],[1,"mono",2,"font-size","11.5px"],[1,"mono","muted",2,"font-size","10.5px","width","38px","text-align","right"],[1,"skel","row-skel"],[1,"prompt-row",3,"first"],[1,"prompt-row"],[1,"mono","muted","tabular","rank"],[1,"prompt-grow"],[1,"prompt-text"],[1,"bar-track",2,"margin-top","4px","max-width","280px"],[1,"bar-fill"],[1,"mono","muted","prompt-model"],[1,"mono","muted","tabular","prompt-toks"],[1,"mono","tabular","cache-cell"],[1,"mono","tabular","cost-cell"]],template:function(t,n){if(t&1&&(r(0,"div",0)(1,"app-page-head",1),H(2,2),r(3,"app-segmented",3),I("change",function(l){return n.setRange(l)}),s(),j(),s(),r(4,"div",4)(5,"div",5)(6,"div",6)(7,"div")(8,"div",7),c(9),s(),r(10,"div",8)(11,"span",9),c(12),s(),_(13,we,1,2,"app-delta",10),s()(),_(14,Se,5,3,"div",11),s(),r(15,"app-line-chart",12),I("hover",function(l){return n.onHover(l)}),s()(),r(16,"div",13)(17,"div",14),m(18,"app-icon",15),r(19,"span",16),c(20,"By model"),s()(),m(21,"app-donut",17),r(22,"div",18),v(23,ke,8,5,"div",19,Me),s()()(),r(25,"div",20)(26,"div",21),m(27,"app-icon",22),r(28,"span",16),c(29,"Most expensive prompts"),s(),r(30,"span",23),c(31),s()(),_(32,Oe,2,1)(33,ze,2,0),s()()),t&2){let a;i(3),g("value",n.range())("options",V(20,ye,C(16,_e),C(17,he),C(18,ve),C(19,be))),i(6),d(n.fmt$(n.total())),i(3),S("total \xB7 ",n.range()),i(),h(n.range()!=="all"&&n.wowDelta()!==0?13:-1),i(),h((a=n.hovered())?14:-1,a),i(),g("series",n.series())("height",200),i(3),g("size",14),i(3),g("data",n.byModel())("size",150)("centerLabel","$"+n.total().toFixed(0))("centerSub",n.range()+" window"),i(2),b(n.byModel()),i(4),g("size",14),i(4),S("\xB7 top ",n.topPrompts().length),i(),h(n.resource.state().loading?32:33)}},dependencies:[te,E,Q,X,ee,J,W],styles:["[_nghost-%COMP%]{display:contents}.page[_ngcontent-%COMP%]{flex:1;padding:18px}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1}.card[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:var(--r-lg)}.card-pad[_ngcontent-%COMP%]{padding:14px}.top-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1.3fr 1fr;gap:14px;margin-bottom:14px}.chart-card[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:10px}.chart-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:flex-start}.big-number[_ngcontent-%COMP%]{font-size:34px;font-weight:700;letter-spacing:-.02em}.donut-card[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;gap:10px}.model-legend[_ngcontent-%COMP%]{width:100%;display:flex;flex-direction:column;gap:6px;margin-top:14px}.legend-row[_ngcontent-%COMP%]{display:flex;gap:8px;align-items:center}.swatch[_ngcontent-%COMP%]{width:9px;height:9px;border-radius:3px;flex-shrink:0}.prompt-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:18px 1fr 70px 54px 44px 56px;gap:12px;align-items:center;padding:9px 0;border-top:1px solid var(--border-subtle)}.prompt-row.first[_ngcontent-%COMP%]{border-top:none}.rank[_ngcontent-%COMP%]{width:16px;font-size:11px}.prompt-grow[_ngcontent-%COMP%]{min-width:0}.prompt-text[_ngcontent-%COMP%]{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bar-track[_ngcontent-%COMP%]{height:4px;background:#ffffff0f;border-radius:999px;overflow:hidden}.bar-fill[_ngcontent-%COMP%]{display:block;height:100%;border-radius:999px}.prompt-model[_ngcontent-%COMP%]{font-size:10.5px;text-align:right}.prompt-toks[_ngcontent-%COMP%], .cache-cell[_ngcontent-%COMP%]{font-size:11px;text-align:right}.cost-cell[_ngcontent-%COMP%]{font-size:13px;font-weight:600;text-align:right}.skel[_ngcontent-%COMP%]{background:var(--bg-elevated);border-radius:5px;animation:_ngcontent-%COMP%_skeleton 1.4s ease-in-out infinite}.row-skel[_ngcontent-%COMP%]{height:30px;margin:6px 0}@keyframes _ngcontent-%COMP%_skeleton{0%,to{opacity:.5}50%{opacity:.9}}"],changeDetection:0})};export{oe as Costs};