@selvakumaresra/specship 0.6.0 → 0.7.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.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * GET /api/events — cross-project alert stream (REQ-PWA-002).
3
+ *
4
+ * A single SSE stream that watches EVERY initialized project (not just the
5
+ * selected one) and emits an event when an alert-worthy transition happens:
6
+ * - `approval` — a workflow run entered `paused` (an approval gate)
7
+ * - `runDone` — a run entered `completed` or `failed`
8
+ * - `drift` — a spec→code link newly entered drifted/broken/orphaned
9
+ *
10
+ * Polls the same SQLite the executor/resolver write to (mirrors the workflow
11
+ * SSE's 500ms-poll approach), diffing against per-connection seen-state so only
12
+ * NEW transitions are emitted — never a backlog burst on connect, never a repeat
13
+ * for the same transition. The client (NotificationsService) turns these into
14
+ * desktop notifications, gated on permission + per-type toggles.
15
+ */
16
+ import os from 'node:os';
17
+ import path from 'node:path';
18
+ import { enumerate } from './projects.js';
19
+ const POLL_MS = 3000;
20
+ const KEEPALIVE_MS = 15000;
21
+ export async function registerEventsRoutes(app) {
22
+ const claudeRoot = path.join(os.homedir(), '.claude', 'projects');
23
+ app.get('/api/events', async (req, reply) => {
24
+ reply.raw.setHeader('Content-Type', 'text/event-stream');
25
+ reply.raw.setHeader('Cache-Control', 'no-cache');
26
+ reply.raw.setHeader('Connection', 'keep-alive');
27
+ reply.raw.write(': connected\n\n');
28
+ // Per-connection seen-state — emit only NEW transitions.
29
+ const lastStatus = new Map(); // `${slug}:${runId}` -> status
30
+ const seenDrift = new Set(); // `${slug}:${linkId}`
31
+ let primed = false; // first pass seeds state silently (no burst on connect)
32
+ let closed = false;
33
+ const send = (ev) => {
34
+ if (closed)
35
+ return;
36
+ try {
37
+ reply.raw.write(`data: ${JSON.stringify(ev)}\n\n`);
38
+ }
39
+ catch { /* peer gone */ }
40
+ };
41
+ const poll = async () => {
42
+ let projects;
43
+ try {
44
+ projects = await enumerate(claudeRoot);
45
+ }
46
+ catch {
47
+ return;
48
+ }
49
+ for (const p of projects) {
50
+ if (!p.initialized || closed)
51
+ continue;
52
+ let cg;
53
+ try {
54
+ cg = await app.projects.getBySlug(p.slug);
55
+ }
56
+ catch {
57
+ cg = null;
58
+ }
59
+ if (!cg)
60
+ continue;
61
+ const sq = cg.getSpecQueries();
62
+ // Workflow run transitions.
63
+ let runs = [];
64
+ try {
65
+ runs = sq.getAllWorkflowRuns(50);
66
+ }
67
+ catch { /* ignore */ }
68
+ for (const r of runs) {
69
+ const key = `${p.slug}:${r.id}`;
70
+ if (lastStatus.get(key) === r.status)
71
+ continue;
72
+ lastStatus.set(key, r.status);
73
+ if (!primed)
74
+ continue;
75
+ if (r.status === 'paused') {
76
+ send({ kind: 'approval', project: p.slug, projectPath: p.path, id: r.id, title: 'Run needs approval', detail: r.workflowName });
77
+ }
78
+ else if (r.status === 'completed' || r.status === 'failed') {
79
+ send({ kind: 'runDone', project: p.slug, projectPath: p.path, id: r.id, title: `Run ${r.status}`, detail: r.workflowName, status: r.status });
80
+ }
81
+ }
82
+ // Newly-drifted links.
83
+ let links = [];
84
+ try {
85
+ links = sq.getLinksByState(['drifted', 'broken', 'orphaned']);
86
+ }
87
+ catch { /* ignore */ }
88
+ for (const l of links) {
89
+ const key = `${p.slug}:${l.id}`;
90
+ if (seenDrift.has(key))
91
+ continue;
92
+ seenDrift.add(key);
93
+ if (!primed)
94
+ continue;
95
+ send({ kind: 'drift', project: p.slug, projectPath: p.path, id: String(l.id), title: 'Drift detected', detail: `${l.specId} → ${l.targetQualifiedName}` });
96
+ }
97
+ }
98
+ primed = true;
99
+ };
100
+ await poll(); // prime: seed seen-state without emitting
101
+ const pollTimer = setInterval(() => { void poll(); }, POLL_MS);
102
+ const kaTimer = setInterval(() => { if (!closed) {
103
+ try {
104
+ reply.raw.write(': ka\n\n');
105
+ }
106
+ catch { /* noop */ }
107
+ } }, KEEPALIVE_MS);
108
+ req.raw.on('close', () => {
109
+ closed = true;
110
+ clearInterval(pollTimer);
111
+ clearInterval(kaTimer);
112
+ });
113
+ });
114
+ }
@@ -18,7 +18,7 @@ import path from 'node:path';
18
18
  import { decodeProjectSlug } from '../ingest/ingestor.js';
19
19
  const projectsBus = new EventEmitter();
20
20
  projectsBus.setMaxListeners(0); // many SSE subscribers + internal
21
- async function enumerate(claudeRoot) {
21
+ export async function enumerate(claudeRoot) {
22
22
  let entries;
23
23
  try {
24
24
  entries = await fs.readdir(claudeRoot, { withFileTypes: true });
@@ -27,6 +27,7 @@ import { registerClaudeRoutes } from './routes/claude.js';
27
27
  import { registerStatusRoutes } from './routes/status.js';
28
28
  import { registerMemoryRoutes } from './routes/memory.js';
29
29
  import { registerProjectsRoutes } from './routes/projects.js';
30
+ import { registerEventsRoutes } from './routes/events.js';
30
31
  /**
31
32
  * Lazy-import the specship core. Two delivery shapes are supported:
32
33
  *
@@ -164,6 +165,7 @@ export async function createServer(options) {
164
165
  await registerClaudeRoutes(app);
165
166
  await registerMemoryRoutes(app);
166
167
  await registerProjectsRoutes(app);
168
+ await registerEventsRoutes(app);
167
169
  // Optional: serve the built Angular UI from `webDir` and fall back to
168
170
  // index.html for client-side routes. Must register AFTER the /api/*
169
171
  // routes so they take precedence over the SPA wildcard.
@@ -0,0 +1 @@
1
+ import{a as D,b as N}from"./chunk-WQWYTRFN.js";import{a as z}from"./chunk-UYC52MBC.js";import{a as T}from"./chunk-SUZYBYDW.js";import{a as M}from"./chunk-E44X4RH2.js";import{Aa as r,Ga as C,I as u,Ia as c,Ka as P,M as S,N as E,Ta as b,Ua as g,Va as n,W as v,Wa as f,ea as o,ka as w,qa as m,ra as x,sa as _,ua as k,va as h,wa as y,xa as s,ya as e,za as t,zb as O}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var B=(a,l)=>l.model;function j(a,l){a&1&&n(0," Enabled \u2014 popups fire while the dashboard is open or backgrounded ")}function A(a,l){a&1&&n(0," Blocked \u2014 re-enable notifications for this site in your browser settings ")}function V(a,l){a&1&&n(0," Not supported in this browser ")}function $(a,l){a&1&&n(0," Get a desktop popup when a run needs approval, finishes, or drift appears ")}function F(a,l){if(a&1){let p=C();e(0,"button",49),c("click",function(){S(p);let d=P();return E(d.enableNotifications())}),n(1,"Enable"),t()}}function L(a,l){a&1&&(e(0,"span",24),n(1,"\u25CF On"),t())}function H(a,l){a&1&&(e(0,"span",25),n(1,"\u2014"),t())}function W(a,l){if(a&1&&(e(0,"div",33)(1,"span",50),n(2),t(),r(3,"input",51)(4,"input",51),t()),a&2){let p=l.$implicit;o(2),f(p.model),o(),s("value",p.input),o(),s("value",p.output)}}function q(a,l){if(a&1&&(e(0,"option"),n(1),t()),a&2){let p=l.$implicit;o(),f(p)}}var I=class a{api=u(M);theme=u(D);notify=u(N);setTheme(l){this.theme.setPref(l)}enableNotifications(){this.notify.requestPermission()}ingest=v(!0);watch=v(!0);motion=v(!1);toggleIngest(){this.ingest.update(l=>!l)}toggleWatch(){this.watch.update(l=>!l)}toggleMotion(){this.motion.update(l=>!l)}replayIntro(){try{sessionStorage.removeItem("specship-booted")}catch{}window.location.reload()}themeOptions=[{value:"dark",label:"Dark"},{value:"light",label:"Light"},{value:"system",label:"System"}];editorOptions=["code (VS Code)","subl (Sublime)","cursor","nvim"];pricing=[{model:"claude-opus-4",input:"15.00",output:"75.00"},{model:"claude-sonnet-4",input:"3.00",output:"15.00"},{model:"claude-haiku-4",input:"0.80",output:"4.00"}];static \u0275fac=function(p){return new(p||a)};static \u0275cmp=w({type:a,selectors:[["app-settings"]],decls:175,vars:37,consts:[[1,"settings-page","scroll-y"],["icon","settings","title","Settings"],[1,"settings-body"],[1,"card","card-pad","section-card"],[1,"section-head","row","gap-8"],["name","folder",2,"color","var(--accent)",3,"size"],[1,"section-title"],[1,"field"],[1,"field-label"],["value","~/dev/specship","readonly","",1,"input","mono",2,"width","100%"],[1,"field-hint"],["value","specs/",1,"input","mono",2,"width","100%"],["value","node_modules/, dist/, .cg/wt/",1,"input","mono",2,"width","100%"],["name","sessions",2,"color","var(--accent)",3,"size"],["value","~/.claude/projects/",1,"input","mono",2,"width","100%"],[1,"toggle-row","row"],[1,"grow"],[1,"toggle-label"],[1,"muted",2,"font-size","11px"],["type","button","aria-label","Enable transcript ingest",1,"toggle-btn",3,"click"],[1,"toggle-thumb"],["type","button","aria-label","Real-time watch",1,"toggle-btn",3,"click"],["name","zap",2,"color","var(--accent)",3,"size"],["type","button",2,"height","30px","padding","0 12px","border-radius","6px","border","1px solid var(--accent)","background","var(--accent)","color","#fff","font-size","12px","cursor","pointer"],[2,"font-size","12px","color","var(--accent)"],[1,"muted",2,"font-size","12px"],["type","button","aria-label","Notify when a run needs approval",1,"toggle-btn",3,"click"],["type","button","aria-label","Notify when a run completes or fails",1,"toggle-btn",3,"click"],["type","button","aria-label","Notify when drift is detected",1,"toggle-btn",3,"click"],["name","dollar",2,"color","var(--accent)",3,"size"],[1,"muted",2,"font-size","11.5px","margin-bottom","12px"],[1,"pricing-header","row"],[1,"price-col"],[1,"pricing-row","row"],["name","sparkles",2,"color","var(--accent)",3,"size"],[3,"change","value","options"],["type","button","aria-label","Reduced motion",1,"toggle-btn",3,"click"],[1,"toggle-row","row",2,"border-top","1px solid var(--border-subtle)","margin-top","4px","padding-top","8px"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","refresh",3,"size"],["name","reveal",2,"color","var(--accent)",3,"size"],[1,"input",2,"width","200px"],["name","graph",2,"color","var(--accent)",3,"size"],[1,"about-grid"],[1,"about-row","row"],[1,"mono",2,"font-size","12px"],[1,"mono",2,"font-size","12px","color","var(--success)"],[1,"status-dot",2,"background","var(--success)"],[2,"height","30px"],["type","button",2,"height","30px","padding","0 12px","border-radius","6px","border","1px solid var(--accent)","background","var(--accent)","color","#fff","font-size","12px","cursor","pointer",3,"click"],[1,"mono","grow",2,"font-size","12px"],[1,"input","mono","tabular","price-input",3,"value"]],template:function(p,i){if(p&1&&(e(0,"div",0),r(1,"app-page-head",1),e(2,"div",2)(3,"section",3)(4,"div",4),r(5,"app-icon",5),e(6,"span",6),n(7,"Project"),t()(),e(8,"div",7)(9,"label",8),n(10,"Project root"),t(),r(11,"input",9),e(12,"div",10),n(13,"Set at app open \xB7 read-only"),t()(),e(14,"div",7)(15,"label",8),n(16,"Spec roots"),t(),r(17,"input",11),t(),e(18,"div",7)(19,"label",8),n(20,"Ignore patterns"),t(),r(21,"input",12),t()(),e(22,"section",3)(23,"div",4),r(24,"app-icon",13),e(25,"span",6),n(26,"Claude Code"),t()(),e(27,"div",7)(28,"label",8),n(29,"Transcripts path"),t(),r(30,"input",14),e(31,"div",10),n(32,"Auto-detected \xB7 overridable"),t()(),e(33,"div",15)(34,"div",16)(35,"div",17),n(36,"Enable transcript ingest"),t(),e(37,"div",18),n(38,"Read JSONL transcripts for analytics"),t()(),e(39,"button",19),c("click",function(){return i.toggleIngest()}),r(40,"span",20),t()(),e(41,"div",15)(42,"div",16)(43,"div",17),n(44,"Real-time watch"),t(),e(45,"div",18),n(46,"Tail new transcript lines as they're written"),t()(),e(47,"button",21),c("click",function(){return i.toggleWatch()}),r(48,"span",20),t()()(),e(49,"section",3)(50,"div",4),r(51,"app-icon",22),e(52,"span",6),n(53,"Notifications"),t()(),e(54,"div",15)(55,"div",16)(56,"div",17),n(57,"Desktop notifications"),t(),e(58,"div",18),x(59,j,1,0)(60,A,1,0)(61,V,1,0)(62,$,1,0),t()(),x(63,F,2,0,"button",23)(64,L,2,0,"span",24)(65,H,2,0,"span",25),t(),e(66,"div",15)(67,"div",16)(68,"div",17),n(69,"Run needs approval"),t(),e(70,"div",18),n(71,"A workflow run paused at an approval gate"),t()(),e(72,"button",26),c("click",function(){return i.notify.toggle("approval")}),r(73,"span",20),t()(),e(74,"div",15)(75,"div",16)(76,"div",17),n(77,"Run completed or failed"),t(),e(78,"div",18),n(79,"A workflow run finished or errored"),t()(),e(80,"button",27),c("click",function(){return i.notify.toggle("runDone")}),r(81,"span",20),t()(),e(82,"div",15)(83,"div",16)(84,"div",17),n(85,"Drift detected"),t(),e(86,"div",18),n(87,"Spec \u2192 code drift appeared"),t()(),e(88,"button",28),c("click",function(){return i.notify.toggle("drift")}),r(89,"span",20),t()()(),e(90,"section",3)(91,"div",4),r(92,"app-icon",29),e(93,"span",6),n(94,"Pricing table"),t()(),e(95,"div",30),n(96,"Per-million-token prices. Bump these when Anthropic publishes new tiers."),t(),e(97,"div",31)(98,"span",16),n(99,"Model"),t(),e(100,"span",32),n(101,"Input $/M"),t(),e(102,"span",32),n(103,"Output $/M"),t()(),h(104,W,5,3,"div",33,B),t(),e(106,"section",3)(107,"div",4),r(108,"app-icon",34),e(109,"span",6),n(110,"Appearance"),t()(),e(111,"div",7)(112,"label",8),n(113,"Theme"),t(),e(114,"app-segmented",35),c("change",function(R){return i.setTheme(R)}),t(),e(115,"div",10),n(116,"Now fully supported \u2014 dark, light, or follow your OS"),t()(),e(117,"div",15)(118,"div",16)(119,"div",17),n(120,"Reduced motion"),t(),e(121,"div",18),n(122,"Honor OS preference \u2014 instant transitions"),t()(),e(123,"button",36),c("click",function(){return i.toggleMotion()}),r(124,"span",20),t()(),e(125,"div",37)(126,"div",16)(127,"div",17),n(128,"Boot animation"),t(),e(129,"div",18),n(130,"The graph-assembly splash on app launch"),t()(),e(131,"button",38),c("click",function(){return i.replayIntro()}),r(132,"app-icon",39),n(133," Replay intro "),t()()(),e(134,"section",3)(135,"div",4),r(136,"app-icon",40),e(137,"span",6),n(138,"Editor"),t()(),e(139,"div",7)(140,"label",8),n(141,"Open files with"),t(),e(142,"select",41),h(143,q,2,1,"option",null,k),t(),e(145,"div",10),n(146,"Detected via code / subl / etc."),t()()(),e(147,"section",3)(148,"div",4),r(149,"app-icon",42),e(150,"span",6),n(151,"About"),t()(),e(152,"div",43)(153,"div",44)(154,"span",25),n(155,"Version"),t(),e(156,"span",45),n(157,"0.4.0"),t()(),e(158,"div",44)(159,"span",25),n(160,"MCP server"),t(),e(161,"span",46),r(162,"span",47),n(163,"running "),t()(),e(164,"div",44)(165,"span",25),n(166,"Backend"),t(),e(167,"span",45),n(168),t()(),e(169,"div",44)(170,"span",25),n(171,"Log path"),t(),e(172,"span",45),n(173,"~/.cg/logs/desktop.log"),t()()()(),r(174,"div",48),t()()),p&2){let d;o(5),s("size",15),o(19),s("size",15),o(15),g("on",i.ingest()),m("aria-pressed",i.ingest()),o(8),g("on",i.watch()),m("aria-pressed",i.watch()),o(4),s("size",15),o(8),_((d=i.notify.permission())==="granted"?59:d==="denied"?60:d==="unsupported"?61:62),o(4),_(i.notify.permission()==="default"?63:i.notify.permission()==="granted"?64:65),o(3),b("opacity",i.notify.granted()?"1":"0.5"),o(6),g("on",i.notify.enabled("approval")),m("aria-pressed",i.notify.enabled("approval")),o(2),b("opacity",i.notify.granted()?"1":"0.5"),o(6),g("on",i.notify.enabled("runDone")),m("aria-pressed",i.notify.enabled("runDone")),o(2),b("opacity",i.notify.granted()?"1":"0.5"),o(6),g("on",i.notify.enabled("drift")),m("aria-pressed",i.notify.enabled("drift")),o(4),s("size",15),o(12),y(i.pricing),o(4),s("size",15),o(6),s("value",i.theme.pref())("options",i.themeOptions),o(9),g("on",i.motion()),m("aria-pressed",i.motion()),o(9),s("size",13),o(4),s("size",15),o(7),y(i.editorOptions),o(6),s("size",15),o(19),f(i.api.isConfigured?i.api.apiBase:"not configured")}},dependencies:[O,z,T],styles:["[_nghost-%COMP%]{display:flex;flex:1;min-height:0}.settings-page[_ngcontent-%COMP%]{flex:1;padding:18px;overflow-y:auto}.settings-body[_ngcontent-%COMP%]{max-width:720px}.section-card[_ngcontent-%COMP%]{margin-bottom:14px}.section-head[_ngcontent-%COMP%]{align-items:center;margin-bottom:14px;padding-bottom:12px;border-bottom:1px solid var(--border-subtle)}.section-title[_ngcontent-%COMP%]{font-weight:600;font-size:13.5px}.field[_ngcontent-%COMP%]{margin-bottom:16px}.field-label[_ngcontent-%COMP%]{display:block;font-size:12.5px;font-weight:500;margin-bottom:6px;color:var(--text-primary)}.field-hint[_ngcontent-%COMP%]{color:var(--text-muted);font-size:11px;margin-top:5px}.toggle-row[_ngcontent-%COMP%]{align-items:center;padding:8px 0}.toggle-label[_ngcontent-%COMP%]{font-size:12.5px;font-weight:500}.toggle-btn[_ngcontent-%COMP%]{width:36px;height:20px;border-radius:999px;border:none;cursor:pointer;padding:2px;background:var(--bg-elevated);display:flex;justify-content:flex-start;align-items:center;transition:background .12s;flex-shrink:0}.toggle-btn.on[_ngcontent-%COMP%]{background:var(--accent);justify-content:flex-end}.toggle-btn[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.toggle-thumb[_ngcontent-%COMP%]{width:16px;height:16px;border-radius:50%;background:#fff;flex-shrink:0}.pricing-header[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;font-weight:600;margin-bottom:6px}.pricing-row[_ngcontent-%COMP%]{padding:6px 0;border-top:1px solid var(--border-subtle);align-items:center;gap:10px}.price-col[_ngcontent-%COMP%]{width:110px;text-align:right}.price-input[_ngcontent-%COMP%]{width:100px;text-align:right;padding:4px 8px}.about-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 1fr;gap:10px 20px}.about-row[_ngcontent-%COMP%]{justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border-subtle)}.status-dot[_ngcontent-%COMP%]{display:inline-block;width:6px;height:6px;border-radius:50%;margin-right:6px;vertical-align:middle}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1}"],changeDetection:0})};export{I as Settings};
@@ -0,0 +1 @@
1
+ import{E as n,I as p,R as l,W as s,X as y,lb as f}from"./chunk-PDN6QYGJ.js";import{a as o,b as d}from"./chunk-Q7L6LLAK.js";var m="specship.theme",h=class i{destroyRef=p(l);pref=s(this.loadPref());effective=s("dark");constructor(){this.applyEffective();let e=window.matchMedia("(prefers-color-scheme: light)"),t=()=>{this.pref()==="system"&&this.applyEffective()};e.addEventListener("change",t),this.destroyRef.onDestroy(()=>e.removeEventListener("change",t)),y(()=>{let r=this.pref();try{localStorage.setItem(m,r)}catch{}this.applyEffective()})}setPref(e){this.pref.set(e)}toggle(){this.setPref(this.effective()==="dark"?"light":"dark")}loadPref(){try{let e=localStorage.getItem(m);if(e==="dark"||e==="light"||e==="system")return e}catch{}return"dark"}applyEffective(){let e=window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark",t=this.pref(),r=t==="system"?e:t;document.documentElement.setAttribute("data-theme",r),this.effective.set(r)}static \u0275fac=function(t){return new(t||i)};static \u0275prov=n({token:i,factory:i.\u0275fac,providedIn:"root"})};var u="specship.notify.prefs",a={approval:!0,runDone:!0,drift:!0},g=class i{permission=s("default");prefs=s(this.loadPrefs());supported=f(()=>this.permission()!=="unsupported");granted=f(()=>this.permission()==="granted");constructor(){if(typeof window>"u"||typeof Notification>"u"){this.permission.set("unsupported");return}this.permission.set(Notification.permission)}enabled(e){return this.prefs()[e]!==!1}toggle(e){let t=d(o({},this.prefs()),{[e]:!this.enabled(e)});this.prefs.set(t),this.savePrefs(t)}async requestPermission(){if(typeof Notification>"u"){this.permission.set("unsupported");return}try{let e=await Notification.requestPermission();this.permission.set(e)}catch{}}notify(e,t,r={}){if(!(this.permission()!=="granted"||!this.enabled(e))&&!(typeof Notification>"u"))try{let c=new Notification(t,{body:r.body,tag:r.tag});r.onClick&&(c.onclick=()=>{try{window.focus()}catch{}r.onClick(),c.close()})}catch{}}loadPrefs(){if(typeof localStorage>"u")return o({},a);try{let e=localStorage.getItem(u);return e?o(o({},a),JSON.parse(e)):o({},a)}catch{return o({},a)}}savePrefs(e){if(!(typeof localStorage>"u"))try{localStorage.setItem(u,JSON.stringify(e))}catch{}}static \u0275fac=function(t){return new(t||i)};static \u0275prov=n({token:i,factory:i.\u0275fac,providedIn:"root"})};export{h as a,g as b};
Binary file
Binary file
@@ -9,6 +9,7 @@
9
9
  <link rel="icon" type="image/png" sizes="32x32" href="favicon-32.png">
10
10
  <link rel="icon" type="image/png" sizes="16x16" href="favicon-16.png">
11
11
  <link rel="apple-touch-icon" sizes="180x180" href="favicon-180.png">
12
+ <link rel="manifest" href="manifest.webmanifest">
12
13
  <meta name="theme-color" content="#0B0D11">
13
14
  <meta name="description" content="SpecShip Desktop — see your codebase, understand your sessions.">
14
15
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -141,5 +142,5 @@
141
142
  })();
142
143
  </script>
143
144
  <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-QAP4FTDP.js" type="module"></script></body>
145
+ <link rel="modulepreload" href="chunk-WQWYTRFN.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-LHCYPOXF.js" type="module"></script></body>
145
146
  </html>
@@ -0,0 +1 @@
1
+ import{a as G,b as he}from"./chunk-WQWYTRFN.js";import{a as fe,b as ve,c as B}from"./chunk-GR72OOCN.js";import{b as ce,c as z,d as me,e as ue,f as ge}from"./chunk-SHPTC4RL.js";import{a as I}from"./chunk-7RNS77UP.js";import{a as H}from"./chunk-E44X4RH2.js";import{$ as ae,Aa as c,Ba as d,Ca as p,Da as C,E as $,Ga as k,Ha as V,I as f,Ia as b,Ja as R,Ka as g,M as y,N as P,O as ee,R as ie,Ta as se,Ua as w,V as oe,Va as s,W as S,Wa as h,Xa as A,Ya as le,ca as re,cb as de,da as te,ea as o,ka as x,lb as D,ob as F,pb as E,qa as M,ra as m,sa as u,va as T,wa as j,wb as pe,xa as v,ya as a,za as r,zb as L}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var _e=[{path:"",pathMatch:"full",redirectTo:"dashboard"},{path:"dashboard",loadComponent:()=>import("./chunk-D5OCNEJA.js").then(e=>e.Dashboard),data:{nav:"dashboard",title:"Dashboard"}},{path:"graph",loadComponent:()=>import("./chunk-WLIMNDS3.js").then(e=>e.Graph),data:{nav:"graph",title:"Graph"}},{path:"specs",loadComponent:()=>import("./chunk-JQ534IB6.js").then(e=>e.Specs),data:{nav:"specs",title:"Specs"}},{path:"drift",loadComponent:()=>import("./chunk-FHZHD2ZG.js").then(e=>e.Drift),data:{nav:"drift",title:"Drift queue"}},{path:"workflows",loadComponent:()=>import("./chunk-NZEZCT65.js").then(e=>e.Workflows),data:{nav:"workflows",title:"Workflows"}},{path:"runs",loadComponent:()=>import("./chunk-UBOZGQNK.js").then(e=>e.Runs),data:{nav:"runs",title:"Runs"}},{path:"runs/:id",loadComponent:()=>import("./chunk-MVOMVPYB.js").then(e=>e.RunDetail),data:{nav:"runs",title:"Run detail"}},{path:"chat",loadComponent:()=>import("./chunk-L37MTFSG.js").then(e=>e.Chat),data:{nav:"chat",title:"Chat"}},{path:"sessions",loadComponent:()=>import("./chunk-ASZ77FMZ.js").then(e=>e.Sessions),data:{nav:"sessions",title:"Sessions"}},{path:"sessions/:id",loadComponent:()=>import("./chunk-RASJHUXS.js").then(e=>e.SessionDetail),data:{nav:"sessions",title:"Session detail"}},{path:"heatmap",loadComponent:()=>import("./chunk-YAMRN47K.js").then(e=>e.Heatmap),data:{nav:"heatmap",title:"Heatmap"}},{path:"costs",loadComponent:()=>import("./chunk-WCHGDXWC.js").then(e=>e.Costs),data:{nav:"costs",title:"Costs"}},{path:"specship-impact",loadComponent:()=>import("./chunk-TQ3P2QZO.js").then(e=>e.SpecshipImpact),data:{nav:"specship-impact",title:"SpecShip Impact"}},{path:"compare",loadComponent:()=>import("./chunk-WCKHQIYN.js").then(e=>e.Compare),data:{nav:"compare",title:"Compare projects"}},{path:"tips",loadComponent:()=>import("./chunk-A5R3MJMO.js").then(e=>e.Tips),data:{nav:"tips",title:"Tips"}},{path:"memory",loadComponent:()=>import("./chunk-45QHGCB4.js").then(e=>e.Memory),data:{nav:"memory",title:"Memory"}},{path:"design",loadComponent:()=>import("./chunk-WAI2JMZP.js").then(e=>e.Design),data:{nav:"design",title:"Design system"}},{path:"settings",loadComponent:()=>import("./chunk-HGBF7AY5.js").then(e=>e.Settings),data:{nav:"settings",title:"Settings"}},{path:"**",redirectTo:"dashboard"}];var be={providers:[oe(),ge(_e)]};function xe(e,n){e&1&&(ee(),C(0,"rect",14)(1,"rect",15))}var q=class e{size=E(24);tile=E(!0);glow=E(!0);static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-logo-mark"]],inputs:{size:[1,"size"],tile:[1,"tile"],glow:[1,"glow"]},decls:16,vars:5,consts:[["viewBox","0 0 32 32","fill","none",2,"flex-shrink","0"],["id","ssTile","x1","0","y1","0","x2","32","y2","32","gradientUnits","userSpaceOnUse"],["stop-color","#12161E"],["offset","1","stop-color","#0B0D11"],["id","ssLead","x1","18","y1","11","x2","26","y2","21","gradientUnits","userSpaceOnUse"],["stop-color","#6FA0F6"],["offset","1","stop-color","#5B93F2"],["transform","translate(4.6 4.6) scale(0.715)"],["d","M5 16 H11 M11 9 L23 16 L11 23","stroke","rgba(150,165,200,0.5)","stroke-width","1.9","stroke-linecap","round","stroke-linejoin","round","fill","none"],["x1","11","y1","9","x2","11","y2","23","stroke","rgba(150,165,200,0.4)","stroke-width","1.9","stroke-linecap","round"],["cx","23","cy","16","r","3.6","fill","url(#ssLead)"],["cx","11","cy","9","r","2.9","fill","#A586F5"],["cx","11","cy","23","r","2.9","fill","#46C26B"],["cx","5","cy","16","r","2.1","fill","#29D2BE"],["width","32","height","32","rx","7.2","fill","url(#ssTile)"],["x","0.5","y","0.5","width","31","height","31","rx","6.7","fill","none","stroke","rgba(255,255,255,0.08)"]],template:function(t,i){t&1&&(ee(),d(0,"svg",0)(1,"defs")(2,"linearGradient",1),C(3,"stop",2)(4,"stop",3),p(),d(5,"linearGradient",4),C(6,"stop",5)(7,"stop",6),p()(),m(8,xe,2,0),d(9,"g",7),C(10,"path",8)(11,"line",9)(12,"circle",10)(13,"circle",11)(14,"circle",12)(15,"circle",13),p()()),t&2&&(se("filter",i.glow()?"drop-shadow(0 0 9px rgba(91,147,242,0.35))":"none"),M("width",i.size())("height",i.size()),o(8),u(i.tile()?8:-1))},styles:["[_nghost-%COMP%]{display:inline-flex;align-items:center;justify-content:center;line-height:0}"],changeDetection:0})};var Ce=e=>["/",e],ye=(e,n)=>n.group,Pe=(e,n)=>n.id;function ke(e,n){e&1&&(a(0,"div",3),s(1,"SpecShip "),a(2,"span",14),s(3,"Desktop"),r()())}function we(e,n){if(e&1&&(a(0,"div",15),s(1),r()),e&2){let t=g().$implicit;o(),h(t.group)}}function Se(e,n){if(e&1&&(a(0,"span",18),s(1),r()),e&2){let t=g(2).$implicit,i=g(2);M("data-kind",t.badgeKind),o(),h(i.badgeOf(t))}}function Me(e,n){if(e&1&&(a(0,"span",11),s(1),r(),m(2,Se,2,2,"span",18)),e&2){let t=g().$implicit,i=g(2);o(),h(t.label),o(),u(i.badgeOf(t)>0?2:-1)}}function Ee(e,n){if(e&1&&(a(0,"a",16),c(1,"span",8),a(2,"span",9),c(3,"app-icon",17),r(),m(4,Me,3,2),r()),e&2){let t=n.$implicit,i=g(2);v("routerLink",de(5,Ce,t.id))("title",i.collapsed()?t.label:null),o(3),v("name",t.icon)("size",16),o(),u(i.collapsed()?-1:4)}}function Oe(e,n){if(e&1&&(a(0,"div",5),m(1,we,2,1,"div",15),T(2,Ee,5,7,"a",16,Pe),r()),e&2){let t=n.$implicit,i=g();o(),u(i.collapsed()?-1:1),o(),j(t.items)}}function Te(e,n){e&1&&(a(0,"span",11),s(1,"Design system"),r())}function je(e,n){e&1&&(a(0,"span",11),s(1,"Settings"),r())}var K=class e{collapsed=E(!1);api=f(H);projects=f(I);status=B(this.api,()=>`/api/status${this.projects.projectQuery()}`);tips=B(this.api,()=>"/api/claude/tips");nav=[{group:"Project",items:[{id:"dashboard",label:"Dashboard",icon:"dashboard"},{id:"graph",label:"Graph",icon:"graph"},{id:"specs",label:"Specs",icon:"book"},{id:"drift",label:"Drift queue",icon:"drift",badge:()=>this.status.state().data?.drift??0,badgeKind:"warn"},{id:"workflows",label:"Workflows",icon:"workflow"},{id:"runs",label:"Runs",icon:"play"},{id:"chat",label:"Chat",icon:"chat"}]},{group:"Claude Code",items:[{id:"sessions",label:"Sessions",icon:"sessions"},{id:"heatmap",label:"Heatmap",icon:"heatmap"},{id:"costs",label:"Costs",icon:"dollar"},{id:"specship-impact",label:"SpecShip Impact",icon:"graph"},{id:"compare",label:"Compare projects",icon:"compare"},{id:"memory",label:"Memory",icon:"memory"},{id:"tips",label:"Tips",icon:"tips",badge:()=>(this.tips.state().data?.tips??[]).filter(n=>n.severity!=="info").length,badgeKind:"error"}]}];badgeOf(n){return n.badge?n.badge():0}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-sidebar"]],inputs:{collapsed:[1,"collapsed"]},decls:18,vars:10,consts:[[1,"sidebar"],[1,"brand"],[3,"size"],[1,"brand-text"],[1,"scroll-y","nav-scroll"],[1,"nav-group"],[1,"pinned"],["routerLink","/design","routerLinkActive","active",1,"nav-item",3,"title"],[1,"indicator"],[1,"nav-icon"],["name","palette",3,"size"],[1,"nav-label"],["routerLink","/settings","routerLinkActive","active",1,"nav-item",3,"title"],["name","settings",3,"size"],[1,"muted"],[1,"eyebrow","group-label"],["routerLinkActive","active",1,"nav-item",3,"routerLink","title"],[3,"name","size"],[1,"badge"]],template:function(t,i){t&1&&(a(0,"nav",0)(1,"div",1),c(2,"app-logo-mark",2),m(3,ke,4,0,"div",3),r(),a(4,"div",4),T(5,Oe,4,1,"div",5,ye),r(),a(7,"div",6)(8,"a",7),c(9,"span",8),a(10,"span",9),c(11,"app-icon",10),r(),m(12,Te,2,0,"span",11),r(),a(13,"a",12),c(14,"span",8),a(15,"span",9),c(16,"app-icon",13),r(),m(17,je,2,0,"span",11),r()()()),t&2&&(w("collapsed",i.collapsed()),o(2),v("size",26),o(),u(i.collapsed()?-1:3),o(2),j(i.nav),o(3),v("title",i.collapsed()?"Design system":null),o(3),v("size",16),o(),u(i.collapsed()?-1:12),o(),v("title",i.collapsed()?"Settings":null),o(3),v("size",16),o(),u(i.collapsed()?-1:17))},dependencies:[me,ue,L,q],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.sidebar[_ngcontent-%COMP%]{width:var(--sidebar-w);flex-shrink:0;background:var(--bg-panel);border-right:1px solid var(--border-subtle);display:flex;flex-direction:column;transition:width .12s;overflow:hidden;font-family:var(--font-ui)}.sidebar.collapsed[_ngcontent-%COMP%]{width:var(--sidebar-w-collapsed)}.brand[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:15px 16px}.collapsed[_ngcontent-%COMP%] .brand[_ngcontent-%COMP%]{justify-content:center;padding:14px 0}.brand-text[_ngcontent-%COMP%]{font-weight:600;font-size:13.5px;letter-spacing:-.01em}.brand-text[_ngcontent-%COMP%] .muted[_ngcontent-%COMP%]{color:var(--text-muted);font-weight:400}.nav-scroll[_ngcontent-%COMP%]{flex:1;padding:4px 8px}.nav-group[_ngcontent-%COMP%]{margin-bottom:10px}.group-label[_ngcontent-%COMP%]{padding:8px 8px 4px}.nav-item[_ngcontent-%COMP%]{position:relative;display:flex;align-items:center;gap:10px;height:32px;padding:0 10px;border-radius:7px;text-decoration:none;margin-bottom:1px;color:var(--text-secondary);font-weight:450;font-size:13px;transition:background .1s}.collapsed[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]{padding:0;justify-content:center}.nav-item[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.nav-item.active[_ngcontent-%COMP%]{background:var(--bg-active);color:var(--text-primary);font-weight:600}.nav-item.active[_ngcontent-%COMP%] .indicator[_ngcontent-%COMP%]{position:absolute;left:0;top:7px;bottom:7px;width:2.5px;border-radius:2px;background:var(--accent)}.nav-icon[_ngcontent-%COMP%]{width:16px;height:16px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary)}.nav-item[_ngcontent-%COMP%]:hover .nav-icon[_ngcontent-%COMP%]{color:var(--text-primary)}.nav-item.active[_ngcontent-%COMP%] .nav-icon[_ngcontent-%COMP%]{color:var(--accent)}.nav-label[_ngcontent-%COMP%]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge[_ngcontent-%COMP%]{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:10px;font-weight:700;padding:1px 6px;min-width:18px;border-radius:999px;display:inline-flex;justify-content:center;color:var(--text-secondary);background:#ffffff0f}.badge[data-kind=error][_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.badge[data-kind=warn][_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.pinned[_ngcontent-%COMP%]{padding:8px;border-top:1px solid var(--border-subtle)}'],changeDetection:0})};function De(e,n){if(e&1&&(d(0,"span",8),s(1),p(),C(2,"span",1),d(3,"span",9)(4,"span",10),s(5,"backend"),p(),d(6,"span",4),s(7),p()(),C(8,"span",1),d(9,"span",9)(10,"span",10),s(11,"nodes"),p(),d(12,"span",4),s(13),p()(),d(14,"span",9)(15,"span",10),s(16,"edges"),p(),d(17,"span",4),s(18),p()(),C(19,"span",1),d(20,"span",9)(21,"span",10),s(22,"drift"),p(),d(23,"span",4),s(24),p()(),C(25,"span",11),d(26,"span",9)(27,"span",10),s(28,"indexed"),p(),d(29,"span",4),s(30),p()()),e&2){let t=g(),i=t.status.state().data;o(),h(i.projectPath),o(6),h(i.backend),o(6),h(i.nodeCount.toLocaleString()),o(5),h(i.edgeCount.toLocaleString()),o(5),w("warn",i.drift>0)("success",i.drift===0),o(),h(i.drift),o(6),h(t.indexedLabel())}}function ze(e,n){e&1&&(d(0,"span",12),s(1,"loading status\u2026"),p(),C(2,"span",11))}function Ie(e,n){if(e&1&&(d(0,"span",12),s(1),p(),C(2,"span",11)),e&2){let t=g();o(),h(t.conn.online()?"no backend":"offline \u2014 no cached data")}}function Le(e,n){e&1&&(d(0,"span",5),s(1),p()),e&2&&(o(),A("\xB7 ",n))}function Ne(e,n){e&1&&(d(0,"span",6),s(1,"\u26A0"),p()),e&2&&V("title",n)}var W=class e{api=f(H);projects=f(I);refreshSvc=f(ve);conn=f(fe);status=B(this.api,()=>`/api/status${this.projects.projectQuery()}`);offlineAge=D(()=>{let n=this.status.state().cachedAt??this.conn.lastOnlineAt();if(n===null)return null;let t=Date.now()-n;return t<1e4?"just now":t<6e4?`${Math.round(t/1e3)}s ago`:t<36e5?`${Math.round(t/6e4)}m ago`:t<864e5?`${Math.round(t/36e5)}h ago`:`${Math.round(t/864e5)}d ago`});indexedLabel=D(()=>{let n=this.status.state().data?.lastIndexed;if(!n)return"never";try{let t=new Date(n),i=Date.now()-t.getTime();return i<6e4?"just now":i<36e5?`${Math.round(i/6e4)}m ago`:i<864e5?`${Math.round(i/36e5)}h ago`:t.toLocaleDateString()}catch{return String(n)}});async refresh(){await this.refreshSvc.triggerGlobalRefresh()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-status-strip"]],decls:13,vars:14,consts:[[1,"status-strip"],[1,"divider"],[1,"seg","conn",3,"title"],[1,"conn-dot"],[1,"mono","val"],[1,"muted","conn-age"],[1,"refresh-error","mono",3,"title"],[1,"refresh",3,"click","disabled","title"],[1,"seg","mono"],[1,"seg"],[1,"lbl"],[1,"grow"],[1,"mono","muted"]],template:function(t,i){if(t&1&&(d(0,"div",0),m(1,De,31,10)(2,ze,3,0)(3,Ie,3,1),C(4,"span",1),d(5,"span",2),C(6,"span",3),d(7,"span",4),s(8),p(),m(9,Le,2,1,"span",5),p(),m(10,Ne,2,1,"span",6),d(11,"button",7),R("click",function(){return i.refresh()}),s(12,"\u21BB"),p()()),t&2){let l,_;o(),u(i.status.state().data?1:i.status.state().loading?2:3),o(4),V("title",i.conn.online()?"Connected to the SpecShip server":"Server unreachable \u2014 showing the last cached data"),o(),w("live",i.conn.online())("off",!i.conn.online()),o(2),h(i.conn.online()?"Live":"Offline"),o(),u((l=!i.conn.online()&&i.offlineAge())?9:-1,l),o(),u((_=i.refreshSvc.error())?10:-1,_),o(),w("spinning",i.refreshSvc.loading()),V("disabled",i.refreshSvc.loading()||!i.conn.online())("title",i.conn.online()?i.refreshSvc.loading()?"Refreshing\u2026":"Refresh: sync index + re-ingest Claude Code transcripts. Updates Sessions, Heatmap, Costs, Memory, Drift, Graph.":"Offline \u2014 reconnect to refresh"),M("aria-label",i.conn.online()?i.refreshSvc.loading()?"Refreshing\u2026":"Refresh everything \u2014 sync index + ingest Claude Code transcripts":"Offline \u2014 reconnect to refresh")}},styles:["[_nghost-%COMP%]{display:contents}.status-strip[_ngcontent-%COMP%]{height:var(--status-h);flex-shrink:0;display:flex;align-items:center;gap:14px;padding:0 14px;border-bottom:1px solid var(--border-subtle);background:var(--bg-panel-2);font-size:11.5px;overflow:hidden;white-space:nowrap}.seg[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px;flex-shrink:0}.lbl[_ngcontent-%COMP%]{color:var(--text-muted);font-size:11px}.val[_ngcontent-%COMP%]{color:var(--text-secondary);font-variant-numeric:tabular-nums}.val.warn[_ngcontent-%COMP%]{color:var(--warn)}.val.success[_ngcontent-%COMP%]{color:var(--success)}.divider[_ngcontent-%COMP%]{width:1px;height:12px;background:var(--border-subtle);flex-shrink:0}.grow[_ngcontent-%COMP%]{flex:1}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.refresh[_ngcontent-%COMP%]{background:transparent;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:4px;transition:background .12s,color .12s}.refresh[_ngcontent-%COMP%]:hover:not(:disabled){background:var(--bg-hover);color:var(--text-primary)}.refresh[_ngcontent-%COMP%]:disabled{cursor:progress;opacity:.8}.refresh[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.refresh.spinning[_ngcontent-%COMP%]{display:inline-block;animation:_ngcontent-%COMP%_spin .9s linear infinite;color:var(--accent)}@keyframes _ngcontent-%COMP%_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.refresh-error[_ngcontent-%COMP%]{color:var(--warn);font-size:12px;padding:0 2px;cursor:help}.conn[_ngcontent-%COMP%]{gap:5px}.conn-dot[_ngcontent-%COMP%]{width:6px;height:6px;border-radius:50%;flex-shrink:0}.conn-dot.live[_ngcontent-%COMP%]{background:var(--success);box-shadow:0 0 6px var(--success)}.conn-dot.off[_ngcontent-%COMP%]{background:var(--warn);box-shadow:0 0 6px var(--warn)}.conn-age[_ngcontent-%COMP%]{font-size:11px}"],changeDetection:0})};var Re=(e,n)=>n.slug;function Ae(e,n){e&1&&c(0,"span",8)}function Fe(e,n){e&1&&c(0,"span",9)}function Be(e,n){e&1&&c(0,"span",10)}function $e(e,n){if(e&1&&(a(0,"span",7),s(1),r(),m(2,Ae,1,0,"span",8)(3,Fe,1,0,"span",9)(4,Be,1,0,"span",10)),e&2){let t=n,i=g();o(),h(i.shortPath(t.path)),o(),u(t.exists?t.initialized?4:3:2)}}function Ve(e,n){e&1&&(a(0,"span",3),s(1,"Select project"),r())}function He(e,n){if(e&1){let t=k();a(0,"button",20),b("click",function(){y(t);let l=g(2);return P(l.clear())}),c(1,"app-icon",21),s(2," Clear "),r()}e&2&&(o(),v("size",11))}function Ge(e,n){e&1&&(a(0,"span",16),s(1,"\xB7 loading\u2026"),r())}function qe(e,n){if(e&1&&(a(0,"span",16),s(1),r()),e&2){let t=g(2);o(),A("\xB7 ",t.filtered().length," shown \xB7 live")}}function Ke(e,n){e&1&&(a(0,"div",18),s(1,"No projects match."),r())}function We(e,n){e&1&&(a(0,"span",25),s(1,"missing"),r())}function Ue(e,n){e&1&&(a(0,"span",26),s(1,"not init"),r())}function Qe(e,n){e&1&&(a(0,"span",27),s(1,"initialized"),r())}function Je(e,n){if(e&1&&(a(0,"span"),s(1),r()),e&2){let t=g().$implicit,i=g(2);o(),A("\xB7 ",i.formatRelative(t.lastModifiedMs))}}function Ye(e,n){e&1&&c(0,"app-icon",29),e&2&&v("size",13)}function Xe(e,n){if(e&1){let t=k();a(0,"button",22),b("click",function(){let l=y(t).$implicit,_=g(2);return P(_.pick(l.slug))}),a(1,"div",23)(2,"div",24)(3,"span",7),s(4),r(),m(5,We,2,0,"span",25)(6,Ue,2,0,"span",26)(7,Qe,2,0,"span",27),r(),a(8,"div",28)(9,"span"),s(10),r(),m(11,Je,2,1,"span"),r()(),m(12,Ye,1,1,"app-icon",29),r()}if(e&2){let t=n.$implicit,i=g(2);w("active",i.projects.activeSlug()===t.slug),o(4),h(i.shortPath(t.path)),o(),u(t.exists?t.initialized?7:6:5),o(5),le("",t.sessionCount," session",t.sessionCount===1?"":"s"),o(),u(t.lastModifiedMs>0?11:-1),o(),u(i.projects.activeSlug()===t.slug?12:-1)}}function Ze(e,n){if(e&1){let t=k();a(0,"div",6)(1,"div",11),c(2,"app-icon",12),a(3,"input",13),b("input",function(l){y(t);let _=g();return P(_.onQuery(l.target.value))}),r(),m(4,He,3,1,"button",14),r(),a(5,"div",15)(6,"span"),s(7),r(),m(8,Ge,2,0,"span",16)(9,qe,2,1,"span",16),r(),a(10,"div",17),m(11,Ke,2,0,"div",18),T(12,Xe,13,8,"button",19,Re),r()()}if(e&2){let t=g();o(2),v("size",13),o(),v("value",t.query()),o(),u(t.projects.activeSlug()?4:-1),o(3),h(t.projects.claudeRoot()??"~/.claude/projects"),o(),u(t.projects.loading()?8:9),o(3),u(t.filtered().length===0&&!t.projects.loading()?11:-1),o(),j(t.filtered())}}var U=class e{host=f(ae);projects=f(I);open=S(!1);query=S("");filtered=D(()=>{let n=this.query().trim().toLowerCase(),t=this.projects.projects();return n?t.filter(i=>i.path.toLowerCase().includes(n)||i.slug.toLowerCase().includes(n)):t});badge=D(()=>this.projects.projects().length);toggle(){this.open.update(n=>!n)}pick(n){this.projects.setActive(n),this.open.set(!1),this.query.set("")}clear(){this.projects.setActive(null),this.open.set(!1)}onQuery(n){this.query.set(n)}onDocumentClick(n){if(!this.open())return;let t=n.target;t&&!this.host.nativeElement.contains(t)&&this.open.set(!1)}onEscape(){this.open()&&this.open.set(!1)}formatRelative(n){if(!n)return"";let t=Date.now()-n,i=Math.round(t/1e3);if(i<60)return`${i}s ago`;let l=Math.round(i/60);if(l<60)return`${l}m ago`;let _=Math.round(l/60);if(_<24)return`${_}h ago`;let ne=Math.round(_/24);return ne<30?`${ne}d ago`:new Date(n).toLocaleDateString()}shortPath(n){if(!n)return"";let t=this.detectHome();return t&&n===t?"~":t&&n.startsWith(t+"/")?"~/"+n.slice(t.length+1):n}detectHome(){let n=this.projects.claudeRoot();if(!n)return null;let t="/.claude/projects";return n.endsWith(t)?n.slice(0,-t.length):null}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-project-picker"]],hostBindings:function(t,i){t&1&&b("click",function(_){return i.onDocumentClick(_)},te)("keydown.escape",function(){return i.onEscape()},te)},decls:9,vars:9,consts:[[1,"wrap"],["type","button","aria-label","Switch project",1,"trigger",3,"click"],["name","folder",3,"size"],[1,"placeholder"],[1,"badge","mono"],[3,"name","size"],["role","dialog","aria-label","Pick a project",1,"popover"],[1,"path","mono"],["title","path no longer exists on disk",1,"status-dot","missing"],["title","not yet specship init'd",1,"status-dot","warn"],["title","initialized",1,"status-dot","ok"],[1,"popover-head","row","gap-8"],["name","search",3,"size"],["type","text","placeholder","Filter projects\u2026","autofocus","",1,"popover-input",3,"input","value"],["title","Clear selection",1,"btn","btn-ghost","btn-xs"],[1,"popover-meta","mono"],[1,"muted"],[1,"popover-list","scroll-y"],[1,"empty"],["type","button",1,"row-item",3,"active"],["title","Clear selection",1,"btn","btn-ghost","btn-xs",3,"click"],["name","x",3,"size"],["type","button",1,"row-item",3,"click"],[1,"col","gap-2","grow"],[1,"row","gap-6","path-line"],[1,"pill","missing"],[1,"pill","warn"],[1,"pill","ok"],[1,"meta","row","gap-8","mono","muted"],["name","check",3,"size"]],template:function(t,i){if(t&1&&(a(0,"div",0)(1,"button",1),b("click",function(){return i.toggle()}),c(2,"app-icon",2),m(3,$e,5,2)(4,Ve,2,0,"span",3),a(5,"span",4),s(6),r(),c(7,"app-icon",5),r(),m(8,Ze,14,6,"div",6),r()),t&2){let l;w("open",i.open()),o(),M("aria-expanded",i.open()),o(),v("size",13),o(),u((l=i.projects.active())?3:4,l),o(3),h(i.badge()),o(),v("name",i.open()?"chevronDown":"chevronRight")("size",11),o(),u(i.open()?8:-1)}},dependencies:[L],styles:["[_nghost-%COMP%]{display:inline-flex;min-width:0}.wrap[_ngcontent-%COMP%]{position:relative;min-width:0}.trigger[_ngcontent-%COMP%]{height:30px;padding:0 11px 0 10px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;color:var(--text-secondary);display:inline-flex;align-items:center;gap:8px;font-family:var(--font-ui);font-size:12.5px;cursor:pointer;max-width:360px;min-width:0;transition:background .1s,border-color .1s}.trigger[_ngcontent-%COMP%]:hover, .wrap.open[_ngcontent-%COMP%] .trigger[_ngcontent-%COMP%]{background:var(--bg-panel-2);border-color:var(--border-strong);color:var(--text-primary)}.trigger[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted);flex-shrink:0}.trigger[_ngcontent-%COMP%]:hover app-icon[_ngcontent-%COMP%]{color:var(--text-secondary)}.path[_ngcontent-%COMP%]{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.placeholder[_ngcontent-%COMP%]{color:var(--text-muted);font-style:italic}.status-dot[_ngcontent-%COMP%]{width:6px;height:6px;border-radius:50%;flex-shrink:0}.status-dot.ok[_ngcontent-%COMP%]{background:var(--success)}.status-dot.warn[_ngcontent-%COMP%]{background:var(--warn)}.status-dot.missing[_ngcontent-%COMP%]{background:var(--error)}.badge[_ngcontent-%COMP%]{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--bg-active);color:var(--text-muted);font-variant-numeric:tabular-nums;flex-shrink:0}.popover[_ngcontent-%COMP%]{position:absolute;top:calc(100% + 6px);left:0;z-index:30;width:420px;max-width:calc(100vw - 40px);background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:10px;box-shadow:var(--shadow-pop);display:flex;flex-direction:column;max-height:480px;animation:_ngcontent-%COMP%_pp-in .12s ease-out}@keyframes _ngcontent-%COMP%_pp-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.popover-head[_ngcontent-%COMP%]{padding:10px 12px 8px;border-bottom:1px solid var(--border-subtle)}.popover-head[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted);flex-shrink:0}.popover-input[_ngcontent-%COMP%]{flex:1;min-width:0;background:transparent;border:none;outline:none;font-family:var(--font-ui);font-size:13px;color:var(--text-primary)}.popover-input[_ngcontent-%COMP%]::placeholder{color:var(--text-muted)}.popover-meta[_ngcontent-%COMP%]{padding:6px 12px 8px;font-size:10.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.popover-meta[_ngcontent-%COMP%] .muted[_ngcontent-%COMP%]{color:var(--text-muted)}.popover-list[_ngcontent-%COMP%]{flex:1;min-height:0;padding:4px}.row-item[_ngcontent-%COMP%]{width:100%;display:flex;align-items:center;gap:10px;padding:9px 10px;border:none;border-radius:7px;background:transparent;color:inherit;cursor:pointer;font-family:inherit;text-align:left;transition:background 80ms}.row-item[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.row-item.active[_ngcontent-%COMP%]{background:var(--accent-soft);color:var(--text-primary)}.row-item.active[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--accent)}.path-line[_ngcontent-%COMP%]{min-width:0}.path-line[_ngcontent-%COMP%] .path[_ngcontent-%COMP%]{font-size:12.5px;font-weight:500;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta[_ngcontent-%COMP%]{font-size:10.5px}.pill[_ngcontent-%COMP%]{font-size:10px;font-weight:600;padding:1px 7px;border-radius:999px;white-space:nowrap;flex-shrink:0}.pill.ok[_ngcontent-%COMP%]{color:var(--success);background:var(--success-soft)}.pill.warn[_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.pill.missing[_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.empty[_ngcontent-%COMP%]{text-align:center;color:var(--text-muted);font-size:12px;padding:18px 12px}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1;min-width:0}.row[_ngcontent-%COMP%]{display:flex;align-items:center}.col[_ngcontent-%COMP%]{display:flex;flex-direction:column}.gap-2[_ngcontent-%COMP%]{gap:2px}.gap-6[_ngcontent-%COMP%]{gap:6px}.gap-8[_ngcontent-%COMP%]{gap:8px}.scroll-y[_ngcontent-%COMP%]{overflow-y:auto;overflow-x:hidden}"],changeDetection:0})};var Q=class e{deferred=null;canInstall=S(!1);constructor(){typeof window>"u"||window.matchMedia?.("(display-mode: standalone)").matches===!0||window.navigator.standalone===!0||(window.addEventListener("beforeinstallprompt",t=>{t.preventDefault(),this.deferred=t,this.canInstall.set(!0)}),window.addEventListener("appinstalled",()=>{this.deferred=null,this.canInstall.set(!1)}))}async install(){let n=this.deferred;if(n){this.deferred=null,this.canInstall.set(!1);try{await n.prompt(),await n.userChoice}catch{}}}static \u0275fac=function(t){return new(t||e)};static \u0275prov=$({token:e,factory:e.\u0275fac,providedIn:"root"})};function et(e,n){if(e&1){let t=k();a(0,"button",9),b("click",function(){y(t);let l=g();return P(l.onInstall())}),c(1,"app-icon",10),a(2,"span"),s(3,"Install"),r()()}e&2&&(o(),v("size",14))}var J=class e{toggleSidebar=F();openPalette=F();theme=f(G);effective=this.theme.effective;pwa=f(Q);onToggleTheme(){this.theme.toggle()}onMenuClick(){this.toggleSidebar.emit()}onPalette(){this.openPalette.emit()}onInstall(){this.pwa.install()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-top-bar"]],outputs:{toggleSidebar:"toggleSidebar",openPalette:"openPalette"},decls:14,vars:6,consts:[[1,"topbar"],["title","Toggle sidebar","aria-label","Toggle sidebar",1,"ghost","icon-btn",3,"click"],["name","menu",3,"size"],["aria-label","Open command palette",1,"palette-trigger",3,"click"],["name","search",3,"size"],[1,"grow"],["title","Install SpecShip as an app","aria-label","Install SpecShip as an app",1,"ghost",2,"display","inline-flex","align-items","center","gap","6px","padding","0 10px","height","30px","font-size","12px"],["aria-label","Toggle theme",1,"ghost","icon-btn",3,"click","title"],[3,"name","size"],["title","Install SpecShip as an app","aria-label","Install SpecShip as an app",1,"ghost",2,"display","inline-flex","align-items","center","gap","6px","padding","0 10px","height","30px","font-size","12px",3,"click"],["name","plus",3,"size"]],template:function(t,i){t&1&&(a(0,"div",0)(1,"button",1),b("click",function(){return i.onMenuClick()}),c(2,"app-icon",2),r(),c(3,"app-project-picker"),a(4,"button",3),b("click",function(){return i.onPalette()}),c(5,"app-icon",4),a(6,"span"),s(7,"Search or jump to\u2026"),r(),a(8,"kbd"),s(9,"\u2318K"),r()(),c(10,"span",5),m(11,et,4,1,"button",6),a(12,"button",7),b("click",function(){return i.onToggleTheme()}),c(13,"app-icon",8),r()()),t&2&&(o(2),v("size",16),o(3),v("size",13),o(6),u(i.pwa.canInstall()?11:-1),o(),v("title","Theme: "+i.effective()),o(),v("name",i.effective()==="dark"?"sun":"moon")("size",16))},dependencies:[L,U],styles:["[_nghost-%COMP%]{display:contents}.topbar[_ngcontent-%COMP%]{height:44px;flex-shrink:0;display:flex;align-items:center;gap:10px;padding:0 14px;border-bottom:1px solid var(--border-subtle);background:var(--bg-canvas)}.ghost[_ngcontent-%COMP%]{background:transparent;border:1px solid transparent;color:var(--text-secondary);cursor:pointer}.ghost[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.icon-btn[_ngcontent-%COMP%]{padding:6px 9px;border-radius:5px;font-size:16px}.palette-trigger[_ngcontent-%COMP%]{flex:1;max-width:380px;height:30px;padding:0 11px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;cursor:text;color:var(--text-muted);font-size:12.5px;text-align:left;display:flex;align-items:center;gap:8px}.palette-trigger[_ngcontent-%COMP%]:hover{border-color:var(--border-strong);color:var(--text-secondary)}.palette-trigger[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted)}.palette-trigger[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{flex:1}kbd[_ngcontent-%COMP%]{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:4px;padding:2px 6px}.grow[_ngcontent-%COMP%]{flex:1}"],changeDetection:0})};var tt=(e,n)=>n.id;function nt(e,n){if(e&1){let t=k();d(0,"button",6),R("click",function(){let l=y(t).$implicit,_=g(2);return P(_.go(l.id))}),d(1,"span",7),s(2),p(),d(3,"span",8),s(4),p()()}if(e&2){let t=n.$implicit;o(2),h(t.label),o(2),h(t.sub)}}function it(e,n){if(e&1){let t=k();d(0,"div",1),R("click",function(l){y(t);let _=g();return P(_.onBackdrop(l))}),d(1,"div",2)(2,"div",3)(3,"span"),s(4,"Jump to\u2026"),p(),d(5,"kbd"),s(6,"esc"),p()(),d(7,"div",4),T(8,nt,5,2,"button",5,tt),p()()()}if(e&2){let t=g();o(8),j(t.items)}}var ot=[{id:"dashboard",label:"Dashboard",sub:"/dashboard"},{id:"graph",label:"Graph",sub:"/graph"},{id:"specs",label:"Specs",sub:"/specs"},{id:"drift",label:"Drift queue",sub:"/drift"},{id:"workflows",label:"Workflows",sub:"/workflows"},{id:"runs",label:"Runs",sub:"/runs"},{id:"chat",label:"Chat",sub:"/chat"},{id:"sessions",label:"Sessions",sub:"/sessions"},{id:"heatmap",label:"Heatmap",sub:"/heatmap"},{id:"costs",label:"Costs",sub:"/costs"},{id:"compare",label:"Compare projects",sub:"/compare"},{id:"tips",label:"Tips",sub:"/tips"},{id:"settings",label:"Settings",sub:"/settings"}],Y=class e{open=E(!1);close=F();router=f(z);items=ot;go(n){this.router.navigate(["/"+n]),this.close.emit()}onBackdrop(n){n.target===n.currentTarget&&this.close.emit()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-command-palette"]],inputs:{open:[1,"open"]},outputs:{close:"close"},decls:1,vars:1,consts:[[1,"backdrop"],[1,"backdrop",3,"click"],[1,"palette"],[1,"palette-header"],[1,"palette-list","scroll-y"],[1,"item"],[1,"item",3,"click"],[1,"label"],[1,"sub","mono"]],template:function(t,i){t&1&&m(0,it,10,0,"div",0),t&2&&u(i.open()?0:-1)},styles:["[_nghost-%COMP%]{display:contents}.backdrop[_ngcontent-%COMP%]{position:fixed;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:grid;place-items:start center;padding-top:12vh;z-index:100;animation:fadeIn .1s}.palette[_ngcontent-%COMP%]{width:560px;max-width:90vw;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:12px;box-shadow:var(--shadow-pop);overflow:hidden}.palette-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1px solid var(--border-subtle);font-size:13px}.palette-list[_ngcontent-%COMP%]{max-height:380px;padding:6px}.item[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;width:100%;padding:8px 10px;background:transparent;border:none;border-radius:7px;cursor:pointer;color:var(--text-primary);font-family:inherit;font-size:13px;text-align:left}.item[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.label[_ngcontent-%COMP%]{flex:1}.sub[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted)}kbd[_ngcontent-%COMP%]{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:4px;padding:2px 6px}"],changeDetection:0})};var X=class e{es=null;notify=f(he);router=f(z);start(){if(!(this.es||typeof EventSource>"u")){try{this.es=new EventSource("/api/events")}catch{return}this.es.onmessage=n=>{let t;try{t=JSON.parse(n.data)}catch{return}!t||t.kind!=="approval"&&t.kind!=="runDone"&&t.kind!=="drift"||this.notify.notify(t.kind,t.title,{body:t.detail,tag:`${t.project}:${t.kind}:${t.id}`,onClick:()=>this.routeFor(t)})}}}stop(){this.es?.close(),this.es=null}routeFor(n){let t={project:n.project};n.kind==="drift"?this.router.navigate(["/drift"],{queryParams:t}):this.router.navigate(["/runs",n.id],{queryParams:t})}static \u0275fac=function(t){return new(t||e)};static \u0275prov=$({token:e,factory:e.\u0275fac,providedIn:"root"})};var Z=class e{_theme=f(G);_destroyRef=f(ie);_router=f(z);_events=f(X);constructor(){this._events.start()}sidebarCollapsed=S(typeof window<"u"&&window.innerWidth<1100);paletteOpen=S(!1);gChord={armed:!1,t:0};onGlobalKey(n){let t=n.metaKey||n.ctrlKey;if(t&&n.key.toLowerCase()==="k"){n.preventDefault(),this.paletteOpen.update(i=>!i);return}if(n.key==="Escape"){this.paletteOpen.set(!1);return}if(!t&&n.key.toLowerCase()==="g"&&!this.gChord.armed){let i=n.target;if(i&&/input|textarea/i.test(i.tagName))return;this.gChord.armed=!0,this.gChord.t=Date.now(),setTimeout(()=>{this.gChord.armed=!1},600);return}if(this.gChord.armed&&!t){let i=n.key.toLowerCase();i==="d"?this._router.navigate(["/dashboard"]):i==="g"?this._router.navigate(["/graph"]):i==="s"&&this._router.navigate(["/specs"]),this.gChord.armed=!1}}toggleSidebar(){this.sidebarCollapsed.update(n=>!n)}onOpenPalette(){this.paletteOpen.set(!0)}onClosePalette(){this.paletteOpen.set(!1)}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=x({type:e,selectors:[["app-root"]],hostBindings:function(t,i){t&1&&b("keydown",function(_){return i.onGlobalKey(_)},re)},decls:8,vars:2,consts:[[1,"shell"],[3,"collapsed"],[1,"main"],[3,"toggleSidebar","openPalette"],[1,"page-frame"],[3,"close","open"]],template:function(t,i){t&1&&(a(0,"div",0),c(1,"app-sidebar",1),a(2,"div",2),c(3,"app-status-strip"),a(4,"app-top-bar",3),b("toggleSidebar",function(){return i.toggleSidebar()})("openPalette",function(){return i.onOpenPalette()}),r(),a(5,"div",4),c(6,"router-outlet"),r()(),a(7,"app-command-palette",5),b("close",function(){return i.onClosePalette()}),r()()),t&2&&(o(),v("collapsed",i.sidebarCollapsed()),o(6),v("open",i.paletteOpen()))},dependencies:[ce,K,W,J,Y],styles:["[_nghost-%COMP%]{display:block;height:100%}.shell[_ngcontent-%COMP%]{display:flex;height:100vh;background:var(--bg-canvas);color:var(--text-primary);font-family:var(--font-ui);font-size:var(--fs-base)}.main[_ngcontent-%COMP%]{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden}.page-frame[_ngcontent-%COMP%]{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--bg-canvas)}"],changeDetection:0})};pe(Z,be).catch(e=>console.error(e));typeof navigator<"u"&&"serviceWorker"in navigator&&window.addEventListener("load",()=>{navigator.serviceWorker.register("sw.js").catch(e=>{console.warn("[specship] offline service worker registration failed",e)})});
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "SpecShip",
3
+ "short_name": "SpecShip",
4
+ "description": "Monitor and review your Claude Code work — specs, drift, runs, and sessions.",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "theme_color": "#0B0D11",
9
+ "background_color": "#0B0D11",
10
+ "icons": [
11
+ { "src": "icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
12
+ { "src": "icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
13
+ { "src": "icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
14
+ ]
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selvakumaresra/specship",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Supercharge Claude Code with semantic code intelligence. 94% fewer tool calls • 77% faster exploration • 100% local.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- import{a as P}from"./chunk-SBWU7JFC.js";import{a as w}from"./chunk-UYC52MBC.js";import{a as E}from"./chunk-SUZYBYDW.js";import{a as y}from"./chunk-E44X4RH2.js";import{Aa as o,I as b,Ia as s,Ua as u,Va as n,W as c,Wa as v,ea as i,ka as f,qa as g,ua as S,va as x,wa as h,xa as l,ya as e,za as t,zb as _}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var O=(p,r)=>r.model;function k(p,r){if(p&1&&(e(0,"div",26)(1,"span",43),n(2),t(),o(3,"input",44)(4,"input",44),t()),p&2){let d=r.$implicit;i(2),v(d.model),i(),l("value",d.input),i(),l("value",d.output)}}function z(p,r){if(p&1&&(e(0,"option"),n(1),t()),p&2){let d=r.$implicit;i(),v(d)}}var C=class p{api=b(y);theme=b(P);setTheme(r){this.theme.setPref(r)}ingest=c(!0);watch=c(!0);motion=c(!1);toggleIngest(){this.ingest.update(r=>!r)}toggleWatch(){this.watch.update(r=>!r)}toggleMotion(){this.motion.update(r=>!r)}replayIntro(){try{sessionStorage.removeItem("specship-booted")}catch{}window.location.reload()}themeOptions=[{value:"dark",label:"Dark"},{value:"light",label:"Light"},{value:"system",label:"System"}];editorOptions=["code (VS Code)","subl (Sublime)","cursor","nvim"];pricing=[{model:"claude-opus-4",input:"15.00",output:"75.00"},{model:"claude-sonnet-4",input:"3.00",output:"15.00"},{model:"claude-haiku-4",input:"0.80",output:"4.00"}];static \u0275fac=function(d){return new(d||p)};static \u0275cmp=f({type:p,selectors:[["app-settings"]],decls:134,vars:19,consts:[[1,"settings-page","scroll-y"],["icon","settings","title","Settings"],[1,"settings-body"],[1,"card","card-pad","section-card"],[1,"section-head","row","gap-8"],["name","folder",2,"color","var(--accent)",3,"size"],[1,"section-title"],[1,"field"],[1,"field-label"],["value","~/dev/specship","readonly","",1,"input","mono",2,"width","100%"],[1,"field-hint"],["value","specs/",1,"input","mono",2,"width","100%"],["value","node_modules/, dist/, .cg/wt/",1,"input","mono",2,"width","100%"],["name","sessions",2,"color","var(--accent)",3,"size"],["value","~/.claude/projects/",1,"input","mono",2,"width","100%"],[1,"toggle-row","row"],[1,"grow"],[1,"toggle-label"],[1,"muted",2,"font-size","11px"],["type","button","aria-label","Enable transcript ingest",1,"toggle-btn",3,"click"],[1,"toggle-thumb"],["type","button","aria-label","Real-time watch",1,"toggle-btn",3,"click"],["name","dollar",2,"color","var(--accent)",3,"size"],[1,"muted",2,"font-size","11.5px","margin-bottom","12px"],[1,"pricing-header","row"],[1,"price-col"],[1,"pricing-row","row"],["name","sparkles",2,"color","var(--accent)",3,"size"],[3,"change","value","options"],["type","button","aria-label","Reduced motion",1,"toggle-btn",3,"click"],[1,"toggle-row","row",2,"border-top","1px solid var(--border-subtle)","margin-top","4px","padding-top","8px"],["type","button",1,"btn","btn-secondary","btn-sm",3,"click"],["name","refresh",3,"size"],["name","reveal",2,"color","var(--accent)",3,"size"],[1,"input",2,"width","200px"],["name","graph",2,"color","var(--accent)",3,"size"],[1,"about-grid"],[1,"about-row","row"],[1,"muted",2,"font-size","12px"],[1,"mono",2,"font-size","12px"],[1,"mono",2,"font-size","12px","color","var(--success)"],[1,"status-dot",2,"background","var(--success)"],[2,"height","30px"],[1,"mono","grow",2,"font-size","12px"],[1,"input","mono","tabular","price-input",3,"value"]],template:function(d,a){d&1&&(e(0,"div",0),o(1,"app-page-head",1),e(2,"div",2)(3,"section",3)(4,"div",4),o(5,"app-icon",5),e(6,"span",6),n(7,"Project"),t()(),e(8,"div",7)(9,"label",8),n(10,"Project root"),t(),o(11,"input",9),e(12,"div",10),n(13,"Set at app open \xB7 read-only"),t()(),e(14,"div",7)(15,"label",8),n(16,"Spec roots"),t(),o(17,"input",11),t(),e(18,"div",7)(19,"label",8),n(20,"Ignore patterns"),t(),o(21,"input",12),t()(),e(22,"section",3)(23,"div",4),o(24,"app-icon",13),e(25,"span",6),n(26,"Claude Code"),t()(),e(27,"div",7)(28,"label",8),n(29,"Transcripts path"),t(),o(30,"input",14),e(31,"div",10),n(32,"Auto-detected \xB7 overridable"),t()(),e(33,"div",15)(34,"div",16)(35,"div",17),n(36,"Enable transcript ingest"),t(),e(37,"div",18),n(38,"Read JSONL transcripts for analytics"),t()(),e(39,"button",19),s("click",function(){return a.toggleIngest()}),o(40,"span",20),t()(),e(41,"div",15)(42,"div",16)(43,"div",17),n(44,"Real-time watch"),t(),e(45,"div",18),n(46,"Tail new transcript lines as they're written"),t()(),e(47,"button",21),s("click",function(){return a.toggleWatch()}),o(48,"span",20),t()()(),e(49,"section",3)(50,"div",4),o(51,"app-icon",22),e(52,"span",6),n(53,"Pricing table"),t()(),e(54,"div",23),n(55,"Per-million-token prices. Bump these when Anthropic publishes new tiers."),t(),e(56,"div",24)(57,"span",16),n(58,"Model"),t(),e(59,"span",25),n(60,"Input $/M"),t(),e(61,"span",25),n(62,"Output $/M"),t()(),x(63,k,5,3,"div",26,O),t(),e(65,"section",3)(66,"div",4),o(67,"app-icon",27),e(68,"span",6),n(69,"Appearance"),t()(),e(70,"div",7)(71,"label",8),n(72,"Theme"),t(),e(73,"app-segmented",28),s("change",function(M){return a.setTheme(M)}),t(),e(74,"div",10),n(75,"Now fully supported \u2014 dark, light, or follow your OS"),t()(),e(76,"div",15)(77,"div",16)(78,"div",17),n(79,"Reduced motion"),t(),e(80,"div",18),n(81,"Honor OS preference \u2014 instant transitions"),t()(),e(82,"button",29),s("click",function(){return a.toggleMotion()}),o(83,"span",20),t()(),e(84,"div",30)(85,"div",16)(86,"div",17),n(87,"Boot animation"),t(),e(88,"div",18),n(89,"The graph-assembly splash on app launch"),t()(),e(90,"button",31),s("click",function(){return a.replayIntro()}),o(91,"app-icon",32),n(92," Replay intro "),t()()(),e(93,"section",3)(94,"div",4),o(95,"app-icon",33),e(96,"span",6),n(97,"Editor"),t()(),e(98,"div",7)(99,"label",8),n(100,"Open files with"),t(),e(101,"select",34),x(102,z,2,1,"option",null,S),t(),e(104,"div",10),n(105,"Detected via code / subl / etc."),t()()(),e(106,"section",3)(107,"div",4),o(108,"app-icon",35),e(109,"span",6),n(110,"About"),t()(),e(111,"div",36)(112,"div",37)(113,"span",38),n(114,"Version"),t(),e(115,"span",39),n(116,"0.4.0"),t()(),e(117,"div",37)(118,"span",38),n(119,"MCP server"),t(),e(120,"span",40),o(121,"span",41),n(122,"running "),t()(),e(123,"div",37)(124,"span",38),n(125,"Backend"),t(),e(126,"span",39),n(127),t()(),e(128,"div",37)(129,"span",38),n(130,"Log path"),t(),e(131,"span",39),n(132,"~/.cg/logs/desktop.log"),t()()()(),o(133,"div",42),t()()),d&2&&(i(5),l("size",15),i(19),l("size",15),i(15),u("on",a.ingest()),g("aria-pressed",a.ingest()),i(8),u("on",a.watch()),g("aria-pressed",a.watch()),i(4),l("size",15),i(12),h(a.pricing),i(4),l("size",15),i(6),l("value",a.theme.pref())("options",a.themeOptions),i(9),u("on",a.motion()),g("aria-pressed",a.motion()),i(9),l("size",13),i(4),l("size",15),i(7),h(a.editorOptions),i(6),l("size",15),i(19),v(a.api.isConfigured?a.api.apiBase:"not configured"))},dependencies:[_,w,E],styles:["[_nghost-%COMP%]{display:flex;flex:1;min-height:0}.settings-page[_ngcontent-%COMP%]{flex:1;padding:18px;overflow-y:auto}.settings-body[_ngcontent-%COMP%]{max-width:720px}.section-card[_ngcontent-%COMP%]{margin-bottom:14px}.section-head[_ngcontent-%COMP%]{align-items:center;margin-bottom:14px;padding-bottom:12px;border-bottom:1px solid var(--border-subtle)}.section-title[_ngcontent-%COMP%]{font-weight:600;font-size:13.5px}.field[_ngcontent-%COMP%]{margin-bottom:16px}.field-label[_ngcontent-%COMP%]{display:block;font-size:12.5px;font-weight:500;margin-bottom:6px;color:var(--text-primary)}.field-hint[_ngcontent-%COMP%]{color:var(--text-muted);font-size:11px;margin-top:5px}.toggle-row[_ngcontent-%COMP%]{align-items:center;padding:8px 0}.toggle-label[_ngcontent-%COMP%]{font-size:12.5px;font-weight:500}.toggle-btn[_ngcontent-%COMP%]{width:36px;height:20px;border-radius:999px;border:none;cursor:pointer;padding:2px;background:var(--bg-elevated);display:flex;justify-content:flex-start;align-items:center;transition:background .12s;flex-shrink:0}.toggle-btn.on[_ngcontent-%COMP%]{background:var(--accent);justify-content:flex-end}.toggle-btn[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.toggle-thumb[_ngcontent-%COMP%]{width:16px;height:16px;border-radius:50%;background:#fff;flex-shrink:0}.pricing-header[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;font-weight:600;margin-bottom:6px}.pricing-row[_ngcontent-%COMP%]{padding:6px 0;border-top:1px solid var(--border-subtle);align-items:center;gap:10px}.price-col[_ngcontent-%COMP%]{width:110px;text-align:right}.price-input[_ngcontent-%COMP%]{width:100px;text-align:right;padding:4px 8px}.about-grid[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 1fr;gap:10px 20px}.about-row[_ngcontent-%COMP%]{justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border-subtle)}.status-dot[_ngcontent-%COMP%]{display:inline-block;width:6px;height:6px;border-radius:50%;margin-right:6px;vertical-align:middle}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1}"],changeDetection:0})};export{C as Settings};
@@ -1 +0,0 @@
1
- import{E as f,I as o,R as c,W as s,X as a}from"./chunk-PDN6QYGJ.js";var n="specship.theme",h=class i{destroyRef=o(c);pref=s(this.loadPref());effective=s("dark");constructor(){this.applyEffective();let e=window.matchMedia("(prefers-color-scheme: light)"),t=()=>{this.pref()==="system"&&this.applyEffective()};e.addEventListener("change",t),this.destroyRef.onDestroy(()=>e.removeEventListener("change",t)),a(()=>{let r=this.pref();try{localStorage.setItem(n,r)}catch{}this.applyEffective()})}setPref(e){this.pref.set(e)}toggle(){this.setPref(this.effective()==="dark"?"light":"dark")}loadPref(){try{let e=localStorage.getItem(n);if(e==="dark"||e==="light"||e==="system")return e}catch{}return"dark"}applyEffective(){let e=window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark",t=this.pref(),r=t==="system"?e:t;document.documentElement.setAttribute("data-theme",r),this.effective.set(r)}static \u0275fac=function(t){return new(t||i)};static \u0275prov=f({token:i,factory:i.\u0275fac,providedIn:"root"})};export{h as a};
@@ -1 +0,0 @@
1
- import{a as V}from"./chunk-SBWU7JFC.js";import{a as me,b as ge,c as A}from"./chunk-GR72OOCN.js";import{b as se,c as $,d as de,e as pe,f as ce}from"./chunk-SHPTC4RL.js";import{a as D}from"./chunk-7RNS77UP.js";import{a as H}from"./chunk-E44X4RH2.js";import{$ as ne,Aa as c,Ba as s,Ca as p,Da as x,Ga as O,Ha as B,I as _,Ia as C,Ja as R,Ka as u,M as w,N as M,O as J,R as ee,Ta as oe,Ua as y,V as te,Va as l,W as T,Wa as v,Xa as N,Ya as ae,ca as ie,cb as re,da as X,ea as o,ka as h,lb as j,ob as F,pb as k,qa as P,ra as m,sa as g,va as S,wa as E,wb as le,xa as f,ya as a,za as r,zb as z}from"./chunk-PDN6QYGJ.js";import"./chunk-Q7L6LLAK.js";var ue=[{path:"",pathMatch:"full",redirectTo:"dashboard"},{path:"dashboard",loadComponent:()=>import("./chunk-D5OCNEJA.js").then(e=>e.Dashboard),data:{nav:"dashboard",title:"Dashboard"}},{path:"graph",loadComponent:()=>import("./chunk-WLIMNDS3.js").then(e=>e.Graph),data:{nav:"graph",title:"Graph"}},{path:"specs",loadComponent:()=>import("./chunk-JQ534IB6.js").then(e=>e.Specs),data:{nav:"specs",title:"Specs"}},{path:"drift",loadComponent:()=>import("./chunk-FHZHD2ZG.js").then(e=>e.Drift),data:{nav:"drift",title:"Drift queue"}},{path:"workflows",loadComponent:()=>import("./chunk-NZEZCT65.js").then(e=>e.Workflows),data:{nav:"workflows",title:"Workflows"}},{path:"runs",loadComponent:()=>import("./chunk-UBOZGQNK.js").then(e=>e.Runs),data:{nav:"runs",title:"Runs"}},{path:"runs/:id",loadComponent:()=>import("./chunk-MVOMVPYB.js").then(e=>e.RunDetail),data:{nav:"runs",title:"Run detail"}},{path:"chat",loadComponent:()=>import("./chunk-L37MTFSG.js").then(e=>e.Chat),data:{nav:"chat",title:"Chat"}},{path:"sessions",loadComponent:()=>import("./chunk-ASZ77FMZ.js").then(e=>e.Sessions),data:{nav:"sessions",title:"Sessions"}},{path:"sessions/:id",loadComponent:()=>import("./chunk-RASJHUXS.js").then(e=>e.SessionDetail),data:{nav:"sessions",title:"Session detail"}},{path:"heatmap",loadComponent:()=>import("./chunk-YAMRN47K.js").then(e=>e.Heatmap),data:{nav:"heatmap",title:"Heatmap"}},{path:"costs",loadComponent:()=>import("./chunk-WCHGDXWC.js").then(e=>e.Costs),data:{nav:"costs",title:"Costs"}},{path:"specship-impact",loadComponent:()=>import("./chunk-TQ3P2QZO.js").then(e=>e.SpecshipImpact),data:{nav:"specship-impact",title:"SpecShip Impact"}},{path:"compare",loadComponent:()=>import("./chunk-WCKHQIYN.js").then(e=>e.Compare),data:{nav:"compare",title:"Compare projects"}},{path:"tips",loadComponent:()=>import("./chunk-A5R3MJMO.js").then(e=>e.Tips),data:{nav:"tips",title:"Tips"}},{path:"memory",loadComponent:()=>import("./chunk-45QHGCB4.js").then(e=>e.Memory),data:{nav:"memory",title:"Memory"}},{path:"design",loadComponent:()=>import("./chunk-WAI2JMZP.js").then(e=>e.Design),data:{nav:"design",title:"Design system"}},{path:"settings",loadComponent:()=>import("./chunk-BYZFQSM6.js").then(e=>e.Settings),data:{nav:"settings",title:"Settings"}},{path:"**",redirectTo:"dashboard"}];var fe={providers:[te(),ce(ue)]};function ve(e,n){e&1&&(J(),x(0,"rect",14)(1,"rect",15))}var G=class e{size=k(24);tile=k(!0);glow=k(!0);static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-logo-mark"]],inputs:{size:[1,"size"],tile:[1,"tile"],glow:[1,"glow"]},decls:16,vars:5,consts:[["viewBox","0 0 32 32","fill","none",2,"flex-shrink","0"],["id","ssTile","x1","0","y1","0","x2","32","y2","32","gradientUnits","userSpaceOnUse"],["stop-color","#12161E"],["offset","1","stop-color","#0B0D11"],["id","ssLead","x1","18","y1","11","x2","26","y2","21","gradientUnits","userSpaceOnUse"],["stop-color","#6FA0F6"],["offset","1","stop-color","#5B93F2"],["transform","translate(4.6 4.6) scale(0.715)"],["d","M5 16 H11 M11 9 L23 16 L11 23","stroke","rgba(150,165,200,0.5)","stroke-width","1.9","stroke-linecap","round","stroke-linejoin","round","fill","none"],["x1","11","y1","9","x2","11","y2","23","stroke","rgba(150,165,200,0.4)","stroke-width","1.9","stroke-linecap","round"],["cx","23","cy","16","r","3.6","fill","url(#ssLead)"],["cx","11","cy","9","r","2.9","fill","#A586F5"],["cx","11","cy","23","r","2.9","fill","#46C26B"],["cx","5","cy","16","r","2.1","fill","#29D2BE"],["width","32","height","32","rx","7.2","fill","url(#ssTile)"],["x","0.5","y","0.5","width","31","height","31","rx","6.7","fill","none","stroke","rgba(255,255,255,0.08)"]],template:function(t,i){t&1&&(J(),s(0,"svg",0)(1,"defs")(2,"linearGradient",1),x(3,"stop",2)(4,"stop",3),p(),s(5,"linearGradient",4),x(6,"stop",5)(7,"stop",6),p()(),m(8,ve,2,0),s(9,"g",7),x(10,"path",8)(11,"line",9)(12,"circle",10)(13,"circle",11)(14,"circle",12)(15,"circle",13),p()()),t&2&&(oe("filter",i.glow()?"drop-shadow(0 0 9px rgba(91,147,242,0.35))":"none"),P("width",i.size())("height",i.size()),o(8),g(i.tile()?8:-1))},styles:["[_nghost-%COMP%]{display:inline-flex;align-items:center;justify-content:center;line-height:0}"],changeDetection:0})};var _e=e=>["/",e],be=(e,n)=>n.group,he=(e,n)=>n.id;function xe(e,n){e&1&&(a(0,"div",3),l(1,"SpecShip "),a(2,"span",14),l(3,"Desktop"),r()())}function Ce(e,n){if(e&1&&(a(0,"div",15),l(1),r()),e&2){let t=u().$implicit;o(),v(t.group)}}function ye(e,n){if(e&1&&(a(0,"span",18),l(1),r()),e&2){let t=u(2).$implicit,i=u(2);P("data-kind",t.badgeKind),o(),v(i.badgeOf(t))}}function Pe(e,n){if(e&1&&(a(0,"span",11),l(1),r(),m(2,ye,2,2,"span",18)),e&2){let t=u().$implicit,i=u(2);o(),v(t.label),o(),g(i.badgeOf(t)>0?2:-1)}}function ke(e,n){if(e&1&&(a(0,"a",16),c(1,"span",8),a(2,"span",9),c(3,"app-icon",17),r(),m(4,Pe,3,2),r()),e&2){let t=n.$implicit,i=u(2);f("routerLink",re(5,_e,t.id))("title",i.collapsed()?t.label:null),o(3),f("name",t.icon)("size",16),o(),g(i.collapsed()?-1:4)}}function we(e,n){if(e&1&&(a(0,"div",5),m(1,Ce,2,1,"div",15),S(2,ke,5,7,"a",16,he),r()),e&2){let t=n.$implicit,i=u();o(),g(i.collapsed()?-1:1),o(),E(t.items)}}function Me(e,n){e&1&&(a(0,"span",11),l(1,"Design system"),r())}function Se(e,n){e&1&&(a(0,"span",11),l(1,"Settings"),r())}var q=class e{collapsed=k(!1);api=_(H);projects=_(D);status=A(this.api,()=>`/api/status${this.projects.projectQuery()}`);tips=A(this.api,()=>"/api/claude/tips");nav=[{group:"Project",items:[{id:"dashboard",label:"Dashboard",icon:"dashboard"},{id:"graph",label:"Graph",icon:"graph"},{id:"specs",label:"Specs",icon:"book"},{id:"drift",label:"Drift queue",icon:"drift",badge:()=>this.status.state().data?.drift??0,badgeKind:"warn"},{id:"workflows",label:"Workflows",icon:"workflow"},{id:"runs",label:"Runs",icon:"play"},{id:"chat",label:"Chat",icon:"chat"}]},{group:"Claude Code",items:[{id:"sessions",label:"Sessions",icon:"sessions"},{id:"heatmap",label:"Heatmap",icon:"heatmap"},{id:"costs",label:"Costs",icon:"dollar"},{id:"specship-impact",label:"SpecShip Impact",icon:"graph"},{id:"compare",label:"Compare projects",icon:"compare"},{id:"memory",label:"Memory",icon:"memory"},{id:"tips",label:"Tips",icon:"tips",badge:()=>(this.tips.state().data?.tips??[]).filter(n=>n.severity!=="info").length,badgeKind:"error"}]}];badgeOf(n){return n.badge?n.badge():0}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-sidebar"]],inputs:{collapsed:[1,"collapsed"]},decls:18,vars:10,consts:[[1,"sidebar"],[1,"brand"],[3,"size"],[1,"brand-text"],[1,"scroll-y","nav-scroll"],[1,"nav-group"],[1,"pinned"],["routerLink","/design","routerLinkActive","active",1,"nav-item",3,"title"],[1,"indicator"],[1,"nav-icon"],["name","palette",3,"size"],[1,"nav-label"],["routerLink","/settings","routerLinkActive","active",1,"nav-item",3,"title"],["name","settings",3,"size"],[1,"muted"],[1,"eyebrow","group-label"],["routerLinkActive","active",1,"nav-item",3,"routerLink","title"],[3,"name","size"],[1,"badge"]],template:function(t,i){t&1&&(a(0,"nav",0)(1,"div",1),c(2,"app-logo-mark",2),m(3,xe,4,0,"div",3),r(),a(4,"div",4),S(5,we,4,1,"div",5,be),r(),a(7,"div",6)(8,"a",7),c(9,"span",8),a(10,"span",9),c(11,"app-icon",10),r(),m(12,Me,2,0,"span",11),r(),a(13,"a",12),c(14,"span",8),a(15,"span",9),c(16,"app-icon",13),r(),m(17,Se,2,0,"span",11),r()()()),t&2&&(y("collapsed",i.collapsed()),o(2),f("size",26),o(),g(i.collapsed()?-1:3),o(2),E(i.nav),o(3),f("title",i.collapsed()?"Design system":null),o(3),f("size",16),o(),g(i.collapsed()?-1:12),o(),f("title",i.collapsed()?"Settings":null),o(3),f("size",16),o(),g(i.collapsed()?-1:17))},dependencies:[de,pe,z,G],styles:['@charset "UTF-8";[_nghost-%COMP%]{display:contents}.sidebar[_ngcontent-%COMP%]{width:var(--sidebar-w);flex-shrink:0;background:var(--bg-panel);border-right:1px solid var(--border-subtle);display:flex;flex-direction:column;transition:width .12s;overflow:hidden;font-family:var(--font-ui)}.sidebar.collapsed[_ngcontent-%COMP%]{width:var(--sidebar-w-collapsed)}.brand[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;padding:15px 16px}.collapsed[_ngcontent-%COMP%] .brand[_ngcontent-%COMP%]{justify-content:center;padding:14px 0}.brand-text[_ngcontent-%COMP%]{font-weight:600;font-size:13.5px;letter-spacing:-.01em}.brand-text[_ngcontent-%COMP%] .muted[_ngcontent-%COMP%]{color:var(--text-muted);font-weight:400}.nav-scroll[_ngcontent-%COMP%]{flex:1;padding:4px 8px}.nav-group[_ngcontent-%COMP%]{margin-bottom:10px}.group-label[_ngcontent-%COMP%]{padding:8px 8px 4px}.nav-item[_ngcontent-%COMP%]{position:relative;display:flex;align-items:center;gap:10px;height:32px;padding:0 10px;border-radius:7px;text-decoration:none;margin-bottom:1px;color:var(--text-secondary);font-weight:450;font-size:13px;transition:background .1s}.collapsed[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]{padding:0;justify-content:center}.nav-item[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.nav-item.active[_ngcontent-%COMP%]{background:var(--bg-active);color:var(--text-primary);font-weight:600}.nav-item.active[_ngcontent-%COMP%] .indicator[_ngcontent-%COMP%]{position:absolute;left:0;top:7px;bottom:7px;width:2.5px;border-radius:2px;background:var(--accent)}.nav-icon[_ngcontent-%COMP%]{width:16px;height:16px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary)}.nav-item[_ngcontent-%COMP%]:hover .nav-icon[_ngcontent-%COMP%]{color:var(--text-primary)}.nav-item.active[_ngcontent-%COMP%] .nav-icon[_ngcontent-%COMP%]{color:var(--accent)}.nav-label[_ngcontent-%COMP%]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge[_ngcontent-%COMP%]{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:10px;font-weight:700;padding:1px 6px;min-width:18px;border-radius:999px;display:inline-flex;justify-content:center;color:var(--text-secondary);background:#ffffff0f}.badge[data-kind=error][_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.badge[data-kind=warn][_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.pinned[_ngcontent-%COMP%]{padding:8px;border-top:1px solid var(--border-subtle)}'],changeDetection:0})};function Ee(e,n){if(e&1&&(s(0,"span",8),l(1),p(),x(2,"span",1),s(3,"span",9)(4,"span",10),l(5,"backend"),p(),s(6,"span",4),l(7),p()(),x(8,"span",1),s(9,"span",9)(10,"span",10),l(11,"nodes"),p(),s(12,"span",4),l(13),p()(),s(14,"span",9)(15,"span",10),l(16,"edges"),p(),s(17,"span",4),l(18),p()(),x(19,"span",1),s(20,"span",9)(21,"span",10),l(22,"drift"),p(),s(23,"span",4),l(24),p()(),x(25,"span",11),s(26,"span",9)(27,"span",10),l(28,"indexed"),p(),s(29,"span",4),l(30),p()()),e&2){let t=u(),i=t.status.state().data;o(),v(i.projectPath),o(6),v(i.backend),o(6),v(i.nodeCount.toLocaleString()),o(5),v(i.edgeCount.toLocaleString()),o(5),y("warn",i.drift>0)("success",i.drift===0),o(),v(i.drift),o(6),v(t.indexedLabel())}}function Oe(e,n){e&1&&(s(0,"span",12),l(1,"loading status\u2026"),p(),x(2,"span",11))}function Te(e,n){if(e&1&&(s(0,"span",12),l(1),p(),x(2,"span",11)),e&2){let t=u();o(),v(t.conn.online()?"no backend":"offline \u2014 no cached data")}}function je(e,n){e&1&&(s(0,"span",5),l(1),p()),e&2&&(o(),N("\xB7 ",n))}function De(e,n){e&1&&(s(0,"span",6),l(1,"\u26A0"),p()),e&2&&B("title",n)}var K=class e{api=_(H);projects=_(D);refreshSvc=_(ge);conn=_(me);status=A(this.api,()=>`/api/status${this.projects.projectQuery()}`);offlineAge=j(()=>{let n=this.status.state().cachedAt??this.conn.lastOnlineAt();if(n===null)return null;let t=Date.now()-n;return t<1e4?"just now":t<6e4?`${Math.round(t/1e3)}s ago`:t<36e5?`${Math.round(t/6e4)}m ago`:t<864e5?`${Math.round(t/36e5)}h ago`:`${Math.round(t/864e5)}d ago`});indexedLabel=j(()=>{let n=this.status.state().data?.lastIndexed;if(!n)return"never";try{let t=new Date(n),i=Date.now()-t.getTime();return i<6e4?"just now":i<36e5?`${Math.round(i/6e4)}m ago`:i<864e5?`${Math.round(i/36e5)}h ago`:t.toLocaleDateString()}catch{return String(n)}});async refresh(){await this.refreshSvc.triggerGlobalRefresh()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-status-strip"]],decls:13,vars:14,consts:[[1,"status-strip"],[1,"divider"],[1,"seg","conn",3,"title"],[1,"conn-dot"],[1,"mono","val"],[1,"muted","conn-age"],[1,"refresh-error","mono",3,"title"],[1,"refresh",3,"click","disabled","title"],[1,"seg","mono"],[1,"seg"],[1,"lbl"],[1,"grow"],[1,"mono","muted"]],template:function(t,i){if(t&1&&(s(0,"div",0),m(1,Ee,31,10)(2,Oe,3,0)(3,Te,3,1),x(4,"span",1),s(5,"span",2),x(6,"span",3),s(7,"span",4),l(8),p(),m(9,je,2,1,"span",5),p(),m(10,De,2,1,"span",6),s(11,"button",7),R("click",function(){return i.refresh()}),l(12,"\u21BB"),p()()),t&2){let d,b;o(),g(i.status.state().data?1:i.status.state().loading?2:3),o(4),B("title",i.conn.online()?"Connected to the SpecShip server":"Server unreachable \u2014 showing the last cached data"),o(),y("live",i.conn.online())("off",!i.conn.online()),o(2),v(i.conn.online()?"Live":"Offline"),o(),g((d=!i.conn.online()&&i.offlineAge())?9:-1,d),o(),g((b=i.refreshSvc.error())?10:-1,b),o(),y("spinning",i.refreshSvc.loading()),B("disabled",i.refreshSvc.loading()||!i.conn.online())("title",i.conn.online()?i.refreshSvc.loading()?"Refreshing\u2026":"Refresh: sync index + re-ingest Claude Code transcripts. Updates Sessions, Heatmap, Costs, Memory, Drift, Graph.":"Offline \u2014 reconnect to refresh"),P("aria-label",i.conn.online()?i.refreshSvc.loading()?"Refreshing\u2026":"Refresh everything \u2014 sync index + ingest Claude Code transcripts":"Offline \u2014 reconnect to refresh")}},styles:["[_nghost-%COMP%]{display:contents}.status-strip[_ngcontent-%COMP%]{height:var(--status-h);flex-shrink:0;display:flex;align-items:center;gap:14px;padding:0 14px;border-bottom:1px solid var(--border-subtle);background:var(--bg-panel-2);font-size:11.5px;overflow:hidden;white-space:nowrap}.seg[_ngcontent-%COMP%]{display:flex;align-items:center;gap:6px;flex-shrink:0}.lbl[_ngcontent-%COMP%]{color:var(--text-muted);font-size:11px}.val[_ngcontent-%COMP%]{color:var(--text-secondary);font-variant-numeric:tabular-nums}.val.warn[_ngcontent-%COMP%]{color:var(--warn)}.val.success[_ngcontent-%COMP%]{color:var(--success)}.divider[_ngcontent-%COMP%]{width:1px;height:12px;background:var(--border-subtle);flex-shrink:0}.grow[_ngcontent-%COMP%]{flex:1}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.refresh[_ngcontent-%COMP%]{background:transparent;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:4px;transition:background .12s,color .12s}.refresh[_ngcontent-%COMP%]:hover:not(:disabled){background:var(--bg-hover);color:var(--text-primary)}.refresh[_ngcontent-%COMP%]:disabled{cursor:progress;opacity:.8}.refresh[_ngcontent-%COMP%]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.refresh.spinning[_ngcontent-%COMP%]{display:inline-block;animation:_ngcontent-%COMP%_spin .9s linear infinite;color:var(--accent)}@keyframes _ngcontent-%COMP%_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.refresh-error[_ngcontent-%COMP%]{color:var(--warn);font-size:12px;padding:0 2px;cursor:help}.conn[_ngcontent-%COMP%]{gap:5px}.conn-dot[_ngcontent-%COMP%]{width:6px;height:6px;border-radius:50%;flex-shrink:0}.conn-dot.live[_ngcontent-%COMP%]{background:var(--success);box-shadow:0 0 6px var(--success)}.conn-dot.off[_ngcontent-%COMP%]{background:var(--warn);box-shadow:0 0 6px var(--warn)}.conn-age[_ngcontent-%COMP%]{font-size:11px}"],changeDetection:0})};var ze=(e,n)=>n.slug;function Ie(e,n){e&1&&c(0,"span",8)}function Le(e,n){e&1&&c(0,"span",9)}function Re(e,n){e&1&&c(0,"span",10)}function Ne(e,n){if(e&1&&(a(0,"span",7),l(1),r(),m(2,Ie,1,0,"span",8)(3,Le,1,0,"span",9)(4,Re,1,0,"span",10)),e&2){let t=n,i=u();o(),v(i.shortPath(t.path)),o(),g(t.exists?t.initialized?4:3:2)}}function Fe(e,n){e&1&&(a(0,"span",3),l(1,"Select project"),r())}function Ae(e,n){if(e&1){let t=O();a(0,"button",20),C("click",function(){w(t);let d=u(2);return M(d.clear())}),c(1,"app-icon",21),l(2," Clear "),r()}e&2&&(o(),f("size",11))}function Be(e,n){e&1&&(a(0,"span",16),l(1,"\xB7 loading\u2026"),r())}function $e(e,n){if(e&1&&(a(0,"span",16),l(1),r()),e&2){let t=u(2);o(),N("\xB7 ",t.filtered().length," shown \xB7 live")}}function He(e,n){e&1&&(a(0,"div",18),l(1,"No projects match."),r())}function Ve(e,n){e&1&&(a(0,"span",25),l(1,"missing"),r())}function Ge(e,n){e&1&&(a(0,"span",26),l(1,"not init"),r())}function qe(e,n){e&1&&(a(0,"span",27),l(1,"initialized"),r())}function Ke(e,n){if(e&1&&(a(0,"span"),l(1),r()),e&2){let t=u().$implicit,i=u(2);o(),N("\xB7 ",i.formatRelative(t.lastModifiedMs))}}function We(e,n){e&1&&c(0,"app-icon",29),e&2&&f("size",13)}function Ue(e,n){if(e&1){let t=O();a(0,"button",22),C("click",function(){let d=w(t).$implicit,b=u(2);return M(b.pick(d.slug))}),a(1,"div",23)(2,"div",24)(3,"span",7),l(4),r(),m(5,Ve,2,0,"span",25)(6,Ge,2,0,"span",26)(7,qe,2,0,"span",27),r(),a(8,"div",28)(9,"span"),l(10),r(),m(11,Ke,2,1,"span"),r()(),m(12,We,1,1,"app-icon",29),r()}if(e&2){let t=n.$implicit,i=u(2);y("active",i.projects.activeSlug()===t.slug),o(4),v(i.shortPath(t.path)),o(),g(t.exists?t.initialized?7:6:5),o(5),ae("",t.sessionCount," session",t.sessionCount===1?"":"s"),o(),g(t.lastModifiedMs>0?11:-1),o(),g(i.projects.activeSlug()===t.slug?12:-1)}}function Qe(e,n){if(e&1){let t=O();a(0,"div",6)(1,"div",11),c(2,"app-icon",12),a(3,"input",13),C("input",function(d){w(t);let b=u();return M(b.onQuery(d.target.value))}),r(),m(4,Ae,3,1,"button",14),r(),a(5,"div",15)(6,"span"),l(7),r(),m(8,Be,2,0,"span",16)(9,$e,2,1,"span",16),r(),a(10,"div",17),m(11,He,2,0,"div",18),S(12,Ue,13,8,"button",19,ze),r()()}if(e&2){let t=u();o(2),f("size",13),o(),f("value",t.query()),o(),g(t.projects.activeSlug()?4:-1),o(3),v(t.projects.claudeRoot()??"~/.claude/projects"),o(),g(t.projects.loading()?8:9),o(3),g(t.filtered().length===0&&!t.projects.loading()?11:-1),o(),E(t.filtered())}}var W=class e{host=_(ne);projects=_(D);open=T(!1);query=T("");filtered=j(()=>{let n=this.query().trim().toLowerCase(),t=this.projects.projects();return n?t.filter(i=>i.path.toLowerCase().includes(n)||i.slug.toLowerCase().includes(n)):t});badge=j(()=>this.projects.projects().length);toggle(){this.open.update(n=>!n)}pick(n){this.projects.setActive(n),this.open.set(!1),this.query.set("")}clear(){this.projects.setActive(null),this.open.set(!1)}onQuery(n){this.query.set(n)}onDocumentClick(n){if(!this.open())return;let t=n.target;t&&!this.host.nativeElement.contains(t)&&this.open.set(!1)}onEscape(){this.open()&&this.open.set(!1)}formatRelative(n){if(!n)return"";let t=Date.now()-n,i=Math.round(t/1e3);if(i<60)return`${i}s ago`;let d=Math.round(i/60);if(d<60)return`${d}m ago`;let b=Math.round(d/60);if(b<24)return`${b}h ago`;let Z=Math.round(b/24);return Z<30?`${Z}d ago`:new Date(n).toLocaleDateString()}shortPath(n){if(!n)return"";let t=this.detectHome();return t&&n===t?"~":t&&n.startsWith(t+"/")?"~/"+n.slice(t.length+1):n}detectHome(){let n=this.projects.claudeRoot();if(!n)return null;let t="/.claude/projects";return n.endsWith(t)?n.slice(0,-t.length):null}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-project-picker"]],hostBindings:function(t,i){t&1&&C("click",function(b){return i.onDocumentClick(b)},X)("keydown.escape",function(){return i.onEscape()},X)},decls:9,vars:9,consts:[[1,"wrap"],["type","button","aria-label","Switch project",1,"trigger",3,"click"],["name","folder",3,"size"],[1,"placeholder"],[1,"badge","mono"],[3,"name","size"],["role","dialog","aria-label","Pick a project",1,"popover"],[1,"path","mono"],["title","path no longer exists on disk",1,"status-dot","missing"],["title","not yet specship init'd",1,"status-dot","warn"],["title","initialized",1,"status-dot","ok"],[1,"popover-head","row","gap-8"],["name","search",3,"size"],["type","text","placeholder","Filter projects\u2026","autofocus","",1,"popover-input",3,"input","value"],["title","Clear selection",1,"btn","btn-ghost","btn-xs"],[1,"popover-meta","mono"],[1,"muted"],[1,"popover-list","scroll-y"],[1,"empty"],["type","button",1,"row-item",3,"active"],["title","Clear selection",1,"btn","btn-ghost","btn-xs",3,"click"],["name","x",3,"size"],["type","button",1,"row-item",3,"click"],[1,"col","gap-2","grow"],[1,"row","gap-6","path-line"],[1,"pill","missing"],[1,"pill","warn"],[1,"pill","ok"],[1,"meta","row","gap-8","mono","muted"],["name","check",3,"size"]],template:function(t,i){if(t&1&&(a(0,"div",0)(1,"button",1),C("click",function(){return i.toggle()}),c(2,"app-icon",2),m(3,Ne,5,2)(4,Fe,2,0,"span",3),a(5,"span",4),l(6),r(),c(7,"app-icon",5),r(),m(8,Qe,14,6,"div",6),r()),t&2){let d;y("open",i.open()),o(),P("aria-expanded",i.open()),o(),f("size",13),o(),g((d=i.projects.active())?3:4,d),o(3),v(i.badge()),o(),f("name",i.open()?"chevronDown":"chevronRight")("size",11),o(),g(i.open()?8:-1)}},dependencies:[z],styles:["[_nghost-%COMP%]{display:inline-flex;min-width:0}.wrap[_ngcontent-%COMP%]{position:relative;min-width:0}.trigger[_ngcontent-%COMP%]{height:30px;padding:0 11px 0 10px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;color:var(--text-secondary);display:inline-flex;align-items:center;gap:8px;font-family:var(--font-ui);font-size:12.5px;cursor:pointer;max-width:360px;min-width:0;transition:background .1s,border-color .1s}.trigger[_ngcontent-%COMP%]:hover, .wrap.open[_ngcontent-%COMP%] .trigger[_ngcontent-%COMP%]{background:var(--bg-panel-2);border-color:var(--border-strong);color:var(--text-primary)}.trigger[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted);flex-shrink:0}.trigger[_ngcontent-%COMP%]:hover app-icon[_ngcontent-%COMP%]{color:var(--text-secondary)}.path[_ngcontent-%COMP%]{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.placeholder[_ngcontent-%COMP%]{color:var(--text-muted);font-style:italic}.status-dot[_ngcontent-%COMP%]{width:6px;height:6px;border-radius:50%;flex-shrink:0}.status-dot.ok[_ngcontent-%COMP%]{background:var(--success)}.status-dot.warn[_ngcontent-%COMP%]{background:var(--warn)}.status-dot.missing[_ngcontent-%COMP%]{background:var(--error)}.badge[_ngcontent-%COMP%]{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--bg-active);color:var(--text-muted);font-variant-numeric:tabular-nums;flex-shrink:0}.popover[_ngcontent-%COMP%]{position:absolute;top:calc(100% + 6px);left:0;z-index:30;width:420px;max-width:calc(100vw - 40px);background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:10px;box-shadow:var(--shadow-pop);display:flex;flex-direction:column;max-height:480px;animation:_ngcontent-%COMP%_pp-in .12s ease-out}@keyframes _ngcontent-%COMP%_pp-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.popover-head[_ngcontent-%COMP%]{padding:10px 12px 8px;border-bottom:1px solid var(--border-subtle)}.popover-head[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted);flex-shrink:0}.popover-input[_ngcontent-%COMP%]{flex:1;min-width:0;background:transparent;border:none;outline:none;font-family:var(--font-ui);font-size:13px;color:var(--text-primary)}.popover-input[_ngcontent-%COMP%]::placeholder{color:var(--text-muted)}.popover-meta[_ngcontent-%COMP%]{padding:6px 12px 8px;font-size:10.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.popover-meta[_ngcontent-%COMP%] .muted[_ngcontent-%COMP%]{color:var(--text-muted)}.popover-list[_ngcontent-%COMP%]{flex:1;min-height:0;padding:4px}.row-item[_ngcontent-%COMP%]{width:100%;display:flex;align-items:center;gap:10px;padding:9px 10px;border:none;border-radius:7px;background:transparent;color:inherit;cursor:pointer;font-family:inherit;text-align:left;transition:background 80ms}.row-item[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.row-item.active[_ngcontent-%COMP%]{background:var(--accent-soft);color:var(--text-primary)}.row-item.active[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--accent)}.path-line[_ngcontent-%COMP%]{min-width:0}.path-line[_ngcontent-%COMP%] .path[_ngcontent-%COMP%]{font-size:12.5px;font-weight:500;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta[_ngcontent-%COMP%]{font-size:10.5px}.pill[_ngcontent-%COMP%]{font-size:10px;font-weight:600;padding:1px 7px;border-radius:999px;white-space:nowrap;flex-shrink:0}.pill.ok[_ngcontent-%COMP%]{color:var(--success);background:var(--success-soft)}.pill.warn[_ngcontent-%COMP%]{color:var(--warn);background:var(--warn-soft)}.pill.missing[_ngcontent-%COMP%]{color:var(--error);background:var(--error-soft)}.empty[_ngcontent-%COMP%]{text-align:center;color:var(--text-muted);font-size:12px;padding:18px 12px}.muted[_ngcontent-%COMP%]{color:var(--text-muted)}.mono[_ngcontent-%COMP%]{font-family:var(--font-mono)}.grow[_ngcontent-%COMP%]{flex:1;min-width:0}.row[_ngcontent-%COMP%]{display:flex;align-items:center}.col[_ngcontent-%COMP%]{display:flex;flex-direction:column}.gap-2[_ngcontent-%COMP%]{gap:2px}.gap-6[_ngcontent-%COMP%]{gap:6px}.gap-8[_ngcontent-%COMP%]{gap:8px}.scroll-y[_ngcontent-%COMP%]{overflow-y:auto;overflow-x:hidden}"],changeDetection:0})};var U=class e{toggleSidebar=F();openPalette=F();theme=_(V);effective=this.theme.effective;onToggleTheme(){this.theme.toggle()}onMenuClick(){this.toggleSidebar.emit()}onPalette(){this.openPalette.emit()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-top-bar"]],outputs:{toggleSidebar:"toggleSidebar",openPalette:"openPalette"},decls:13,vars:5,consts:[[1,"topbar"],["title","Toggle sidebar","aria-label","Toggle sidebar",1,"ghost","icon-btn",3,"click"],["name","menu",3,"size"],["aria-label","Open command palette",1,"palette-trigger",3,"click"],["name","search",3,"size"],[1,"grow"],["aria-label","Toggle theme",1,"ghost","icon-btn",3,"click","title"],[3,"name","size"]],template:function(t,i){t&1&&(a(0,"div",0)(1,"button",1),C("click",function(){return i.onMenuClick()}),c(2,"app-icon",2),r(),c(3,"app-project-picker"),a(4,"button",3),C("click",function(){return i.onPalette()}),c(5,"app-icon",4),a(6,"span"),l(7,"Search or jump to\u2026"),r(),a(8,"kbd"),l(9,"\u2318K"),r()(),c(10,"span",5),a(11,"button",6),C("click",function(){return i.onToggleTheme()}),c(12,"app-icon",7),r()()),t&2&&(o(2),f("size",16),o(3),f("size",13),o(6),f("title","Theme: "+i.effective()),o(),f("name",i.effective()==="dark"?"sun":"moon")("size",16))},dependencies:[z,W],styles:["[_nghost-%COMP%]{display:contents}.topbar[_ngcontent-%COMP%]{height:44px;flex-shrink:0;display:flex;align-items:center;gap:10px;padding:0 14px;border-bottom:1px solid var(--border-subtle);background:var(--bg-canvas)}.ghost[_ngcontent-%COMP%]{background:transparent;border:1px solid transparent;color:var(--text-secondary);cursor:pointer}.ghost[_ngcontent-%COMP%]:hover{background:var(--bg-hover);color:var(--text-primary)}.icon-btn[_ngcontent-%COMP%]{padding:6px 9px;border-radius:5px;font-size:16px}.palette-trigger[_ngcontent-%COMP%]{flex:1;max-width:380px;height:30px;padding:0 11px;background:var(--bg-panel);border:1px solid var(--border-subtle);border-radius:8px;cursor:text;color:var(--text-muted);font-size:12.5px;text-align:left;display:flex;align-items:center;gap:8px}.palette-trigger[_ngcontent-%COMP%]:hover{border-color:var(--border-strong);color:var(--text-secondary)}.palette-trigger[_ngcontent-%COMP%] app-icon[_ngcontent-%COMP%]{color:var(--text-muted)}.palette-trigger[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{flex:1}kbd[_ngcontent-%COMP%]{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:4px;padding:2px 6px}.grow[_ngcontent-%COMP%]{flex:1}"],changeDetection:0})};var Ye=(e,n)=>n.id;function Je(e,n){if(e&1){let t=O();s(0,"button",6),R("click",function(){let d=w(t).$implicit,b=u(2);return M(b.go(d.id))}),s(1,"span",7),l(2),p(),s(3,"span",8),l(4),p()()}if(e&2){let t=n.$implicit;o(2),v(t.label),o(2),v(t.sub)}}function Xe(e,n){if(e&1){let t=O();s(0,"div",1),R("click",function(d){w(t);let b=u();return M(b.onBackdrop(d))}),s(1,"div",2)(2,"div",3)(3,"span"),l(4,"Jump to\u2026"),p(),s(5,"kbd"),l(6,"esc"),p()(),s(7,"div",4),S(8,Je,5,2,"button",5,Ye),p()()()}if(e&2){let t=u();o(8),E(t.items)}}var Ze=[{id:"dashboard",label:"Dashboard",sub:"/dashboard"},{id:"graph",label:"Graph",sub:"/graph"},{id:"specs",label:"Specs",sub:"/specs"},{id:"drift",label:"Drift queue",sub:"/drift"},{id:"workflows",label:"Workflows",sub:"/workflows"},{id:"runs",label:"Runs",sub:"/runs"},{id:"chat",label:"Chat",sub:"/chat"},{id:"sessions",label:"Sessions",sub:"/sessions"},{id:"heatmap",label:"Heatmap",sub:"/heatmap"},{id:"costs",label:"Costs",sub:"/costs"},{id:"compare",label:"Compare projects",sub:"/compare"},{id:"tips",label:"Tips",sub:"/tips"},{id:"settings",label:"Settings",sub:"/settings"}],Q=class e{open=k(!1);close=F();router=_($);items=Ze;go(n){this.router.navigate(["/"+n]),this.close.emit()}onBackdrop(n){n.target===n.currentTarget&&this.close.emit()}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-command-palette"]],inputs:{open:[1,"open"]},outputs:{close:"close"},decls:1,vars:1,consts:[[1,"backdrop"],[1,"backdrop",3,"click"],[1,"palette"],[1,"palette-header"],[1,"palette-list","scroll-y"],[1,"item"],[1,"item",3,"click"],[1,"label"],[1,"sub","mono"]],template:function(t,i){t&1&&m(0,Xe,10,0,"div",0),t&2&&g(i.open()?0:-1)},styles:["[_nghost-%COMP%]{display:contents}.backdrop[_ngcontent-%COMP%]{position:fixed;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:grid;place-items:start center;padding-top:12vh;z-index:100;animation:fadeIn .1s}.palette[_ngcontent-%COMP%]{width:560px;max-width:90vw;background:var(--bg-elevated);border:1px solid var(--border-strong);border-radius:12px;box-shadow:var(--shadow-pop);overflow:hidden}.palette-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1px solid var(--border-subtle);font-size:13px}.palette-list[_ngcontent-%COMP%]{max-height:380px;padding:6px}.item[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;width:100%;padding:8px 10px;background:transparent;border:none;border-radius:7px;cursor:pointer;color:var(--text-primary);font-family:inherit;font-size:13px;text-align:left}.item[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.label[_ngcontent-%COMP%]{flex:1}.sub[_ngcontent-%COMP%]{font-size:10.5px;color:var(--text-muted)}kbd[_ngcontent-%COMP%]{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);background:var(--bg-canvas);border:1px solid var(--border-subtle);border-radius:4px;padding:2px 6px}"],changeDetection:0})};var Y=class e{_theme=_(V);_destroyRef=_(ee);_router=_($);sidebarCollapsed=T(typeof window<"u"&&window.innerWidth<1100);paletteOpen=T(!1);gChord={armed:!1,t:0};onGlobalKey(n){let t=n.metaKey||n.ctrlKey;if(t&&n.key.toLowerCase()==="k"){n.preventDefault(),this.paletteOpen.update(i=>!i);return}if(n.key==="Escape"){this.paletteOpen.set(!1);return}if(!t&&n.key.toLowerCase()==="g"&&!this.gChord.armed){let i=n.target;if(i&&/input|textarea/i.test(i.tagName))return;this.gChord.armed=!0,this.gChord.t=Date.now(),setTimeout(()=>{this.gChord.armed=!1},600);return}if(this.gChord.armed&&!t){let i=n.key.toLowerCase();i==="d"?this._router.navigate(["/dashboard"]):i==="g"?this._router.navigate(["/graph"]):i==="s"&&this._router.navigate(["/specs"]),this.gChord.armed=!1}}toggleSidebar(){this.sidebarCollapsed.update(n=>!n)}onOpenPalette(){this.paletteOpen.set(!0)}onClosePalette(){this.paletteOpen.set(!1)}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=h({type:e,selectors:[["app-root"]],hostBindings:function(t,i){t&1&&C("keydown",function(b){return i.onGlobalKey(b)},ie)},decls:8,vars:2,consts:[[1,"shell"],[3,"collapsed"],[1,"main"],[3,"toggleSidebar","openPalette"],[1,"page-frame"],[3,"close","open"]],template:function(t,i){t&1&&(a(0,"div",0),c(1,"app-sidebar",1),a(2,"div",2),c(3,"app-status-strip"),a(4,"app-top-bar",3),C("toggleSidebar",function(){return i.toggleSidebar()})("openPalette",function(){return i.onOpenPalette()}),r(),a(5,"div",4),c(6,"router-outlet"),r()(),a(7,"app-command-palette",5),C("close",function(){return i.onClosePalette()}),r()()),t&2&&(o(),f("collapsed",i.sidebarCollapsed()),o(6),f("open",i.paletteOpen()))},dependencies:[se,q,K,U,Q],styles:["[_nghost-%COMP%]{display:block;height:100%}.shell[_ngcontent-%COMP%]{display:flex;height:100vh;background:var(--bg-canvas);color:var(--text-primary);font-family:var(--font-ui);font-size:var(--fs-base)}.main[_ngcontent-%COMP%]{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden}.page-frame[_ngcontent-%COMP%]{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--bg-canvas)}"],changeDetection:0})};le(Y,fe).catch(e=>console.error(e));typeof navigator<"u"&&"serviceWorker"in navigator&&window.addEventListener("load",()=>{navigator.serviceWorker.register("sw.js").catch(e=>{console.warn("[specship] offline service worker registration failed",e)})});