git-history-ui 2.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +75 -13
  3. package/build/frontend/chunk-36NFLS3P.js +1 -0
  4. package/build/frontend/chunk-3BORCJKI.js +2 -0
  5. package/build/frontend/chunk-3FFYILBL.js +1 -0
  6. package/build/frontend/chunk-B7ZU76GM.js +1 -0
  7. package/build/frontend/chunk-HQV5AEMT.js +3 -0
  8. package/build/frontend/chunk-ITIFFECZ.js +1 -0
  9. package/build/frontend/chunk-N7UHDKJ7.js +1 -0
  10. package/build/frontend/chunk-NUMLL3OZ.js +1 -0
  11. package/build/frontend/chunk-QUDEGJKI.js +1 -0
  12. package/build/frontend/chunk-R33W2FKN.js +7 -0
  13. package/build/frontend/chunk-TQE5NWMZ.js +5 -0
  14. package/build/frontend/chunk-TQVUSJBM.js +1 -0
  15. package/build/frontend/chunk-YSTG766K.js +1 -0
  16. package/build/frontend/index.html +2 -2
  17. package/build/frontend/main-6LGPRO6C.js +1 -0
  18. package/build/frontend/styles-BOEVKAI2.css +1 -0
  19. package/dist/backend/aggregations.d.ts +57 -0
  20. package/dist/backend/aggregations.js +130 -0
  21. package/dist/backend/aggregations.js.map +1 -0
  22. package/dist/backend/annotations.d.ts +30 -0
  23. package/dist/backend/annotations.js +90 -0
  24. package/dist/backend/annotations.js.map +1 -0
  25. package/dist/backend/gitService.d.ts +14 -0
  26. package/dist/backend/gitService.js +81 -0
  27. package/dist/backend/gitService.js.map +1 -1
  28. package/dist/backend/grouping/prGrouping.d.ts +39 -0
  29. package/dist/backend/grouping/prGrouping.js +210 -0
  30. package/dist/backend/grouping/prGrouping.js.map +1 -0
  31. package/dist/backend/impact.d.ts +16 -0
  32. package/dist/backend/impact.js +66 -0
  33. package/dist/backend/impact.js.map +1 -0
  34. package/dist/backend/insights.d.ts +9 -0
  35. package/dist/backend/insights.js +44 -0
  36. package/dist/backend/insights.js.map +1 -0
  37. package/dist/backend/llm/anthropicProvider.d.ts +13 -0
  38. package/dist/backend/llm/anthropicProvider.js +90 -0
  39. package/dist/backend/llm/anthropicProvider.js.map +1 -0
  40. package/dist/backend/llm/heuristicProvider.d.ts +15 -0
  41. package/dist/backend/llm/heuristicProvider.js +127 -0
  42. package/dist/backend/llm/heuristicProvider.js.map +1 -0
  43. package/dist/backend/llm/index.d.ts +17 -0
  44. package/dist/backend/llm/index.js +66 -0
  45. package/dist/backend/llm/index.js.map +1 -0
  46. package/dist/backend/llm/openaiProvider.d.ts +13 -0
  47. package/dist/backend/llm/openaiProvider.js +100 -0
  48. package/dist/backend/llm/openaiProvider.js.map +1 -0
  49. package/dist/backend/llm/types.d.ts +32 -0
  50. package/dist/backend/llm/types.js +3 -0
  51. package/dist/backend/llm/types.js.map +1 -0
  52. package/dist/backend/search/datePhrase.d.ts +20 -0
  53. package/dist/backend/search/datePhrase.js +113 -0
  54. package/dist/backend/search/datePhrase.js.map +1 -0
  55. package/dist/backend/search/nlSearch.d.ts +39 -0
  56. package/dist/backend/search/nlSearch.js +90 -0
  57. package/dist/backend/search/nlSearch.js.map +1 -0
  58. package/dist/backend/server.d.ts +3 -0
  59. package/dist/backend/server.js +137 -2
  60. package/dist/backend/server.js.map +1 -1
  61. package/dist/backend/snapshot.d.ts +16 -0
  62. package/dist/backend/snapshot.js +42 -0
  63. package/dist/backend/snapshot.js.map +1 -0
  64. package/docs/launch.md +66 -0
  65. package/package.json +12 -3
  66. package/build/frontend/main-VDZAFLAV.js +0 -11
  67. package/build/frontend/styles-CO6MLMTR.css +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,104 @@ All notable changes to this project are documented in this file.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
5
5
  this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.1.0] - 2026-05-02
8
+
9
+ Visual polish on top of v3.0 — d3 visualizations replace the placeholder
10
+ HTML/CSS charts, and the split diff view gets the niceties developers expect
11
+ from elite diff tools.
12
+
13
+ ### Added
14
+
15
+ - **d3 force-directed impact graph.** New `ImpactGraphComponent` renders
16
+ changed files, their JS/TS imports, and module groupings as a draggable
17
+ force-directed graph in the commit detail panel.
18
+ - **d3 hotspots treemap.** The Insights dashboard now renders hotspots as a
19
+ proportional treemap (cell size = commit count, color = saturation).
20
+ Cells are clickable and open the file's history view.
21
+ - **d3 churn chart.** Insights "Churn over time" upgraded from CSS bars to a
22
+ proper time-axis area chart with a smooth curve.
23
+ - **Side-by-side diff scroll-sync.** The split diff view now mirrors scroll
24
+ position and direction across both panes.
25
+ - **Intra-line word highlighting.** When a `del`/`add` line pair is shown
26
+ side-by-side, only the changed words are accented (not the whole line) —
27
+ uses an LCS word-tokenizer with a 20k character cap to stay snappy.
28
+
29
+ ### Changed
30
+
31
+ - The previous "list of dependency-ripple lines" inside the impact card has
32
+ been replaced with the interactive graph; the related-commits and modules
33
+ lists stay below it for screen-reader / keyboard users.
34
+
35
+ ## [3.0.0] - 2026-05-02
36
+
37
+ This release transforms `git-history-ui` from a "pretty git log" into a
38
+ **Git Intelligence** platform. Everything is still zero-config; new AI
39
+ features are opt-in via your own API key.
40
+
41
+ ### Added — Headline features
42
+
43
+ - **Natural-language search.** A new `/api/search` endpoint and toggle in the
44
+ toolbar accept queries like "login bug last month" or "payments by alice".
45
+ A built-in heuristic intent parser handles dates, authors, and synonym
46
+ expansion (`fix` → `bug, hotfix, patch`, `auth` → `login, oauth, jwt`,
47
+ etc.). When `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` is set, results are
48
+ semantically re-ranked. The interpreted query is shown as removable chips
49
+ so you always know how the tool understood your request.
50
+ - **PR / feature grouping.** A new `/api/groups` endpoint clusters commits by
51
+ GitHub merge commits (`Merge pull request #N from ...`), squash merges
52
+ (`subject (#N)`), and Conventional Commits scope (`feat(auth):`,
53
+ `fix(payments):`). Toggle the flat / grouped view from the toolbar.
54
+ Optional `GITHUB_TOKEN` enriches groups with PR title, author, labels.
55
+ - **Time travel.** New `/timeline` route with a horizontal slider. Drag to any
56
+ point in history to see HEAD, branch, and tag positions at that moment, and
57
+ a live diff against current HEAD.
58
+ - **File history & blame.** Click any file in a commit's Files panel to open
59
+ `/file/:path` — full history of every commit that touched the file, plus a
60
+ tabbed blame view (the existing `/api/blame` endpoint now has a UI).
61
+ - **Commit impact analysis.** New `/api/impact/:hash` endpoint shows files
62
+ touched, modules affected, dependency ripple parsed from JS/TS imports, and
63
+ related commits that touched the same files.
64
+ - **Insights dashboard.** New `/insights` route with top contributors,
65
+ hotspots (most-changed files), churn over time, and a heuristic risky-files
66
+ score that blends churn × contributor diversity × recency.
67
+ - **AI extras (optional).** "Explain this change" on commits and "Summarize"
68
+ on diffs, both powered by `LlmService.summarize()`. Disabled with a clear
69
+ tooltip when no provider key is configured.
70
+ - **Local-first annotations.** Add notes to any commit; stored at
71
+ `~/.git-history-ui/<repo-hash>/annotations.json`. New endpoints under
72
+ `/api/annotations/:hash`.
73
+ - **Shareable links.** A "Share" button on the commit detail copies a URL
74
+ with the commit hash encoded.
75
+ - **Collapse unchanged blocks.** Diffs default to ±3 lines of context with an
76
+ Expand button to reveal the rest.
77
+
78
+ ### Added — Plumbing
79
+
80
+ - New `LlmService` abstraction (`src/backend/llm/`) with three providers:
81
+ `HeuristicProvider` (always-on, pure heuristic), `AnthropicProvider`
82
+ (claude-3-5-haiku by default), and `OpenAiProvider` (gpt-4o-mini by
83
+ default). Provider selected via `GHUI_LLM_PROVIDER` + key auto-detection.
84
+ - New aggregations module (`src/backend/aggregations.ts`) with pure functions
85
+ for contributor stats, file churn, churn-by-day, and risky-file scoring.
86
+ - Real Angular routing replaces the previous single-page layout. New routes:
87
+ `/`, `/timeline`, `/file/:path`, `/insights`. Components are lazy-loaded.
88
+ - Backend POST/DELETE endpoints (annotations, summarize, explain). CORS
89
+ updated to allow these methods on local origins only.
90
+
91
+ ### Changed
92
+
93
+ - Toolbar adds nav links (History / Timeline / Insights), a search-mode
94
+ toggle (literal vs. AI-flavored), and a flat/grouped toggle.
95
+ - Commit detail panel now has Explain / Show Impact / Share action buttons,
96
+ per-file "View history" buttons, an Impact card, and an Annotations
97
+ collapsible panel.
98
+ - Diff viewer adds Collapse / Summarize buttons.
99
+
100
+ ### Tests
101
+
102
+ - 24 new tests for the LLM heuristic provider, NL query parser, aggregation
103
+ functions, and PR grouping. Total suite: 43 tests.
104
+
7
105
  ## [2.0.3] - 2026-05-01
8
106
 
9
107
  ### Added
package/README.md CHANGED
@@ -7,8 +7,11 @@
7
7
  [![GitHub stars](https://img.shields.io/github/stars/beingmartinbmc/git-history-ui.svg)](https://github.com/beingmartinbmc/git-history-ui)
8
8
  [![GitHub issues](https://img.shields.io/github/issues/beingmartinbmc/git-history-ui.svg)](https://github.com/beingmartinbmc/git-history-ui/issues)
9
9
 
10
- A fast, zero-setup web UI to explore your git history visually. Run it in any
11
- repo and inspect branches, commits, and diffs interactively in your browser.
10
+ **Git Intelligence in your browser.** A fast, zero-setup web UI that turns your
11
+ git history from a flat log into a navigable narrative — with natural-language
12
+ search, PR/feature grouping, time-travel snapshots, file-level history, blame,
13
+ commit impact analysis, and an insights dashboard. Optional AI integration
14
+ (Anthropic / OpenAI) supercharges search and explanations.
12
15
 
13
16
  ## 👀 Preview
14
17
 
@@ -36,20 +39,49 @@ No installs. No config. Just your commits, visualized.
36
39
  - Desktop clients can be heavy when you just want a quick read on one repo.
37
40
  - `git-history-ui` gives you a fast, local, visual way to explore history from any git repository.
38
41
 
39
- ## ✨ Features
40
-
41
- - **Canvas-based commit graph** - Branch lanes, hover states, selected commits, and branch/tag pills.
42
- - **Real-time filtering** - Filter by author, date range, commit text, or file path.
43
- - **Diff-first commit review** - Move from graph to commit list to unified or split diffs without leaving the browser.
44
- - **Local by default** - Runs against the repo on your machine, including unpushed commits.
45
- - **Dark/light/system mode** - Toggle manually or follow your OS preference.
46
- - **Zero setup** - Unlike GitKraken or SourceTree, it runs instantly with `npx`.
42
+ ## ✨ What's new in v3 — "Git Intelligence"
43
+
44
+ - **Natural-language search** Ask "login bug last month" or "payments by alice".
45
+ A built-in heuristic intent parser extracts dates, authors, and keyword
46
+ synonyms; if you set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`, it adds
47
+ semantic re-ranking on top.
48
+ - **PR & feature grouping** Switch the commit list to "Grouped" mode to see
49
+ commits clustered by pull request (GitHub merge & squash patterns) or
50
+ Conventional Commits scope (`feat(auth):`, `fix(payments):`). Optional
51
+ GitHub PR enrichment when `GITHUB_TOKEN` is set.
52
+ - **Time travel** — A horizontal timeline slider that shows the repo state
53
+ (HEAD, branches, tags) at any point and computes a live diff vs HEAD.
54
+ - **File history & blame** — Click any file in a commit's Files panel to see
55
+ every commit that touched it, with a tabbed blame view powered by
56
+ `highlight.js`.
57
+ - **Commit impact analysis** — One click reveals files touched, modules
58
+ affected, dependency ripple (parsed from JS/TS imports), and other commits
59
+ that touched the same files.
60
+ - **Insights dashboard** — Top contributors, hotspots, churn over time, and
61
+ a heuristic risky-files score for code reviewers and tech leads.
62
+ - **AI extras (optional)** — "Explain this change" on commits and "Summarize"
63
+ on diffs, both gated on a configured API key.
64
+ - **Local-first annotations** — Add notes to commits stored at
65
+ `~/.git-history-ui/<repo>/annotations.json`.
66
+
67
+ Plus everything from v2:
68
+
69
+ - **Canvas commit graph** with branch lanes, ref pills, hover/selected states
70
+ - **Real-time filtering** by author, date, text, file path
71
+ - **Unified & split diffs** with `highlight.js`, plus collapse-unchanged blocks
72
+ - **Dark / light / system theme** with single-click toggle
73
+ - **Zero setup** — `npx git-history-ui@latest`, that's it
47
74
 
48
75
  ## ⚖️ How it compares
49
76
 
50
- - **vs GitHub UI**: shows local branches and unpushed commits because it runs inside your repository.
51
- - **vs `tig` or `git log`**: gives you visual lanes, filters, and browser-based diffs instead of a terminal-only view.
52
- - **vs desktop Git clients**: starts on demand with no workspace setup, account login, or project import.
77
+ - **vs GitHub UI**: NL search and PR grouping work *with* your unpushed
78
+ commits, and time travel + impact analysis aren't on GitHub at all.
79
+ - **vs `tig` or `git log`**: visual lanes, browser diffs, AI explanations,
80
+ insights dashboard.
81
+ - **vs desktop clients (GitKraken, SourceTree, Fork)**: starts on demand with
82
+ no project import, no account, no native install. AI features are
83
+ pay-as-you-go on *your* key — nothing about your code leaves your machine
84
+ unless you opt in.
53
85
 
54
86
  ## 📖 Usage
55
87
 
@@ -77,6 +109,36 @@ npx git-history-ui@latest --no-open
77
109
  npx git-history-ui@latest --help
78
110
  ```
79
111
 
112
+ ### Optional: bring your own AI key
113
+
114
+ Natural-language search and "Explain change" / "Summarize diff" actions all
115
+ work without an API key (heuristic mode). Set one of these to upgrade them
116
+ with a real model — your code never leaves the host running git-history-ui
117
+ except for the prompt you explicitly trigger:
118
+
119
+ ```bash
120
+ # Anthropic (recommended; uses claude-3-5-haiku by default)
121
+ export ANTHROPIC_API_KEY=sk-ant-...
122
+
123
+ # Or OpenAI (uses gpt-4o-mini by default)
124
+ export OPENAI_API_KEY=sk-...
125
+
126
+ # Force a specific provider when both are set
127
+ export GHUI_LLM_PROVIDER=anthropic # or openai, or heuristic
128
+
129
+ npx git-history-ui@latest
130
+ ```
131
+
132
+ ### Optional: GitHub PR enrichment
133
+
134
+ Set `GITHUB_TOKEN` (a fine-grained PAT with read access to your repo) to
135
+ hydrate the grouped view with PR titles, authors, and labels:
136
+
137
+ ```bash
138
+ export GITHUB_TOKEN=ghp_...
139
+ npx git-history-ui@latest
140
+ ```
141
+
80
142
  ## 🏭 Production
81
143
 
82
144
  ### Build for Production
@@ -0,0 +1 @@
1
+ import{$ as a,Rb as v,Sb as l,Tb as m,U as f,W as h,ja as y,ka as b,l as g,la as d,oa as i}from"./chunk-TQE5NWMZ.js";function U(n,e){let t=e?.injector??a(y),s=new g(1),u=m(()=>{let r;try{r=n()}catch(c){v(()=>s.error(c));return}v(()=>s.next(r))},{injector:t,manualCleanup:!0});return t.get(d).onDestroy(()=>{u.destroy(),s.complete()}),s.asObservable()}function L(n,e){let s=!e?.manualCleanup?e?.injector?.get(d)??a(d):null,u=w(e?.equal),r;e?.requireSync?r=i({kind:0},{equal:u}):r=i({kind:1,value:e?.initialValue},{equal:u});let c,p=n.subscribe({next:o=>r.set({kind:1,value:o}),error:o=>{r.set({kind:2,error:o}),c?.()},complete:()=>{c?.()}});if(e?.requireSync&&r().kind===0)throw new f(601,!1);return c=s?.onDestroy(p.unsubscribe.bind(p)),l(()=>{let o=r();switch(o.kind){case 1:return o.value;case 2:throw o.error;case 0:throw new f(601,!1)}},{equal:e?.equal})}function w(n=Object.is){return(e,t)=>e.kind===1&&t.kind===1&&n(e.value,t.value)}var k="ghui:theme",D=class n{doc=a(b);mediaQuery=null;preference=i(this.read());systemDark=i(!1);resolved=l(()=>{let e=this.preference();return e==="system"?this.systemDark()?"dark":"light":e});constructor(){typeof window<"u"&&window.matchMedia&&(this.mediaQuery=window.matchMedia("(prefers-color-scheme: dark)"),this.systemDark.set(this.mediaQuery.matches),this.mediaQuery.addEventListener("change",e=>this.systemDark.set(e.matches))),m(()=>{let e=this.resolved(),t=this.doc.documentElement;t.classList.toggle("dark",e==="dark"),t.dataset.theme=e,this.doc.body?.classList?.toggle("dark",e==="dark")})}setPreference(e){this.preference.set(e);try{localStorage.setItem(k,e)}catch{}}cycle(){let t=this.resolved()==="light"?"dark":"light";this.setPreference(t)}read(){try{let e=localStorage.getItem(k);if(e==="light"||e==="dark"||e==="system")return e}catch{}return"system"}static \u0275fac=function(t){return new(t||n)};static \u0275prov=h({token:n,factory:n.\u0275fac,providedIn:"root"})};export{U as a,L as b,D as c};
@@ -0,0 +1,2 @@
1
+ import{a as Zt,b as Qt,c as an}from"./chunk-36NFLS3P.js";import{a as Kt}from"./chunk-R33W2FKN.js";import{a as Ht,b as Wt,c as $t,h as Ut}from"./chunk-YSTG766K.js";import{a as Yt}from"./chunk-NUMLL3OZ.js";import{a as G}from"./chunk-N7UHDKJ7.js";import{e as Xt,f as Jt,g as en,h as tn,i as nn,j as on,k as rn}from"./chunk-QUDEGJKI.js";import{d as Gt}from"./chunk-3FFYILBL.js";import{a as qt}from"./chunk-ITIFFECZ.js";import{$ as f,$a as Dt,Ab as _,B as mt,Bb as B,C as Ce,Cb as De,D as pt,Db as Vt,Ea as Se,Fb as qe,Ga as l,Gb as Ke,Hb as Xe,I as ft,Ia as yt,Ja as Qe,Jb as ce,Ka as St,Lb as de,Ma as Mt,Mb as me,N as gt,Ob as ke,P as ut,Pa as D,Q as Ze,Qa as ae,R as xe,Ra as J,Rb as Ft,S as be,Sa as Ye,Sb as te,Ta as v,Tb as j,V as ht,Va as Ot,W as V,Wa as se,Wb as zt,X as oe,Xa as Et,Xb as Lt,Z,Zb as At,ab as le,db as u,dc as $,e as st,ea as M,eb as s,ec as R,fa as O,fb as a,fc as Pe,g as Ue,ga as _t,gb as b,hb as kt,hc as T,i as lt,ib as Pt,j as N,ja as vt,jb as It,ka as we,kb as Me,la as Ct,lb as Oe,lc as Bt,m as ct,mb as F,mc as jt,n as dt,nb as w,nc as Ie,oa as x,ob as C,pb as Rt,q as W,qa as xt,qb as Tt,ra as bt,s as ve,sa as ye,sb as Q,tb as Y,ub as q,va as re,vb as Ee,wb as ee,xb as z,yb as Nt,za as wt,zb as c}from"./chunk-TQE5NWMZ.js";var Re=class i{http=f(Ie);base="/api";list(n){return this.http.get(`${this.base}/annotations/${n}`)}add(n,e,t){return this.http.post(`${this.base}/annotations/${n}`,{author:e,body:t})}remove(n,e){return this.http.delete(`${this.base}/annotations/${n}/${e}`)}static \u0275fac=function(e){return new(e||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})};var xn=["svg"];function bn(i,n){i&1&&(s(0,"div",8),c(1,"No graph data \u2014 changed files have no detectable internal imports."),a())}var Te=class i{impact=null;svgRef;simulation=null;hasData=!1;ngAfterViewInit(){this.render()}ngOnChanges(){this.render()}ngOnDestroy(){this.simulation?.stop()}render(){if(!this.svgRef)return;let n=this.svgRef.nativeElement,e=Xt(n);e.selectAll("*").remove();let t=this.impact;if(!t||t.dependencyRipple.length===0&&t.modules.length===0){this.hasData=!1;return}this.hasData=!0;let o=n.clientWidth||600,r=280,p=new Map,g=(m,I,P)=>{let y=p.get(m);return y||(y={id:m,group:I,label:P},p.set(m,y)),y};for(let m of t.files)g(`f:${m}`,"changed",Je(m));for(let m of t.modules)g(`m:${m}`,"module",m);for(let m of t.dependencyRipple)g(`f:${m.from}`,"changed",Je(m.from)),g(`f:${m.to}`,"imported",Je(m.to));let d=[];for(let m of t.dependencyRipple)d.push({source:`f:${m.from}`,target:`f:${m.to}`,type:"imports"});for(let m of t.files){let I=wn(m);t.modules.includes(I)&&d.push({source:`f:${m}`,target:`m:${I}`,type:"in-module"})}let h=Array.from(p.values()),E=e.append("g").attr("stroke","var(--border-strong)").attr("stroke-opacity",.5).selectAll("line").data(d).join("line").attr("stroke-dasharray",m=>m.type==="in-module"?"2,3":null),k=e.append("g").selectAll("circle").data(h).join("circle").attr("r",m=>m.group==="module"?7:5).attr("fill",m=>m.group==="changed"?"var(--accent)":m.group==="imported"?"#f59e0b":"#8b5cf6").attr("stroke","var(--bg-app)").attr("stroke-width",1.5).call(U());k.append("title").text(m=>m.id.slice(2));let _e=e.append("g").selectAll("text").data(h).join("text").attr("class","node-label").attr("dx",8).attr("dy",3).text(m=>m.label);this.simulation?.stop(),this.simulation=on(h).force("link",nn(d).id(m=>m.id).distance(60).strength(.4)).force("charge",rn().strength(-160)).force("center",en(o/2,r/2)).force("collide",tn(14)).on("tick",()=>{E.attr("x1",m=>m.source.x??0).attr("y1",m=>m.source.y??0).attr("x2",m=>m.target.x??0).attr("y2",m=>m.target.y??0),k.attr("cx",m=>m.x??0).attr("cy",m=>m.y??0),_e.attr("x",m=>m.x??0).attr("y",m=>m.y??0)});function U(){function m(y,S){y.active||window.__impactSim?.alphaTarget?.(.3).restart(),S.fx=S.x,S.fy=S.y}function I(y,S){S.fx=y.x,S.fy=y.y}function P(y,S){y.active||window.__impactSim?.alphaTarget?.(0),S.fx=null,S.fy=null}return Jt().on("start",m).on("drag",I).on("end",P)}window.__impactSim=this.simulation}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-impact-graph"]],viewQuery:function(e,t){if(e&1&&Q(xn,7),e&2){let o;Y(o=q())&&(t.svgRef=o.first)}},inputs:{impact:"impact"},features:[ye],decls:11,vars:1,consts:[["svg",""],[1,"legend"],[1,"dot","dot-changed"],[1,"dot","dot-imported"],[1,"dot","dot-module"],[1,"canvas-wrap"],["width","100%","height","280","aria-label","Commit impact graph"],["class","empty",4,"ngIf"],[1,"empty"]],template:function(e,t){e&1&&(s(0,"div",1),b(1,"span",2),c(2," changed "),b(3,"span",3),c(4," imports "),b(5,"span",4),c(6," module "),a(),s(7,"div",5),_t(),b(8,"svg",6,0),v(10,bn,2,0,"div",7),a()),e&2&&(l(10),u("ngIf",!t.hasData))},dependencies:[T,R],styles:["[_nghost-%COMP%]{display:block}.legend[_ngcontent-%COMP%]{display:flex;gap:.75rem;align-items:center;font-size:11px;color:var(--fg-muted);margin-bottom:.4rem}.dot[_ngcontent-%COMP%]{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px}.dot-changed[_ngcontent-%COMP%]{background:var(--accent)}.dot-imported[_ngcontent-%COMP%]{background:#f59e0b}.dot-module[_ngcontent-%COMP%]{background:#8b5cf6}.canvas-wrap[_ngcontent-%COMP%]{position:relative;background:var(--bg-app);border-radius:var(--radius-sm);border:1px solid var(--border-soft);overflow:hidden}.empty[_ngcontent-%COMP%]{position:absolute;inset:0;display:grid;place-items:center;color:var(--fg-muted);font-size:11px;pointer-events:none}svg[_ngcontent-%COMP%] [_ngcontent-%COMP%]:global(.node-label){font-size:9px;fill:var(--fg-secondary);font-family:var(--font-mono, monospace);pointer-events:none}"],changeDetection:0})};function Je(i){let n=i.split("/");return n[n.length-1]}function wn(i){let n=i.split("/");return n.length===1?"(root)":n.slice(0,Math.min(n.length-1,3)).join("/")}function yn(i,n){if(i&1&&(s(0,"span",36),c(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Sn(i,n){if(i&1&&(s(0,"span",37),c(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Mn(i,n){i&1&&(s(0,"span",38),c(1,"merge"),a())}function On(i,n){if(i&1&&(s(0,"pre",39),c(1),a()),i&2){let e=C().ngIf;l(),_(e.body)}}function En(i,n){if(i&1){let e=F();s(0,"div",40)(1,"span",41),c(2,"AI"),a(),s(3,"span",42),c(4),a(),s(5,"button",43),w("click",function(){M(e);let o=C(2);return O(o.explanation.set(null))}),c(6,"\xD7"),a()()}if(i&2){let e=n.ngIf;l(4),_(e)}}function Dn(i,n){if(i&1&&(s(0,"div",44),c(1),a()),i&2){let e=n.ngIf;l(),_(e)}}function kn(i,n){if(i&1&&(s(0,"li"),c(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Pn(i,n){if(i&1){let e=F();s(0,"li",28),w("click",function(){let o=M(e).$implicit,r=C(3);return O(r.state.selectHash(o.hash))}),s(1,"code"),c(2),a(),s(3,"span"),c(4),a()()}if(i&2){let e=n.$implicit;l(2),_(e.hash.slice(0,7)),l(2),_(e.subject)}}function In(i,n){if(i&1&&(s(0,"div",45)(1,"div",46)(2,"span"),c(3,"Impact"),a(),s(4,"span",47),c(5),a()(),b(6,"app-impact-graph",48),s(7,"div",49)(8,"div")(9,"h4"),c(10,"Modules"),a(),s(11,"ul",50),v(12,kn,2,1,"li",51),a()(),s(13,"div")(14,"h4"),c(15,"Related commits"),a(),s(16,"ul",52),v(17,Pn,5,2,"li",53),a()()()()),i&2){let e=n.ngIf;l(5),Vt(" ",e.files.length," files \xB7 ",e.modules.length," modules \xB7 ",e.relatedCommits.length," related commits "),l(),u("impact",e),l(6),u("ngForOf",e.modules),l(5),u("ngForOf",e.relatedCommits)}}function Rn(i,n){if(i&1&&(s(0,"span",54),c(1),a()),i&2){let e=C(2);l(),_(e.files().length)}}function Tn(i,n){if(i&1&&(s(0,"span",63),c(1),a()),i&2){let e=C().$implicit;l(),B("+",e.additions)}}function Nn(i,n){if(i&1&&(s(0,"span",64),c(1),a()),i&2){let e=C().$implicit;l(),B("\u2212",e.deletions)}}function Vn(i,n){if(i&1){let e=F();s(0,"div",55)(1,"button",56),w("click",function(){let o=M(e).$implicit,r=C(2);return O(r.selectFile(o))}),b(2,"span",57),s(3,"span",58),c(4),a(),s(5,"span",59),v(6,Tn,2,1,"span",60)(7,Nn,2,1,"span",61),a()(),s(8,"button",62),w("click",function(){let o=M(e).$implicit,r=C(2);return O(r.openFileHistory(o.file))}),c(9," \u23F1 "),a()()}if(i&2){let e,t=n.$implicit,o=C(2);l(),z("selected",t.file===((e=o.activeFile())==null?null:e.file)),l(),le("data-status",t.status),l(),u("title",t.file),l(),_(t.file),l(2),u("ngIf",t.additions),l(),u("ngIf",t.deletions)}}function Fn(i,n){i&1&&(s(0,"div",65),c(1," No files changed. "),a())}function zn(i,n){i&1&&(s(0,"div",65),c(1,"Loading\u2026"),a())}function Ln(i,n){if(i&1){let e=F();s(0,"div",66)(1,"div",67)(2,"strong"),c(3),a(),s(4,"span",68),c(5),de(6,"date"),a(),s(7,"button",69),w("click",function(){let o=M(e).$implicit,r=C(2);return O(r.deleteComment(o.id))}),c(8,"\xD7"),a()(),s(9,"p",70),c(10),a()()}if(i&2){let e=n.$implicit;l(3),_(e.author),l(2),_(me(6,3,e.createdAt,"short")),l(5),_(e.body)}}function An(i,n){if(i&1){let e=F();Me(0),s(1,"header",2)(2,"div",3)(3,"span",4),c(4),a(),s(5,"span",5),v(6,yn,2,1,"span",6)(7,Sn,2,1,"span",7)(8,Mn,2,0,"span",8),a()(),s(9,"h2",9),c(10),a(),s(11,"div",10)(12,"span"),c(13),a(),s(14,"span",11),c(15,"\u2022"),a(),s(16,"span"),c(17),de(18,"date"),a()(),v(19,On,2,1,"pre",12),s(20,"div",13)(21,"button",14),w("click",function(){M(e);let o=C();return O(o.onExplain())}),c(22),a(),s(23,"button",14),w("click",function(){M(e);let o=C();return O(o.onLoadImpact())}),c(24),a(),s(25,"button",15),w("click",function(){M(e);let o=C();return O(o.copyShareLink())}),c(26),a()(),v(27,En,7,1,"div",16)(28,Dn,2,1,"div",17),a(),v(29,In,18,6,"div",18),s(30,"div",19)(31,"aside",20)(32,"div",21)(33,"span"),c(34,"Files"),a(),v(35,Rn,2,1,"span",22),a(),s(36,"div",23),v(37,Vn,10,7,"div",24)(38,Fn,2,0,"div",25)(39,zn,2,0,"div",25),a()(),s(40,"section",26)(41,"details",27)(42,"summary",28),w("click",function(o){M(e);let r=C();return O(r.toggleAnnotations(o))}),c(43),a(),s(44,"div",29),v(45,Ln,11,6,"div",30),s(46,"div",31)(47,"input",32),Xe("ngModelChange",function(o){M(e);let r=C();return Ke(r.commentAuthor,o)||(r.commentAuthor=o),O(o)}),a(),s(48,"textarea",33),Xe("ngModelChange",function(o){M(e);let r=C();return Ke(r.commentDraft,o)||(r.commentDraft=o),O(o)}),a(),s(49,"button",34),w("click",function(){M(e);let o=C();return O(o.addComment())}),c(50,"Post"),a()()()(),b(51,"app-diff-viewer",35),a()(),Oe()}if(i&2){let e=n.ngIf,t=C();l(4),_(e.shortHash),l(2),u("ngForOf",e.tags),l(),u("ngForOf",e.branches),l(),u("ngIf",e.isMerge),l(2),_(e.subject),l(3),De("",e.author," <",e.authorEmail,">"),l(4),_(me(18,29,e.date,"medium")),l(2),u("ngIf",e.body),l(2),u("disabled",t.explaining()),l(),B(" ",t.explaining()?"...":"\u2728 Explain change"," "),l(),u("disabled",t.loadingImpact()),l(),B(" ",t.loadingImpact()?"...":t.impact()?"Refresh impact":"Show impact"," "),l(2),B(" ",t.shareCopied()?"Copied!":"\u{1F517} Share"," "),l(),u("ngIf",t.explanation()),l(),u("ngIf",t.explainError()),l(),u("ngIf",t.impact()),l(6),u("ngIf",t.files().length),l(2),u("ngForOf",t.files())("ngForTrackBy",t.trackByFile),l(),u("ngIf",!t.files().length&&!t.loading()),l(),u("ngIf",t.loading()),l(2),u("open",t.annotationsOpen()),l(2),B(" \u{1F4AC} Notes (",t.comments().length,") "),l(2),u("ngForOf",t.comments()),l(2),qe("ngModel",t.commentAuthor),l(),qe("ngModel",t.commentDraft),l(),u("disabled",!t.commentDraft.trim()),l(2),u("fileInput",t.activeFile())}}function Bn(i,n){i&1&&(s(0,"div",71)(1,"p",72),c(2,"No commit selected"),a(),s(3,"p",73),c(4," Pick a commit from the list, or press "),s(5,"kbd",74),c(6,"\u2318K"),a(),c(7," to open the command palette. "),a()())}var Ne=class i{state=f(G);git=f(Yt);insightsApi=f(qt);annotationsApi=f(Re);router=f(Gt);commit=this.state.selected;impact=x(null);loadingImpact=x(!1);explanation=x(null);explainError=x(null);explaining=x(!1);comments=x([]);annotationsOpen=x(!1);shareCopied=x(!1);commentDraft="";commentAuthor="me";loading=x(!1);files=Qt(Zt(this.commit).pipe(xe(n=>n?(this.loading.set(!0),this.git.getDiff(n.hash).pipe(pt(()=>W([])))):(this.loading.set(!1),W([])))),{initialValue:[]});activeFileIndex=x(0);activeFile=te(()=>{let n=this.files();if(!n.length)return null;let e=Math.min(this.activeFileIndex(),n.length-1);return n[e]});constructor(){j(()=>{this.files(),this.activeFileIndex.set(0),this.loading.set(!1)}),j(()=>{let n=this.commit();if(this.impact.set(null),this.explanation.set(null),this.explainError.set(null),this.shareCopied.set(!1),!n){this.comments.set([]);return}this.annotationsApi.list(n.hash).subscribe({next:e=>this.comments.set(e),error:()=>this.comments.set([])})})}trackByFile(n,e){return e.file}selectFile(n){let e=this.files().findIndex(t=>t.file===n.file);e>=0&&this.activeFileIndex.set(e)}openFileHistory(n){this.router.navigate(["/file",encodeURIComponent(n)])}shortPath(n){if(n.length<=32)return n;let e=n.split("/");return e.length<=2?n:e[0]+"/.../"+e.slice(-2).join("/")}onLoadImpact(){let n=this.commit();n&&(this.loadingImpact.set(!0),this.insightsApi.impact(n.hash).subscribe({next:e=>{this.impact.set(e),this.loadingImpact.set(!1)},error:()=>this.loadingImpact.set(!1)}))}onExplain(){let n=this.commit();!n||this.explaining()||(this.explaining.set(!0),this.explainError.set(null),this.insightsApi.explainCommit(n.hash).subscribe({next:e=>{this.explanation.set(e.summary),this.explaining.set(!1)},error:e=>{this.explainError.set(e?.error?.error??"AI explanation unavailable. Set ANTHROPIC_API_KEY or OPENAI_API_KEY."),this.explaining.set(!1)}}))}copyShareLink(){let n=this.commit();if(!n)return;let e=`${window.location.origin}/?commit=${n.hash}`;navigator.clipboard?.writeText(e).then(()=>{this.shareCopied.set(!0),setTimeout(()=>this.shareCopied.set(!1),1500)}).catch(()=>{this.shareCopied.set(!1)})}toggleAnnotations(n){setTimeout(()=>this.annotationsOpen.set(!this.annotationsOpen()),0)}addComment(){let n=this.commit();!n||!this.commentDraft.trim()||this.annotationsApi.add(n.hash,this.commentAuthor||"anonymous",this.commentDraft.trim()).subscribe({next:e=>{this.comments.set([...this.comments(),e]),this.commentDraft=""}})}deleteComment(n){let e=this.commit();e&&this.annotationsApi.remove(e.hash,n).subscribe({next:()=>this.comments.set(this.comments().filter(t=>t.id!==n))})}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-commit-detail"]],decls:3,vars:2,consts:[["empty",""],[4,"ngIf","ngIfElse"],[1,"head"],[1,"row"],[1,"hash"],[1,"badges"],["class","badge tag",4,"ngFor","ngForOf"],["class","badge branch",4,"ngFor","ngForOf"],["class","badge merge",4,"ngIf"],[1,"subject"],[1,"meta"],[1,"dot"],["class","body",4,"ngIf"],[1,"actions"],[1,"btn","btn-ghost","btn-sm",3,"click","disabled"],[1,"btn","btn-ghost","btn-sm",3,"click"],["class","ai-card",4,"ngIf"],["class","ai-card error",4,"ngIf"],["class","impact-card",4,"ngIf"],[1,"split"],[1,"files"],[1,"files-header"],["class","count",4,"ngIf"],[1,"files-list"],["class","file-row",4,"ngFor","ngForOf","ngForTrackBy"],["class","files-empty",4,"ngIf"],[1,"diff"],[1,"annotations",3,"open"],[3,"click"],[1,"annot-body"],["class","comment",4,"ngFor","ngForOf"],[1,"comment-form"],["placeholder","Your name",1,"input",3,"ngModelChange","ngModel"],["placeholder","Add a note for your team\u2026",1,"input",3,"ngModelChange","ngModel"],[1,"btn",3,"click","disabled"],[3,"fileInput"],[1,"badge","tag"],[1,"badge","branch"],[1,"badge","merge"],[1,"body"],[1,"ai-card"],[1,"ai-pill"],[1,"ai-text"],[1,"btn","btn-ghost","btn-icon","close",3,"click"],[1,"ai-card","error"],[1,"impact-card"],[1,"impact-head"],[1,"impact-meta"],[3,"impact"],[1,"impact-body"],[1,"modules"],[4,"ngFor","ngForOf"],[1,"related"],[3,"click",4,"ngFor","ngForOf"],[1,"count"],[1,"file-row"],[1,"file",3,"click"],[1,"status-dot"],[1,"path",3,"title"],[1,"counts"],["class","add",4,"ngIf"],["class","del",4,"ngIf"],["title","View file history",1,"file-history",3,"click"],[1,"add"],[1,"del"],[1,"files-empty"],[1,"comment"],[1,"comment-head"],[1,"comment-date"],["title","Delete",1,"btn","btn-ghost","btn-icon",3,"click"],[1,"comment-body"],[1,"placeholder"],[1,"title"],[1,"hint"],[1,"kbd"]],template:function(e,t){if(e&1&&v(0,An,52,32,"ng-container",1)(1,Bn,8,0,"ng-template",null,0,ke),e&2){let o=Ee(2);u("ngIf",t.commit())("ngIfElse",o)}},dependencies:[T,$,R,Ut,Ht,Wt,$t,Kt,Te,Pe],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg-app)}.head[_ngcontent-%COMP%]{padding:.85rem 1rem;border-bottom:1px solid var(--border-soft);background:var(--bg-surface)}.row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.4rem}.hash[_ngcontent-%COMP%]{font-family:var(--font-mono);font-size:12px;color:var(--fg-muted);padding:2px 6px;background:var(--bg-surface-2);border:1px solid var(--border-soft);border-radius:4px}.badges[_ngcontent-%COMP%]{display:flex;flex-wrap:wrap;gap:4px}.badge[_ngcontent-%COMP%]{font-size:10px;font-weight:600;padding:2px 6px;border-radius:999px}.badge.tag[_ngcontent-%COMP%]{background:#d9770626;color:var(--warning)}.badge.branch[_ngcontent-%COMP%]{background:var(--accent-soft);color:var(--accent)}.badge.merge[_ngcontent-%COMP%]{background:#8b5cf62e;color:#8b5cf6}.subject[_ngcontent-%COMP%]{font-size:18px;margin:0 0 4px;font-weight:600}.meta[_ngcontent-%COMP%]{display:flex;gap:6px;align-items:center;font-size:12px;color:var(--fg-muted)}.meta[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{opacity:.5}.body[_ngcontent-%COMP%]{white-space:pre-wrap;font-family:var(--font-mono);font-size:12px;color:var(--fg-secondary);background:var(--bg-surface-2);border:1px solid var(--border-soft);border-radius:var(--radius-sm);padding:.5rem .75rem;margin-top:.5rem;max-height:160px;overflow:auto}.split[_ngcontent-%COMP%]{flex:1;display:grid;grid-template-columns:280px 1fr;grid-template-rows:minmax(0,1fr);min-height:0;overflow:hidden}.files[_ngcontent-%COMP%]{display:flex;flex-direction:column;min-height:0;background:var(--bg-surface);border-right:1px solid var(--border-soft)}.files-header[_ngcontent-%COMP%]{display:flex;justify-content:space-between;padding:.5rem .85rem;font-size:12px;color:var(--fg-muted);border-bottom:1px solid var(--border-soft)}.count[_ngcontent-%COMP%]{background:var(--bg-surface-2);padding:0 6px;border-radius:999px;font-size:11px}.files-list[_ngcontent-%COMP%]{overflow:auto;flex:1;min-height:0}.file[_ngcontent-%COMP%]{display:grid;grid-template-columns:10px 1fr auto;gap:.5rem;align-items:center;width:100%;padding:.5rem .85rem;border:0;background:transparent;color:inherit;cursor:pointer;text-align:left;border-bottom:1px solid var(--border-soft);font-size:12px}.file[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.file.selected[_ngcontent-%COMP%]{background:var(--bg-selected)}.file[_ngcontent-%COMP%] .path[_ngcontent-%COMP%]{font-family:var(--font-mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;text-align:left}.status-dot[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;background:var(--accent)}.status-dot[data-status=added][_ngcontent-%COMP%]{background:var(--success)}.status-dot[data-status=deleted][_ngcontent-%COMP%]{background:var(--danger)}.status-dot[data-status=renamed][_ngcontent-%COMP%], .status-dot[data-status=copied][_ngcontent-%COMP%]{background:var(--warning)}.status-dot[data-status=binary][_ngcontent-%COMP%]{background:var(--fg-muted)}.counts[_ngcontent-%COMP%]{display:flex;gap:6px;font-family:var(--font-mono);font-size:11px}.counts[_ngcontent-%COMP%] .add[_ngcontent-%COMP%]{color:var(--success)}.counts[_ngcontent-%COMP%] .del[_ngcontent-%COMP%]{color:var(--danger)}.files-empty[_ngcontent-%COMP%]{padding:1rem;color:var(--fg-muted);font-size:12px;text-align:center}.diff[_ngcontent-%COMP%]{min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden}.placeholder[_ngcontent-%COMP%]{flex:1;display:grid;place-items:center;text-align:center;color:var(--fg-muted)}.placeholder[_ngcontent-%COMP%] .title[_ngcontent-%COMP%]{font-size:16px;margin-bottom:4px;color:var(--fg-secondary)}.placeholder[_ngcontent-%COMP%] .hint[_ngcontent-%COMP%]{font-size:13px}.actions[_ngcontent-%COMP%]{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.6rem}.btn-sm[_ngcontent-%COMP%]{font-size:11px;padding:.3rem .65rem}.ai-card[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:.5rem;padding:.55rem .75rem;margin-top:.5rem;background:color-mix(in oklab,var(--accent) 12%,transparent);border-radius:var(--radius-sm);font-size:12px;color:var(--fg-secondary)}.ai-card.error[_ngcontent-%COMP%]{background:#ef44441f;color:var(--danger)}.ai-pill[_ngcontent-%COMP%]{flex-shrink:0;font-size:10px;font-weight:700;letter-spacing:.04em;background:var(--accent);color:var(--accent-fg);padding:1px 5px;border-radius:4px}.ai-text[_ngcontent-%COMP%]{flex:1;line-height:1.5}.ai-card[_ngcontent-%COMP%] .close[_ngcontent-%COMP%]{font-size:14px;line-height:1;padding:0 6px}.impact-card[_ngcontent-%COMP%]{margin:.6rem 1rem;background:var(--bg-surface);border:1px solid var(--border-soft);border-radius:var(--radius-md);padding:.75rem 1rem}.impact-head[_ngcontent-%COMP%]{display:flex;justify-content:space-between;font-weight:600;margin-bottom:.5rem;font-size:13px}.impact-meta[_ngcontent-%COMP%]{color:var(--fg-muted);font-weight:400;font-size:11px}.impact-body[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:.75rem;font-size:12px}.impact-body[_ngcontent-%COMP%] h4[_ngcontent-%COMP%]{margin:0 0 .4rem;font-size:11px;color:var(--fg-muted);text-transform:uppercase;letter-spacing:.04em}.impact-body[_ngcontent-%COMP%] ul[_ngcontent-%COMP%]{list-style:none;margin:0;padding:0}.impact-body[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{padding:2px 0;word-break:break-all}.modules[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{font-family:var(--font-mono, monospace)}.ripple[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{font-size:11px;color:var(--fg-secondary)}.related[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{cursor:pointer;display:flex;gap:.4rem}.related[_ngcontent-%COMP%] li[_ngcontent-%COMP%]:hover{color:var(--accent)}.related[_ngcontent-%COMP%] code[_ngcontent-%COMP%]{font-family:var(--font-mono, monospace);color:var(--fg-muted);flex-shrink:0}.impact-body[_ngcontent-%COMP%] .muted[_ngcontent-%COMP%]{color:var(--fg-muted);font-style:italic;margin:0;font-size:11px}.file-row[_ngcontent-%COMP%]{display:flex}.file-row[_ngcontent-%COMP%] .file[_ngcontent-%COMP%]{flex:1}.file-history[_ngcontent-%COMP%]{background:transparent;border:0;border-bottom:1px solid var(--border-soft);cursor:pointer;color:var(--fg-muted);padding:0 .6rem;font-size:12px}.file-history[_ngcontent-%COMP%]:hover{background:var(--bg-elevated);color:var(--accent)}.annotations[_ngcontent-%COMP%]{margin:.5rem;background:var(--bg-surface);border:1px solid var(--border-soft);border-radius:var(--radius-sm)}.annotations[_ngcontent-%COMP%] summary[_ngcontent-%COMP%]{padding:.4rem .7rem;cursor:pointer;font-size:12px;color:var(--fg-secondary);-webkit-user-select:none;user-select:none}.annotations[open][_ngcontent-%COMP%] summary[_ngcontent-%COMP%]{border-bottom:1px solid var(--border-soft)}.annot-body[_ngcontent-%COMP%]{padding:.5rem .7rem}.comment[_ngcontent-%COMP%]{padding:.4rem 0;border-bottom:1px dashed var(--border-soft);font-size:12px}.comment[_ngcontent-%COMP%]:last-of-type{border-bottom:0}.comment-head[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.5rem}.comment-date[_ngcontent-%COMP%]{color:var(--fg-muted);font-size:11px;flex:1}.comment-body[_ngcontent-%COMP%]{margin:.2rem 0 0;line-height:1.4;white-space:pre-wrap}.comment-form[_ngcontent-%COMP%]{display:flex;gap:.4rem;flex-direction:column;margin-top:.5rem}.comment-form[_ngcontent-%COMP%] .input[_ngcontent-%COMP%]{width:100%;padding:.35rem .5rem;background:var(--bg-app);border:1px solid var(--border-soft);border-radius:var(--radius-sm);color:var(--fg-primary);font-family:inherit;font-size:12px}.comment-form[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%]{min-height:60px;resize:vertical}.comment-form[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%]{align-self:flex-end}"],changeDetection:0})};var jn=["canvas"],Gn=["scroll"];function Hn(i,n){if(i&1&&b(0,"span",10),i&2){let e=n.$implicit;ee("background",e)}}function Wn(i,n){i&1&&(s(0,"div",11),c(1," No commits to draw. "),a())}var H=34,ge=24,Ve=5.5,et=16,Fe=16,tt=["#4f46e5","#06b6d4","#f59e0b","#ef4444","#10b981","#8b5cf6","#ec4899","#0ea5e9"],ze=class i{state=f(G);theme=f(an);legendColors=tt.slice(0,5);graphSummary=te(()=>{let n=this.state.commits().length,e=this.laneCount;return n?`${n.toLocaleString()} commits across ${e} lane${e===1?"":"s"}`:"Swim-lane visualization"});canvasRef;scrollRef;nodes=[];rowByHash=new Map;laneCount=1;hoverRow=-1;onCanvasClick=n=>this.onClick(n);onCanvasMove=n=>this.onMouseMove(n);onCanvasLeave=()=>this.onMouseLeave();constructor(){j(()=>{this.layout(this.state.commits()),this.draw()}),j(()=>{this.state.selectedHash(),this.theme.resolved(),this.draw()})}ngAfterViewInit(){let n=this.canvasRef.nativeElement;n.addEventListener("click",this.onCanvasClick),n.addEventListener("mousemove",this.onCanvasMove),n.addEventListener("mouseleave",this.onCanvasLeave),this.draw()}ngOnDestroy(){let n=this.canvasRef?.nativeElement;n?.removeEventListener("click",this.onCanvasClick),n?.removeEventListener("mousemove",this.onCanvasMove),n?.removeEventListener("mouseleave",this.onCanvasLeave)}onResize(){this.draw()}layout(n){if(this.nodes=[],this.rowByHash.clear(),this.hoverRow=-1,!n.length){this.laneCount=1;return}let e=[],t=o=>{let r=e.indexOf(o);if(r>=0)return r;let p=e.indexOf(null);return p>=0?(e[p]=o,p):(e.push(o),e.length-1)};for(let o=0;o<n.length;o++){let r=n[o],p=t(r.hash),g={commit:r,row:o,lane:p};this.nodes.push(g),this.rowByHash.set(r.hash,g);let[d,...h]=r.parents;e[p]=d??null;for(let E of h)if(e.indexOf(E)===-1){let k=e.indexOf(null);k>=0?e[k]=E:e.push(E)}for(;e.length&&e[e.length-1]===null;)e.pop()}this.laneCount=Math.max(1,this.nodes.reduce((o,r)=>Math.max(o,r.lane+1),0))}draw(){let n=this.canvasRef?.nativeElement;if(!n)return;let e=this.scrollRef?.nativeElement,t=window.devicePixelRatio||1,o=et*2+this.laneCount*ge,r=Fe*2+Math.max(this.nodes.length,1)*H,p=Math.max(o,e?.clientWidth??o),g=Math.max(r,e?.clientHeight??r);n.width=Math.floor(p*t),n.height=Math.floor(g*t),n.style.width=`${p}px`,n.style.height=`${g}px`;let d=n.getContext("2d"),h=this.readTheme(n);if(d.setTransform(t,0,0,t,0,0),d.clearRect(0,0,p,g),d.fillStyle=h.surface,d.fillRect(0,0,p,g),!this.nodes.length)return;let E=m=>et+m*ge+ge/2,k=m=>Fe+m*H+H/2,_e=m=>tt[m%tt.length],U=this.state.selectedHash();this.drawRows(d,p,E,k,h,U),this.drawGuides(d,k,h),d.lineCap="round",d.lineJoin="round";for(let m of this.nodes)for(let I of m.commit.parents){let P=this.rowByHash.get(I);if(!P)continue;let y=E(m.lane),S=k(m.row),ie=E(P.lane),X=k(P.row);d.strokeStyle=h.shadow,d.lineWidth=4,d.globalAlpha=.35,this.drawEdge(d,y,S,ie,X),d.strokeStyle=_e(P.lane),d.lineWidth=m.commit.isMerge?2.6:2.2,d.globalAlpha=U&&U!==m.commit.hash&&U!==P.commit.hash?.55:.9,this.drawEdge(d,y,S,ie,X),d.globalAlpha=1}for(let m of this.nodes){let I=E(m.lane),P=k(m.row),y=_e(m.lane),S=m.commit.hash===U,ie=m.row===this.hoverRow,X=S?Ve+2:ie?Ve+1:Ve;d.beginPath(),d.arc(I,P,X+3,0,Math.PI*2),d.fillStyle=h.nodeRing,d.fill(),d.beginPath(),d.arc(I,P,X,0,Math.PI*2),d.fillStyle=m.commit.isMerge?h.surface:y,d.fill(),d.lineWidth=m.commit.isMerge||S?2.5:1.75,d.strokeStyle=y,d.stroke(),(S||ie)&&(d.beginPath(),d.arc(I,P,X+5,0,Math.PI*2),d.strokeStyle=y,d.globalAlpha=S?.42:.24,d.lineWidth=2,d.stroke(),d.globalAlpha=1)}}drawRows(n,e,t,o,r,p){for(let g of this.nodes){let d=o(g.row)-H/2;if(g.row%2===1&&(n.fillStyle=r.rowAlt,n.fillRect(0,d,e,H)),(g.row===this.hoverRow||g.commit.hash===p)&&(n.fillStyle=g.commit.hash===p?r.rowSelected:r.rowHover,this.roundRect(n,6,d+3,e-12,H-6,8),n.fill()),g.commit.branches.length||g.commit.tags.length){let h=t(g.lane)+Ve+8;this.drawRefPill(n,h,o(g.row),g.commit,r)}}}drawGuides(n,e,t){n.save(),n.strokeStyle=t.guide,n.lineWidth=1,n.setLineDash([3,5]);for(let o=0;o<this.laneCount;o++){let r=et+o*ge+ge/2;n.beginPath(),n.moveTo(r,Fe/2),n.lineTo(r,e(this.nodes.length-1)+H/2),n.stroke()}n.restore()}drawEdge(n,e,t,o,r){if(n.beginPath(),n.moveTo(e,t),e===o)n.lineTo(o,r);else{let p=t+Math.min(H*.75,Math.max(12,(r-t)*.36));n.bezierCurveTo(e,p,o,p,o,r)}n.stroke()}drawRefPill(n,e,t,o,r){let p=o.tags[0]??o.branches[0];if(!p)return;let g=p.length>16?`${p.slice(0,15)}...`:p;n.font="600 10px ui-sans-serif, system-ui, sans-serif";let d=Math.min(96,n.measureText(g).width+14),h=18;n.fillStyle=o.tags.length?r.warningSoft:r.accentSoft,this.roundRect(n,e,t-h/2,d,h,999),n.fill(),n.fillStyle=o.tags.length?r.warning:r.accent,n.fillText(g,e+7,t+3.5)}onClick(n){let e=this.nodeFromEvent(n);e&&this.state.selectHash(e.commit.hash)}onMouseMove(n){let e=this.nodeFromEvent(n),t=e?.row??-1;t!==this.hoverRow&&(this.hoverRow=t,this.canvasRef.nativeElement.style.cursor=e?"pointer":"default",this.draw())}onMouseLeave(){this.hoverRow!==-1&&(this.hoverRow=-1,this.canvasRef.nativeElement.style.cursor="default",this.draw())}nodeFromEvent(n){let e=this.canvasRef.nativeElement.getBoundingClientRect(),t=n.clientY-e.top,o=Math.floor((t-Fe)/H);return this.nodes[o]}readTheme(n){let e=getComputedStyle(n);return{accent:this.css(e,"--accent","#4f46e5"),accentSoft:this.css(e,"--accent-soft","#eef2ff"),guide:this.css(e,"--graph-guide","rgba(148, 163, 184, 0.28)"),nodeRing:this.css(e,"--graph-node-ring","#ffffff"),rowAlt:this.css(e,"--graph-row-alt","rgba(15, 23, 42, 0.025)"),rowHover:this.css(e,"--graph-row-hover","rgba(79, 70, 229, 0.08)"),rowSelected:this.css(e,"--graph-row-selected","rgba(79, 70, 229, 0.14)"),shadow:this.css(e,"--graph-shadow","rgba(15, 23, 42, 0.12)"),surface:this.css(e,"--bg-surface","#ffffff"),warning:this.css(e,"--warning","#d97706"),warningSoft:"rgba(217, 119, 6, 0.15)"}}css(n,e,t){return n.getPropertyValue(e).trim()||t}roundRect(n,e,t,o,r,p){let g=Math.min(p,o/2,r/2);n.beginPath(),n.moveTo(e+g,t),n.arcTo(e+o,t,e+o,t+r,g),n.arcTo(e+o,t+r,e,t+r,g),n.arcTo(e,t+r,e,t,g),n.arcTo(e,t,e+o,t,g),n.closePath()}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-commit-graph"]],viewQuery:function(e,t){if(e&1&&(Q(jn,7),Q(Gn,7)),e&2){let o;Y(o=q())&&(t.canvasRef=o.first),Y(o=q())&&(t.scrollRef=o.first)}},hostBindings:function(e,t){e&1&&w("resize",function(){return t.onResize()},Se)},decls:13,vars:3,consts:[["scroll",""],["canvas",""],[1,"header"],[1,"title"],[1,"hint"],["aria-hidden","true",1,"legend"],["class","swatch",3,"background",4,"ngFor","ngForOf"],[1,"scroll"],["aria-label","Commit graph","role","img"],["class","empty",4,"ngIf"],[1,"swatch"],[1,"empty"]],template:function(e,t){e&1&&(s(0,"div",2)(1,"div",3)(2,"span"),c(3,"Graph"),a(),s(4,"span",4),c(5),a()(),s(6,"div",5),v(7,Hn,1,2,"span",6),a()(),s(8,"div",7,0),b(10,"canvas",8,1),v(12,Wn,2,0,"div",9),a()),e&2&&(l(5),_(t.graphSummary()),l(2),u("ngForOf",t.legendColors),l(5),u("ngIf",!t.state.commits().length&&!t.state.loading()))},dependencies:[T,$,R],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg-surface);border-right:1px solid var(--border-soft)}.header[_ngcontent-%COMP%]{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.6rem .85rem;border-bottom:1px solid var(--border-soft);font-size:12px;color:var(--fg-muted);background:color-mix(in oklab,var(--bg-surface) 96%,var(--bg-surface-2))}.title[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:1px;min-width:0}.title[_ngcontent-%COMP%] > span[_ngcontent-%COMP%]:first-child{color:var(--fg-primary);font-size:13px;font-weight:600}.hint[_ngcontent-%COMP%]{white-space:nowrap}.legend[_ngcontent-%COMP%]{display:flex;gap:4px;align-items:center;flex:0 0 auto}.swatch[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:999px;box-shadow:0 0 0 2px var(--bg-surface)}.scroll[_ngcontent-%COMP%]{position:relative;flex:1;overflow:auto;min-height:0;background:radial-gradient(circle at 24px 24px,var(--graph-row-alt) 0 1px,transparent 1px 100%),var(--bg-surface);background-size:24px 24px}canvas[_ngcontent-%COMP%]{display:block}.empty[_ngcontent-%COMP%]{position:absolute;inset:0;display:grid;place-items:center;padding:1rem;color:var(--fg-muted);font-size:12px;text-align:center}"],changeDetection:0})};function ue(i,n=0){return $n(i)?Number(i):arguments.length===2?n:0}function $n(i){return!isNaN(parseFloat(i))&&!isNaN(Number(i))}function sn(i){return i instanceof re?i.nativeElement:i}var nt;try{nt=typeof Intl<"u"&&Intl.v8BreakIterator}catch{nt=!1}var Le=(()=>{class i{_platformId=f(wt);isBrowser=this._platformId?Bt(this._platformId):typeof document=="object"&&!!document;EDGE=this.isBrowser&&/(edge)/i.test(navigator.userAgent);TRIDENT=this.isBrowser&&/(msie|trident)/i.test(navigator.userAgent);BLINK=this.isBrowser&&!!(window.chrome||nt)&&typeof CSS<"u"&&!this.EDGE&&!this.TRIDENT;WEBKIT=this.isBrowser&&/AppleWebKit/i.test(navigator.userAgent)&&!this.BLINK&&!this.EDGE&&!this.TRIDENT;IOS=this.isBrowser&&/iPad|iPhone|iPod/.test(navigator.userAgent)&&!("MSStream"in window);FIREFOX=this.isBrowser&&/(firefox|minefield)/i.test(navigator.userAgent);ANDROID=this.isBrowser&&/android/i.test(navigator.userAgent)&&!this.TRIDENT;SAFARI=this.isBrowser&&/safari/i.test(navigator.userAgent)&&this.WEBKIT;constructor(){}static \u0275fac=function(t){return new(t||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();var Un=new Z("cdk-dir-doc",{providedIn:"root",factory:Zn});function Zn(){return f(we)}var Qn=/^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i;function Yn(i){let n=i?.toLowerCase()||"";return n==="auto"&&typeof navigator<"u"&&navigator?.language?Qn.test(navigator.language)?"rtl":"ltr":n==="rtl"?"rtl":"ltr"}var ln=(()=>{class i{get value(){return this.valueSignal()}valueSignal=x("ltr");change=new Ot;constructor(){let e=f(Un,{optional:!0});if(e){let t=e.body?e.body.dir:null,o=e.documentElement?e.documentElement.dir:null;this.valueSignal.set(Yn(t||o||"ltr"))}}ngOnDestroy(){this.change.complete()}static \u0275fac=function(t){return new(t||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();var A=(function(i){return i[i.NORMAL=0]="NORMAL",i[i.NEGATED=1]="NEGATED",i[i.INVERTED=2]="INVERTED",i})(A||{}),Ae,K;function cn(){if(K==null){if(typeof document!="object"||!document||typeof Element!="function"||!Element)return K=!1,K;if(document.documentElement?.style&&"scrollBehavior"in document.documentElement.style)K=!0;else{let i=Element.prototype.scrollTo;i?K=!/\{\s*\[native code\]\s*\}/.test(i.toString()):K=!1}}return K}function ne(){if(typeof document!="object"||!document)return A.NORMAL;if(Ae==null){let i=document.createElement("div"),n=i.style;i.dir="rtl",n.width="1px",n.overflow="auto",n.visibility="hidden",n.pointerEvents="none",n.position="absolute";let e=document.createElement("div"),t=e.style;t.width="2px",t.height="1px",i.appendChild(e),document.body.appendChild(i),Ae=A.NORMAL,i.scrollLeft===0&&(i.scrollLeft=1,Ae=i.scrollLeft===0?A.NEGATED:A.INVERTED),i.remove()}return Ae}var it=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=ae({type:i});static \u0275inj=oe({})}return i})();var Be=class{};function dn(i){return i&&typeof i.connect=="function"&&!(i instanceof lt)}var je=class extends Be{_data;constructor(n){super(),this._data=n}connect(){return ve(this._data)?this._data:W(this._data)}disconnect(){}},he=(function(i){return i[i.REPLACED=0]="REPLACED",i[i.INSERTED=1]="INSERTED",i[i.MOVED=2]="MOVED",i[i.REMOVED=3]="REMOVED",i})(he||{}),ot=new Z("_ViewRepeater"),Ge=class{viewCacheSize=20;_viewCache=[];applyChanges(n,e,t,o,r){n.forEachOperation((p,g,d)=>{let h,E;if(p.previousIndex==null){let k=()=>t(p,g,d);h=this._insertView(k,d,e,o(p)),E=h?he.INSERTED:he.REPLACED}else d==null?(this._detachAndCacheView(g,e),E=he.REMOVED):(h=this._moveView(g,d,e,o(p)),E=he.MOVED);r&&r({context:h?.context,operation:E,record:p})})}detach(){for(let n of this._viewCache)n.destroy();this._viewCache=[]}_insertView(n,e,t,o){let r=this._insertViewFromCache(e,t);if(r){r.context.$implicit=o;return}let p=n();return t.createEmbeddedView(p.templateRef,p.context,p.index)}_detachAndCacheView(n,e){let t=e.detach(n);this._maybeCacheView(t,e)}_moveView(n,e,t,o){let r=t.get(n);return t.move(r,e),r.context.$implicit=o,r}_maybeCacheView(n,e){if(this._viewCache.length<this.viewCacheSize)this._viewCache.push(n);else{let t=e.indexOf(n);t===-1?n.destroy():e.remove(t)}}_insertViewFromCache(n,e){let t=this._viewCache.pop();return t&&e.insert(t,n),t||null}};var qn=["contentWrapper"],Kn=["*"],gn=new Z("VIRTUAL_SCROLL_STRATEGY"),rt=class{_scrolledIndexChange=new N;scrolledIndexChange=this._scrolledIndexChange.pipe(ft());_viewport=null;_itemSize;_minBufferPx;_maxBufferPx;constructor(n,e,t){this._itemSize=n,this._minBufferPx=e,this._maxBufferPx=t}attach(n){this._viewport=n,this._updateTotalContentSize(),this._updateRenderedRange()}detach(){this._scrolledIndexChange.complete(),this._viewport=null}updateItemAndBufferSize(n,e,t){t<e,this._itemSize=n,this._minBufferPx=e,this._maxBufferPx=t,this._updateTotalContentSize(),this._updateRenderedRange()}onContentScrolled(){this._updateRenderedRange()}onDataLengthChanged(){this._updateTotalContentSize(),this._updateRenderedRange()}onContentRendered(){}onRenderedOffsetChanged(){}scrollToIndex(n,e){this._viewport&&this._viewport.scrollToOffset(n*this._itemSize,e)}_updateTotalContentSize(){this._viewport&&this._viewport.setTotalContentSize(this._viewport.getDataLength()*this._itemSize)}_updateRenderedRange(){if(!this._viewport)return;let n=this._viewport.getRenderedRange(),e={start:n.start,end:n.end},t=this._viewport.getViewportSize(),o=this._viewport.getDataLength(),r=this._viewport.measureScrollOffset(),p=this._itemSize>0?r/this._itemSize:0;if(e.end>o){let d=Math.ceil(t/this._itemSize),h=Math.max(0,Math.min(p,o-d));p!=h&&(p=h,r=h*this._itemSize,e.start=Math.floor(p)),e.end=Math.max(0,Math.min(o,e.start+d))}let g=r-e.start*this._itemSize;if(g<this._minBufferPx&&e.start!=0){let d=Math.ceil((this._maxBufferPx-g)/this._itemSize);e.start=Math.max(0,e.start-d),e.end=Math.min(o,Math.ceil(p+(t+this._minBufferPx)/this._itemSize))}else{let d=e.end*this._itemSize-(r+t);if(d<this._minBufferPx&&e.end!=o){let h=Math.ceil((this._maxBufferPx-d)/this._itemSize);h>0&&(e.end=Math.min(o,e.end+h),e.start=Math.max(0,Math.floor(p-this._minBufferPx/this._itemSize)))}}this._viewport.setRenderedRange(e),this._viewport.setRenderedContentOffset(Math.round(this._itemSize*e.start)),this._scrolledIndexChange.next(Math.floor(p))}};function Xn(i){return i._scrollStrategy}var un=(()=>{class i{get itemSize(){return this._itemSize}set itemSize(e){this._itemSize=ue(e)}_itemSize=20;get minBufferPx(){return this._minBufferPx}set minBufferPx(e){this._minBufferPx=ue(e)}_minBufferPx=100;get maxBufferPx(){return this._maxBufferPx}set maxBufferPx(e){this._maxBufferPx=ue(e)}_maxBufferPx=200;_scrollStrategy=new rt(this.itemSize,this.minBufferPx,this.maxBufferPx);ngOnChanges(){this._scrollStrategy.updateItemAndBufferSize(this.itemSize,this.minBufferPx,this.maxBufferPx)}static \u0275fac=function(t){return new(t||i)};static \u0275dir=J({type:i,selectors:[["cdk-virtual-scroll-viewport","itemSize",""]],inputs:{itemSize:"itemSize",minBufferPx:"minBufferPx",maxBufferPx:"maxBufferPx"},features:[ce([{provide:gn,useFactory:Xn,deps:[ht(()=>i)]}]),ye]})}return i})(),Jn=20,ei=(()=>{class i{_ngZone=f(se);_platform=f(Le);_renderer=f(Qe).createRenderer(null,null);_cleanupGlobalListener;constructor(){}_scrolled=new N;_scrolledCount=0;scrollContainers=new Map;register(e){this.scrollContainers.has(e)||this.scrollContainers.set(e,e.elementScrolled().subscribe(()=>this._scrolled.next(e)))}deregister(e){let t=this.scrollContainers.get(e);t&&(t.unsubscribe(),this.scrollContainers.delete(e))}scrolled(e=Jn){return this._platform.isBrowser?new Ue(t=>{this._cleanupGlobalListener||(this._cleanupGlobalListener=this._ngZone.runOutsideAngular(()=>this._renderer.listen("document","scroll",()=>this._scrolled.next())));let o=e>0?this._scrolled.pipe(Ce(e)).subscribe(t):this._scrolled.subscribe(t);return this._scrolledCount++,()=>{o.unsubscribe(),this._scrolledCount--,this._scrolledCount||(this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0)}}):W()}ngOnDestroy(){this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0,this.scrollContainers.forEach((e,t)=>this.deregister(t)),this._scrolled.complete()}ancestorScrolled(e,t){let o=this.getAncestorScrollContainers(e);return this.scrolled(t).pipe(mt(r=>!r||o.indexOf(r)>-1))}getAncestorScrollContainers(e){let t=[];return this.scrollContainers.forEach((o,r)=>{this._scrollableContainsElement(r,e)&&t.push(r)}),t}_scrollableContainsElement(e,t){let o=sn(t),r=e.getElementRef().nativeElement;do if(o==r)return!0;while(o=o.parentElement);return!1}static \u0275fac=function(t){return new(t||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})(),hn=(()=>{class i{elementRef=f(re);scrollDispatcher=f(ei);ngZone=f(se);dir=f(ln,{optional:!0});_scrollElement=this.elementRef.nativeElement;_destroyed=new N;_renderer=f(St);_cleanupScroll;_elementScrolled=new N;constructor(){}ngOnInit(){this._cleanupScroll=this.ngZone.runOutsideAngular(()=>this._renderer.listen(this._scrollElement,"scroll",e=>this._elementScrolled.next(e))),this.scrollDispatcher.register(this)}ngOnDestroy(){this._cleanupScroll?.(),this._elementScrolled.complete(),this.scrollDispatcher.deregister(this),this._destroyed.next(),this._destroyed.complete()}elementScrolled(){return this._elementScrolled}getElementRef(){return this.elementRef}scrollTo(e){let t=this.elementRef.nativeElement,o=this.dir&&this.dir.value=="rtl";e.left==null&&(e.left=o?e.end:e.start),e.right==null&&(e.right=o?e.start:e.end),e.bottom!=null&&(e.top=t.scrollHeight-t.clientHeight-e.bottom),o&&ne()!=A.NORMAL?(e.left!=null&&(e.right=t.scrollWidth-t.clientWidth-e.left),ne()==A.INVERTED?e.left=e.right:ne()==A.NEGATED&&(e.left=e.right?-e.right:e.right)):e.right!=null&&(e.left=t.scrollWidth-t.clientWidth-e.right),this._applyScrollToOptions(e)}_applyScrollToOptions(e){let t=this.elementRef.nativeElement;cn()?t.scrollTo(e):(e.top!=null&&(t.scrollTop=e.top),e.left!=null&&(t.scrollLeft=e.left))}measureScrollOffset(e){let t="left",o="right",r=this.elementRef.nativeElement;if(e=="top")return r.scrollTop;if(e=="bottom")return r.scrollHeight-r.clientHeight-r.scrollTop;let p=this.dir&&this.dir.value=="rtl";return e=="start"?e=p?o:t:e=="end"&&(e=p?t:o),p&&ne()==A.INVERTED?e==t?r.scrollWidth-r.clientWidth-r.scrollLeft:r.scrollLeft:p&&ne()==A.NEGATED?e==t?r.scrollLeft+r.scrollWidth-r.clientWidth:-r.scrollLeft:e==t?r.scrollLeft:r.scrollWidth-r.clientWidth-r.scrollLeft}static \u0275fac=function(t){return new(t||i)};static \u0275dir=J({type:i,selectors:[["","cdk-scrollable",""],["","cdkScrollable",""]]})}return i})(),ti=20,ni=(()=>{class i{_platform=f(Le);_listeners;_viewportSize;_change=new N;_document=f(we);constructor(){let e=f(se),t=f(Qe).createRenderer(null,null);e.runOutsideAngular(()=>{if(this._platform.isBrowser){let o=r=>this._change.next(r);this._listeners=[t.listen("window","resize",o),t.listen("window","orientationchange",o)]}this.change().subscribe(()=>this._viewportSize=null)})}ngOnDestroy(){this._listeners?.forEach(e=>e()),this._change.complete()}getViewportSize(){this._viewportSize||this._updateViewportSize();let e={width:this._viewportSize.width,height:this._viewportSize.height};return this._platform.isBrowser||(this._viewportSize=null),e}getViewportRect(){let e=this.getViewportScrollPosition(),{width:t,height:o}=this.getViewportSize();return{top:e.top,left:e.left,bottom:e.top+o,right:e.left+t,height:o,width:t}}getViewportScrollPosition(){if(!this._platform.isBrowser)return{top:0,left:0};let e=this._document,t=this._getWindow(),o=e.documentElement,r=o.getBoundingClientRect(),p=-r.top||e.body.scrollTop||t.scrollY||o.scrollTop||0,g=-r.left||e.body.scrollLeft||t.scrollX||o.scrollLeft||0;return{top:p,left:g}}change(e=ti){return e>0?this._change.pipe(Ce(e)):this._change}_getWindow(){return this._document.defaultView||window}_updateViewportSize(){let e=this._getWindow();this._viewportSize=this._platform.isBrowser?{width:e.innerWidth,height:e.innerHeight}:{width:0,height:0}}static \u0275fac=function(t){return new(t||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})(),mn=new Z("VIRTUAL_SCROLLABLE"),ii=(()=>{class i extends hn{constructor(){super()}measureViewportSize(e){let t=this.elementRef.nativeElement;return e==="horizontal"?t.clientWidth:t.clientHeight}static \u0275fac=function(t){return new(t||i)};static \u0275dir=J({type:i,features:[Ye]})}return i})();function oi(i,n){return i.start==n.start&&i.end==n.end}var ri=typeof requestAnimationFrame<"u"?dt:ct,at=(()=>{class i extends ii{elementRef=f(re);_changeDetectorRef=f(zt);_scrollStrategy=f(gn,{optional:!0});scrollable=f(mn,{optional:!0});_platform=f(Le);_detachedSubject=new N;_renderedRangeSubject=new N;get orientation(){return this._orientation}set orientation(e){this._orientation!==e&&(this._orientation=e,this._calculateSpacerSize())}_orientation="vertical";appendOnly=!1;scrolledIndexChange=new Ue(e=>this._scrollStrategy.scrolledIndexChange.subscribe(t=>Promise.resolve().then(()=>this.ngZone.run(()=>e.next(t)))));_contentWrapper;renderedRangeStream=this._renderedRangeSubject;_totalContentSize=0;_totalContentWidth=x("");_totalContentHeight=x("");_renderedContentTransform;_renderedRange={start:0,end:0};_dataLength=0;_viewportSize=0;_forOf;_renderedContentOffset=0;_renderedContentOffsetNeedsRewrite=!1;_changeDetectionNeeded=x(!1);_runAfterChangeDetection=[];_viewportChanges=st.EMPTY;_injector=f(vt);_isDestroyed=!1;constructor(){super();let e=f(ni);this._scrollStrategy,this._viewportChanges=e.change().subscribe(()=>{this.checkViewportSize()}),this.scrollable||(this.elementRef.nativeElement.classList.add("cdk-virtual-scrollable"),this.scrollable=this);let t=j(()=>{this._changeDetectionNeeded()&&this._doChangeDetection()},{injector:f(Dt).injector});f(Ct).onDestroy(()=>void t.destroy())}ngOnInit(){this._platform.isBrowser&&(this.scrollable===this&&super.ngOnInit(),this.ngZone.runOutsideAngular(()=>Promise.resolve().then(()=>{this._measureViewportSize(),this._scrollStrategy.attach(this),this.scrollable.elementScrolled().pipe(Ze(null),Ce(0,ri),be(this._destroyed)).subscribe(()=>this._scrollStrategy.onContentScrolled()),this._markChangeDetectionNeeded()})))}ngOnDestroy(){this.detach(),this._scrollStrategy.detach(),this._renderedRangeSubject.complete(),this._detachedSubject.complete(),this._viewportChanges.unsubscribe(),this._isDestroyed=!0,super.ngOnDestroy()}attach(e){this._forOf,this.ngZone.runOutsideAngular(()=>{this._forOf=e,this._forOf.dataStream.pipe(be(this._detachedSubject)).subscribe(t=>{let o=t.length;o!==this._dataLength&&(this._dataLength=o,this._scrollStrategy.onDataLengthChanged()),this._doChangeDetection()})})}detach(){this._forOf=null,this._detachedSubject.next()}getDataLength(){return this._dataLength}getViewportSize(){return this._viewportSize}getRenderedRange(){return this._renderedRange}measureBoundingClientRectWithScrollOffset(e){return this.getElementRef().nativeElement.getBoundingClientRect()[e]}setTotalContentSize(e){this._totalContentSize!==e&&(this._totalContentSize=e,this._calculateSpacerSize(),this._markChangeDetectionNeeded())}setRenderedRange(e){oi(this._renderedRange,e)||(this.appendOnly&&(e={start:0,end:Math.max(this._renderedRange.end,e.end)}),this._renderedRangeSubject.next(this._renderedRange=e),this._markChangeDetectionNeeded(()=>this._scrollStrategy.onContentRendered()))}getOffsetToRenderedContentStart(){return this._renderedContentOffsetNeedsRewrite?null:this._renderedContentOffset}setRenderedContentOffset(e,t="to-start"){e=this.appendOnly&&t==="to-start"?0:e;let o=this.dir&&this.dir.value=="rtl",r=this.orientation=="horizontal",p=r?"X":"Y",d=`translate${p}(${Number((r&&o?-1:1)*e)}px)`;this._renderedContentOffset=e,t==="to-end"&&(d+=` translate${p}(-100%)`,this._renderedContentOffsetNeedsRewrite=!0),this._renderedContentTransform!=d&&(this._renderedContentTransform=d,this._markChangeDetectionNeeded(()=>{this._renderedContentOffsetNeedsRewrite?(this._renderedContentOffset-=this.measureRenderedContentSize(),this._renderedContentOffsetNeedsRewrite=!1,this.setRenderedContentOffset(this._renderedContentOffset)):this._scrollStrategy.onRenderedOffsetChanged()}))}scrollToOffset(e,t="auto"){let o={behavior:t};this.orientation==="horizontal"?o.start=e:o.top=e,this.scrollable.scrollTo(o)}scrollToIndex(e,t="auto"){this._scrollStrategy.scrollToIndex(e,t)}measureScrollOffset(e){let t;return this.scrollable==this?t=o=>super.measureScrollOffset(o):t=o=>this.scrollable.measureScrollOffset(o),Math.max(0,t(e??(this.orientation==="horizontal"?"start":"top"))-this.measureViewportOffset())}measureViewportOffset(e){let t,o="left",r="right",p=this.dir?.value=="rtl";e=="start"?t=p?r:o:e=="end"?t=p?o:r:e?t=e:t=this.orientation==="horizontal"?"left":"top";let g=this.scrollable.measureBoundingClientRectWithScrollOffset(t);return this.elementRef.nativeElement.getBoundingClientRect()[t]-g}measureRenderedContentSize(){let e=this._contentWrapper.nativeElement;return this.orientation==="horizontal"?e.offsetWidth:e.offsetHeight}measureRangeSize(e){return this._forOf?this._forOf.measureRangeSize(e,this.orientation):0}checkViewportSize(){this._measureViewportSize(),this._scrollStrategy.onDataLengthChanged()}_measureViewportSize(){this._viewportSize=this.scrollable.measureViewportSize(this.orientation)}_markChangeDetectionNeeded(e){e&&this._runAfterChangeDetection.push(e),!Ft(this._changeDetectionNeeded)&&this.ngZone.runOutsideAngular(()=>{Promise.resolve().then(()=>{this.ngZone.run(()=>{this._changeDetectionNeeded.set(!0)})})})}_doChangeDetection(){this._isDestroyed||this.ngZone.run(()=>{this._changeDetectorRef.markForCheck(),this._contentWrapper.nativeElement.style.transform=this._renderedContentTransform,Et(()=>{this._changeDetectionNeeded.set(!1);let e=this._runAfterChangeDetection;this._runAfterChangeDetection=[];for(let t of e)t()},{injector:this._injector})})}_calculateSpacerSize(){this._totalContentHeight.set(this.orientation==="horizontal"?"":`${this._totalContentSize}px`),this._totalContentWidth.set(this.orientation==="horizontal"?`${this._totalContentSize}px`:"")}static \u0275fac=function(t){return new(t||i)};static \u0275cmp=D({type:i,selectors:[["cdk-virtual-scroll-viewport"]],viewQuery:function(t,o){if(t&1&&Q(qn,7),t&2){let r;Y(r=q())&&(o._contentWrapper=r.first)}},hostAttrs:[1,"cdk-virtual-scroll-viewport"],hostVars:4,hostBindings:function(t,o){t&2&&z("cdk-virtual-scroll-orientation-horizontal",o.orientation==="horizontal")("cdk-virtual-scroll-orientation-vertical",o.orientation!=="horizontal")},inputs:{orientation:"orientation",appendOnly:[2,"appendOnly","appendOnly",At]},outputs:{scrolledIndexChange:"scrolledIndexChange"},features:[ce([{provide:hn,useFactory:(e,t)=>e||t,deps:[[new bt,new xt(mn)],i]}]),Ye],ngContentSelectors:Kn,decls:4,vars:4,consts:[["contentWrapper",""],[1,"cdk-virtual-scroll-content-wrapper"],[1,"cdk-virtual-scroll-spacer"]],template:function(t,o){t&1&&(Rt(),kt(0,"div",1,0),Tt(2),Pt(),It(3,"div",2)),t&2&&(l(3),ee("width",o._totalContentWidth())("height",o._totalContentHeight()))},styles:[`cdk-virtual-scroll-viewport{display:block;position:relative;transform:translateZ(0)}.cdk-virtual-scrollable{overflow:auto;will-change:scroll-position;contain:strict}.cdk-virtual-scroll-content-wrapper{position:absolute;top:0;left:0;contain:content}[dir=rtl] .cdk-virtual-scroll-content-wrapper{right:0;left:auto}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper{min-height:100%}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-left:0;padding-right:0;margin-left:0;margin-right:0;border-left-width:0;border-right-width:0;outline:none}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper{min-width:100%}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;border-top-width:0;border-bottom-width:0;outline:none}.cdk-virtual-scroll-spacer{height:1px;transform-origin:0 0;flex:0 0 auto}[dir=rtl] .cdk-virtual-scroll-spacer{transform-origin:100% 0}
2
+ `],encapsulation:2,changeDetection:0})}return i})();function pn(i,n,e){let t=e;if(!t.getBoundingClientRect)return 0;let o=t.getBoundingClientRect();return i==="horizontal"?n==="start"?o.left:o.right:n==="start"?o.top:o.bottom}var _n=(()=>{class i{_viewContainerRef=f(Mt);_template=f(yt);_differs=f(Lt);_viewRepeater=f(ot);_viewport=f(at,{skipSelf:!0});viewChange=new N;_dataSourceChanges=new N;get cdkVirtualForOf(){return this._cdkVirtualForOf}set cdkVirtualForOf(e){this._cdkVirtualForOf=e,dn(e)?this._dataSourceChanges.next(e):this._dataSourceChanges.next(new je(ve(e)?e:Array.from(e||[])))}_cdkVirtualForOf;get cdkVirtualForTrackBy(){return this._cdkVirtualForTrackBy}set cdkVirtualForTrackBy(e){this._needsUpdate=!0,this._cdkVirtualForTrackBy=e?(t,o)=>e(t+(this._renderedRange?this._renderedRange.start:0),o):void 0}_cdkVirtualForTrackBy;set cdkVirtualForTemplate(e){e&&(this._needsUpdate=!0,this._template=e)}get cdkVirtualForTemplateCacheSize(){return this._viewRepeater.viewCacheSize}set cdkVirtualForTemplateCacheSize(e){this._viewRepeater.viewCacheSize=ue(e)}dataStream=this._dataSourceChanges.pipe(Ze(null),gt(),xe(([e,t])=>this._changeDataSource(e,t)),ut(1));_differ=null;_data;_renderedItems;_renderedRange;_needsUpdate=!1;_destroyed=new N;constructor(){let e=f(se);this.dataStream.subscribe(t=>{this._data=t,this._onRenderedDataChange()}),this._viewport.renderedRangeStream.pipe(be(this._destroyed)).subscribe(t=>{this._renderedRange=t,this.viewChange.observers.length&&e.run(()=>this.viewChange.next(this._renderedRange)),this._onRenderedDataChange()}),this._viewport.attach(this)}measureRangeSize(e,t){if(e.start>=e.end)return 0;e.start<this._renderedRange.start||e.end>this._renderedRange.end;let o=e.start-this._renderedRange.start,r=e.end-e.start,p,g;for(let d=0;d<r;d++){let h=this._viewContainerRef.get(d+o);if(h&&h.rootNodes.length){p=g=h.rootNodes[0];break}}for(let d=r-1;d>-1;d--){let h=this._viewContainerRef.get(d+o);if(h&&h.rootNodes.length){g=h.rootNodes[h.rootNodes.length-1];break}}return p&&g?pn(t,"end",g)-pn(t,"start",p):0}ngDoCheck(){if(this._differ&&this._needsUpdate){let e=this._differ.diff(this._renderedItems);e?this._applyChanges(e):this._updateContext(),this._needsUpdate=!1}}ngOnDestroy(){this._viewport.detach(),this._dataSourceChanges.next(void 0),this._dataSourceChanges.complete(),this.viewChange.complete(),this._destroyed.next(),this._destroyed.complete(),this._viewRepeater.detach()}_onRenderedDataChange(){this._renderedRange&&(this._renderedItems=this._data.slice(this._renderedRange.start,this._renderedRange.end),this._differ||(this._differ=this._differs.find(this._renderedItems).create((e,t)=>this.cdkVirtualForTrackBy?this.cdkVirtualForTrackBy(e,t):t)),this._needsUpdate=!0)}_changeDataSource(e,t){return e&&e.disconnect(this),this._needsUpdate=!0,t?t.connect(this):W()}_updateContext(){let e=this._data.length,t=this._viewContainerRef.length;for(;t--;){let o=this._viewContainerRef.get(t);o.context.index=this._renderedRange.start+t,o.context.count=e,this._updateComputedContextProperties(o.context),o.detectChanges()}}_applyChanges(e){this._viewRepeater.applyChanges(e,this._viewContainerRef,(r,p,g)=>this._getEmbeddedViewArgs(r,g),r=>r.item),e.forEachIdentityChange(r=>{let p=this._viewContainerRef.get(r.currentIndex);p.context.$implicit=r.item});let t=this._data.length,o=this._viewContainerRef.length;for(;o--;){let r=this._viewContainerRef.get(o);r.context.index=this._renderedRange.start+o,r.context.count=t,this._updateComputedContextProperties(r.context)}}_updateComputedContextProperties(e){e.first=e.index===0,e.last=e.index===e.count-1,e.even=e.index%2===0,e.odd=!e.even}_getEmbeddedViewArgs(e,t){return{templateRef:this._template,context:{$implicit:e.item,cdkVirtualForOf:this._cdkVirtualForOf,index:-1,count:-1,first:!1,last:!1,odd:!1,even:!1},index:t}}static ngTemplateContextGuard(e,t){return!0}static \u0275fac=function(t){return new(t||i)};static \u0275dir=J({type:i,selectors:[["","cdkVirtualFor","","cdkVirtualForOf",""]],inputs:{cdkVirtualForOf:"cdkVirtualForOf",cdkVirtualForTrackBy:"cdkVirtualForTrackBy",cdkVirtualForTemplate:"cdkVirtualForTemplate",cdkVirtualForTemplateCacheSize:"cdkVirtualForTemplateCacheSize"},features:[ce([{provide:ot,useClass:Ge}])]})}return i})();var fn=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=ae({type:i});static \u0275inj=oe({})}return i})(),vn=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=ae({type:i});static \u0275inj=oe({imports:[it,fn,it,fn]})}return i})();function si(i,n){i&1&&b(0,"span",19)}function li(i,n){if(i&1&&(s(0,"span",23),c(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function ci(i,n){if(i&1&&(s(0,"span",24),c(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function di(i,n){if(i&1&&(s(0,"span",20),v(1,li,2,1,"span",21)(2,ci,2,1,"span",22),a()),i&2){let e=C().$implicit;l(),u("ngForOf",e.tags),l(),u("ngForOf",e.branches)}}function mi(i,n){if(i&1){let e=F();s(0,"button",7),w("click",function(){let o=M(e).$implicit,r=C();return O(r.select(o))}),s(1,"span",8),b(2,"span",9),v(3,si,1,0,"span",10),a(),s(4,"span",11)(5,"span",12),c(6),a(),s(7,"span",13)(8,"span",14),c(9),a(),s(10,"span",15),c(11,"\u2022"),a(),s(12,"span",16),c(13),a(),s(14,"span",15),c(15,"\u2022"),a(),s(16,"span",17),c(17),de(18,"date"),a()()(),v(19,di,3,2,"span",18),a()}if(i&2){let e=n.$implicit,t=n.index,o=C();z("selected",e.hash===o.selectedHash()),le("aria-current",e.hash===o.selectedHash()?"true":null),l(2),ee("background",o.laneColor(e)),z("merge",e.isMerge),l(),u("ngIf",t<o.commits().length-1),l(2),u("title",e.subject),l(),_(e.subject),l(3),_(e.shortHash),l(4),_(e.author),l(4),_(me(18,14,e.date,"MMM d, y, h:mm a")),l(2),u("ngIf",e.branches.length||e.tags.length)}}function pi(i,n){i&1&&(s(0,"div",25)(1,"p"),c(2,"No commits match your filters."),a()())}var He=class i{state=f(G);commits=this.state.commits;selectedHash=this.state.selectedHash;trackByHash(n,e){return e.hash}select(n){this.state.selectHash(n.hash)}laneColors=["#4f46e5","#06b6d4","#f59e0b","#ef4444","#10b981","#8b5cf6"];laneColor(n){let e=0,t=n.branches[0]??n.parents[0]??n.hash;for(let o=0;o<t.length;o++)e=e*31+t.charCodeAt(o)>>>0;return this.laneColors[e%this.laneColors.length]}onKey(n){if(!this.isTyping(n.target)){if(n.key==="j")n.preventDefault(),this.state.selectByOffset(1);else if(n.key==="k")n.preventDefault(),this.state.selectByOffset(-1);else if(n.key==="g"){n.preventDefault();let e=this.state.commits();e.length&&this.state.selectHash(e[0].hash)}else if(n.key==="G"){n.preventDefault();let e=this.state.commits();e.length&&this.state.selectHash(e[e.length-1].hash)}}}isTyping(n){return n instanceof HTMLElement?["INPUT","TEXTAREA","SELECT"].includes(n.tagName)||n.isContentEditable:!1}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-commit-list"]],hostBindings:function(e,t){e&1&&w("keydown",function(r){return t.onKey(r)},Se)},decls:13,vars:6,consts:[[1,"header"],[1,"count"],[1,"hint"],[1,"kbd"],["minBufferPx","640","maxBufferPx","1280",1,"viewport",3,"itemSize"],["class","row",3,"selected","click",4,"cdkVirtualFor","cdkVirtualForOf","cdkVirtualForTrackBy"],["class","empty",4,"ngIf"],[1,"row",3,"click"],[1,"lane"],[1,"dot"],["class","line",4,"ngIf"],[1,"content"],[1,"subject",3,"title"],[1,"meta"],[1,"hash"],[1,"dot-sep"],[1,"author"],[1,"date"],["class","badges",4,"ngIf"],[1,"line"],[1,"badges"],["class","badge tag",4,"ngFor","ngForOf"],["class","badge branch",4,"ngFor","ngForOf"],[1,"badge","tag"],[1,"badge","branch"],[1,"empty"]],template:function(e,t){e&1&&(s(0,"div",0)(1,"span",1),c(2),a(),s(3,"span",2)(4,"kbd",3),c(5,"j"),a(),c(6,"/"),s(7,"kbd",3),c(8,"k"),a(),c(9," navigate "),a()(),s(10,"cdk-virtual-scroll-viewport",4),v(11,mi,20,17,"button",5)(12,pi,3,0,"div",6),a()),e&2&&(l(2),De("",t.commits().length," of ",t.state.total()),l(8),u("itemSize",64),l(),u("cdkVirtualForOf",t.commits())("cdkVirtualForTrackBy",t.trackByHash),l(),u("ngIf",!t.commits().length&&!t.state.loading()))},dependencies:[T,$,R,vn,un,_n,at,Pe],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--bg-surface);border-right:1px solid var(--border-soft)}.header[_ngcontent-%COMP%]{display:flex;align-items:center;justify-content:space-between;padding:.5rem .85rem;border-bottom:1px solid var(--border-soft);font-size:12px;color:var(--fg-muted);background:var(--bg-surface)}.viewport[_ngcontent-%COMP%]{flex:1;min-height:0}.row[_ngcontent-%COMP%]{display:grid;grid-template-columns:24px 1fr auto;gap:.5rem;align-items:center;width:100%;height:64px;padding:.45rem .85rem;background:transparent;border:0;border-bottom:1px solid var(--border-soft);color:inherit;text-align:left;cursor:pointer;transition:background .1s}.row[_ngcontent-%COMP%]:hover{background:var(--bg-hover)}.row.selected[_ngcontent-%COMP%]{background:var(--bg-selected)}.row.selected[_ngcontent-%COMP%] .subject[_ngcontent-%COMP%]{color:var(--fg-primary)}.lane[_ngcontent-%COMP%]{position:relative;width:24px;height:100%;display:flex;align-items:center;justify-content:center}.dot[_ngcontent-%COMP%]{width:10px;height:10px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 2px var(--bg-surface);z-index:1}.dot.merge[_ngcontent-%COMP%]{background:transparent;border:2px solid var(--accent)}.line[_ngcontent-%COMP%]{position:absolute;top:50%;left:50%;width:2px;height:50%;background:var(--border-strong);transform:translate(-50%)}.content[_ngcontent-%COMP%]{display:flex;flex-direction:column;min-width:0;gap:2px}.subject[_ngcontent-%COMP%]{font-weight:500;color:var(--fg-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.meta[_ngcontent-%COMP%]{display:flex;gap:6px;font-size:11px;color:var(--fg-muted);align-items:center}.hash[_ngcontent-%COMP%]{font-family:var(--font-mono)}.dot-sep[_ngcontent-%COMP%]{opacity:.5}.badges[_ngcontent-%COMP%]{display:flex;gap:4px;flex-wrap:wrap}.badge[_ngcontent-%COMP%]{font-size:10px;font-weight:600;padding:2px 6px;border-radius:999px;letter-spacing:.02em}.badge.tag[_ngcontent-%COMP%]{background:#d9770626;color:var(--warning)}.badge.branch[_ngcontent-%COMP%]{background:var(--accent-soft);color:var(--accent)}.empty[_ngcontent-%COMP%]{padding:2rem 1rem;text-align:center;color:var(--fg-muted)}"],changeDetection:0})};var We=class i{http=f(Ie);base="/api";list(n={}){let e=new jt;for(let[t,o]of Object.entries(n))o&&(e=e.set(t,String(o)));return this.http.get(`${this.base}/groups`,{params:e})}static \u0275fac=function(e){return new(e||i)};static \u0275prov=V({token:i,factory:i.\u0275fac,providedIn:"root"})};function fi(i,n){if(i&1&&(s(0,"span",7),c(1),a()),i&2){let e=n.ngIf;l(),B("",e," groups")}}function gi(i,n){i&1&&(s(0,"div",8),c(1,"Loading groups\u2026"),a())}function ui(i,n){if(i&1&&(s(0,"div",9),c(1),a()),i&2){let e=n.ngIf;l(),_(e)}}function hi(i,n){i&1&&(s(0,"div",8),c(1," No groups detected. Try the flat view. "),a())}function _i(i,n){if(i&1&&(s(0,"span",18),c(1),a()),i&2){let e=C().$implicit;l(),B("#",e.prNumber)}}function vi(i,n){if(i&1){let e=F();s(0,"li",21),w("click",function(){let o=M(e).$implicit,r=C(3);return O(r.state.selectHash(o))}),s(1,"code",22),c(2),a(),s(3,"span",23),c(4),a()()}if(i&2){let e=n.$implicit,t=C(3);z("selected",e===t.state.selectedHash()),l(2),_(t.shortHash(e)),l(2),_(t.subjectFor(e))}}function Ci(i,n){if(i&1&&(s(0,"ul",19),v(1,vi,5,4,"li",20),a()),i&2){let e=C().$implicit;l(),u("ngForOf",e.commits)}}function xi(i,n){if(i&1){let e=F();s(0,"li",10)(1,"button",11),w("click",function(){let o=M(e).$implicit,r=C();return O(r.toggle(o.id))}),s(2,"span",12),c(3,"\u25B8"),a(),s(4,"span",13),c(5),a(),v(6,_i,2,1,"span",14),s(7,"span",15),c(8),a(),s(9,"span",16),c(10),a()(),v(11,Ci,2,1,"ul",17),a()}if(i&2){let e=n.$implicit,t=C();z("expanded",t.isExpanded(e.id)),l(2),z("open",t.isExpanded(e.id)),l(2),Nt("src-"+e.source),l(),_(t.sourceLabel(e.source)),l(),u("ngIf",e.prNumber),l(2),_(e.title),l(2),_(e.commits.length),l(),u("ngIf",t.isExpanded(e.id))}}var $e=class i{state=f(G);groupsApi=f(We);groups=x(null);loading=x(!1);error=x(null);expanded=x(new Set);subjectMap=te(()=>{let n=new Map;for(let e of this.state.commits())n.set(e.hash,e.subject);return n});constructor(){j(()=>{let n=this.state.filters();this.load(n.since,n.until,n.author)})}isExpanded(n){return this.expanded().has(n)}toggle(n){let e=new Set(this.expanded());e.has(n)?e.delete(n):e.add(n),this.expanded.set(e)}shortHash(n){return n.slice(0,7)}subjectFor(n){return this.subjectMap().get(n)??n.slice(0,7)}sourceLabel(n){switch(n){case"merge":return"PR";case"squash":return"PR (sq)";case"conventional":return"Feat";case"standalone":return"commit"}}load(n,e,t){this.loading.set(!0),this.error.set(null),this.groupsApi.list({since:n,until:e,author:t}).subscribe({next:o=>{this.groups.set(o),this.loading.set(!1);let r=o.find(p=>p.prNumber);r&&this.expanded.set(new Set([r.id]))},error:o=>{this.error.set(this.errMsg(o)),this.loading.set(!1)}})}errMsg(n){if(n&&typeof n=="object"&&"error"in n){let e=n.error;if(e?.error)return e.error}return n instanceof Error?n.message:"Failed to load groups"}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-grouped-list"]],decls:9,vars:5,consts:[[1,"head"],[1,"title"],["class","meta",4,"ngIf"],["class","empty",4,"ngIf"],["class","empty error",4,"ngIf"],[1,"groups"],["class","group",3,"expanded",4,"ngFor","ngForOf"],[1,"meta"],[1,"empty"],[1,"empty","error"],[1,"group"],[1,"group-head",3,"click"],[1,"caret"],[1,"badge"],["class","pr",4,"ngIf"],[1,"g-title"],[1,"count"],["class","commits",4,"ngIf"],[1,"pr"],[1,"commits"],["class","commit",3,"selected","click",4,"ngFor","ngForOf"],[1,"commit",3,"click"],[1,"hash"],[1,"subject"]],template:function(e,t){if(e&1&&(s(0,"div",0)(1,"span",1),c(2,"PR / feature groups"),a(),v(3,fi,2,1,"span",2),a(),v(4,gi,2,0,"div",3)(5,ui,2,1,"div",4)(6,hi,2,0,"div",3),s(7,"ul",5),v(8,xi,12,11,"li",6),a()),e&2){let o,r;l(3),u("ngIf",(o=t.groups())==null?null:o.length),l(),u("ngIf",t.loading()),l(),u("ngIf",t.error()),l(),u("ngIf",!t.loading()&&!t.error()&&(((r=t.groups())==null?null:r.length)??0)===0),l(2),u("ngForOf",t.groups())}},dependencies:[T,$,R],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;height:100%;overflow:hidden}.head[_ngcontent-%COMP%]{display:flex;justify-content:space-between;padding:.6rem .85rem;border-bottom:1px solid var(--border-soft);background:var(--bg-surface)}.title[_ngcontent-%COMP%]{font-weight:600;font-size:13px}.meta[_ngcontent-%COMP%]{font-size:11px;color:var(--fg-muted)}.empty[_ngcontent-%COMP%]{padding:1rem .85rem;color:var(--fg-muted);font-size:12px}.empty.error[_ngcontent-%COMP%]{color:var(--danger)}.groups[_ngcontent-%COMP%]{list-style:none;margin:0;padding:0;overflow-y:auto;flex:1}.group[_ngcontent-%COMP%]{border-bottom:1px solid var(--border-soft)}.group-head[_ngcontent-%COMP%]{width:100%;display:flex;align-items:center;gap:.5rem;padding:.55rem .85rem;background:transparent;border:0;color:var(--fg-primary);cursor:pointer;text-align:left}.group-head[_ngcontent-%COMP%]:hover{background:var(--bg-elevated)}.caret[_ngcontent-%COMP%]{display:inline-block;width:10px;transition:transform .15s ease;color:var(--fg-muted)}.caret.open[_ngcontent-%COMP%]{transform:rotate(90deg)}.badge[_ngcontent-%COMP%]{font-size:10px;letter-spacing:.04em;padding:1px 6px;border-radius:3px;background:var(--bg-elevated);color:var(--fg-secondary);text-transform:uppercase}.badge.src-merge[_ngcontent-%COMP%]{background:#6366f12e;color:var(--accent)}.badge.src-squash[_ngcontent-%COMP%]{background:#10b9812e;color:#10b981}.badge.src-conventional[_ngcontent-%COMP%]{background:#f59e0b2e;color:#d97706}.pr[_ngcontent-%COMP%]{font-size:11px;color:var(--fg-muted)}.g-title[_ngcontent-%COMP%]{flex:1;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.count[_ngcontent-%COMP%]{font-size:11px;color:var(--fg-muted);background:var(--bg-elevated);padding:1px 6px;border-radius:999px}.commits[_ngcontent-%COMP%]{list-style:none;margin:0;padding:0 0 .4rem;background:var(--bg-app)}.commit[_ngcontent-%COMP%]{display:flex;gap:.6rem;align-items:center;padding:.3rem .85rem .3rem 2rem;cursor:pointer;font-size:12px}.commit[_ngcontent-%COMP%]:hover{background:var(--bg-elevated)}.commit.selected[_ngcontent-%COMP%]{background:color-mix(in oklab,var(--accent) 20%,transparent)}.commit[_ngcontent-%COMP%] .hash[_ngcontent-%COMP%]{font-family:var(--font-mono, monospace);font-size:11px;color:var(--fg-muted)}.commit[_ngcontent-%COMP%] .subject[_ngcontent-%COMP%]{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"],changeDetection:0})};function bi(i,n){i&1&&(Me(0),b(1,"app-grouped-list"),Oe())}function wi(i,n){i&1&&b(0,"app-commit-list")}var Cn=class i{state=f(G);static \u0275fac=function(e){return new(e||i)};static \u0275cmp=D({type:i,selectors:[["app-home-shell"]],decls:9,vars:2,consts:[["flat",""],[1,"layout"],[1,"pane","graph"],[1,"pane","list"],[4,"ngIf","ngIfElse"],[1,"pane","detail"]],template:function(e,t){if(e&1&&(s(0,"main",1)(1,"aside",2),b(2,"app-commit-graph"),a(),s(3,"section",3),v(4,bi,2,0,"ng-container",4)(5,wi,1,0,"ng-template",null,0,ke),a(),s(7,"section",5),b(8,"app-commit-detail"),a()()),e&2){let o=Ee(6);l(4),u("ngIf",t.state.viewMode()==="grouped")("ngIfElse",o)}},dependencies:[T,R,ze,He,Ne,$e],styles:["[_nghost-%COMP%]{display:block;flex:1;min-height:0}.layout[_ngcontent-%COMP%]{height:100%;display:grid;grid-template-columns:220px 380px 1fr;min-height:0}.pane[_ngcontent-%COMP%]{min-width:0;min-height:0;overflow:hidden}.pane.graph[_ngcontent-%COMP%]{border-right:1px solid var(--border-soft)}@media (max-width: 1100px){.layout[_ngcontent-%COMP%]{grid-template-columns:320px 1fr}.pane.graph[_ngcontent-%COMP%]{display:none}}@media (max-width: 720px){.layout[_ngcontent-%COMP%]{grid-template-columns:1fr}.pane.list[_ngcontent-%COMP%]{display:none}}"],changeDetection:0})};export{Cn as HomeShellComponent};