@selvakumaresra/specship 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +11 -1
  2. package/commands/ss-brainstorm.md +68 -0
  3. package/dist/analytics/specship-impact.d.ts +72 -0
  4. package/dist/analytics/specship-impact.d.ts.map +1 -0
  5. package/dist/analytics/specship-impact.js +216 -0
  6. package/dist/analytics/specship-impact.js.map +1 -0
  7. package/dist/bin/specship.js +4 -4
  8. package/dist/bin/specship.js.map +1 -1
  9. package/dist/db/migrations.d.ts +1 -1
  10. package/dist/db/migrations.d.ts.map +1 -1
  11. package/dist/db/migrations.js +15 -1
  12. package/dist/db/migrations.js.map +1 -1
  13. package/dist/db/schema.sql +8 -0
  14. package/dist/index.d.ts +27 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +48 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/installer/index.d.ts +2 -2
  19. package/dist/installer/index.d.ts.map +1 -1
  20. package/dist/installer/targets/claude.d.ts.map +1 -1
  21. package/dist/installer/targets/claude.js +2 -0
  22. package/dist/installer/targets/claude.js.map +1 -1
  23. package/dist/server/ingest/impact-backfill.js +69 -0
  24. package/dist/server/ingest/impact-query.js +343 -0
  25. package/dist/server/ingest/index.js +2 -1
  26. package/dist/server/ingest/ingestor.js +41 -6
  27. package/dist/server/ingest/specship-classify.js +153 -0
  28. package/dist/server/routes/claude.js +32 -0
  29. package/dist/server/routes/spec.js +94 -0
  30. package/dist/server/server.js +26 -2
  31. package/dist/web/chunk-O7434ZMN.js +1 -0
  32. package/dist/web/{chunk-2YUJNZ2Y.js → chunk-ODX6CT3I.js} +6 -6
  33. package/dist/web/chunk-RASJHUXS.js +1 -0
  34. package/dist/web/chunk-TQ3P2QZO.js +1 -0
  35. package/dist/web/chunk-WCHGDXWC.js +1 -0
  36. package/dist/web/index.html +1 -1
  37. package/dist/web/main-X2KCYXZ4.js +1 -0
  38. package/package.json +1 -1
  39. package/dist/web/chunk-B3YPFY6A.js +0 -1
  40. package/dist/web/chunk-GWPVKJIY.js +0 -1
  41. package/dist/web/main-R53HA54V.js +0 -1
@@ -25,6 +25,54 @@ function safeProjectPath(projectRoot, relPath) {
25
25
  return null;
26
26
  return abs;
27
27
  }
28
+ /**
29
+ * Extract the `brief:` value from the leading YAML frontmatter block of a
30
+ * spec source file (between the first pair of `---` fences). Returns null
31
+ * when there is no frontmatter, no `brief:` key, or the value is empty.
32
+ *
33
+ * Deliberately dependency-free: a simple line scan that mirrors the style
34
+ * used by `MarkdownSpecExtractor.parseFrontmatter`. Exported so tests can
35
+ * exercise the parsing logic in isolation.
36
+ */
37
+ export function parseBriefField(source) {
38
+ const lines = source.split(/\r?\n/);
39
+ if (lines.length === 0 || (lines[0] ?? '').trim() !== '---')
40
+ return null;
41
+ // Find the closing `---` fence.
42
+ let closingIdx = -1;
43
+ for (let i = 1; i < lines.length; i++) {
44
+ if ((lines[i] ?? '').trim() === '---') {
45
+ closingIdx = i;
46
+ break;
47
+ }
48
+ }
49
+ if (closingIdx === -1)
50
+ return null;
51
+ // Scan frontmatter body for `brief: <value>`.
52
+ for (let i = 1; i < closingIdx; i++) {
53
+ const line = (lines[i] ?? '').trim();
54
+ if (!line.startsWith('brief:'))
55
+ continue;
56
+ let value = line.slice('brief:'.length).trim();
57
+ if (!value)
58
+ return null;
59
+ // Strip surrounding quotes.
60
+ if ((value.startsWith('"') && value.endsWith('"')) ||
61
+ (value.startsWith("'") && value.endsWith("'"))) {
62
+ value = value.slice(1, -1).trim();
63
+ }
64
+ else {
65
+ // Strip a trailing ` # comment` from an UNQUOTED value only. Requires
66
+ // whitespace before the `#` so a `#fragment` inside the path — or a `#`
67
+ // inside a quoted value (handled above) — isn't mangled.
68
+ const hashIdx = value.search(/\s#/);
69
+ if (hashIdx !== -1)
70
+ value = value.slice(0, hashIdx).trim();
71
+ }
72
+ return value || null;
73
+ }
74
+ return null;
75
+ }
28
76
  /**
29
77
  * Atomic file write: tmp + rename. Mirrors `atomicWriteFileSync` in
30
78
  * `src/installer/targets/shared.ts` — kept local here to avoid the
@@ -83,6 +131,52 @@ export async function registerSpecRoutes(app) {
83
131
  }
84
132
  return { spec, parent, siblings, children, links, source };
85
133
  });
134
+ /**
135
+ * GET /api/spec/:id/brief — return the brainstorm brief for a spec.
136
+ *
137
+ * Reads the spec's source file, parses the `brief:` frontmatter key, and
138
+ * returns the brief markdown from the path it names. The `brief:` value is
139
+ * resolved relative to the spec file's own directory (so a nested spec at
140
+ * `specs/area/foo.md` with `brief: foo/brief.md` resolves to
141
+ * `specs/area/foo/brief.md`). safeProjectPath guards against traversal.
142
+ */
143
+ app.get('/api/spec/:id/brief', async (req, reply) => {
144
+ const cg = await resolveCg(app, req, reply);
145
+ if (!cg)
146
+ return;
147
+ const spec = cg.getSpecQueries().getSpecById(req.params.id);
148
+ if (!spec)
149
+ return reply.code(404).send({ error: 'spec not found' });
150
+ const projectRoot = cg.getProjectRoot();
151
+ const specAbs = safeProjectPath(projectRoot, spec.sourcePath);
152
+ if (!specAbs)
153
+ return reply.code(404).send({ error: 'no brief' });
154
+ // TOCTOU: read directly under try/catch rather than existsSync-then-read,
155
+ // so a file deleted between the check and the read degrades to 404 (the
156
+ // sibling GET /api/spec/:id read is guarded the same way) instead of 500.
157
+ let specSource;
158
+ try {
159
+ specSource = fs.readFileSync(specAbs, 'utf-8');
160
+ }
161
+ catch {
162
+ return reply.code(404).send({ error: 'no brief' });
163
+ }
164
+ const briefRel = parseBriefField(specSource);
165
+ if (!briefRel)
166
+ return reply.code(404).send({ error: 'no brief' });
167
+ // CONVENTION: `brief:` is relative to the SPEC FILE's own directory (resolves
168
+ // whether the spec is flat at specs/<id>.md or nested at specs/<area>/<id>.md).
169
+ // safeProjectPath GUARDS against traversal outside the project root.
170
+ const briefAbs = safeProjectPath(projectRoot, path.join(path.dirname(spec.sourcePath), briefRel));
171
+ if (!briefAbs)
172
+ return reply.code(404).send({ error: 'no brief' });
173
+ try {
174
+ return { path: briefRel, markdown: fs.readFileSync(briefAbs, 'utf-8') };
175
+ }
176
+ catch {
177
+ return reply.code(404).send({ error: 'no brief' });
178
+ }
179
+ });
86
180
  app.get('/api/drift', async (req, reply) => {
87
181
  const cg = await resolveCg(app, req, reply);
88
182
  if (!cg)
@@ -16,7 +16,8 @@ import { existsSync, promises as fs } from 'node:fs';
16
16
  import { fileURLToPath, pathToFileURL } from 'node:url';
17
17
  import Fastify from 'fastify';
18
18
  import cors from '@fastify/cors';
19
- import { startWatcher } from './ingest/index.js';
19
+ import { startWatcher, primaryProjectMatcher } from './ingest/index.js';
20
+ import { backfillDisplaced } from './ingest/impact-backfill.js';
20
21
  import { ProjectRegistry } from './project-registry.js';
21
22
  import { makeStaticHandler } from './static-handler.js';
22
23
  import { registerGraphRoutes } from './routes/graph.js';
@@ -101,10 +102,33 @@ export async function createServer(options) {
101
102
  const cgAny = primaryCg;
102
103
  const dbHandle = cgAny.db?.getDb ? cgAny.db.getDb() : cgAny.queries?.db;
103
104
  if (dbHandle) {
104
- watcher = startWatcher(dbHandle, { verbose });
105
+ // Build a sync resolveGraph: for the primary project path return the
106
+ // already-open SpecShip instance (which satisfies GraphLike).
107
+ // Sessions from other project paths resolve to null — they'll be left
108
+ // as 'unresolved' and retried on the next boot when that project is primary.
109
+ // The stored project_path is the lossy-decoded slug (every '-' → '/'), so
110
+ // an exact compare against the real primaryPath never matched and savings
111
+ // stayed 0. primaryProjectMatcher accepts both the real path and its
112
+ // mangled stored form. Sessions from OTHER projects still resolve to null
113
+ // (left 'unresolved', retried when that project is primary).
114
+ const primaryPath = options.projectRoot ?? null;
115
+ const isPrimary = primaryPath ? primaryProjectMatcher(primaryPath) : () => false;
116
+ const resolveGraph = (projectPath) => primaryPath && isPrimary(projectPath) ? primaryCg : null;
117
+ watcher = startWatcher(dbHandle, { verbose, resolveGraph });
105
118
  ownedWatcher = true;
106
119
  if (verbose)
107
120
  console.error('[specship-server] JSONL ingest watcher started');
121
+ // Backfill displaced_files / resolution for pre-upgrade rows (is_specship=1,
122
+ // resolution IS NULL). Idempotent — safe to run on every boot. Non-fatal:
123
+ // a failure here must never abort server startup.
124
+ try {
125
+ backfillDisplaced(dbHandle, resolveGraph);
126
+ if (verbose)
127
+ console.error('[specship-server] specship-impact backfill complete');
128
+ }
129
+ catch (err) {
130
+ console.error('[specship-server] specship-impact backfill failed (non-fatal):', err instanceof Error ? err.message : String(err));
131
+ }
108
132
  }
109
133
  }
110
134
  else if (!primaryCg && options.ingest !== false && verbose) {
@@ -0,0 +1 @@
1
+ import{Ba as v,Ca as f,Da as y,Ja as D,Ka as M,O as d,ea as m,ka as u,lb as c,ob as b,pb as l,qa as h,ra as p,sa as g}from"./chunk-PDN6QYGJ.js";function L(i,e){if(i&1&&(d(),y(0,"path",1)(1,"path",2)),i&2){let t=M();h("d",t.fillD())("fill",t.color()),m(),h("d",t.pathD())("stroke",t.color())}}var C=class i{series=l([]);color=l("var(--accent)");height=l(200);hover=b();width=600;stats=c(()=>{let e=this.series();if(!e.length)return{max:1,min:0};let t=e.map(n=>n.cost);return{max:Math.max(...t,.01),min:0}});pathD=c(()=>{let e=this.series();if(!e.length)return"";let t=this.width,n=this.height(),{max:r}=this.stats();return e.map((o,s)=>{let a=s/Math.max(1,e.length-1)*t,_=n-o.cost/r*(n-20)-10;return(s===0?"M":"L")+a.toFixed(2)+" "+_.toFixed(2)}).join(" ")});fillD=c(()=>{let e=this.pathD();return e?e+` L ${this.width} ${this.height()} L 0 ${this.height()} Z`:""});onLeave(){this.hover.emit(null)}onMove(e){let n=e.currentTarget.getBoundingClientRect(),r=(e.clientX-n.left)/n.width,o=this.series();if(!o.length)return;let s=Math.min(o.length-1,Math.max(0,Math.round(r*(o.length-1)))),a=o[s];a&&this.hover.emit(a)}static \u0275fac=function(t){return new(t||i)};static \u0275cmp=u({type:i,selectors:[["app-line-chart"]],inputs:{series:[1,"series"],color:[1,"color"],height:[1,"height"]},outputs:{hover:"hover"},decls:2,vars:3,consts:[["width","100%","preserveAspectRatio","none",1,"line-chart",3,"mousemove","mouseleave"],["opacity","0.10"],["fill","none","stroke-width","1.6","stroke-linecap","round","stroke-linejoin","round"]],template:function(t,n){t&1&&(d(),v(0,"svg",0),D("mousemove",function(o){return n.onMove(o)})("mouseleave",function(){return n.onLeave()}),p(1,L,2,4),f()),t&2&&(h("viewBox","0 0 "+n.width+" "+n.height())("height",n.height()),m(),g(n.pathD()?1:-1))},styles:["[_nghost-%COMP%]{display:block}.line-chart[_ngcontent-%COMP%]{cursor:crosshair}"],changeDetection:0})};export{C as a};
@@ -1,6 +1,6 @@
1
- import{a as ne}from"./chunk-G7VZT5KB.js";import{a as ie}from"./chunk-EMGMOEVR.js";import{a as oe}from"./chunk-X2HTISHL.js";import{a as te}from"./chunk-R5W2MDZN.js";import{a as Z}from"./chunk-WDU3WICG.js";import{a as F,b as ee}from"./chunk-HZA6NEAB.js";import{a as Y,c as W}from"./chunk-GR72OOCN.js";import{c as B}from"./chunk-SHPTC4RL.js";import{a as J}from"./chunk-7RNS77UP.js";import{a as X}from"./chunk-E44X4RH2.js";import{Aa as p,Ga as w,I as y,Ia as x,Ka as s,M as f,N as h,Oa as H,Pa as Q,Qa as U,R as G,T as S,Ta as $,Ua as R,Va as d,W as _,Wa as g,Xa as b,aa as z,ea as o,ka as k,lb as C,ra as m,sa as u,va as I,wa as N,xa as c,ya as r,yb as K,za as l,zb as E}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var V=null,ge="self.onmessage = () => {};";function re(){return V||(typeof window<"u"&&!window.MonacoEnvironment&&(window.MonacoEnvironment={getWorker:(i,t)=>{let e=new Blob([ge],{type:"text/javascript"}),n=URL.createObjectURL(e);return new Worker(n,{name:t||"monaco-noop"})}}),V=import("./chunk-FMV5PXRC.js"),V)}var ae=!1;function le(i){ae||(ae=!0,_e(i))}function de(i,t){let e=[],n=t.getValue().split(`
2
- `),a=null,v=new Map,P=/<!--\s*id\s*:\s*([^\s-][^\s]*)\s*-->/,ue=/^#{1,6}\s+/;for(let O=0;O<n.length;O++){let T=n[O]??"",D=O+1,j=T.match(P);if(j&&j[1]){a!==null&&e.push({severity:i.MarkerSeverity.Warning,message:`Stranded <!-- id: ${a.id} -->: no heading follows on line ${a.line+1}.`,startLineNumber:a.line+1,startColumn:1,endLineNumber:a.line+1,endColumn:(n[a.line]??"").length+1,code:"spec_stranded_id"});let M=j[1];a={id:M,line:O};let q=v.get(M);q!==void 0?e.push({severity:i.MarkerSeverity.Warning,message:`Duplicate spec ID "${M}" \u2014 first seen on line ${q+1}. The second occurrence will be silently lost by the parser.`,startLineNumber:D,startColumn:T.indexOf(M)+1,endLineNumber:D,endColumn:T.indexOf(M)+M.length+1,code:"spec_duplicate_id"}):v.set(M,O);continue}ue.test(T)&&(a===null?e.push({severity:i.MarkerSeverity.Error,message:"This heading has no embedded ID. Add `<!-- id: REQ-X -->` on the line above it \u2014 required by the SpecShip parser.",startLineNumber:D,startColumn:1,endLineNumber:D,endColumn:T.length+1,code:"spec_missing_id"}):a=null)}i.editor.setModelMarkers(t,"specship-spec",e)}function _e(i){i.languages.registerCompletionItemProvider("markdown",{provideCompletionItems:(t,e)=>{let n=t.getWordUntilPosition(e),a={startLineNumber:e.lineNumber,endLineNumber:e.lineNumber,startColumn:n.startColumn,endColumn:n.endColumn},v=i.languages.CompletionItemKind,P=i.languages.CompletionItemInsertTextRule;return{suggestions:[{label:"req",kind:v.Snippet,insertText:["<!-- id: REQ-${1:AREA}-${2:001} -->","## ${3:Title MUST be concrete}","","${4:Body \u2014 describe the contract, not the implementation.}","","## Acceptance","<!-- id: REQ-${1:AREA}-${2:001}.A1 -->","- ${5:First testable acceptance criterion}","$0"].join(`
3
- `),insertTextRules:P.InsertAsSnippet,documentation:"Insert a full requirement (heading + acceptance) with embedded IDs.",detail:"SpecShip \xB7 requirement",range:a},{label:"doc",kind:v.Snippet,insertText:["---","id: ${1:AREA}-DOC","title: ${2:Title}","owner: ${3:team-or-person}","priority: ${4|high,medium,low|}","---","","<!-- id: ${1:AREA}-DOC -->","# ${2:Title}","","${5:One-paragraph summary of what this document covers.}","$0"].join(`
4
- `),insertTextRules:P.InsertAsSnippet,documentation:"Insert a new spec document with frontmatter.",detail:"SpecShip \xB7 document",range:a},{label:"accept",kind:v.Snippet,insertText:["## Acceptance","<!-- id: ${1:REQ-X}.A1 -->","- ${2:First testable acceptance criterion}","<!-- id: ${1:REQ-X}.A2 -->","- ${3:Second testable acceptance criterion}","$0"].join(`
5
- `),insertTextRules:P.InsertAsSnippet,documentation:"Insert an Acceptance section with two ID-d bullets.",detail:"SpecShip \xB7 acceptance",range:a},{label:"impl",kind:v.Snippet,insertText:["implementations:"," - ${1:src/path/to/file.ts}:${2:QualifiedSymbol}","$0"].join(`
6
- `),insertTextRules:P.InsertAsSnippet,documentation:"Insert an implementations block linking the spec to code.",detail:"SpecShip \xB7 implementations",range:a}]}},triggerCharacters:["r","d","a","i"]})}var ve=["host"];function xe(i,t){if(i&1&&(r(0,"span",5),d(1),l()),i&2){let e=s();c("title",e.path),o(),g(e.path)}}function fe(i,t){i&1&&(r(0,"div",15),d(1,"Loading editor\u2026"),l())}var L=class i{sanitizer=y(K);destroyRef=y(G);value="";path="";valueChange=new S;validationChange=new S;save=new S;cancel=new S;hostRef;loading=_(!0);currentValue=_("");errorCount=_(0);warningCount=_(0);dirty=_(!1);previewHtml=C(()=>this.sanitizer.bypassSecurityTrustHtml(ne(this.currentValue()||"")));tokenCount=C(()=>Math.max(1,Math.round((this.currentValue()||"").length/4)));statusLabel=C(()=>{let t=this.errorCount(),e=this.warningCount();return t>0?`${t} error${t===1?"":"s"}`:e>0?`${e} warning${e===1?"":"s"}`:"clean"});statusKind=C(()=>this.errorCount()>0?"error":this.warningCount()>0?"warn":"ok");monaco=null;editor=null;model=null;diagnosticsTimer=null;async ngAfterViewInit(){this.currentValue.set(this.value);try{let t=await re();this.monaco=t,le(t),this.model=t.editor.createModel(this.value,"markdown"),this.editor=t.editor.create(this.hostRef.nativeElement,{model:this.model,theme:this.preferDark()?"vs-dark":"vs",automaticLayout:!0,wordWrap:"on",minimap:{enabled:!1},scrollBeyondLastLine:!1,renderWhitespace:"none",fontSize:13,lineNumbers:"on",folding:!1,tabSize:2,insertSpaces:!0,contextmenu:!1}),this.editor.onDidChangeModelContent(()=>this.handleEdit()),this.runDiagnosticsNow(),this.loading.set(!1)}catch(t){console.error("[spec-editor] failed to load Monaco",t),this.loading.set(!1)}this.destroyRef.onDestroy(()=>this.dispose())}ngOnDestroy(){this.dispose()}onSave(){this.save.emit()}onCancel(){this.cancel.emit()}handleEdit(){if(!this.model)return;let t=this.model.getValue();this.currentValue.set(t),this.dirty.set(t!==this.value),this.valueChange.emit(t),this.diagnosticsTimer!==null&&clearTimeout(this.diagnosticsTimer),this.diagnosticsTimer=setTimeout(()=>this.runDiagnosticsNow(),500)}runDiagnosticsNow(){if(!this.monaco||!this.model)return;de(this.monaco,this.model);let t=this.monaco.editor.getModelMarkers({resource:this.model.uri,owner:"specship-spec"}),e=t.filter(a=>a.severity===this.monaco.MarkerSeverity.Error).length,n=t.filter(a=>a.severity===this.monaco.MarkerSeverity.Warning).length;this.errorCount.set(e),this.warningCount.set(n),this.validationChange.emit({errors:e,warnings:n})}preferDark(){if(typeof window>"u")return!1;try{return window.matchMedia("(prefers-color-scheme: dark)").matches}catch{return!1}}dispose(){this.diagnosticsTimer!==null&&(clearTimeout(this.diagnosticsTimer),this.diagnosticsTimer=null),this.editor?.dispose(),this.editor=null,this.model?.dispose(),this.model=null}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-spec-editor"]],viewQuery:function(e,n){if(e&1&&H(ve,7),e&2){let a;Q(a=U())&&(n.hostRef=a.first)}},inputs:{value:"value",path:"path"},outputs:{valueChange:"valueChange",validationChange:"validationChange",save:"save",cancel:"cancel"},decls:23,vars:16,consts:[["host",""],[1,"editor-shell","col"],[1,"toolbar","row","gap-8"],["name","memory",3,"size"],[1,"title"],[1,"path","mono",3,"title"],[1,"grow"],[1,"status-pill"],[1,"dot"],[1,"tokens","mono","tabular","muted",3,"title"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["type","button",1,"btn","btn-primary","btn-sm",3,"click","disabled","title"],["name","check",3,"size"],[1,"panes","row"],[1,"editor-pane"],[1,"loading"],[1,"monaco-host"],[1,"preview-pane","md-content",3,"innerHTML"]],template:function(e,n){e&1&&(r(0,"div",1)(1,"div",2),p(2,"app-icon",3),r(3,"span",4),d(4,"Spec editor"),l(),m(5,xe,2,2,"span",5),p(6,"span",6),r(7,"span",7),p(8,"span",8),d(9),l(),r(10,"span",9),d(11),l(),r(12,"button",10),x("click",function(){return n.onCancel()}),d(13," Cancel "),l(),r(14,"button",11),x("click",function(){return n.onSave()}),p(15,"app-icon",12),d(16," Save "),l()(),r(17,"div",13)(18,"div",14),m(19,fe,2,0,"div",15),p(20,"div",16,0),l(),p(22,"div",17),l()()),e&2&&(o(2),c("size",14),o(3),u(n.path?5:-1),o(2),R("ok",n.statusKind()==="ok")("warn",n.statusKind()==="warn")("error",n.statusKind()==="error"),o(2),b(" ",n.statusLabel()," "),o(),c("title","~"+n.tokenCount()+" tokens (rough estimate)"),o(),b(" ~",n.tokenCount(),"t "),o(3),c("disabled",!n.dirty()||n.errorCount()>0)("title",n.errorCount()>0?"Fix errors before saving":n.dirty()?"Save changes":"No changes to save"),o(),c("size",13),o(4),u(n.loading()?19:-1),o(3),c("innerHTML",n.previewHtml(),z))},dependencies:[E],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:flex;flex:1;min-height:0}.editor-shell[_ngcontent-%COMP%]{flex:1;min-height:0;border:1px solid var(--border-subtle);border-radius:10px;overflow:hidden;background:var(--bg-panel)}.toolbar[_ngcontent-%COMP%]{align-items:center;padding:8px 12px;border-bottom:1px solid var(--border-subtle);background:var(--bg-panel);flex-wrap:wrap;row-gap:6px}.toolbar[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{font-size:12.5px;font-weight:650;color:var(--text-primary)}.toolbar[_ngcontent-%COMP%] .path[_ngcontent-%COMP%]{font-size:11px;color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:320px}.toolbar[_ngcontent-%COMP%] .tokens[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted)}.status-pill[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;padding:3px 9px;border-radius:999px;white-space:nowrap}.status-pill[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%}.status-pill.ok[_ngcontent-%COMP%]{color:var(--success);background:var(--success-soft)}.status-pill.ok[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--success)}.status-pill.warn[_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.status-pill.warn[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--warn)}.status-pill.error[_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.status-pill.error[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--error)}.panes[_ngcontent-%COMP%]{flex:1;min-height:0}.editor-pane[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;position:relative;border-right:1px solid var(--border-subtle)}.loading[_ngcontent-%COMP%]{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:12px;background:var(--bg-canvas);z-index:1}.monaco-host[_ngcontent-%COMP%]{position:absolute;inset:0}.preview-pane[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;padding:14px 18px;overflow:auto;background:var(--bg-canvas)}.preview-pane.md-content[_ngcontent-%COMP%] .md-h1[_ngcontent-%COMP%]{font-size:15px;font-weight:650;color:var(--text-primary);letter-spacing:-.01em;margin:0 0 10px}.preview-pane.md-content[_ngcontent-%COMP%] .md-h2[_ngcontent-%COMP%]{font-size:13px;font-weight:650;color:var(--text-primary);letter-spacing:-.005em;margin:18px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-h3[_ngcontent-%COMP%]{font-size:12px;font-weight:650;color:var(--text-primary);margin:14px 0 6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-h4[_ngcontent-%COMP%]{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:12px 0 4px}.preview-pane.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%]{font-size:12.5px;line-height:1.6;color:var(--text-secondary);margin:0 0 4px;overflow-wrap:anywhere}.preview-pane.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-primary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%]{padding:0;margin:4px 0 6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{position:relative;padding:2px 0 2px 22px;font-size:12.5px;line-height:1.6;color:var(--text-secondary);overflow-wrap:anywhere}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%] strong[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-primary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%]{list-style:none}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:before{content:"\\2022";position:absolute;left:6px;top:2px;color:var(--text-faint)}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%]{list-style:none;counter-reset:md-ol}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{counter-increment:md-ol}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:before{content:counter(md-ol) ".";position:absolute;left:0;top:2px;color:var(--text-faint);font-family:var(--font-mono);font-size:11.5px;min-width:18px;text-align:right}.preview-pane.md-content[_ngcontent-%COMP%] .md-spacer[_ngcontent-%COMP%]{height:6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-import[_ngcontent-%COMP%]{display:inline-block;font-size:11.5px;color:var(--node-route);background:var(--node-route-soft);padding:2px 8px;border-radius:5px;margin:4px 0}.preview-pane.md-content[_ngcontent-%COMP%] .md-inline-code[_ngcontent-%COMP%]{font-size:11.5px;background:var(--bg-canvas);padding:1px 6px;border-radius:4px;border:1px solid var(--border-subtle);color:var(--node-spec)}.preview-pane.md-content[_ngcontent-%COMP%] .md-blockquote[_ngcontent-%COMP%]{margin:6px 0;padding:8px 12px;border-left:3px solid var(--accent);background:var(--accent-soft);color:var(--text-secondary);font-size:12.5px;line-height:1.55;border-radius:0 6px 6px 0}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%]{position:relative;margin:8px 0 10px;padding:12px 14px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;overflow-x:auto}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%]{display:block;font-size:11.5px;line-height:1.55;color:var(--text-primary);white-space:pre}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre-lang[_ngcontent-%COMP%]{position:absolute;top:6px;right:10px;font-size:10px;font-weight:600;color:var(--text-muted);text-transform:lowercase;background:var(--bg-canvas);padding:1px 6px;border-radius:4px;border:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table-scroll[_ngcontent-%COMP%]{overflow-x:auto;margin:10px 0;border:1px solid var(--border-subtle);border-radius:8px}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%]{width:100%;border-collapse:collapse;font-size:11.5px}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{padding:8px 12px;text-align:left;vertical-align:top;border-bottom:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] th[_ngcontent-%COMP%]{background:var(--bg-panel);font-weight:650;color:var(--text-primary);font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--border-strong);white-space:nowrap}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{color:var(--text-secondary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%]{border-bottom:0}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.preview-pane.md-content[_ngcontent-%COMP%] > [_ngcontent-%COMP%]:first-child{margin-top:0}'],changeDetection:0})};function he(i,t){i&1&&(r(0,"div",14),d(1),l()),i&2&&(o(),g(t))}function Ce(i,t){if(i&1){let e=w();r(0,"div",0),x("click",function(){f(e);let a=s();return h(a.onBackdropClick())}),l(),r(1,"div",1)(2,"header",2)(3,"div",3)(4,"div",4),d(5,"Draft a new spec with Claude"),l(),r(6,"div",5),d(7,"Describe the feature in one sentence. Claude takes it from there."),l()(),r(8,"button",6),x("click",function(){f(e);let a=s();return h(a.onCloseClick())}),p(9,"app-icon",7),l()(),r(10,"div",8)(11,"label",9),d(12,"Feature description"),l(),r(13,"textarea",10),x("input",function(a){f(e);let v=s();return h(v.onDescriptionInput(a))}),l(),r(14,"div",11),d(15,"This will run in Claude Code:"),l(),r(16,"pre",12),d(17),l()(),r(18,"footer",13),m(19,he,2,1,"div",14),p(20,"span",15),r(21,"button",16),x("click",function(){f(e);let a=s();return h(a.onCopyToClipboard())}),p(22,"app-icon",17),d(23," Copy prompt "),l(),r(24,"button",18),x("click",function(){f(e);let a=s();return h(a.onOpenInClaude())}),p(25,"app-icon",19),d(26," Open in Claude Code "),l()()()}if(i&2){let e,n=s();o(9),c("size",16),o(4),c("value",n.description()),o(4),g(n.slashCommand()),o(2),u((e=n.toast())?19:-1,e),o(2),c("disabled",!n.canSubmit()),o(),c("size",13),o(2),c("disabled",!n.canSubmit()),o(),c("size",13)}}var A=class i{open=!1;close=new S;description=_("");toast=_(null);slashCommand=C(()=>{let t=this.description().trim();return t?`/ss-spec-author "${t.replace(/"/g,'\\"')}"`:"/ss-spec-author"});canSubmit=C(()=>this.description().trim().length>0);onDescriptionInput(t){let e=t.target;this.description.set(e.value)}onBackdropClick(){this.dismiss()}onCloseClick(){this.dismiss()}async onOpenInClaude(){if(!this.canSubmit())return;let t=this.slashCommand();await this.writeToClipboard(t);try{window.location.href=`claude://prompt?text=${encodeURIComponent(t)}`,this.flashToast("Opening Claude Code\u2026 (also copied to clipboard)")}catch{this.flashToast("Claude Code handler unavailable \u2014 prompt copied to clipboard.")}}async onCopyToClipboard(){if(!this.canSubmit())return;let t=this.slashCommand(),e=await this.writeToClipboard(t);this.flashToast(e?"Copied! Switch to Claude Code and paste.":"Could not access clipboard \u2014 copy the prompt manually.")}async writeToClipboard(t){if(!navigator.clipboard)return!1;try{return await navigator.clipboard.writeText(t),!0}catch{return!1}}flashToast(t){this.toast.set(t),setTimeout(()=>this.toast.set(null),2500)}dismiss(){this.description.set(""),this.toast.set(null),this.close.emit()}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-draft-with-claude-modal"]],inputs:{open:"open"},outputs:{close:"close"},decls:1,vars:1,consts:[["role","presentation",1,"backdrop",3,"click"],["role","dialog","aria-labelledby","draft-modal-title","aria-modal","true",1,"modal"],[1,"head"],[1,"title-block"],["id","draft-modal-title",1,"title"],[1,"sub"],["type","button","aria-label","Close",1,"close",3,"click"],["name","x",3,"size"],[1,"body"],["for","draft-description",1,"field-label"],["id","draft-description","rows","3","placeholder","e.g. Failed payment retries should back off with T+1d, T+3d, T+7d then downgrade the account.","autofocus","",1,"field",3,"input","value"],[1,"preview-label"],[1,"preview","mono"],[1,"foot"],[1,"toast"],[1,"grow"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click","disabled"],["name","copy",3,"size"],["type","button",1,"btn","btn-primary","btn-sm",3,"click","disabled"],["name","external",3,"size"]],template:function(e,n){e&1&&m(0,Ce,27,8),e&2&&u(n.open?0:-1)},dependencies:[E],styles:[".backdrop[_ngcontent-%COMP%]{position:fixed;inset:0;background:#00000080;z-index:100;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.modal[_ngcontent-%COMP%]{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:min(560px,100vw - 32px);max-height:calc(100vh - 64px);background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:12px;box-shadow:0 24px 48px #0006;z-index:101;display:flex;flex-direction:column;overflow:hidden}.head[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:12px;padding:16px 18px;border-bottom:1px solid var(--border-subtle)}.head[_ngcontent-%COMP%] .title-block[_ngcontent-%COMP%]{flex:1;min-width:0}.head[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{font-size:14.5px;font-weight:650;color:var(--text-primary);letter-spacing:-.01em}.head[_ngcontent-%COMP%] .sub[_ngcontent-%COMP%]{font-size:12px;color:var(--text-secondary);margin-top:2px;line-height:1.45}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]{appearance:none;background:transparent;border:0;width:28px;height:28px;border-radius:6px;color:var(--text-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .12s,color .12s}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.body[_ngcontent-%COMP%]{padding:16px 18px;display:flex;flex-direction:column;gap:10px;overflow-y:auto}.field-label[_ngcontent-%COMP%]{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em}.field[_ngcontent-%COMP%]{width:100%;box-sizing:border-box;padding:10px 12px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:8px;color:var(--text-primary);font-family:var(--font-ui);font-size:13px;line-height:1.5;resize:vertical}.field[_ngcontent-%COMP%]:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}.preview-label[_ngcontent-%COMP%]{font-size:11px;color:var(--text-muted);margin-top:4px}.preview[_ngcontent-%COMP%]{margin:0;padding:10px 12px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:8px;font-size:11.5px;line-height:1.5;color:var(--node-spec);font-family:var(--font-mono);white-space:pre-wrap;word-break:break-all}.foot[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:12px 18px;border-top:1px solid var(--border-subtle);background:var(--bg-canvas);flex-wrap:wrap}.foot[_ngcontent-%COMP%] .grow[_ngcontent-%COMP%]{flex:1}.toast[_ngcontent-%COMP%]{font-size:11.5px;color:var(--success);background:var(--success-soft);padding:4px 10px;border-radius:999px;white-space:nowrap}"],changeDetection:0})};var be=(i,t)=>t.path,me=(i,t)=>t.id;function ye(i,t){i&1&&p(0,"app-pick-project-empty",0)}function we(i,t){i&1&&p(0,"app-empty",9)}function Me(i,t){if(i&1&&(r(0,"span",18),d(1),l()),i&2){let e=s().$implicit,n=s(2);o(),b(" ",n.driftCountForGroup(e)," ")}}function Se(i,t){if(i&1){let e=w();r(0,"div",21),x("click",function(){let a=f(e).$implicit,v=s(4);return h(v.select(a.id))}),p(1,"span",22),r(2,"div",23)(3,"div",24),d(4),l(),r(5,"div",25),d(6),l()()()}if(i&2){let e=t.$implicit,n=s(4);R("selected",n.sel()===e.id),o(),$("background",n.stateColorFor(e.kind)),o(2),$("color",n.sel()===e.id?"var(--accent)":"var(--text-primary)"),o(),g(e.id),o(2),g(e.title)}}function Pe(i,t){if(i&1&&(r(0,"div",19),I(1,Se,7,8,"div",20,me),l()),i&2){let e=s().$implicit;o(),N(e.specs)}}function Oe(i,t){if(i&1){let e=w();r(0,"div",14),x("click",function(){let a=f(e).$implicit,v=s(2);return h(v.toggleGroup(a.path))}),p(1,"app-icon",15)(2,"app-icon",16),r(3,"span",17),d(4),l(),m(5,Me,2,1,"span",18),l(),m(6,Pe,3,0,"div",19)}if(i&2){let e=t.$implicit,n=s(2);o(),c("name",n.isGroupOpen(e.path)?"chevronDown":"chevronRight")("size",12),o(),c("size",13),o(2),g(e.title),o(),u(n.driftCountForGroup(e)>0?5:-1),o(),u(n.isGroupOpen(e.path)?6:-1)}}function ke(i,t){if(i&1&&(r(0,"div",26),d(1),l()),i&2){let e=s();o(),b("Loading source for ",e.id,"\u2026")}}function Ee(i,t){if(i&1){let e=w();r(0,"app-spec-editor",30),x("valueChange",function(a){f(e);let v=s(4);return h(v.onEditorValueChange(a))})("save",function(){f(e);let a=s(4);return h(a.onEditorSave())})("cancel",function(){f(e);let a=s(4);return h(a.onEditorCancel())}),l()}if(i&2){let e=s(),n=s(3);c("value",n.editingSource())("path",e.sourcePath)}}function Te(i,t){i&1&&(r(0,"div",28),d(1),l()),i&2&&(o(),g(t))}function De(i,t){i&1&&(r(0,"div",29),d(1,"Saving\u2026"),l())}function ze(i,t){if(i&1&&(m(0,ke,2,1,"div",26)(1,Ee,1,2,"app-spec-editor",27),m(2,Te,2,1,"div",28),m(3,De,2,0,"div",29)),i&2){let e,n=s(3);u(n.editingSource()===null?0:1),o(2),u((e=n.saveError())?2:-1,e),o(),u(n.saving()?3:-1)}}function Ie(i,t){if(i&1&&m(0,ze,4,3),i&2){let e,n=s(2);u((e=n.selectedSpec())?0:-1,e)}}function Ne(i,t){if(i&1&&(r(0,"app-pill"),d(1),l()),i&2){let e=s();o(),g(e.priority)}}function $e(i,t){if(i&1&&(r(0,"div",37)(1,"div",38),d(2,"Owner"),l(),r(3,"div",39),d(4),l()()),i&2){let e=s();o(4),g(e.owner)}}function Re(i,t){i&1&&p(0,"div",42)}function Ve(i,t){if(i&1&&(r(0,"app-pill",56),d(1),l()),i&2){let e=s().$implicit;o(),b("",e.driftAxis," drift")}}function Le(i,t){if(i&1&&(r(0,"div",55),p(1,"app-state-pill",34),m(2,Ve,2,1,"app-pill",56),r(3,"span",57),d(4),l(),r(5,"app-pill"),d(6),l(),r(7,"button",58),p(8,"app-icon",51),d(9," Reveal "),l()()),i&2){let e=t.$implicit,n=t.$index;$("border-top",n>0?"1px solid var(--border-subtle)":"none"),o(),c("state",e.state),o(),u(e.driftAxis?2:-1),o(2),g(e.targetQualifiedName),o(2),g(e.provenance),o(2),c("size",12)}}function Ae(i,t){if(i&1&&(r(0,"div",43),I(1,Le,10,7,"div",54,me),l()),i&2){let e=s(3);o(),N(e.selectedLinks())}}function je(i,t){i&1&&(r(0,"div",44),p(1,"app-icon",59),r(2,"div",60),d(3," Orphaned \u2014 no code implements this requirement yet "),l()()),i&2&&(o(),c("size",16))}function We(i,t){i&1&&(r(0,"div",53),d(1),l()),i&2&&(o(),g(t))}function Fe(i,t){if(i&1){let e=w();r(0,"div",11)(1,"div",31)(2,"span",32),d(3),l(),p(4,"app-copy-btn",33)(5,"div",6)(6,"app-state-pill",34),m(7,Ne,2,1,"app-pill"),l(),r(8,"h1",35),d(9),l(),r(10,"div",36)(11,"div",37)(12,"div",38),d(13,"Kind"),l(),r(14,"div",39),d(15),l()(),m(16,$e,5,1,"div",37),r(17,"div",37)(18,"div",38),d(19,"Doc"),l(),r(20,"div",39),d(21),l()(),r(22,"div",37)(23,"div",38),d(24,"Siblings"),l(),r(25,"div",39),d(26),l()(),r(27,"div",37)(28,"div",38),d(29,"Links"),l(),r(30,"div",39),d(31),l()()(),r(32,"div",40),d(33,"Requirement"),l(),p(34,"div",41),r(35,"div",40),d(36,"Linked code"),l(),m(37,Re,1,0,"div",42)(38,Ae,3,0,"div",43)(39,je,4,1,"div",44),r(40,"div",45)(41,"button",46),p(42,"app-icon",47),d(43," Implement "),l(),r(44,"button",48),p(45,"app-icon",49),d(46," Verify "),l(),r(47,"button",50),x("click",function(){f(e);let a=s(2);return h(a.onEditClick())}),p(48,"app-icon",51),d(49," Edit spec "),l(),r(50,"button",50),x("click",function(){let a=f(e),v=s(2);return h(v.goToGraph(a.id))}),p(51,"app-icon",52),d(52," Show in graph "),l()(),m(53,We,2,1,"div",53),l()}if(i&2){let e,n=t,a=s(2);o(3),g(n.id),o(),c("text",n.id),o(2),c("state",n.kind),o(),u(n.priority?7:-1),o(2),g(n.title),o(6),g(n.kind),o(),u(n.owner?16:-1),o(5),g(n.sourcePath.replace("specs/","")),o(5),b("",a.selectedSiblings().length," reqs"),o(5),g(a.selectedLinks().length),o(3),c("innerHTML",a.mdHtml(n.body),z),o(3),u(a.detailResource.state().loading?37:a.selectedLinks().length>0?38:39),o(5),c("size",13),o(3),c("size",13),o(3),c("size",13),o(3),c("size",13),o(2),u((e=a.saveError())?53:-1,e)}}function qe(i,t){i&1&&(r(0,"app-empty",12)(1,"code",61),d(2,"specship init -i"),l()())}function Ge(i,t){if(i&1){let e=w();r(0,"div",1)(1,"div",2)(2,"div",3),p(3,"app-icon",4),r(4,"span",5),d(5,"Specs"),l(),p(6,"span",6),r(7,"span",7),d(8),l()(),r(9,"div",8),m(10,we,1,0,"app-empty",9),I(11,Oe,7,6,null,null,be),l()(),r(13,"div",10),m(14,Ie,1,1)(15,Fe,54,17,"div",11)(16,qe,3,0,"app-empty",12),l()(),r(17,"app-draft-with-claude-modal",13),x("close",function(){f(e);let a=s();return h(a.onDraftModalClose())}),l()}if(i&2){let e,n=s();o(3),c("size",15),o(5),b(" ",n.resource.state().loading?"\u2026":n.totalCount()+" reqs"," "),o(2),u(n.groups().length===0&&!n.resource.state().loading?10:-1),o(),N(n.groups()),o(3),u(n.editing()?14:(e=n.selectedSpec())?15:16,e),o(3),c("open",n.draftModalOpen())}}function He(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\*\*(.+?)\*\*/g,"<strong style='color:var(--text-primary)'>$1</strong>").replace(/`(.+?)`/g,"<code class='mono' style='font-size:12px;background:var(--bg-canvas);padding:1px 5px;border-radius:4px;border:1px solid var(--border-subtle)'>$1</code>")}var pe=class i{api=y(X);projects=y(J);router=y(B);conn=y(Y);resource=W(this.api,()=>`/api/specs${this.projects.projectQuery()}`);detailResource=W(this.api,()=>{let t=this.sel();return t?`/api/spec/${encodeURIComponent(t)}${this.projects.projectQuery()}`:null});sel=_(null);expandedGroups=_(new Set);editing=_(!1);editingSource=_(null);editingDirty=_(!1);saving=_(!1);saveError=_(null);draftModalOpen=_(!1);STATE=F;groups=C(()=>{let t=this.resource.state().data?.specs??[],e=new Map;for(let n of t){let a=n.sourcePath||"(unknown)";e.has(a)||e.set(a,{path:a,title:a.replace(/^specs\//,""),specs:[]}),e.get(a).specs.push(n)}return[...e.values()]});totalCount=C(()=>this.groups().reduce((t,e)=>t+e.specs.length,0));selectedSpec=C(()=>{let t=this.sel();if(!t)return null;for(let e of this.groups())for(let n of e.specs)if(n.id===t)return n;return null});selectedLinks=C(()=>this.detailResource.state().data?.links??[]);selectedSiblings=C(()=>{let t=this.selectedSpec();if(!t)return[];let e=this.groups().find(n=>n.path===t.sourcePath);return e?e.specs.filter(n=>n.id!==t.id):[]});isGroupOpen(t){return!this.expandedGroups().has("closed:"+t)}toggleGroup(t){this.expandedGroups.update(e=>{let n="closed:"+t,a=new Set(e);return a.has(n)?a.delete(n):a.add(n),a})}driftCountForGroup(t){let e=this.selectedSpec();return!e||e.sourcePath!==t.path?0:this.selectedLinks().filter(n=>["drifted","broken","orphaned"].includes(n.state)).length}stateColorFor(t){return F[t]?.color??"var(--node-spec)"}select(t){this.editing()&&(this.editing.set(!1),this.editingSource.set(null),this.editingDirty.set(!1)),this.sel.set(t)}mdHtml(t){return He(t)}goToGraph(t){this.router.navigate(["/graph"],{queryParams:{focus:"spec:"+t}})}async onEditClick(){let t=this.selectedSpec();if(t){this.saveError.set(null),this.editing.set(!0),this.editingSource.set(null);try{let e=this.projects.projectQuery(),n=await this.api.get(`/api/spec/${encodeURIComponent(t.id)}${e}`);this.editingSource.set(n.source??t.body)}catch(e){this.saveError.set(`Couldn't load source: ${e instanceof Error?e.message:String(e)}`),this.editing.set(!1)}}}onEditorValueChange(t){this.editingSource.set(t),this.editingDirty.set(!0)}async onEditorSave(){let t=this.selectedSpec(),e=this.editingSource();if(!(!t||e===null)){if(!this.conn.online()){this.saveError.set("Offline \u2014 reconnect to save your changes");return}this.saving.set(!0),this.saveError.set(null);try{let n=this.projects.projectQuery();await this.api.put(`/api/spec/${encodeURIComponent(t.id)}${n}`,{content:e}),this.editing.set(!1),this.editingDirty.set(!1),this.editingSource.set(null),this.resource.refetch()}catch(n){this.saveError.set(`Save failed: ${n instanceof Error?n.message:String(n)}`)}finally{this.saving.set(!1)}}}onEditorCancel(){this.editingDirty()&&!window.confirm("Discard unsaved changes?")||(this.editing.set(!1),this.editingSource.set(null),this.editingDirty.set(!1),this.saveError.set(null))}onDraftWithClaudeClick(){this.draftModalOpen.set(!0)}onDraftModalClose(){this.draftModalOpen.set(!1)}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-specs"]],decls:2,vars:1,consts:[["surface","Specs"],[1,"specs-shell"],[1,"tree-col"],[1,"tree-header","row","gap-8"],["name","book",2,"color","var(--accent)",3,"size"],[2,"font-weight","600","font-size","13px"],[1,"grow"],[1,"muted","tabular",2,"font-size","11px"],[1,"scroll-y","tree-scroll"],["icon","book","title","No specs found","body","Run specship init -i against a project with a specs/ directory."],[1,"detail-col","col"],[1,"scroll-y","detail-scroll"],["icon","book","title","Pick a spec from the tree","body","Or run specship init -i to index your specs/ folder if you haven't yet."],[3,"close","open"],[1,"doc-row","row","gap-6",3,"click"],[2,"color","var(--text-muted)",3,"name","size"],["name","book",2,"color","var(--node-spec)",3,"size"],[1,"mono","grow","doc-name"],[1,"pill",2,"font-size","9.5px","color","var(--warn)","background","var(--warn-soft)"],[1,"group-body"],[1,"req-row","row","gap-6",3,"selected"],[1,"req-row","row","gap-6",3,"click"],[1,"pill-dot",2,"flex-shrink","0"],[1,"grow",2,"min-width","0"],[1,"mono","req-id"],[1,"muted","req-title"],[1,"loading-detail"],[3,"value","path"],[1,"save-error"],[1,"save-status"],[3,"valueChange","save","cancel","value","path"],[1,"row","gap-8",2,"margin-bottom","4px"],[1,"mono",2,"font-size","12px","color","var(--node-spec)"],[3,"text"],[3,"state"],[1,"detail-title"],[1,"meta-row","row"],[1,"meta-item"],[1,"muted",2,"font-size","10.5px"],[1,"mono",2,"font-size","12.5px","margin-top","1px"],[1,"eyebrow",2,"margin-bottom","8px"],[1,"detail-body",3,"innerHTML"],[1,"skel",2,"height","42px","border-radius","8px","margin-bottom","22px"],[1,"card",2,"overflow","hidden","margin-bottom","22px"],[1,"card","card-pad","orphan-card"],[1,"row","gap-8",2,"flex-wrap","wrap"],["type","button",1,"btn","btn-primary","btn-sm"],["name","play",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm"],["name","check",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","reveal",3,"size"],["name","graph",3,"size"],[1,"save-error",2,"margin-top","16px"],[1,"row","gap-10","link-row",3,"border-top"],[1,"row","gap-10","link-row"],["color","var(--warn)","bg","var(--warn-soft)"],[1,"mono","grow","link-target"],["type","button",1,"btn","btn-ghost","btn-xs"],["name","drift",3,"size"],[2,"margin-top","6px","font-size","12.5px"],["action","",1,"mono",2,"font-size","11.5px","color","var(--accent)","background","var(--bg-canvas)","padding","5px 10px","border-radius","6px","border","1px solid var(--border-subtle)"]],template:function(e,n){e&1&&m(0,ye,1,0,"app-pick-project-empty",0)(1,Ge,18,5),e&2&&u(n.resource.state().noProject?0:1)},dependencies:[Z,E,L,A,ie,ee,oe,te],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.specs-shell[_ngcontent-%COMP%]{flex:1;display:flex;min-height:0}.tree-col[_ngcontent-%COMP%]{width:280px;flex-shrink:0;border-right:1px solid var(--border-subtle);background:var(--bg-panel);display:flex;flex-direction:column;min-height:0}.tree-header[_ngcontent-%COMP%]{padding:11px 12px;border-bottom:1px solid var(--border-subtle);align-items:center}.tree-scroll[_ngcontent-%COMP%]{flex:1;padding:6px}.doc-row[_ngcontent-%COMP%]{padding:6px 8px;border-radius:6px;cursor:pointer;color:var(--text-secondary);align-items:center}.doc-row[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.doc-name[_ngcontent-%COMP%]{font-size:12px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-body[_ngcontent-%COMP%]{margin-left:14px;border-left:1px solid var(--border-subtle);padding-left:6px}.req-row[_ngcontent-%COMP%]{padding:5px 8px;border-radius:6px;cursor:pointer;align-items:flex-start;background:transparent}.req-row[_ngcontent-%COMP%]:not(.selected):hover{background:var(--bg-hover)}.req-row.selected[_ngcontent-%COMP%]{background:var(--accent-soft)}.req-id[_ngcontent-%COMP%]{font-size:11px}.req-title[_ngcontent-%COMP%]{font-size:10.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-col[_ngcontent-%COMP%]{flex:1;min-width:0}.detail-scroll[_ngcontent-%COMP%]{flex:1;padding:22px}.detail-title[_ngcontent-%COMP%]{font-size:22px;font-weight:650;letter-spacing:-.01em;margin:2px 0 14px;text-wrap:pretty}.meta-row[_ngcontent-%COMP%]{gap:18px;padding:10px 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle);margin-bottom:18px;flex-wrap:wrap}.detail-body[_ngcontent-%COMP%]{font-size:13.5px;line-height:1.65;color:var(--text-secondary);margin-bottom:22px;text-wrap:pretty}.link-row[_ngcontent-%COMP%]{padding:10px 12px}.link-target[_ngcontent-%COMP%]{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orphan-card[_ngcontent-%COMP%]{text-align:center;color:var(--error);margin-bottom:22px;border-color:#f2555a4d}.loading-detail[_ngcontent-%COMP%]{padding:40px 18px;color:var(--text-muted);font-size:13px;text-align:center}.save-error[_ngcontent-%COMP%]{padding:10px 14px;border:1px solid rgba(255,90,90,.35);background:var(--error-soft);color:var(--error);border-radius:8px;font-size:12.5px;line-height:1.5}.save-status[_ngcontent-%COMP%]{margin-top:8px;color:var(--text-muted);font-size:12px}'],changeDetection:0})};export{pe as Specs};
1
+ import{a as ne}from"./chunk-G7VZT5KB.js";import{a as ie}from"./chunk-EMGMOEVR.js";import{a as oe}from"./chunk-X2HTISHL.js";import{a as te}from"./chunk-R5W2MDZN.js";import{a as Z}from"./chunk-WDU3WICG.js";import{a as F,b as ee}from"./chunk-HZA6NEAB.js";import{a as Y,c as V}from"./chunk-GR72OOCN.js";import{c as K}from"./chunk-SHPTC4RL.js";import{a as J}from"./chunk-7RNS77UP.js";import{a as X}from"./chunk-E44X4RH2.js";import{Aa as p,Ga as w,I as y,Ia as x,Ka as s,M as f,N as b,Oa as G,Pa as Q,Qa as U,R as q,T as S,Ta as N,Ua as R,Va as d,W as _,Wa as g,Xa as h,aa as D,ea as o,ka as k,lb as C,ra as m,sa as u,va as I,wa as $,xa as c,ya as r,yb as B,za as a,zb as E}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var L=null,ge="self.onmessage = () => {};";function re(){return L||(typeof window<"u"&&!window.MonacoEnvironment&&(window.MonacoEnvironment={getWorker:(i,t)=>{let e=new Blob([ge],{type:"text/javascript"}),n=URL.createObjectURL(e);return new Worker(n,{name:t||"monaco-noop"})}}),L=import("./chunk-FMV5PXRC.js"),L)}var ae=!1;function le(i){ae||(ae=!0,_e(i))}function de(i,t){let e=[],n=t.getValue().split(`
2
+ `),l=null,v=new Map,P=/<!--\s*id\s*:\s*([^\s-][^\s]*)\s*-->/,ue=/^#{1,6}\s+/;for(let O=0;O<n.length;O++){let T=n[O]??"",z=O+1,W=T.match(P);if(W&&W[1]){l!==null&&e.push({severity:i.MarkerSeverity.Warning,message:`Stranded <!-- id: ${l.id} -->: no heading follows on line ${l.line+1}.`,startLineNumber:l.line+1,startColumn:1,endLineNumber:l.line+1,endColumn:(n[l.line]??"").length+1,code:"spec_stranded_id"});let M=W[1];l={id:M,line:O};let H=v.get(M);H!==void 0?e.push({severity:i.MarkerSeverity.Warning,message:`Duplicate spec ID "${M}" \u2014 first seen on line ${H+1}. The second occurrence will be silently lost by the parser.`,startLineNumber:z,startColumn:T.indexOf(M)+1,endLineNumber:z,endColumn:T.indexOf(M)+M.length+1,code:"spec_duplicate_id"}):v.set(M,O);continue}ue.test(T)&&(l===null?e.push({severity:i.MarkerSeverity.Error,message:"This heading has no embedded ID. Add `<!-- id: REQ-X -->` on the line above it \u2014 required by the SpecShip parser.",startLineNumber:z,startColumn:1,endLineNumber:z,endColumn:T.length+1,code:"spec_missing_id"}):l=null)}i.editor.setModelMarkers(t,"specship-spec",e)}function _e(i){i.languages.registerCompletionItemProvider("markdown",{provideCompletionItems:(t,e)=>{let n=t.getWordUntilPosition(e),l={startLineNumber:e.lineNumber,endLineNumber:e.lineNumber,startColumn:n.startColumn,endColumn:n.endColumn},v=i.languages.CompletionItemKind,P=i.languages.CompletionItemInsertTextRule;return{suggestions:[{label:"req",kind:v.Snippet,insertText:["<!-- id: REQ-${1:AREA}-${2:001} -->","## ${3:Title MUST be concrete}","","${4:Body \u2014 describe the contract, not the implementation.}","","## Acceptance","<!-- id: REQ-${1:AREA}-${2:001}.A1 -->","- ${5:First testable acceptance criterion}","$0"].join(`
3
+ `),insertTextRules:P.InsertAsSnippet,documentation:"Insert a full requirement (heading + acceptance) with embedded IDs.",detail:"SpecShip \xB7 requirement",range:l},{label:"doc",kind:v.Snippet,insertText:["---","id: ${1:AREA}-DOC","title: ${2:Title}","owner: ${3:team-or-person}","priority: ${4|high,medium,low|}","---","","<!-- id: ${1:AREA}-DOC -->","# ${2:Title}","","${5:One-paragraph summary of what this document covers.}","$0"].join(`
4
+ `),insertTextRules:P.InsertAsSnippet,documentation:"Insert a new spec document with frontmatter.",detail:"SpecShip \xB7 document",range:l},{label:"accept",kind:v.Snippet,insertText:["## Acceptance","<!-- id: ${1:REQ-X}.A1 -->","- ${2:First testable acceptance criterion}","<!-- id: ${1:REQ-X}.A2 -->","- ${3:Second testable acceptance criterion}","$0"].join(`
5
+ `),insertTextRules:P.InsertAsSnippet,documentation:"Insert an Acceptance section with two ID-d bullets.",detail:"SpecShip \xB7 acceptance",range:l},{label:"impl",kind:v.Snippet,insertText:["implementations:"," - ${1:src/path/to/file.ts}:${2:QualifiedSymbol}","$0"].join(`
6
+ `),insertTextRules:P.InsertAsSnippet,documentation:"Insert an implementations block linking the spec to code.",detail:"SpecShip \xB7 implementations",range:l}]}},triggerCharacters:["r","d","a","i"]})}var ve=["host"];function xe(i,t){if(i&1&&(r(0,"span",5),d(1),a()),i&2){let e=s();c("title",e.path),o(),g(e.path)}}function fe(i,t){i&1&&(r(0,"div",15),d(1,"Loading editor\u2026"),a())}var j=class i{sanitizer=y(B);destroyRef=y(q);value="";path="";valueChange=new S;validationChange=new S;save=new S;cancel=new S;hostRef;loading=_(!0);currentValue=_("");errorCount=_(0);warningCount=_(0);dirty=_(!1);previewHtml=C(()=>this.sanitizer.bypassSecurityTrustHtml(ne(this.currentValue()||"")));tokenCount=C(()=>Math.max(1,Math.round((this.currentValue()||"").length/4)));statusLabel=C(()=>{let t=this.errorCount(),e=this.warningCount();return t>0?`${t} error${t===1?"":"s"}`:e>0?`${e} warning${e===1?"":"s"}`:"clean"});statusKind=C(()=>this.errorCount()>0?"error":this.warningCount()>0?"warn":"ok");monaco=null;editor=null;model=null;diagnosticsTimer=null;async ngAfterViewInit(){this.currentValue.set(this.value);try{let t=await re();this.monaco=t,le(t),this.model=t.editor.createModel(this.value,"markdown"),this.editor=t.editor.create(this.hostRef.nativeElement,{model:this.model,theme:this.preferDark()?"vs-dark":"vs",automaticLayout:!0,wordWrap:"on",minimap:{enabled:!1},scrollBeyondLastLine:!1,renderWhitespace:"none",fontSize:13,lineNumbers:"on",folding:!1,tabSize:2,insertSpaces:!0,contextmenu:!1}),this.editor.onDidChangeModelContent(()=>this.handleEdit()),this.runDiagnosticsNow(),this.loading.set(!1)}catch(t){console.error("[spec-editor] failed to load Monaco",t),this.loading.set(!1)}this.destroyRef.onDestroy(()=>this.dispose())}ngOnDestroy(){this.dispose()}onSave(){this.save.emit()}onCancel(){this.cancel.emit()}handleEdit(){if(!this.model)return;let t=this.model.getValue();this.currentValue.set(t),this.dirty.set(t!==this.value),this.valueChange.emit(t),this.diagnosticsTimer!==null&&clearTimeout(this.diagnosticsTimer),this.diagnosticsTimer=setTimeout(()=>this.runDiagnosticsNow(),500)}runDiagnosticsNow(){if(!this.monaco||!this.model)return;de(this.monaco,this.model);let t=this.monaco.editor.getModelMarkers({resource:this.model.uri,owner:"specship-spec"}),e=t.filter(l=>l.severity===this.monaco.MarkerSeverity.Error).length,n=t.filter(l=>l.severity===this.monaco.MarkerSeverity.Warning).length;this.errorCount.set(e),this.warningCount.set(n),this.validationChange.emit({errors:e,warnings:n})}preferDark(){if(typeof window>"u")return!1;try{return window.matchMedia("(prefers-color-scheme: dark)").matches}catch{return!1}}dispose(){this.diagnosticsTimer!==null&&(clearTimeout(this.diagnosticsTimer),this.diagnosticsTimer=null),this.editor?.dispose(),this.editor=null,this.model?.dispose(),this.model=null}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-spec-editor"]],viewQuery:function(e,n){if(e&1&&G(ve,7),e&2){let l;Q(l=U())&&(n.hostRef=l.first)}},inputs:{value:"value",path:"path"},outputs:{valueChange:"valueChange",validationChange:"validationChange",save:"save",cancel:"cancel"},decls:23,vars:16,consts:[["host",""],[1,"editor-shell","col"],[1,"toolbar","row","gap-8"],["name","memory",3,"size"],[1,"title"],[1,"path","mono",3,"title"],[1,"grow"],[1,"status-pill"],[1,"dot"],[1,"tokens","mono","tabular","muted",3,"title"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["type","button",1,"btn","btn-primary","btn-sm",3,"click","disabled","title"],["name","check",3,"size"],[1,"panes","row"],[1,"editor-pane"],[1,"loading"],[1,"monaco-host"],[1,"preview-pane","md-content",3,"innerHTML"]],template:function(e,n){e&1&&(r(0,"div",1)(1,"div",2),p(2,"app-icon",3),r(3,"span",4),d(4,"Spec editor"),a(),m(5,xe,2,2,"span",5),p(6,"span",6),r(7,"span",7),p(8,"span",8),d(9),a(),r(10,"span",9),d(11),a(),r(12,"button",10),x("click",function(){return n.onCancel()}),d(13," Cancel "),a(),r(14,"button",11),x("click",function(){return n.onSave()}),p(15,"app-icon",12),d(16," Save "),a()(),r(17,"div",13)(18,"div",14),m(19,fe,2,0,"div",15),p(20,"div",16,0),a(),p(22,"div",17),a()()),e&2&&(o(2),c("size",14),o(3),u(n.path?5:-1),o(2),R("ok",n.statusKind()==="ok")("warn",n.statusKind()==="warn")("error",n.statusKind()==="error"),o(2),h(" ",n.statusLabel()," "),o(),c("title","~"+n.tokenCount()+" tokens (rough estimate)"),o(),h(" ~",n.tokenCount(),"t "),o(3),c("disabled",!n.dirty()||n.errorCount()>0)("title",n.errorCount()>0?"Fix errors before saving":n.dirty()?"Save changes":"No changes to save"),o(),c("size",13),o(4),u(n.loading()?19:-1),o(3),c("innerHTML",n.previewHtml(),D))},dependencies:[E],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:flex;flex:1;min-height:0}.editor-shell[_ngcontent-%COMP%]{flex:1;min-height:0;border:1px solid var(--border-subtle);border-radius:10px;overflow:hidden;background:var(--bg-panel)}.toolbar[_ngcontent-%COMP%]{align-items:center;padding:8px 12px;border-bottom:1px solid var(--border-subtle);background:var(--bg-panel);flex-wrap:wrap;row-gap:6px}.toolbar[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{font-size:12.5px;font-weight:650;color:var(--text-primary)}.toolbar[_ngcontent-%COMP%] .path[_ngcontent-%COMP%]{font-size:11px;color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:320px}.toolbar[_ngcontent-%COMP%] .tokens[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted)}.status-pill[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;padding:3px 9px;border-radius:999px;white-space:nowrap}.status-pill[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%}.status-pill.ok[_ngcontent-%COMP%]{color:var(--success);background:var(--success-soft)}.status-pill.ok[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--success)}.status-pill.warn[_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.status-pill.warn[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--warn)}.status-pill.error[_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.status-pill.error[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--error)}.panes[_ngcontent-%COMP%]{flex:1;min-height:0}.editor-pane[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;position:relative;border-right:1px solid var(--border-subtle)}.loading[_ngcontent-%COMP%]{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--text-muted);font-size:12px;background:var(--bg-canvas);z-index:1}.monaco-host[_ngcontent-%COMP%]{position:absolute;inset:0}.preview-pane[_ngcontent-%COMP%]{flex:1;min-width:0;min-height:0;padding:14px 18px;overflow:auto;background:var(--bg-canvas)}.preview-pane.md-content[_ngcontent-%COMP%] .md-h1[_ngcontent-%COMP%]{font-size:15px;font-weight:650;color:var(--text-primary);letter-spacing:-.01em;margin:0 0 10px}.preview-pane.md-content[_ngcontent-%COMP%] .md-h2[_ngcontent-%COMP%]{font-size:13px;font-weight:650;color:var(--text-primary);letter-spacing:-.005em;margin:18px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-h3[_ngcontent-%COMP%]{font-size:12px;font-weight:650;color:var(--text-primary);margin:14px 0 6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-h4[_ngcontent-%COMP%]{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:12px 0 4px}.preview-pane.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%]{font-size:12.5px;line-height:1.6;color:var(--text-secondary);margin:0 0 4px;overflow-wrap:anywhere}.preview-pane.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-primary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%]{padding:0;margin:4px 0 6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{position:relative;padding:2px 0 2px 22px;font-size:12.5px;line-height:1.6;color:var(--text-secondary);overflow-wrap:anywhere}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%] strong[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-primary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%]{list-style:none}.preview-pane.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:before{content:"\\2022";position:absolute;left:6px;top:2px;color:var(--text-faint)}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%]{list-style:none;counter-reset:md-ol}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{counter-increment:md-ol}.preview-pane.md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:before{content:counter(md-ol) ".";position:absolute;left:0;top:2px;color:var(--text-faint);font-family:var(--font-mono);font-size:11.5px;min-width:18px;text-align:right}.preview-pane.md-content[_ngcontent-%COMP%] .md-spacer[_ngcontent-%COMP%]{height:6px}.preview-pane.md-content[_ngcontent-%COMP%] .md-import[_ngcontent-%COMP%]{display:inline-block;font-size:11.5px;color:var(--node-route);background:var(--node-route-soft);padding:2px 8px;border-radius:5px;margin:4px 0}.preview-pane.md-content[_ngcontent-%COMP%] .md-inline-code[_ngcontent-%COMP%]{font-size:11.5px;background:var(--bg-canvas);padding:1px 6px;border-radius:4px;border:1px solid var(--border-subtle);color:var(--node-spec)}.preview-pane.md-content[_ngcontent-%COMP%] .md-blockquote[_ngcontent-%COMP%]{margin:6px 0;padding:8px 12px;border-left:3px solid var(--accent);background:var(--accent-soft);color:var(--text-secondary);font-size:12.5px;line-height:1.55;border-radius:0 6px 6px 0}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%]{position:relative;margin:8px 0 10px;padding:12px 14px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;overflow-x:auto}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%]{display:block;font-size:11.5px;line-height:1.55;color:var(--text-primary);white-space:pre}.preview-pane.md-content[_ngcontent-%COMP%] .md-pre-lang[_ngcontent-%COMP%]{position:absolute;top:6px;right:10px;font-size:10px;font-weight:600;color:var(--text-muted);text-transform:lowercase;background:var(--bg-canvas);padding:1px 6px;border-radius:4px;border:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table-scroll[_ngcontent-%COMP%]{overflow-x:auto;margin:10px 0;border:1px solid var(--border-subtle);border-radius:8px}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%]{width:100%;border-collapse:collapse;font-size:11.5px}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], .preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{padding:8px 12px;text-align:left;vertical-align:top;border-bottom:1px solid var(--border-subtle)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] th[_ngcontent-%COMP%]{background:var(--bg-panel);font-weight:650;color:var(--text-primary);font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--border-strong);white-space:nowrap}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{color:var(--text-secondary)}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:last-child td[_ngcontent-%COMP%]{border-bottom:0}.preview-pane.md-content[_ngcontent-%COMP%] .md-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.preview-pane.md-content[_ngcontent-%COMP%] > [_ngcontent-%COMP%]:first-child{margin-top:0}'],changeDetection:0})};function be(i,t){i&1&&(r(0,"div",14),d(1),a()),i&2&&(o(),g(t))}function Ce(i,t){if(i&1){let e=w();r(0,"div",0),x("click",function(){f(e);let l=s();return b(l.onBackdropClick())}),a(),r(1,"div",1)(2,"header",2)(3,"div",3)(4,"div",4),d(5,"Draft a new spec with Claude"),a(),r(6,"div",5),d(7,"Describe the feature in one sentence. Claude takes it from there."),a()(),r(8,"button",6),x("click",function(){f(e);let l=s();return b(l.onCloseClick())}),p(9,"app-icon",7),a()(),r(10,"div",8)(11,"label",9),d(12,"Feature description"),a(),r(13,"textarea",10),x("input",function(l){f(e);let v=s();return b(v.onDescriptionInput(l))}),a(),r(14,"div",11),d(15,"This will run in Claude Code:"),a(),r(16,"pre",12),d(17),a()(),r(18,"footer",13),m(19,be,2,1,"div",14),p(20,"span",15),r(21,"button",16),x("click",function(){f(e);let l=s();return b(l.onCopyToClipboard())}),p(22,"app-icon",17),d(23," Copy prompt "),a(),r(24,"button",18),x("click",function(){f(e);let l=s();return b(l.onOpenInClaude())}),p(25,"app-icon",19),d(26," Open in Claude Code "),a()()()}if(i&2){let e,n=s();o(9),c("size",16),o(4),c("value",n.description()),o(4),g(n.slashCommand()),o(2),u((e=n.toast())?19:-1,e),o(2),c("disabled",!n.canSubmit()),o(),c("size",13),o(2),c("disabled",!n.canSubmit()),o(),c("size",13)}}var A=class i{open=!1;close=new S;description=_("");toast=_(null);slashCommand=C(()=>{let t=this.description().trim();return t?`/ss-spec-author "${t.replace(/"/g,'\\"')}"`:"/ss-spec-author"});canSubmit=C(()=>this.description().trim().length>0);onDescriptionInput(t){let e=t.target;this.description.set(e.value)}onBackdropClick(){this.dismiss()}onCloseClick(){this.dismiss()}async onOpenInClaude(){if(!this.canSubmit())return;let t=this.slashCommand();await this.writeToClipboard(t);try{window.location.href=`claude://prompt?text=${encodeURIComponent(t)}`,this.flashToast("Opening Claude Code\u2026 (also copied to clipboard)")}catch{this.flashToast("Claude Code handler unavailable \u2014 prompt copied to clipboard.")}}async onCopyToClipboard(){if(!this.canSubmit())return;let t=this.slashCommand(),e=await this.writeToClipboard(t);this.flashToast(e?"Copied! Switch to Claude Code and paste.":"Could not access clipboard \u2014 copy the prompt manually.")}async writeToClipboard(t){if(!navigator.clipboard)return!1;try{return await navigator.clipboard.writeText(t),!0}catch{return!1}}flashToast(t){this.toast.set(t),setTimeout(()=>this.toast.set(null),2500)}dismiss(){this.description.set(""),this.toast.set(null),this.close.emit()}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-draft-with-claude-modal"]],inputs:{open:"open"},outputs:{close:"close"},decls:1,vars:1,consts:[["role","presentation",1,"backdrop",3,"click"],["role","dialog","aria-labelledby","draft-modal-title","aria-modal","true",1,"modal"],[1,"head"],[1,"title-block"],["id","draft-modal-title",1,"title"],[1,"sub"],["type","button","aria-label","Close",1,"close",3,"click"],["name","x",3,"size"],[1,"body"],["for","draft-description",1,"field-label"],["id","draft-description","rows","3","placeholder","e.g. Failed payment retries should back off with T+1d, T+3d, T+7d then downgrade the account.","autofocus","",1,"field",3,"input","value"],[1,"preview-label"],[1,"preview","mono"],[1,"foot"],[1,"toast"],[1,"grow"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click","disabled"],["name","copy",3,"size"],["type","button",1,"btn","btn-primary","btn-sm",3,"click","disabled"],["name","external",3,"size"]],template:function(e,n){e&1&&m(0,Ce,27,8),e&2&&u(n.open?0:-1)},dependencies:[E],styles:[".backdrop[_ngcontent-%COMP%]{position:fixed;inset:0;background:#00000080;z-index:100;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.modal[_ngcontent-%COMP%]{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:min(560px,100vw - 32px);max-height:calc(100vh - 64px);background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:12px;box-shadow:0 24px 48px #0006;z-index:101;display:flex;flex-direction:column;overflow:hidden}.head[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:12px;padding:16px 18px;border-bottom:1px solid var(--border-subtle)}.head[_ngcontent-%COMP%] .title-block[_ngcontent-%COMP%]{flex:1;min-width:0}.head[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{font-size:14.5px;font-weight:650;color:var(--text-primary);letter-spacing:-.01em}.head[_ngcontent-%COMP%] .sub[_ngcontent-%COMP%]{font-size:12px;color:var(--text-secondary);margin-top:2px;line-height:1.45}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]{appearance:none;background:transparent;border:0;width:28px;height:28px;border-radius:6px;color:var(--text-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .12s,color .12s}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.head[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.body[_ngcontent-%COMP%]{padding:16px 18px;display:flex;flex-direction:column;gap:10px;overflow-y:auto}.field-label[_ngcontent-%COMP%]{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em}.field[_ngcontent-%COMP%]{width:100%;box-sizing:border-box;padding:10px 12px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:8px;color:var(--text-primary);font-family:var(--font-ui);font-size:13px;line-height:1.5;resize:vertical}.field[_ngcontent-%COMP%]:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}.preview-label[_ngcontent-%COMP%]{font-size:11px;color:var(--text-muted);margin-top:4px}.preview[_ngcontent-%COMP%]{margin:0;padding:10px 12px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:8px;font-size:11.5px;line-height:1.5;color:var(--node-spec);font-family:var(--font-mono);white-space:pre-wrap;word-break:break-all}.foot[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:12px 18px;border-top:1px solid var(--border-subtle);background:var(--bg-canvas);flex-wrap:wrap}.foot[_ngcontent-%COMP%] .grow[_ngcontent-%COMP%]{flex:1}.toast[_ngcontent-%COMP%]{font-size:11.5px;color:var(--success);background:var(--success-soft);padding:4px 10px;border-radius:999px;white-space:nowrap}"],changeDetection:0})};var he=(i,t)=>t.path,me=(i,t)=>t.id;function ye(i,t){i&1&&p(0,"app-pick-project-empty",0)}function we(i,t){i&1&&p(0,"app-empty",9)}function Me(i,t){if(i&1&&(r(0,"span",18),d(1),a()),i&2){let e=s().$implicit,n=s(2);o(),h(" ",n.driftCountForGroup(e)," ")}}function Se(i,t){if(i&1){let e=w();r(0,"div",21),x("click",function(){let l=f(e).$implicit,v=s(4);return b(v.select(l.id))}),p(1,"span",22),r(2,"div",23)(3,"div",24),d(4),a(),r(5,"div",25),d(6),a()()()}if(i&2){let e=t.$implicit,n=s(4);R("selected",n.sel()===e.id),o(),N("background",n.stateColorFor(e.kind)),o(2),N("color",n.sel()===e.id?"var(--accent)":"var(--text-primary)"),o(),g(e.id),o(2),g(e.title)}}function Pe(i,t){if(i&1&&(r(0,"div",19),I(1,Se,7,8,"div",20,me),a()),i&2){let e=s().$implicit;o(),$(e.specs)}}function Oe(i,t){if(i&1){let e=w();r(0,"div",14),x("click",function(){let l=f(e).$implicit,v=s(2);return b(v.toggleGroup(l.path))}),p(1,"app-icon",15)(2,"app-icon",16),r(3,"span",17),d(4),a(),m(5,Me,2,1,"span",18),a(),m(6,Pe,3,0,"div",19)}if(i&2){let e=t.$implicit,n=s(2);o(),c("name",n.isGroupOpen(e.path)?"chevronDown":"chevronRight")("size",12),o(),c("size",13),o(2),g(e.title),o(),u(n.driftCountForGroup(e)>0?5:-1),o(),u(n.isGroupOpen(e.path)?6:-1)}}function ke(i,t){if(i&1&&(r(0,"div",26),d(1),a()),i&2){let e=s();o(),h("Loading source for ",e.id,"\u2026")}}function Ee(i,t){if(i&1){let e=w();r(0,"app-spec-editor",30),x("valueChange",function(l){f(e);let v=s(4);return b(v.onEditorValueChange(l))})("save",function(){f(e);let l=s(4);return b(l.onEditorSave())})("cancel",function(){f(e);let l=s(4);return b(l.onEditorCancel())}),a()}if(i&2){let e=s(),n=s(3);c("value",n.editingSource())("path",e.sourcePath)}}function Te(i,t){i&1&&(r(0,"div",28),d(1),a()),i&2&&(o(),g(t))}function De(i,t){i&1&&(r(0,"div",29),d(1,"Saving\u2026"),a())}function ze(i,t){if(i&1&&(m(0,ke,2,1,"div",26)(1,Ee,1,2,"app-spec-editor",27),m(2,Te,2,1,"div",28),m(3,De,2,0,"div",29)),i&2){let e,n=s(3);u(n.editingSource()===null?0:1),o(2),u((e=n.saveError())?2:-1,e),o(),u(n.saving()?3:-1)}}function Ie(i,t){if(i&1&&m(0,ze,4,3),i&2){let e,n=s(2);u((e=n.selectedSpec())?0:-1,e)}}function $e(i,t){if(i&1&&(r(0,"app-pill"),d(1),a()),i&2){let e=s();o(),g(e.priority)}}function Ne(i,t){if(i&1&&(r(0,"div",37)(1,"div",38),d(2,"Owner"),a(),r(3,"div",39),d(4),a()()),i&2){let e=s();o(4),g(e.owner)}}function Re(i,t){if(i&1&&(r(0,"details",42)(1,"summary",55),d(2,"Brainstorm"),a(),p(3,"div",56),a()),i&2){let e=s(3);o(3),c("innerHTML",e.briefHtml(),D)}}function Ve(i,t){i&1&&p(0,"div",43)}function Le(i,t){if(i&1&&(r(0,"app-pill",59),d(1),a()),i&2){let e=s().$implicit;o(),h("",e.driftAxis," drift")}}function je(i,t){if(i&1&&(r(0,"div",58),p(1,"app-state-pill",34),m(2,Le,2,1,"app-pill",59),r(3,"span",60),d(4),a(),r(5,"app-pill"),d(6),a(),r(7,"button",61),p(8,"app-icon",52),d(9," Reveal "),a()()),i&2){let e=t.$implicit,n=t.$index;N("border-top",n>0?"1px solid var(--border-subtle)":"none"),o(),c("state",e.state),o(),u(e.driftAxis?2:-1),o(2),g(e.targetQualifiedName),o(2),g(e.provenance),o(2),c("size",12)}}function Ae(i,t){if(i&1&&(r(0,"div",44),I(1,je,10,7,"div",57,me),a()),i&2){let e=s(3);o(),$(e.selectedLinks())}}function We(i,t){i&1&&(r(0,"div",45),p(1,"app-icon",62),r(2,"div",63),d(3," Orphaned \u2014 no code implements this requirement yet "),a()()),i&2&&(o(),c("size",16))}function Fe(i,t){i&1&&(r(0,"div",54),d(1),a()),i&2&&(o(),g(t))}function He(i,t){if(i&1){let e=w();r(0,"div",11)(1,"div",31)(2,"span",32),d(3),a(),p(4,"app-copy-btn",33)(5,"div",6)(6,"app-state-pill",34),m(7,$e,2,1,"app-pill"),a(),r(8,"h1",35),d(9),a(),r(10,"div",36)(11,"div",37)(12,"div",38),d(13,"Kind"),a(),r(14,"div",39),d(15),a()(),m(16,Ne,5,1,"div",37),r(17,"div",37)(18,"div",38),d(19,"Doc"),a(),r(20,"div",39),d(21),a()(),r(22,"div",37)(23,"div",38),d(24,"Siblings"),a(),r(25,"div",39),d(26),a()(),r(27,"div",37)(28,"div",38),d(29,"Links"),a(),r(30,"div",39),d(31),a()()(),r(32,"div",40),d(33,"Requirement"),a(),p(34,"div",41),m(35,Re,4,1,"details",42),r(36,"div",40),d(37,"Linked code"),a(),m(38,Ve,1,0,"div",43)(39,Ae,3,0,"div",44)(40,We,4,1,"div",45),r(41,"div",46)(42,"button",47),p(43,"app-icon",48),d(44," Implement "),a(),r(45,"button",49),p(46,"app-icon",50),d(47," Verify "),a(),r(48,"button",51),x("click",function(){f(e);let l=s(2);return b(l.onEditClick())}),p(49,"app-icon",52),d(50," Edit spec "),a(),r(51,"button",51),x("click",function(){let l=f(e),v=s(2);return b(v.goToGraph(l.id))}),p(52,"app-icon",53),d(53," Show in graph "),a()(),m(54,Fe,2,1,"div",54),a()}if(i&2){let e,n=t,l=s(2);o(3),g(n.id),o(),c("text",n.id),o(2),c("state",n.kind),o(),u(n.priority?7:-1),o(2),g(n.title),o(6),g(n.kind),o(),u(n.owner?16:-1),o(5),g(n.sourcePath.replace("specs/","")),o(5),h("",l.selectedSiblings().length," reqs"),o(5),g(l.selectedLinks().length),o(3),c("innerHTML",l.mdHtml(n.body),D),o(),u(l.briefResource.state().data?35:-1),o(3),u(l.detailResource.state().loading?38:l.selectedLinks().length>0?39:40),o(5),c("size",13),o(3),c("size",13),o(3),c("size",13),o(3),c("size",13),o(2),u((e=l.saveError())?54:-1,e)}}function qe(i,t){i&1&&(r(0,"app-empty",12)(1,"code",64),d(2,"specship init -i"),a()())}function Ge(i,t){if(i&1){let e=w();r(0,"div",1)(1,"div",2)(2,"div",3),p(3,"app-icon",4),r(4,"span",5),d(5,"Specs"),a(),p(6,"span",6),r(7,"span",7),d(8),a()(),r(9,"div",8),m(10,we,1,0,"app-empty",9),I(11,Oe,7,6,null,null,he),a()(),r(13,"div",10),m(14,Ie,1,1)(15,He,55,18,"div",11)(16,qe,3,0,"app-empty",12),a()(),r(17,"app-draft-with-claude-modal",13),x("close",function(){f(e);let l=s();return b(l.onDraftModalClose())}),a()}if(i&2){let e,n=s();o(3),c("size",15),o(5),h(" ",n.resource.state().loading?"\u2026":n.totalCount()+" reqs"," "),o(2),u(n.groups().length===0&&!n.resource.state().loading?10:-1),o(),$(n.groups()),o(3),u(n.editing()?14:(e=n.selectedSpec())?15:16,e),o(3),c("open",n.draftModalOpen())}}function Qe(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\*\*(.+?)\*\*/g,"<strong style='color:var(--text-primary)'>$1</strong>").replace(/`(.+?)`/g,"<code class='mono' style='font-size:12px;background:var(--bg-canvas);padding:1px 5px;border-radius:4px;border:1px solid var(--border-subtle)'>$1</code>")}var pe=class i{api=y(X);projects=y(J);router=y(K);conn=y(Y);resource=V(this.api,()=>`/api/specs${this.projects.projectQuery()}`);detailResource=V(this.api,()=>{let t=this.sel();return t?`/api/spec/${encodeURIComponent(t)}${this.projects.projectQuery()}`:null});briefResource=V(this.api,()=>{let t=this.sel();return t?`/api/spec/${encodeURIComponent(t)}/brief${this.projects.projectQuery()}`:null});briefHtml=C(()=>this.mdHtml(this.briefResource.state().data?.markdown??""));sel=_(null);expandedGroups=_(new Set);editing=_(!1);editingSource=_(null);editingDirty=_(!1);saving=_(!1);saveError=_(null);draftModalOpen=_(!1);STATE=F;groups=C(()=>{let t=this.resource.state().data?.specs??[],e=new Map;for(let n of t){let l=n.sourcePath||"(unknown)";e.has(l)||e.set(l,{path:l,title:l.replace(/^specs\//,""),specs:[]}),e.get(l).specs.push(n)}return[...e.values()]});totalCount=C(()=>this.groups().reduce((t,e)=>t+e.specs.length,0));selectedSpec=C(()=>{let t=this.sel();if(!t)return null;for(let e of this.groups())for(let n of e.specs)if(n.id===t)return n;return null});selectedLinks=C(()=>this.detailResource.state().data?.links??[]);selectedSiblings=C(()=>{let t=this.selectedSpec();if(!t)return[];let e=this.groups().find(n=>n.path===t.sourcePath);return e?e.specs.filter(n=>n.id!==t.id):[]});isGroupOpen(t){return!this.expandedGroups().has("closed:"+t)}toggleGroup(t){this.expandedGroups.update(e=>{let n="closed:"+t,l=new Set(e);return l.has(n)?l.delete(n):l.add(n),l})}driftCountForGroup(t){let e=this.selectedSpec();return!e||e.sourcePath!==t.path?0:this.selectedLinks().filter(n=>["drifted","broken","orphaned"].includes(n.state)).length}stateColorFor(t){return F[t]?.color??"var(--node-spec)"}select(t){this.editing()&&(this.editing.set(!1),this.editingSource.set(null),this.editingDirty.set(!1)),this.sel.set(t)}mdHtml(t){return Qe(t)}goToGraph(t){this.router.navigate(["/graph"],{queryParams:{focus:"spec:"+t}})}async onEditClick(){let t=this.selectedSpec();if(t){this.saveError.set(null),this.editing.set(!0),this.editingSource.set(null);try{let e=this.projects.projectQuery(),n=await this.api.get(`/api/spec/${encodeURIComponent(t.id)}${e}`);this.editingSource.set(n.source??t.body)}catch(e){this.saveError.set(`Couldn't load source: ${e instanceof Error?e.message:String(e)}`),this.editing.set(!1)}}}onEditorValueChange(t){this.editingSource.set(t),this.editingDirty.set(!0)}async onEditorSave(){let t=this.selectedSpec(),e=this.editingSource();if(!(!t||e===null)){if(!this.conn.online()){this.saveError.set("Offline \u2014 reconnect to save your changes");return}this.saving.set(!0),this.saveError.set(null);try{let n=this.projects.projectQuery();await this.api.put(`/api/spec/${encodeURIComponent(t.id)}${n}`,{content:e}),this.editing.set(!1),this.editingDirty.set(!1),this.editingSource.set(null),this.resource.refetch()}catch(n){this.saveError.set(`Save failed: ${n instanceof Error?n.message:String(n)}`)}finally{this.saving.set(!1)}}}onEditorCancel(){this.editingDirty()&&!window.confirm("Discard unsaved changes?")||(this.editing.set(!1),this.editingSource.set(null),this.editingDirty.set(!1),this.saveError.set(null))}onDraftWithClaudeClick(){this.draftModalOpen.set(!0)}onDraftModalClose(){this.draftModalOpen.set(!1)}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=k({type:i,selectors:[["app-specs"]],decls:2,vars:1,consts:[["surface","Specs"],[1,"specs-shell"],[1,"tree-col"],[1,"tree-header","row","gap-8"],["name","book",2,"color","var(--accent)",3,"size"],[2,"font-weight","600","font-size","13px"],[1,"grow"],[1,"muted","tabular",2,"font-size","11px"],[1,"scroll-y","tree-scroll"],["icon","book","title","No specs found","body","Run specship init -i against a project with a specs/ directory."],[1,"detail-col","col"],[1,"scroll-y","detail-scroll"],["icon","book","title","Pick a spec from the tree","body","Or run specship init -i to index your specs/ folder if you haven't yet."],[3,"close","open"],[1,"doc-row","row","gap-6",3,"click"],[2,"color","var(--text-muted)",3,"name","size"],["name","book",2,"color","var(--node-spec)",3,"size"],[1,"mono","grow","doc-name"],[1,"pill",2,"font-size","9.5px","color","var(--warn)","background","var(--warn-soft)"],[1,"group-body"],[1,"req-row","row","gap-6",3,"selected"],[1,"req-row","row","gap-6",3,"click"],[1,"pill-dot",2,"flex-shrink","0"],[1,"grow",2,"min-width","0"],[1,"mono","req-id"],[1,"muted","req-title"],[1,"loading-detail"],[3,"value","path"],[1,"save-error"],[1,"save-status"],[3,"valueChange","save","cancel","value","path"],[1,"row","gap-8",2,"margin-bottom","4px"],[1,"mono",2,"font-size","12px","color","var(--node-spec)"],[3,"text"],[3,"state"],[1,"detail-title"],[1,"meta-row","row"],[1,"meta-item"],[1,"muted",2,"font-size","10.5px"],[1,"mono",2,"font-size","12.5px","margin-top","1px"],[1,"eyebrow",2,"margin-bottom","8px"],[1,"detail-body",3,"innerHTML"],[1,"brief-panel"],[1,"skel",2,"height","42px","border-radius","8px","margin-bottom","22px"],[1,"card",2,"overflow","hidden","margin-bottom","22px"],[1,"card","card-pad","orphan-card"],[1,"row","gap-8",2,"flex-wrap","wrap"],["type","button",1,"btn","btn-primary","btn-sm"],["name","play",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm"],["name","check",3,"size"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","reveal",3,"size"],["name","graph",3,"size"],[1,"save-error",2,"margin-top","16px"],[1,"eyebrow","brief-summary"],[1,"detail-body","brief-body",3,"innerHTML"],[1,"row","gap-10","link-row",3,"border-top"],[1,"row","gap-10","link-row"],["color","var(--warn)","bg","var(--warn-soft)"],[1,"mono","grow","link-target"],["type","button",1,"btn","btn-ghost","btn-xs"],["name","drift",3,"size"],[2,"margin-top","6px","font-size","12.5px"],["action","",1,"mono",2,"font-size","11.5px","color","var(--accent)","background","var(--bg-canvas)","padding","5px 10px","border-radius","6px","border","1px solid var(--border-subtle)"]],template:function(e,n){e&1&&m(0,ye,1,0,"app-pick-project-empty",0)(1,Ge,18,5),e&2&&u(n.resource.state().noProject?0:1)},dependencies:[Z,E,j,A,ie,ee,oe,te],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.specs-shell[_ngcontent-%COMP%]{flex:1;display:flex;min-height:0}.tree-col[_ngcontent-%COMP%]{width:280px;flex-shrink:0;border-right:1px solid var(--border-subtle);background:var(--bg-panel);display:flex;flex-direction:column;min-height:0}.tree-header[_ngcontent-%COMP%]{padding:11px 12px;border-bottom:1px solid var(--border-subtle);align-items:center}.tree-scroll[_ngcontent-%COMP%]{flex:1;padding:6px}.doc-row[_ngcontent-%COMP%]{padding:6px 8px;border-radius:6px;cursor:pointer;color:var(--text-secondary);align-items:center}.doc-row[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.doc-name[_ngcontent-%COMP%]{font-size:12px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-body[_ngcontent-%COMP%]{margin-left:14px;border-left:1px solid var(--border-subtle);padding-left:6px}.req-row[_ngcontent-%COMP%]{padding:5px 8px;border-radius:6px;cursor:pointer;align-items:flex-start;background:transparent}.req-row[_ngcontent-%COMP%]:not(.selected):hover{background:var(--bg-hover)}.req-row.selected[_ngcontent-%COMP%]{background:var(--accent-soft)}.req-id[_ngcontent-%COMP%]{font-size:11px}.req-title[_ngcontent-%COMP%]{font-size:10.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-col[_ngcontent-%COMP%]{flex:1;min-width:0}.detail-scroll[_ngcontent-%COMP%]{flex:1;padding:22px}.detail-title[_ngcontent-%COMP%]{font-size:22px;font-weight:650;letter-spacing:-.01em;margin:2px 0 14px;text-wrap:pretty}.meta-row[_ngcontent-%COMP%]{gap:18px;padding:10px 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle);margin-bottom:18px;flex-wrap:wrap}.detail-body[_ngcontent-%COMP%]{font-size:13.5px;line-height:1.65;color:var(--text-secondary);margin-bottom:22px;text-wrap:pretty}.brief-panel[_ngcontent-%COMP%]{border:1px solid var(--border-subtle);border-radius:8px;padding:0 12px;margin-bottom:22px;background:var(--bg-panel)}.brief-summary[_ngcontent-%COMP%]{padding:10px 0;cursor:pointer;list-style:none;display:flex;align-items:center;gap:6px;-webkit-user-select:none;user-select:none;margin-bottom:0}.brief-summary[_ngcontent-%COMP%]::-webkit-details-marker{display:none}.brief-summary[_ngcontent-%COMP%]:before{content:"\\25b6";font-size:8px;color:var(--text-muted);transition:transform .15s}details[open][_ngcontent-%COMP%] > .brief-summary[_ngcontent-%COMP%]:before{transform:rotate(90deg)}.brief-body[_ngcontent-%COMP%]{margin-bottom:12px}.link-row[_ngcontent-%COMP%]{padding:10px 12px}.link-target[_ngcontent-%COMP%]{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orphan-card[_ngcontent-%COMP%]{text-align:center;color:var(--error);margin-bottom:22px;border-color:#f2555a4d}.loading-detail[_ngcontent-%COMP%]{padding:40px 18px;color:var(--text-muted);font-size:13px;text-align:center}.save-error[_ngcontent-%COMP%]{padding:10px 14px;border:1px solid rgba(255,90,90,.35);background:var(--error-soft);color:var(--error);border-radius:8px;font-size:12.5px;line-height:1.5}.save-status[_ngcontent-%COMP%]{margin-top:8px;color:var(--text-muted);font-size:12px}'],changeDetection:0})};export{pe as Specs};
@@ -0,0 +1 @@
1
+ import{a as ce}from"./chunk-SEXBRGYK.js";import{a as se}from"./chunk-G7VZT5KB.js";import{a as de}from"./chunk-X2HTISHL.js";import{a as le}from"./chunk-DTRN7FZR.js";import{b as re,c as U}from"./chunk-GR72OOCN.js";import{a as ne,d as ie}from"./chunk-SHPTC4RL.js";import"./chunk-7RNS77UP.js";import{a as oe}from"./chunk-E44X4RH2.js";import{Aa as f,C as V,Ga as $,I as M,Ia as O,Ka as d,M as I,N as z,R as F,Ta as y,Ua as R,Va as l,W as w,Wa as x,X as B,Xa as _,Ya as N,Za as Q,aa as H,bb as X,ea as r,gb as S,hb as Z,ib as P,ka as Y,lb as b,qa as q,ra as p,sa as m,ua as K,va as E,vb as ee,wa as T,xa as u,ya as a,yb as te,za as o,zb as ae}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";function pe(n,t){let i=!t?.manualCleanup?t?.injector?.get(F)??M(F):null,s=Ce(t?.equal),c;t?.requireSync?c=w({kind:0},{equal:s}):c=w({kind:1,value:t?.initialValue},{equal:s});let h,k=n.subscribe({next:v=>c.set({kind:1,value:v}),error:v=>{c.set({kind:2,error:v}),h?.()},complete:()=>{h?.()}});if(t?.requireSync&&c().kind===0)throw new V(601,!1);return h=i?.onDestroy(k.unsubscribe.bind(k)),b(()=>{let v=c();switch(v.kind){case 1:return v.value;case 2:throw v.error;case 0:throw new V(601,!1)}},{equal:t?.equal})}function Ce(n=Object.is){return(t,e)=>t.kind===1&&e.kind===1&&n(t.value,e.value)}var ye=()=>["/sessions"],xe=(n,t)=>t.prompt.id,ke=(n,t)=>t.id,me=(n,t)=>t.name;function Se(n,t){if(n&1&&(a(0,"app-pill",7),l(1),o()),n&2){let e=d();u("color","var(--node-spec)")("bg","var(--node-spec-soft)"),r(),x(e.session().last_model)}}function Me(n,t){if(n&1&&l(0),n&2){let e=d(),i=d();Q(" \xB7 ",i.fmtClock(e.started_at)," \u2013 ",i.fmtClock(e.ended_at)," (",i.fmtDurationSec(e.started_at,e.ended_at),") ")}}function we(n,t){if(n&1&&(a(0,"div",10),l(1),p(2,Me,1,3),o()),n&2){let e=t;r(),_("",e.project_path," "),r(),m(e.started_at&&e.ended_at?2:-1)}}function Pe(n,t){n&1&&(a(0,"div",14),l(1,"Loading session\u2026"),o())}function Ee(n,t){if(n&1){let e=$();a(0,"div",15)(1,"div",18),l(2,"Couldn't load session"),o(),a(3,"div",19),l(4),o(),a(5,"button",20),O("click",function(){I(e);let s=d();return z(s.forceRefresh())}),l(6,"Retry"),o()()}if(n&2){let e,i=d();r(4),x((e=i.resource.state().error)==null?null:e.message)}}function Te(n,t){n&1&&(a(0,"app-pill",59),f(1,"app-icon",73),l(2,"subagent "),o()),n&2&&(u("color","var(--node-route)")("bg","var(--node-route-soft)"),r(),u("size",10))}function Oe(n,t){if(n&1&&f(0,"app-icon",60),n&2){let e=d().$implicit;u("size",12)("title",e.slashCommand)}}function De(n,t){if(n&1&&l(0),n&2){let e=d().$implicit;_(" ",e.prompt.text.length>140?e.prompt.text.slice(0,140)+"\u2026":e.prompt.text," ")}}function Ie(n,t){n&1&&(a(0,"span",62),l(1,"(assistant-only turn)"),o())}function ze(n,t){if(n&1&&(a(0,"span",63),l(1),o()),n&2){let e=d().$implicit;u("title","SpecShip \xB7 spent ~"+e.specshipSpendTokens+" tok \xB7 est. saved ~"+e.specshipSavedTokens+" tok"),r(),_("SS ~",e.specshipSpendTokens," tok")}}function Re(n,t){if(n&1&&(a(0,"span",67),l(1),o()),n&2){let e=d().$implicit,i=d(2);r(),x(i.fmtDurationMs(e.prompt.durationMs))}}function Fe(n,t){if(n&1&&(a(0,"section",74)(1,"div",75),l(2,"User prompt"),o(),a(3,"pre",81),l(4),o()()),n&2){let e=d(2).$implicit;r(4),x(e.prompt.text)}}function $e(n,t){if(n&1&&(a(0,"section",74)(1,"div",75),l(2,"Assistant response"),o(),f(3,"div",82),o()),n&2){let e=d(2).$implicit,i=d(2);r(3),u("innerHTML",i.renderAssistant(e.prompt.assistant_text),H)}}function Be(n,t){if(n&1&&(a(0,"section",74)(1,"details",83)(2,"summary",84),l(3," Extended thinking "),a(4,"span",85),l(5,"(click to expand)"),o()(),f(6,"div",86),o()()),n&2){let e=d(2).$implicit,i=d(2);r(6),u("innerHTML",i.renderAssistant(e.prompt.thinking_text),H)}}function Ne(n,t){if(n&1&&(a(0,"div",77)(1,"div",78),l(2,"Duration"),o(),a(3,"div",79),l(4),o()()),n&2){let e=d(2).$implicit,i=d(2);r(4),x(i.fmtDurationMs(e.prompt.durationMs))}}function je(n,t){if(n&1&&(a(0,"div",77)(1,"div",78),l(2,"Model"),o(),a(3,"div",79),l(4),o()()),n&2){let e=d(2).$implicit;r(4),x(e.prompt.model)}}function Le(n,t){if(n&1&&(a(0,"div",88)(1,"span",89),l(2),o()()),n&2){let e=t.$implicit;u("title",e),r(2),x(e)}}function Ae(n,t){if(n&1&&(a(0,"section",74)(1,"div",75),l(2),o(),a(3,"div",87),E(4,Le,3,2,"div",88,K),o()()),n&2){let e=d(2).$implicit;r(2),_("Files touched \xB7 ",e.filesTouched.length),r(2),T(e.filesTouched)}}function Ve(n,t){n&1&&l(0," \u203A ")}function He(n,t){if(n&1&&(a(0,"span",95),l(1),o()),n&2){let e=d().$implicit;r(),x(e.input_summary)}}function qe(n,t){if(n&1&&(a(0,"span",96),l(1),o()),n&2){let e=d().$implicit,i=d(5);r(),x(i.fmtBytes(e.result_length))}}function Ue(n,t){if(n&1&&(a(0,"pre",97),l(1),o()),n&2){let e=d().$implicit,i=d(5);r(),x(i.formatToolInput(e.input_json))}}function We(n,t){if(n&1){let e=$();a(0,"div",91)(1,"button",92),O("click",function(){let s=I(e).$implicit,c=d(5);return z(c.toggleToolInput(s.id))}),a(2,"span",93),p(3,Ve,1,0),o(),a(4,"span",94),l(5),o(),p(6,He,2,1,"span",95),f(7,"span",31),p(8,qe,2,1,"span",96),o(),p(9,Ue,2,1,"pre",97),o()}if(n&2){let e=t.$implicit,i=d(5);r(),R("has-input",!!e.input_json)("expanded",i.isToolInputExpanded(e.id)),u("disabled",!e.input_json),q("aria-expanded",i.isToolInputExpanded(e.id)),r(),R("open",i.isToolInputExpanded(e.id)),r(),m(e.input_json?3:-1),r(),y("color",i.toolColor(e.tool_name)),r(),x(e.tool_name),r(),m(e.input_summary?6:-1),r(2),m(e.result_length?8:-1),r(),m(e.input_json&&i.isToolInputExpanded(e.id)?9:-1)}}function Ge(n,t){if(n&1&&(a(0,"section",74)(1,"div",75),l(2),o(),a(3,"div",90),E(4,We,10,15,"div",91,ke),o()()),n&2){let e=d(2).$implicit,i=d(2);r(2),N("Tool calls \xB7 ",e.tools.length," \xB7 returned ",i.fmtBytes(e.toolBytes)),r(2),T(e.tools)}}function Je(n,t){if(n&1&&(a(0,"div",72),p(1,Fe,5,1,"section",74),p(2,$e,4,1,"section",74),p(3,Be,7,1,"section",74),a(4,"section",74)(5,"div",75),l(6,"Token breakdown"),o(),a(7,"div",76)(8,"div",77)(9,"div",78),l(10,"Input"),o(),a(11,"div",79),l(12),o()(),a(13,"div",77)(14,"div",78),l(15,"Output"),o(),a(16,"div",79),l(17),o()(),a(18,"div",77)(19,"div",78),l(20,"Cache write"),o(),a(21,"div",79),l(22),o()(),a(23,"div",77)(24,"div",78),l(25,"Cache read"),o(),a(26,"div",79),l(27),o()(),a(28,"div",77)(29,"div",78),l(30,"Cost"),o(),a(31,"div",80),l(32),o()(),p(33,Ne,5,1,"div",77),p(34,je,5,1,"div",77),o()(),p(35,Ae,6,1,"section",74),p(36,Ge,6,2,"section",74),o()),n&2){let e=d().$implicit,i=d(2);r(),m(e.prompt.text?1:-1),r(),m(e.prompt.assistant_text?2:-1),r(),m(e.prompt.thinking_text?3:-1),r(9),x(i.fmtTokens(e.prompt.input_tokens)),r(5),x(i.fmtTokens(e.prompt.output_tokens)),r(5),x(i.fmtTokens(e.prompt.cache_creation_tokens)),r(5),x(i.fmtTokens(e.prompt.cache_read_tokens)),r(5),_("$",e.prompt.cost_usd.toFixed(4)),r(),m(e.prompt.durationMs?33:-1),r(),m(e.prompt.model?34:-1),r(),m(e.filesTouched.length>0?35:-1),r(),m(e.tools.length>0?36:-1)}}function Ye(n,t){if(n&1){let e=$();a(0,"div",34)(1,"button",56),O("click",function(){let s=I(e).$implicit,c=d(2);return z(c.toggleExpand(s.prompt.id))}),a(2,"span",57),l(3),o(),f(4,"span",58),S(5,"number"),p(6,Te,3,3,"app-pill",59),p(7,Oe,1,2,"app-icon",60),a(8,"span",61),p(9,De,1,1)(10,Ie,2,0,"span",62),o(),p(11,ze,2,2,"span",63),a(12,"span",64),f(13,"app-icon",65),a(14,"span",66),l(15),o()(),p(16,Re,2,1,"span",67),a(17,"span",68),l(18),o(),a(19,"span",69),l(20),S(21,"number"),o(),a(22,"span",70),l(23),o(),f(24,"app-icon",71),o(),p(25,Je,37,12,"div",72),o()}if(n&2){let e=t.$implicit,i=d(2);r(),R("expanded",i.isExpanded(e.prompt.id)),q("aria-expanded",i.isExpanded(e.prompt.id)),r(2),x(i.fmtClock(e.prompt.ts)),r(),y("background",i.cacheColor(e.prompt.cache_read_tokens>0?e.prompt.cache_read_tokens/(e.prompt.input_tokens+e.prompt.cache_creation_tokens+e.prompt.cache_read_tokens||1):0)),u("title","Cache: "+Z(5,23,e.prompt.cache_read_tokens)),r(2),m(e.prompt.is_sidechain===1?6:-1),r(),m(e.slashCommand?7:-1),r(2),m(e.prompt.text?9:10),r(2),m(e.specshipSpendTokens>0?11:-1),r(2),u("size",11),r(2),x(e.tools.length),r(),m(e.prompt.durationMs?16:-1),r(2),x(i.fmtTokens(e.totalTokens)),r(),y("color",i.cacheColor(e.totalTokens>0?(e.prompt.cache_read_tokens||0)/e.totalTokens:0)),r(),_(" ",e.totalTokens>0?P(21,25,(e.prompt.cache_read_tokens||0)/e.totalTokens*100,"1.0-0"):0,"% "),r(2),y("color",e.prompt.cost_usd>2.5?"var(--error)":"var(--text-primary)"),r(),_("$",e.prompt.cost_usd.toFixed(2)),r(),u("size",14),r(),m(i.isExpanded(e.prompt.id)?25:-1)}}function Ke(n,t){n&1&&(a(0,"div",17),l(1,"No prompts logged for this session yet."),o())}function Qe(n,t){if(n&1&&(a(0,"span",104),l(1),o()),n&2){let e=d().$implicit;r(),_("\xD7",e.count)}}function Xe(n,t){if(n&1&&(a(0,"span",101),f(1,"app-icon",103),l(2),p(3,Qe,2,1,"span",104),o()),n&2){let e=t.$implicit;r(),u("size",10),r(),_("",e.name," "),r(),m(e.count>1?3:-1)}}function Ze(n,t){if(n&1&&(a(0,"span",104),l(1),o()),n&2){let e=d().$implicit;r(),_("\xD7",e.count)}}function et(n,t){if(n&1&&(a(0,"span",102),f(1,"app-icon",105),l(2),p(3,Ze,2,1,"span",104),o()),n&2){let e=t.$implicit;r(),u("size",10),r(),_("",e.name," "),r(),m(e.count>1?3:-1)}}function tt(n,t){if(n&1&&(a(0,"div",98)(1,"div",36),l(2,"Commands & skills"),o(),a(3,"div",100),E(4,Xe,4,3,"span",101,me),E(6,et,4,3,"span",102,me),o()()),n&2){let e=d();r(4),T(e.slashCommands),r(2),T(e.skills)}}function nt(n,t){if(n&1&&(a(0,"div",36),l(1),o(),f(2,"app-h-bars",106),a(3,"div",107),l(4,"bar = call count"),o()),n&2){let e=d(),i=d(2);r(),_("Tools used \xB7 ",e.byTool.length),r(),u("items",e.byTool)("fmt",i.fmtTokensFn)("color",i.toolBarColor)}}function it(n,t){if(n&1&&(a(0,"div",99)(1,"div",108)(2,"span",109),l(3,"SpecShip"),o(),a(4,"span",110),l(5,"est."),o()(),a(6,"div",111)(7,"div",112)(8,"span",113),l(9,"Spent"),o(),a(10,"span",114),l(11),o()(),a(12,"div",112)(13,"span",113),l(14,"Est. saved"),o(),a(15,"span",115),l(16),o()(),a(17,"div",116)(18,"span",113),l(19,"Net"),o(),a(20,"span",117),l(21),o()()()()),n&2){let e=d(),i=d(2);r(11),_("~",i.fmtTokens(e.specship.spendTokens)," tok"),r(5),_("~",i.fmtTokens(e.specship.savedTokens)," tok"),r(4),y("color",e.specship.netTokens>=0?"var(--success)":"var(--warn)"),r(),N("",e.specship.netTokens>0?"+":"","",i.fmtTokens(e.specship.netTokens)," tok")}}function ot(n,t){if(n&1&&(p(0,tt,8,0,"div",98),p(1,nt,5,4),p(2,it,22,6,"div",99)),n&2){let e=t;m(e.slashCommands.length||e.skills.length?0:-1),r(),m(e.byTool.length?1:-1),r(),m(e.specship&&e.specship.spendTokens>0?2:-1)}}function rt(n,t){if(n&1&&(a(0,"div",43)(1,"span",120),l(2),o(),a(3,"div",31),f(4,"app-bar",121),o(),a(5,"span",122),l(6),o()()),n&2){let e=t.$implicit,i=d(3);r(2),x(i.fmtClock(e.prompt.ts)),r(2),u("frac",e.prompt.cost_usd/i.maxPromptCost())("color",e.prompt.cost_usd>2.5?"var(--error)":"var(--accent)"),r(2),_("$",e.prompt.cost_usd.toFixed(2))}}function at(n,t){if(n&1&&(a(0,"div",118),l(1,"Cost per prompt"),o(),a(2,"div",119),E(3,rt,7,4,"div",43,xe),o()),n&2){let e=d(2);r(3),T(e.groups())}}function lt(n,t){if(n&1){let e=$();a(0,"div",16)(1,"div",21)(2,"div",22)(3,"div",23)(4,"div",24),l(5,"Cost"),o(),a(6,"div",25),l(7),o()(),a(8,"div",23)(9,"div",24),l(10,"Prompts"),o(),a(11,"div",26),l(12),o()(),a(13,"div",23)(14,"div",24),l(15,"Window"),o(),a(16,"div",27),l(17),o()(),a(18,"div",23)(19,"div",24),l(20,"Cache hit"),o(),a(21,"div",26),l(22),S(23,"number"),o()(),a(24,"div",23)(25,"div",24),l(26,"Tool calls"),o(),a(27,"div",26),l(28),o()()(),a(29,"div",28)(30,"span",29),l(31),o(),a(32,"span",30),l(33,"click a prompt for full detail"),o(),f(34,"div",31),a(35,"button",32),O("click",function(){I(e);let s=d();return z(s.expandAll())}),l(36,"Expand all"),o(),a(37,"button",32),O("click",function(){I(e);let s=d();return z(s.collapseAll())}),l(38,"Collapse all"),o()(),a(39,"div",33),E(40,Ye,26,28,"div",34,xe,!1,Ke,2,0,"div",17),o()(),a(43,"div",35)(44,"div",36),l(45,"Token mix"),o(),a(46,"div",37),f(47,"div",38)(48,"div",39)(49,"div",40)(50,"div",41),o(),a(51,"div",42)(52,"div",43),f(53,"span",44),a(54,"span",45),l(55,"Input"),o(),a(56,"span",46),l(57),o(),a(58,"span",47),l(59),S(60,"number"),o()(),a(61,"div",43),f(62,"span",48),a(63,"span",45),l(64,"Output"),o(),a(65,"span",46),l(66),o(),a(67,"span",47),l(68),S(69,"number"),o()(),a(70,"div",43),f(71,"span",49),a(72,"span",45),l(73,"Cache write"),o(),a(74,"span",46),l(75),o(),a(76,"span",47),l(77),S(78,"number"),o()(),a(79,"div",43),f(80,"span",50),a(81,"span",45),l(82,"Cache read"),o(),a(83,"span",46),l(84),o(),a(85,"span",47),l(86),S(87,"number"),o()()(),a(88,"div",51)(89,"div",43),f(90,"app-icon",52),a(91,"span",53),l(92,"Cache effectiveness"),o()(),a(93,"div",54)(94,"span",55),l(95),S(96,"number"),o(),a(97,"span",53),l(98,"of input served from cache"),o()()(),p(99,ot,3,3),p(100,at,5,0),o()()}if(n&2){let e,i=t,s=d();r(7),_("$",i.total_cost_usd.toFixed(2)),r(5),x(i.prompt_count),r(5),N(" ",s.fmtClock(i.started_at)," \u2013 ",s.fmtClock(i.ended_at).split(" ").pop()," "),r(4),y("color",s.cacheColor(s.cacheReadRate())),r(),_(" ",P(23,34,s.cacheReadRate()*100,"1.0-0"),"% "),r(6),x(s.totalToolCalls()),r(3),_("Prompt timeline \xB7 ",s.groups().length),r(9),T(s.groups()),r(7),y("width",s.tokPct("input"),"%"),u("title","Input: "+s.fmtTokens(i.total_input_tokens)),r(),y("width",s.tokPct("output"),"%"),u("title","Output: "+s.fmtTokens(i.total_output_tokens)),r(),y("width",s.tokPct("cacheWrite"),"%"),u("title","Cache write: "+s.fmtTokens(i.total_cache_creation_tokens)),r(),y("width",s.tokPct("cacheRead"),"%"),u("title","Cache read: "+s.fmtTokens(i.total_cache_read_tokens)),r(7),x(s.fmtTokens(i.total_input_tokens)),r(2),_("",P(60,37,s.tokPct("input"),"1.0-0"),"%"),r(7),x(s.fmtTokens(i.total_output_tokens)),r(2),_("",P(69,40,s.tokPct("output"),"1.0-0"),"%"),r(7),x(s.fmtTokens(i.total_cache_creation_tokens)),r(2),_("",P(78,43,s.tokPct("cacheWrite"),"1.0-0"),"%"),r(7),x(s.fmtTokens(i.total_cache_read_tokens)),r(2),_("",P(87,46,s.tokPct("cacheRead"),"1.0-0"),"%"),r(4),u("size",13),r(5),_("",P(96,49,s.cacheReadRate()*100,"1.0-0"),"%"),r(4),m((e=s.summaryData())?99:-1,e),r(),m(s.groups().length>0?100:-1)}}function st(n,t){n&1&&(a(0,"div",17),l(1,"Session not found."),o())}var dt=5e3,ct=/<command-name>([^<]+)<\/command-name>/,pt=new Set(["Read","Edit","Write","NotebookEdit","MultiEdit"]);function mt(n){if(!n)return[];try{let t=JSON.parse(n);return Array.isArray(t)?t.filter(e=>Array.isArray(e)&&e.length>=2&&typeof e[0]=="string"&&typeof e[1]=="number"):[]}catch{return[]}}var ue=class n{api=M(oe);route=M(ne);sanitizer=M(te);destroyRef=M(F);refresh=M(re);liveMode=w("idle");idSig=pe(this.route.paramMap,{initialValue:this.route.snapshot.paramMap});id=b(()=>this.idSig().get("id")??"");localRefreshing=w(!1);expandedIds=w(new Set);expandedToolInputs=w(new Set);resource=U(this.api,()=>{let t=this.id();return t?`/api/claude/session/${encodeURIComponent(t)}`:null});summary=U(this.api,()=>{let t=this.id();return t?`/api/claude/session/${encodeURIComponent(t)}/summary`:null});session=b(()=>this.resource.state().data?.session??null);prompts=b(()=>this.resource.state().data?.prompts??[]);toolCalls=b(()=>this.resource.state().data?.toolCalls??[]);summaryData=b(()=>this.summary.state().data??null);groups=b(()=>{let t=this.prompts(),e=this.toolCalls(),i=new Map;for(let s of e){let c=i.get(s.prompt_id)??[];c.push(s),i.set(s.prompt_id,c)}return t.map(s=>{let c=i.get(s.id)??[],h=(s.input_tokens||0)+(s.output_tokens||0)+(s.cache_creation_tokens||0)+(s.cache_read_tokens||0),k=c.reduce((g,C)=>g+(C.result_length||0),0),v=s.text?ct.exec(s.text):null,D=v?v[1].trim():null,W=c.length>0?Math.max(...c.map(g=>g.ts||0)):0,_e=W>s.ts?W-s.ts:0,j=new Map;for(let g of c)j.set(g.tool_name,(j.get(g.tool_name)??0)+1);let ge=Array.from(j.entries()).map(([g,C])=>({name:g,count:C,color:this.toolColor(g)})).sort((g,C)=>C.count-g.count),G=new Set;for(let g of c)pt.has(g.tool_name)&&g.input_summary&&G.add(g.input_summary);let L=c.filter(g=>g.is_specship===1),J=L.reduce((g,C)=>g+(C.result_length||0),0),fe=L.length>0?Math.ceil(J/4):0,A=new Map;for(let g of L)if(g.resolution==="resolved")for(let[C,be]of mt(g.displaced_files))A.has(C)||A.set(C,be);let ve=Array.from(A.values()).reduce((g,C)=>g+C,0),he=Math.ceil(Math.max(0,ve-J)/4);return{prompt:s,tools:c,totalTokens:h,toolBytes:k,slashCommand:D,durationMs:_e,toolBreakdown:ge,filesTouched:Array.from(G),specshipSpendTokens:fe,specshipSavedTokens:he}})});totalToolCalls=b(()=>this.toolCalls().length);totalTokens=b(()=>{let t=this.session();return t&&(t.total_input_tokens||0)+(t.total_output_tokens||0)+(t.total_cache_creation_tokens||0)+(t.total_cache_read_tokens||0)||1});maxPromptCost=b(()=>{let t=this.groups();return Math.max(1e-4,...t.map(e=>e.prompt.cost_usd||0))});toolBarColor=t=>{let e=t.name;return e==="Read"?"var(--node-spec)":e==="Edit"||e==="Write"||e==="MultiEdit"?"var(--accent)":e==="Bash"?"var(--warn)":e==="Grep"||e==="Glob"?"var(--node-test)":e==="Task"?"var(--node-route)":e.startsWith("mcp__")?"var(--success)":"var(--node-code)"};cacheReadRate=b(()=>{let t=this.session();if(!t)return 0;let e=(t.total_input_tokens||0)+(t.total_cache_creation_tokens||0)+(t.total_cache_read_tokens||0);return e>0?(t.total_cache_read_tokens||0)/e:0});constructor(){B(()=>{!this.resource.state().loading&&this.localRefreshing()&&this.localRefreshing.set(!1)});let t=null,e=null,i=()=>{e!==null&&(clearInterval(e),e=null)},s=()=>{e===null&&(e=setInterval(()=>{typeof document<"u"&&document.visibilityState==="visible"&&this.resource.refetch()},dt))},c=()=>{t&&(t(),t=null)},h=v=>{v&&(c(),t=this.api.openEventStream(`/api/claude/session/${encodeURIComponent(v)}/events`,D=>{D==="prompt_added"||D==="tool_call_added"?this.resource.refetch():D==="snapshot"?(this.liveMode.set("live"),i()):D==="stream_error"&&(this.liveMode.set("polling"),s())},()=>{this.liveMode.set("polling"),s()},["prompt_added","tool_call_added","snapshot","stream_error"]))},k=()=>{if(!(typeof document>"u"))if(document.visibilityState==="visible"){this.resource.refetch();let v=this.id();v&&this.liveMode()==="polling"&&h(v)}else i()};B(()=>{let v=this.id();if(c(),i(),!v){this.liveMode.set("idle");return}h(v)}),typeof document<"u"&&document.addEventListener("visibilitychange",k),this.destroyRef.onDestroy(()=>{c(),i(),this.liveMode.set("idle"),typeof document<"u"&&document.removeEventListener("visibilitychange",k)})}async forceRefresh(){this.localRefreshing.set(!0),await this.refresh.triggerGlobalRefresh()}isExpanded(t){return this.expandedIds().has(t)}toggleExpand(t){this.expandedIds.update(e=>{let i=new Set(e);return i.has(t)?i.delete(t):i.add(t),i})}expandAll(){this.expandedIds.set(new Set(this.prompts().map(t=>t.id)))}collapseAll(){this.expandedIds.set(new Set)}isToolInputExpanded(t){return this.expandedToolInputs().has(t)}toggleToolInput(t){this.expandedToolInputs.update(e=>{let i=new Set(e);return i.has(t)?i.delete(t):i.add(t),i})}renderAssistant(t){return this.sanitizer.bypassSecurityTrustHtml(se(t??""))}formatToolInput(t){if(!t)return"";try{return JSON.stringify(JSON.parse(t),null,2)}catch{return t}}fmtBytes(t){return t?t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} kB`:`${(t/(1024*1024)).toFixed(1)} MB`:"0"}fmtTokens(t){return t<1e3?`${t}`:t<1e6?`${(t/1e3).toFixed(1)}k`:`${(t/1e6).toFixed(2)}M`}fmtTokensFn=t=>this.fmtTokens(t);fmtClock(t){if(!t)return"";try{let e=new Date(t),i=new Date;i.setHours(0,0,0,0);let s=864e5,c=i.getTime()-new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime(),h=e.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"});return c===0?h:c===s?"Yest "+h:e.toLocaleDateString([],{month:"short",day:"numeric"})+" "+h}catch{return String(t)}}fmtDurationSec(t,e){if(!t||!e)return"";let i=Math.round((e-t)/1e3);if(i<60)return`${i}s`;let s=Math.floor(i/60),c=i%60;if(s<60)return`${s}m ${c}s`;let h=Math.floor(s/60),k=s%60;return`${h}h ${k}m`}fmtDurationMs(t){if(!t||t<=0)return"";if(t<1e3)return`${t}ms`;let e=t/1e3;if(e<60)return`${e.toFixed(e<10?1:0)}s`;let i=Math.floor(e/60),s=Math.round(e%60);if(i<60)return`${i}m ${s}s`;let c=Math.floor(i/60),h=i%60;return`${c}h ${h}m`}basename(t){if(!t)return"";if(t.length<=40)return t;let e=t.split("/");return e.length<=2?t:"\u2026/"+e.slice(-2).join("/")}toolColor(t){return t==="Read"?"var(--node-spec)":t==="Edit"||t==="Write"||t==="MultiEdit"||t==="NotebookEdit"?"var(--accent)":t==="Bash"?"var(--warn)":t==="Grep"||t==="Glob"?"var(--node-test)":t==="Task"?"var(--node-route)":t.startsWith("mcp__")?"var(--success)":"var(--text-secondary)"}cacheColor(t){return t>=.7?"var(--success)":t>=.5?"var(--warn)":"var(--error)"}tokPct(t){let e=this.session();if(!e)return 0;let i=this.totalTokens();return(t==="input"?e.total_input_tokens||0:t==="output"?e.total_output_tokens||0:t==="cacheWrite"?e.total_cache_creation_tokens||0:e.total_cache_read_tokens||0)/i*100}static \u0275fac=function(e){return new(e||n)};static \u0275cmp=Y({type:n,selectors:[["app-session-detail"]],decls:22,vars:20,consts:[[1,"sd-root"],[1,"page-head"],[1,"back-link","btn","btn-ghost","btn-sm",3,"routerLink"],["name","chevronLeft",3,"size"],[1,"title-block"],[1,"title"],[1,"mono","session-id"],[3,"color","bg"],[1,"live-pill",3,"title"],[1,"dot"],[1,"sub"],[1,"page-actions"],["type","button","aria-label","Refresh session detail",1,"btn","btn-secondary","btn-sm","refresh-btn",3,"click","disabled"],["name","refresh",3,"size"],[1,"loading"],[1,"error"],[1,"sd-body"],[1,"empty"],[1,"error-title"],[1,"error-detail"],[1,"btn","btn-secondary","btn-sm",3,"click"],[1,"scroll-y","sd-main"],[1,"stat-strip"],[1,"stat"],[1,"stat-lbl"],[1,"stat-val","cost"],[1,"stat-val","mono"],[1,"stat-val","mono",2,"font-size","12px"],[1,"row","gap-8",2,"justify-content","space-between","margin-bottom","8px"],[1,"eyebrow"],[1,"muted",2,"font-size","11px"],[1,"grow"],["type","button",1,"btn","btn-ghost","btn-sm",3,"click"],[1,"col","gap-6",2,"padding-bottom","16px"],[1,"prompt","card"],[1,"scroll-y","sd-rail"],[1,"eyebrow",2,"margin-bottom","10px"],[1,"tok-bar",2,"margin-bottom","10px"],[1,"tok-seg",2,"background","var(--node-spec)",3,"title"],[1,"tok-seg",2,"background","var(--node-code)",3,"title"],[1,"tok-seg",2,"background","var(--warn)",3,"title"],[1,"tok-seg",2,"background","var(--node-route)",3,"title"],[1,"col","gap-6",2,"margin-bottom","20px"],[1,"row","gap-8"],[2,"width","9px","height","9px","border-radius","2px","background","var(--node-spec)","flex-shrink","0"],[1,"muted","grow",2,"font-size","11.5px"],[1,"mono","tabular",2,"font-size","11.5px"],[1,"mono","tabular","muted",2,"font-size","10px","width","32px","text-align","right"],[2,"width","9px","height","9px","border-radius","2px","background","var(--node-code)","flex-shrink","0"],[2,"width","9px","height","9px","border-radius","2px","background","var(--warn)","flex-shrink","0"],[2,"width","9px","height","9px","border-radius","2px","background","var(--node-route)","flex-shrink","0"],[1,"cache-card",2,"margin-bottom","20px"],["name","database",2,"color","var(--success)",3,"size"],[1,"muted",2,"font-size","10.5px"],[1,"row",2,"align-items","baseline","gap","8px","margin-top","4px"],[1,"tabular",2,"font-size","24px","font-weight","700","color","var(--success)"],["type","button",1,"prompt-head","row","gap-10",3,"click"],[1,"mono","muted",2,"font-size","10.5px","width","52px","flex-shrink","0"],[2,"width","8px","height","8px","border-radius","50%","flex-shrink","0",3,"title"],[2,"flex-shrink","0",3,"color","bg"],["name","command",2,"color","var(--node-code)","flex-shrink","0",3,"size","title"],[1,"grow",2,"font-size","12.5px","overflow","hidden","text-overflow","ellipsis","white-space","nowrap","min-width","0"],[1,"muted"],[1,"pill",2,"color","var(--success)","background","var(--success-soft)","font-family","var(--font-mono)","font-size","10px","font-weight","600","flex-shrink","0","cursor","help",3,"title"],[1,"row","gap-4",2,"flex-shrink","0"],["name","wrench",2,"color","var(--text-muted)",3,"size"],[1,"mono","tabular","muted",2,"font-size","11px"],["title","Wall-clock duration for this prompt",1,"mono","tabular","muted",2,"font-size","11px","width","44px","text-align","right","flex-shrink","0"],[1,"mono","tabular","muted",2,"font-size","11px","width","50px","text-align","right","flex-shrink","0"],[1,"mono","tabular",2,"font-size","11px","width","38px","text-align","right","flex-shrink","0"],[1,"mono","tabular",2,"font-size","13px","font-weight","600","width","54px","text-align","right","flex-shrink","0"],["name","chevronRight",2,"color","var(--text-muted)","flex-shrink","0",3,"size"],[1,"prompt-body"],["name","bot",3,"size"],[1,"prompt-section"],[1,"section-lbl"],[1,"kv-row"],[1,"kv"],[1,"kv-lbl"],[1,"kv-val","mono"],[1,"kv-val","mono","cost"],[1,"prompt-pre"],[1,"assistant-body","md-content",3,"innerHTML"],[1,"thinking-details"],[1,"section-lbl","thinking-summary"],[1,"muted","xs"],[1,"thinking-body","md-content",3,"innerHTML"],[1,"files-touched"],[1,"file-row",3,"title"],[1,"file-path","mono"],[1,"tools"],[1,"tool-block"],["type","button",1,"tool-row",3,"click","disabled"],["aria-hidden","true",1,"tool-chevron","mono"],[1,"tool-name","mono"],[1,"tool-input","mono"],[1,"tool-bytes","mono","muted"],[1,"tool-input-json","mono"],[2,"margin-bottom","20px"],[2,"margin-top","16px","padding","10px 12px","background","var(--success-soft)","border","1px solid rgba(70,194,107,0.25)","border-radius","8px"],[1,"row","gap-6",2,"flex-wrap","wrap"],[1,"pill",2,"color","var(--node-code)","background","var(--node-code-soft)","font-family","var(--font-mono)","font-weight","500"],[1,"pill",2,"color","var(--node-test)","background","var(--node-test-soft)","font-family","var(--font-mono)","font-weight","500"],["name","command",3,"size"],[2,"opacity","0.7"],["name","sparkles",3,"size"],["valueKey","calls",3,"items","fmt","color"],[1,"muted",2,"font-size","10px","margin-top","8px"],[1,"row","gap-8",2,"margin-bottom","4px"],[2,"font-size","10.5px","font-weight","700","color","var(--success)","text-transform","uppercase","letter-spacing","0.04em"],[1,"pill",2,"color","var(--success)","background","rgba(70,194,107,0.15)","font-size","9.5px","padding","1px 5px"],[1,"col","gap-4"],[1,"row","gap-6"],[1,"muted","grow",2,"font-size","11px"],[1,"mono","tabular",2,"font-size","11px","color","var(--text-primary)"],[1,"mono","tabular",2,"font-size","11px","color","var(--success)"],[1,"row","gap-6",2,"border-top","1px dashed rgba(70,194,107,0.3)","padding-top","4px","margin-top","2px"],[1,"mono","tabular",2,"font-size","11px"],[1,"eyebrow",2,"margin","20px 0 10px"],[1,"col","gap-5"],[1,"mono","muted",2,"font-size","10px","width","44px","flex-shrink","0"],[3,"frac","color"],[1,"mono","tabular",2,"font-size","10.5px","width","40px","text-align","right"]],template:function(e,i){if(e&1&&(a(0,"div",0)(1,"header",1)(2,"a",2),f(3,"app-icon",3),l(4," Sessions "),o(),a(5,"div",4)(6,"div",5)(7,"span",6),l(8),o(),p(9,Se,2,3,"app-pill",7),a(10,"span",8),f(11,"span",9),l(12),o()(),p(13,we,3,2,"div",10),o(),a(14,"div",11)(15,"button",12),O("click",function(){return i.forceRefresh()}),f(16,"app-icon",13),l(17),o()()(),p(18,Pe,2,0,"div",14)(19,Ee,7,1,"div",15)(20,lt,101,52,"div",16)(21,st,2,0,"div",17),o()),e&2){let s,c,h;r(2),u("routerLink",X(19,ye)),r(),u("size",14),r(5),x(i.id().slice(0,12)),r(),m((s=i.session())!=null&&s.last_model?9:-1),r(),R("live",i.liveMode()==="live")("polling",i.liveMode()==="polling")("idle",i.liveMode()==="idle"),u("title",i.liveMode()==="live"?"Live \u2014 new prompts push as Claude Code writes them":i.liveMode()==="polling"?"Polling \u2014 SSE unavailable, refetching every 5s":"Idle \u2014 live updates paused"),r(2),_(" ",i.liveMode()==="live"?"Live":i.liveMode()==="polling"?"Polling":"Idle"," "),r(),m((c=i.session())?13:-1,c),r(2),R("spinning",i.localRefreshing()||i.refresh.loading()),u("disabled",i.refresh.loading()),r(),u("size",13),r(),_(" ",i.localRefreshing()||i.refresh.loading()?"Refreshing\u2026":"Refresh"," "),r(),m(i.resource.state().loading&&!i.session()?18:i.resource.state().error?19:(h=i.session())?20:21,h)}},dependencies:[ie,ae,de,le,ce,ee],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.sd-root[_ngcontent-%COMP%]{flex:1;display:flex;flex-direction:column;min-height:0;padding:16px 18px 0}.page-head[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:12px;margin-bottom:14px;flex-wrap:wrap}.back-link[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:4px;color:var(--text-muted);text-decoration:none}.title-block[_ngcontent-%COMP%]{flex:1;min-width:250px}.title[_ngcontent-%COMP%]{font-size:var(--fs-title);font-weight:600;letter-spacing:-.01em;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.session-id[_ngcontent-%COMP%]{color:var(--accent);font-size:14px}.live-pill[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:6px;font-size:10.5px;font-weight:600;padding:2px 8px;border-radius:999px;white-space:nowrap;cursor:help}.live-pill[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{width:7px;height:7px;border-radius:50%;flex-shrink:0}.live-pill.live[_ngcontent-%COMP%]{color:var(--success);background:var(--success-soft)}.live-pill.live[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--success);animation:_ngcontent-%COMP%_live-pulse 1.6s ease-in-out infinite}.live-pill.polling[_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.live-pill.polling[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--warn)}.live-pill.idle[_ngcontent-%COMP%]{color:var(--text-muted);background:var(--bg-canvas)}.live-pill.idle[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--text-muted)}@keyframes _ngcontent-%COMP%_live-pulse{0%,to{opacity:.4;transform:scale(.85)}50%{opacity:1;transform:scale(1.05)}}.sub[_ngcontent-%COMP%]{color:var(--text-secondary);font-size:12px;margin-top:2px}.page-actions[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px}.refresh-btn[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:6px}.refresh-btn.spinning[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{animation:_ngcontent-%COMP%_spin .9s linear infinite;color:var(--accent)}.refresh-btn[_ngcontent-%COMP%]:disabled{cursor:progress;opacity:.8}@keyframes _ngcontent-%COMP%_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.sd-body[_ngcontent-%COMP%]{flex:1;display:flex;min-height:0;gap:0}.sd-main[_ngcontent-%COMP%]{flex:1;min-width:0;padding:0 0 18px}.sd-rail[_ngcontent-%COMP%]{width:290px;flex-shrink:0;border-left:1px solid var(--border-subtle);background:var(--bg-panel);padding:16px}.stat-strip[_ngcontent-%COMP%]{display:flex;gap:24px;padding:12px 0;border-top:1px solid var(--border-subtle);border-bottom:1px solid var(--border-subtle);margin-bottom:16px}.stat[_ngcontent-%COMP%] .stat-lbl[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted)}.stat[_ngcontent-%COMP%] .stat-val[_ngcontent-%COMP%]{font-size:16px;font-weight:600;margin-top:2px;color:var(--text-primary);font-variant-numeric:tabular-nums}.stat-val.cost[_ngcontent-%COMP%]{color:var(--accent)}.tok-bar[_ngcontent-%COMP%]{display:flex;height:10px;border-radius:999px;overflow:hidden}.tok-seg[_ngcontent-%COMP%]{height:100%}.cache-card[_ngcontent-%COMP%]{background:var(--success-soft);border:1px solid rgba(70,194,107,.25);border-radius:8px;padding:10px 12px}.prompt.card[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:9px;overflow:hidden;transition:border-color .12s}.prompt.card[_ngcontent-%COMP%]:hover{border-color:var(--border-strong)}.prompt-head[_ngcontent-%COMP%]{width:100%;padding:11px 12px;background:transparent;border:0;text-align:left;cursor:pointer;color:inherit;font-family:inherit}.prompt-head[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.prompt-head[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.prompt-body[_ngcontent-%COMP%]{border-top:1px solid var(--border-subtle);padding:14px 16px;background:var(--bg-canvas)}.prompt-section[_ngcontent-%COMP%]{margin-bottom:14px}.prompt-section[_ngcontent-%COMP%]:last-child{margin-bottom:0}.section-lbl[_ngcontent-%COMP%]{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:6px}.prompt-pre[_ngcontent-%COMP%]{margin:0;padding:10px 12px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:7px;font-family:var(--font-mono);font-size:11.5px;line-height:1.55;color:var(--text-primary);white-space:pre-wrap;word-break:break-word;max-height:280px;overflow-y:auto}.kv-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:10px 18px}.kv[_ngcontent-%COMP%] .kv-lbl[_ngcontent-%COMP%]{font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;font-weight:600}.kv[_ngcontent-%COMP%] .kv-val[_ngcontent-%COMP%]{font-size:12.5px;font-weight:600;margin-top:2px;color:var(--text-primary);font-variant-numeric:tabular-nums}.kv-val.cost[_ngcontent-%COMP%]{color:var(--accent)}.tools[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:3px}.tool-block[_ngcontent-%COMP%]{display:flex;flex-direction:column}.tool-row[_ngcontent-%COMP%]{appearance:none;background:var(--bg-panel);border:1px solid var(--border-subtle);color:inherit;font-family:inherit;text-align:left;display:flex;align-items:center;gap:8px;width:100%;padding:5px 10px;border-radius:6px;font-size:11.5px;cursor:pointer}.tool-row[_ngcontent-%COMP%]:hover:not(:disabled){background:var(--bg-hover)}.tool-row[_ngcontent-%COMP%]:disabled{cursor:default}.tool-row[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.tool-row.expanded[_ngcontent-%COMP%]{border-bottom-left-radius:0;border-bottom-right-radius:0}.tool-chevron[_ngcontent-%COMP%]{width:10px;color:var(--text-muted);transition:transform .12s;font-size:12px;flex-shrink:0;display:inline-block}.tool-chevron.open[_ngcontent-%COMP%]{transform:rotate(90deg);color:var(--accent)}.tool-name[_ngcontent-%COMP%]{font-weight:600;flex-shrink:0}.tool-input[_ngcontent-%COMP%]{color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:11px}.tool-bytes[_ngcontent-%COMP%]{font-size:10.5px;flex-shrink:0}.tool-input-json[_ngcontent-%COMP%]{margin:0;padding:10px 14px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-top:0;border-radius:0 0 6px 6px;font-family:var(--font-mono);font-size:11px;line-height:1.5;color:var(--text-primary);white-space:pre-wrap;word-break:break-word;max-height:320px;overflow-y:auto}.assistant-body[_ngcontent-%COMP%]{padding:10px 14px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:7px;font-size:12.5px;line-height:1.55;color:var(--text-primary);max-height:480px;overflow-y:auto;overflow-wrap:anywhere}.thinking-details[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:7px;padding:8px 12px}.thinking-summary[_ngcontent-%COMP%]{cursor:pointer;list-style:none;display:flex;align-items:center;gap:6px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted)}.thinking-summary[_ngcontent-%COMP%]::-webkit-details-marker{display:none}.thinking-summary[_ngcontent-%COMP%]:before{content:"\\203a";display:inline-block;transition:transform .12s;font-size:14px;color:var(--text-muted)}.thinking-details[open][_ngcontent-%COMP%] .thinking-summary[_ngcontent-%COMP%]:before{transform:rotate(90deg);color:var(--accent)}.thinking-body[_ngcontent-%COMP%]{margin-top:10px;padding:8px 0 0;border-top:1px dashed var(--border-subtle);font-size:12px;line-height:1.55;color:var(--text-secondary);max-height:320px;overflow-y:auto;overflow-wrap:anywhere}.files-touched[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:3px;font-size:11.5px;max-height:180px;overflow-y:auto;padding:6px 10px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:6px}.files-touched[_ngcontent-%COMP%] .file-row[_ngcontent-%COMP%]{display:flex;align-items:center;min-width:0}.files-touched[_ngcontent-%COMP%] .file-path[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-secondary)}.loading[_ngcontent-%COMP%]{padding:40px 18px;text-align:center;color:var(--text-muted);font-size:13px}.error[_ngcontent-%COMP%]{padding:16px;margin:12px 0;border-radius:8px;border:1px solid rgba(255,90,90,.35);background:var(--error-soft);color:var(--error);display:flex;flex-direction:column;gap:8px;align-items:flex-start}.error-title[_ngcontent-%COMP%]{font-weight:600;font-size:13px}.error-detail[_ngcontent-%COMP%]{font-size:12px;color:var(--text-secondary)}.empty[_ngcontent-%COMP%]{padding:30px 18px;color:var(--text-muted);font-size:12.5px;text-align:center}.md-content[_ngcontent-%COMP%] .md-h1[_ngcontent-%COMP%]{font-size:14px;font-weight:650;color:var(--text-primary);letter-spacing:-.01em;margin:0 0 8px}.md-content[_ngcontent-%COMP%] .md-h2[_ngcontent-%COMP%]{font-size:12.5px;font-weight:650;color:var(--text-primary);margin:14px 0 4px;padding-bottom:3px;border-bottom:1px solid var(--border-subtle)}.md-content[_ngcontent-%COMP%] .md-h3[_ngcontent-%COMP%]{font-size:12px;font-weight:650;color:var(--text-primary);margin:12px 0 4px}.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%]{font-size:12.5px;line-height:1.6;color:var(--text-primary);margin:0 0 4px;overflow-wrap:anywhere}.md-content[_ngcontent-%COMP%] .md-p[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{color:var(--text-primary);font-weight:650}.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%], .md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%]{padding:0;margin:4px 0 6px}.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%], .md-content[_ngcontent-%COMP%] .md-ol[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{position:relative;padding:2px 0 2px 20px;font-size:12.5px;line-height:1.55;color:var(--text-primary);overflow-wrap:anywhere}.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%]{list-style:none}.md-content[_ngcontent-%COMP%] .md-list[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:before{content:"\\2022";position:absolute;left:4px;top:1px;color:var(--text-faint)}.md-content[_ngcontent-%COMP%] .md-spacer[_ngcontent-%COMP%]{height:5px}.md-content[_ngcontent-%COMP%] .md-inline-code[_ngcontent-%COMP%]{font-size:11px;background:var(--bg-canvas);padding:1px 5px;border-radius:3px;border:1px solid var(--border-subtle);color:var(--node-spec)}.md-content[_ngcontent-%COMP%] .md-blockquote[_ngcontent-%COMP%]{margin:5px 0;padding:6px 10px;border-left:3px solid var(--accent);background:var(--accent-soft);color:var(--text-secondary);font-size:12px;line-height:1.5;border-radius:0 5px 5px 0}.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%]{position:relative;margin:6px 0 8px;padding:10px 12px;background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:6px;overflow-x:auto}.md-content[_ngcontent-%COMP%] .md-pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%]{display:block;font-size:11px;line-height:1.5;color:var(--text-primary);white-space:pre}.md-content[_ngcontent-%COMP%] > [_ngcontent-%COMP%]:first-child{margin-top:0}'],changeDetection:0})};export{ue as SessionDetail};
@@ -0,0 +1 @@
1
+ import{a as V}from"./chunk-O7434ZMN.js";import{a as L}from"./chunk-UYC52MBC.js";import{a as H}from"./chunk-SUZYBYDW.js";import{c as $}from"./chunk-GR72OOCN.js";import{a as K}from"./chunk-7RNS77UP.js";import{a as B}from"./chunk-E44X4RH2.js";import{Aa as c,Ea as z,Fa as D,Ga as j,I as M,Ia as S,Ka as m,M as T,N as E,Ta as k,Va as a,W as C,Wa as v,Xa as u,Ya as w,bb as _,ea as o,fb as N,gb as b,hb as F,ib as I,ka as O,lb as s,ra as g,sa as x,ua as P,va as y,vb as U,wa as f,xa as p,ya as e,za as t,zb as R}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var A=()=>({value:"today",label:"Today"}),Q=()=>({value:"week",label:"This week"}),G=()=>({value:"month",label:"This month"}),J=()=>({value:"all",label:"All time"}),W=(n,i,r,l)=>[n,i,r,l],X=()=>[0,1,2,3],Y=()=>[0,1,2],Z=(n,i)=>i.tool,ee=(n,i)=>i.project;function te(n,i){n&1&&(e(0,"div",4),c(1,"app-icon",5),e(2,"div",6),a(3,"No SpecShip tool usage in this range"),t()()),n&2&&(o(),p("size",28))}function ne(n,i){n&1&&(e(0,"div",27),a(1),b(2,"number"),t()),n&2&&(o(),u("",I(2,1,i.cost,"1.1-1"),"k tokens"))}function ie(n,i){if(n&1&&(e(0,"div",27),a(1),t()),n&2){let r=m(2);o(),u("",r.fmtK(r.spendTokens())," total")}}function ae(n,i){n&1&&(e(0,"div",27),a(1),b(2,"number"),t()),n&2&&(o(),u("",I(2,1,i.cost,"1.1-1"),"k tokens"))}function oe(n,i){if(n&1&&(e(0,"div",27),a(1),t()),n&2){let r=m(2);o(),u("",r.fmtK(r.savedTokens())," est. total")}}function re(n,i){n&1&&c(0,"div",36)}function de(n,i){n&1&&y(0,re,1,0,"div",36,P),n&2&&f(_(0,X))}function le(n,i){if(n&1&&(e(0,"div",42)(1,"span",44),a(2),t(),e(3,"span",45),a(4),b(5,"number"),t(),e(6,"span",46),a(7),t(),e(8,"span",47),a(9),t()()),n&2){let r=i.$implicit,l=m(3);o(2),v(r.tool),o(2),v(F(5,4,r.calls)),o(3),v(l.fmtK(r.spendTokens)),o(2),v(l.fmtK(r.savedTokens))}}function se(n,i){n&1&&(e(0,"div",43),a(1,"No tool data"),t())}function pe(n,i){if(n&1&&(e(0,"div",37)(1,"span",38),a(2,"Tool"),t(),e(3,"span",39),a(4,"Calls"),t(),e(5,"span",40),a(6,"Spend (tokens)"),t(),e(7,"span",41),a(8,"Est. saved"),t()(),y(9,le,10,6,"div",42,Z),g(11,se,2,0,"div",43)),n&2){let r=m(2);o(9),f(r.byTool()),o(2),x(r.byTool().length===0?11:-1)}}function ce(n,i){n&1&&c(0,"div",36)}function me(n,i){n&1&&y(0,ce,1,0,"div",36,P),n&2&&f(_(0,Y))}function ve(n,i){if(n&1&&(e(0,"div",52)(1,"span",53),a(2),t(),e(3,"span",46),a(4),t(),e(5,"span",47),a(6),t(),e(7,"span",54),a(8),t()()),n&2){let r=i.$implicit,l=m(4);o(2),v(r.project),o(2),v(l.fmtK(r.spendTokens)),o(2),v(l.fmtK(r.savedTokens)),o(),k("color",r.netTokens>=0?"var(--success)":"var(--warn)"),o(),v(l.fmtK(r.netTokens))}}function ue(n,i){if(n&1&&(e(0,"div",49)(1,"span",50),a(2,"Project"),t(),e(3,"span",40),a(4,"Spend (tokens)"),t(),e(5,"span",41),a(6,"Est. saved"),t(),e(7,"span",51),a(8,"Net"),t()(),y(9,ve,9,6,"div",52,ee)),n&2){let r=m(3);o(9),f(r.byProject())}}function ge(n,i){if(n&1&&(e(0,"div",30)(1,"div",31),c(2,"app-icon",48),e(3,"span",26),a(4,"By project"),t()(),g(5,me,2,1)(6,ue,11,0),t()),n&2){let r=m(2);o(2),p("size",14),o(3),x(r.resource.state().loading?5:6)}}function xe(n,i){if(n&1){let r=j();e(0,"div",7)(1,"div",8)(2,"div",9),c(3,"app-icon",10),e(4,"span",11),a(5,"Spend"),t()(),e(6,"div",12),a(7),t(),e(8,"div",13),a(9),t()(),e(10,"div",14)(11,"div",9),c(12,"app-icon",15),e(13,"span",11),a(14,"Est. saved"),t(),e(15,"span",16),a(16,"est."),t()(),e(17,"div",17),a(18),t(),e(19,"div",13),a(20),t()(),e(21,"div",8)(22,"div",9),c(23,"app-icon",18),e(24,"span",11),a(25,"Net"),t()(),e(26,"div",19)(27,"div",12),a(28),t()(),e(29,"div",13),a(30),t()(),e(31,"div",8)(32,"div",9),c(33,"app-icon",20),e(34,"span",11),a(35,"Coverage"),t()(),e(36,"div",12),a(37),t(),e(38,"div",13),a(39),t()()(),e(40,"div",21)(41,"div",22)(42,"div",23)(43,"div")(44,"div",24),c(45,"app-icon",25),e(46,"span",26),a(47,"Spend trend"),t()(),g(48,ne,3,4,"div",27)(49,ie,2,1,"div",27),t()(),e(50,"app-line-chart",28),S("hover",function(d){T(r);let h=m();return E(h.onHoverSpend(d))}),t()(),e(51,"div",22)(52,"div",23)(53,"div")(54,"div",24),c(55,"app-icon",15),e(56,"span",26),a(57,"Est. saved trend"),t(),e(58,"span",16),a(59,"est."),t()(),g(60,ae,3,4,"div",27)(61,oe,2,1,"div",27),t()(),e(62,"app-line-chart",29),S("hover",function(d){T(r);let h=m();return E(h.onHoverSaved(d))}),t()()(),e(63,"div",30)(64,"div",31),c(65,"app-icon",32),e(66,"span",26),a(67,"By tool"),t()(),g(68,de,2,1)(69,pe,12,1),t(),g(70,ge,7,2,"div",30),e(71,"details",33)(72,"summary",34),a(73,"Methodology & estimates"),t(),e(74,"p",35),a(75,` Estimates. Tokens \u2248 chars \xF7 4. "Saved" = the size of files a SpecShip query made unnecessary to Read, deduplicated per prompt; unresolved/natural-language queries count as 0. Cost priced at the model input rate. Net = saved \u2212 spend \u2212 overhead (a fixed per-session tool-definition cost). Per-project rows show saved \u2212 spend only \u2014 the per-session overhead isn't attributed to individual projects, so they won't sum to the headline net. Savings currently estimated for the primary project only. `),t(),e(76,"p",35)(77,"strong"),a(78,'"Saved" is a conservative lower bound.'),t(),a(79," It credits only a single direct read of the files a query's symbols live in \u2014 not the multi-call grep + read exploration (re-reads, dead-ends, and extra turns) SpecShip actually replaces. So net can read negative even when SpecShip reduced your real cost; the measured end-to-end savings (fewer turns over a smaller context) come from a with-vs-without comparison, which this per-session estimate can't see. Treat spend as exact and saved as a floor. "),t()()}if(n&2){let r,l,d=m();o(3),p("size",12),o(4),v(d.fmtK(d.spendTokens())),o(2),u("tokens \xB7 ",d.fmt$(d.spendCostUsd())),o(3),p("size",12),o(6),v(d.fmtK(d.savedTokens())),o(2),u("tokens \xB7 ",d.fmt$(d.savedCostUsd())),o(3),p("size",12),o(4),k("color",d.netTokens()>=0?"var(--success)":"var(--warn)"),o(),w("",d.netTokens()>0?"+":"","",d.fmtK(d.netTokens())),o(2),u("tokens \xB7 ",d.fmt$(d.netCostUsd())," \xB7 saved \u2212 spend \u2212 overhead"),o(3),p("size",12),o(4),w("",d.resolvedCalls(),"/",d.totalCalls()),o(2),u("resolved calls \xB7 ",d.unresolvedCalls()," unresolved"),o(6),p("size",13),o(3),x((r=d.hoveredSpend())?48:49,r),o(2),p("series",d.spendSeries())("height",150),o(5),p("size",13),o(5),x((l=d.hoveredSaved())?60:61,l),o(2),p("series",d.savedSeries())("height",150),o(3),p("size",14),o(3),x(d.resource.state().loading?68:69),o(2),x(d.byProject().length>0?70:-1)}}var q=class n{api=M(B);projects=M(K);range=C("month");ranges=["today","week","month","all"];hoveredSpend=C(null);hoveredSaved=C(null);resource=$(this.api,()=>`/api/claude/specship-impact?range=${this.range()}${this.projects.projectQuery("&")}`);data=s(()=>this.resource.state().data);spendTokens=s(()=>this.data()?.spendTokens??0);spendCostUsd=s(()=>this.data()?.spendCostUsd??0);savedTokens=s(()=>this.data()?.savedTokens??0);savedCostUsd=s(()=>this.data()?.savedCostUsd??0);netTokens=s(()=>this.data()?.netTokens??0);netCostUsd=s(()=>this.data()?.netCostUsd??0);overheadTokens=s(()=>this.data()?.overheadTokens??0);unresolvedCalls=s(()=>this.data()?.unresolvedCalls??0);totalCalls=s(()=>this.data()?.totalSpecshipCalls??0);resolvedCalls=s(()=>this.totalCalls()-this.unresolvedCalls());byTool=s(()=>this.data()?.byTool??[]);byProject=s(()=>this.data()?.byProject??[]);spendSeries=s(()=>(this.data()?.trend??[]).map((i,r)=>({day:r+1,cost:i.spendTokens/1e3,prompts:0})));savedSeries=s(()=>(this.data()?.trend??[]).map((i,r)=>({day:r+1,cost:i.savedTokens/1e3,prompts:0})));isEmpty=s(()=>this.totalCalls()===0&&!this.resource.state().loading);setRange(i){this.range.set(i)}onHoverSpend(i){this.hoveredSpend.set(i)}onHoverSaved(i){this.hoveredSaved.set(i)}fmt$(i){return"$"+i.toLocaleString("en-US",{minimumFractionDigits:2,maximumFractionDigits:2})}fmtK(i){return i>=1e6?(i/1e6).toFixed(1)+"M":i>=1e3?(i/1e3).toFixed(i>=1e4?0:1)+"k":String(i)}static \u0275fac=function(r){return new(r||n)};static \u0275cmp=O({type:n,selectors:[["app-specship-impact"]],decls:6,vars:12,consts:[[1,"page","scroll-y"],["icon","graph","title","SpecShip Impact","sub","Token spend vs estimated savings from SpecShip tool calls"],["actions",""],["size","sm",3,"change","value","options"],[1,"empty-state","card","card-pad"],["name","graph",2,"color","var(--text-muted)",3,"size"],[1,"muted",2,"margin-top","8px","font-size","13px"],[1,"tiles"],[1,"card","card-pad","tile"],[1,"tile-label","row","gap-6"],["name","dollar",2,"color","var(--text-muted)",3,"size"],[1,"muted"],[1,"big-number","mono"],[1,"muted","tile-sub"],[1,"card","card-pad","tile","tile-saved"],["name","trendDown",2,"color","var(--success)",3,"size"],[1,"est-badge"],[1,"big-number","mono",2,"color","var(--success)"],["name","coins",2,"color","var(--text-muted)",3,"size"],[1,"row","gap-8",2,"align-items","baseline"],["name","check",2,"color","var(--text-muted)",3,"size"],[1,"top-grid"],[1,"card","card-pad","chart-card"],[1,"chart-header"],[1,"row","gap-6"],["name","dollar",2,"color","var(--accent)",3,"size"],[2,"font-weight","600","font-size","12.5px"],[1,"mono","muted",2,"font-size","11px","margin-top","2px"],["color","var(--accent)",3,"hover","series","height"],["color","var(--success)",3,"hover","series","height"],[1,"card","card-pad",2,"margin-bottom","14px"],[1,"row","gap-8",2,"margin-bottom","12px"],["name","layers",2,"color","var(--accent)",3,"size"],[1,"card","card-pad","methodology"],[1,"muted","methodology-summary"],[1,"muted","methodology-body"],[1,"skel","row-skel"],[1,"tool-row","tool-header"],[1,"muted","col-tool"],[1,"muted","col-calls"],[1,"muted","col-spend"],[1,"muted","col-saved"],[1,"tool-row"],[1,"muted",2,"font-size","12px","padding","6px 0"],[1,"mono","col-tool","tool-name"],[1,"mono","tabular","col-calls"],[1,"mono","tabular","col-spend"],[1,"mono","tabular","col-saved","est-val"],["name","folder",2,"color","var(--accent)",3,"size"],[1,"proj-row","proj-header"],[1,"muted","col-proj"],[1,"muted","col-net"],[1,"proj-row"],[1,"mono","col-proj"],[1,"mono","tabular","col-net"]],template:function(r,l){r&1&&(e(0,"div",0)(1,"app-page-head",1),z(2,2),e(3,"app-segmented",3),S("change",function(h){return l.setRange(h)}),t(),D(),t(),g(4,te,4,1,"div",4)(5,xe,80,27),t()),r&2&&(o(3),p("value",l.range())("options",N(7,W,_(3,A),_(4,Q),_(5,G),_(6,J))),o(),x(l.isEmpty()?4:5))},dependencies:[V,L,H,R,U],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)}.row[_ngcontent-%COMP%]{display:flex;align-items:center}.gap-6[_ngcontent-%COMP%]{gap:6px}.gap-8[_ngcontent-%COMP%]{gap:8px}.tabular[_ngcontent-%COMP%]{font-variant-numeric:tabular-nums}.card[_ngcontent-%COMP%]{background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:var(--r-lg)}.card-pad[_ngcontent-%COMP%]{padding:14px}.tiles[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:14px}.tile[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:4px}.tile-label[_ngcontent-%COMP%]{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.tile-sub[_ngcontent-%COMP%]{font-size:11.5px;margin-top:1px}.big-number[_ngcontent-%COMP%]{font-size:28px;font-weight:700;letter-spacing:-.02em;line-height:1.1}.est-badge[_ngcontent-%COMP%]{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--success);border:1px solid var(--success);border-radius:3px;padding:0 3px;line-height:14px;opacity:.75}.est-val[_ngcontent-%COMP%]{color:var(--success)}.top-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 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}.tool-header[_ngcontent-%COMP%]{border-top:none!important;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.tool-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 60px 110px 110px;gap:12px;align-items:center;padding:8px 0;border-top:1px solid var(--border-subtle);font-size:12.5px}.tool-name[_ngcontent-%COMP%]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.col-tool[_ngcontent-%COMP%]{min-width:0}.col-calls[_ngcontent-%COMP%], .col-spend[_ngcontent-%COMP%], .col-saved[_ngcontent-%COMP%]{text-align:right}.proj-header[_ngcontent-%COMP%]{border-top:none!important;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.05em}.proj-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 110px 110px 80px;gap:12px;align-items:center;padding:8px 0;border-top:1px solid var(--border-subtle);font-size:12.5px}.col-proj[_ngcontent-%COMP%]{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.col-net[_ngcontent-%COMP%]{text-align:right}.methodology[_ngcontent-%COMP%]{margin-top:0}.methodology-summary[_ngcontent-%COMP%]{cursor:pointer;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;-webkit-user-select:none;user-select:none;list-style:none}.methodology-summary[_ngcontent-%COMP%]::marker{display:none}.methodology-body[_ngcontent-%COMP%]{font-size:11.5px;line-height:1.6;margin-top:8px}.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}}.empty-state[_ngcontent-%COMP%]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center}"],changeDetection:0})};export{q as SpecshipImpact};
@@ -0,0 +1 @@
1
+ import{a as te}from"./chunk-O7434ZMN.js";import{a as ee}from"./chunk-MC4DFIHG.js";import{a as Q}from"./chunk-UYC52MBC.js";import{a as X}from"./chunk-SUZYBYDW.js";import{c as q}from"./chunk-GR72OOCN.js";import"./chunk-7RNS77UP.js";import{a as Z}from"./chunk-E44X4RH2.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-PDN6QYGJ.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};
@@ -141,5 +141,5 @@
141
141
  })();
142
142
  </script>
143
143
  <app-root></app-root>
144
- <link rel="modulepreload" href="chunk-SBWU7JFC.js"><link rel="modulepreload" href="chunk-GR72OOCN.js"><link rel="modulepreload" href="chunk-SHPTC4RL.js"><link rel="modulepreload" href="chunk-7RNS77UP.js"><link rel="modulepreload" href="chunk-E44X4RH2.js"><link rel="modulepreload" href="chunk-PDN6QYGJ.js"><link rel="modulepreload" href="chunk-Q7L6LLAK.js"><script src="main-R53HA54V.js" type="module"></script></body>
144
+ <link rel="modulepreload" href="chunk-SBWU7JFC.js"><link rel="modulepreload" href="chunk-GR72OOCN.js"><link rel="modulepreload" href="chunk-SHPTC4RL.js"><link rel="modulepreload" href="chunk-7RNS77UP.js"><link rel="modulepreload" href="chunk-E44X4RH2.js"><link rel="modulepreload" href="chunk-PDN6QYGJ.js"><link rel="modulepreload" href="chunk-Q7L6LLAK.js"><script src="main-X2KCYXZ4.js" type="module"></script></body>
145
145
  </html>