git-history-ui 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,65 @@ 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.2.1] - 2026-05-02
8
+
9
+ ### Fixed
10
+
11
+ - **`npx git-history-ui` no longer prints help and exits.** When the
12
+ `presets` subcommand was added in v3.2.0, the root commander program
13
+ was left without a default `.action()`. Commander v12 reacts to that
14
+ by printing help and exiting whenever the user invokes the binary
15
+ without a subcommand. A no-args invocation now correctly starts the
16
+ server, matching pre-v3.2.0 behavior.
17
+ - Added a CLI smoke test (`src/__tests__/cli.test.ts`) that runs the
18
+ built binary with `--help`, `--version`, and no args so this kind of
19
+ regression can't ship silently again.
20
+
21
+ ## [3.2.0] - 2026-05-02
22
+
23
+ The "Distribution & scale" release. This phase makes the tool faster on big
24
+ repos, easier to embed elsewhere, and easier to share a view with a teammate
25
+ — all without breaking the zero-config promise.
26
+
27
+ ### Added
28
+
29
+ - **SQLite commit index (optional).** New `src/backend/cache/sqliteIndex.ts`
30
+ builds a per-repo index in `~/.git-history-ui/<hash>.db` keyed off
31
+ `git rev-list --all` and an FTS5 virtual table over subject/body. Falls
32
+ back to git-shelling when `better-sqlite3` is not installed. Surfaced via
33
+ `GET /api/index/stats` and `POST /api/index/build`.
34
+ - **SSE streaming endpoint.** `GET /api/commits/stream` streams commits as
35
+ they are produced from `git log`, so very large repos render incrementally
36
+ instead of waiting for the full payload.
37
+ - **Virtualized commit graph.** `CommitGraphComponent` now culls offscreen
38
+ rows on each draw and re-paints on scroll via `requestAnimationFrame`,
39
+ keeping the canvas paint cost bounded by viewport height instead of total
40
+ commit count.
41
+ - **Shareable URLs.** `POST /api/share` returns a URL with the supplied
42
+ view-state encoded in the query string. The common case ("send my
43
+ colleague the link") needs no relay server.
44
+ - **Annotations & "Explain this change".** Local-first per-commit comment
45
+ threads stored under `~/.git-history-ui/<repo>/annotations.json`, plus a
46
+ one-click ✨ Explain card that calls the configured LLM to summarize a
47
+ commit's intent.
48
+ - **CLI presets.** New `--preset <name>` and `--save-preset <name>` flags
49
+ plus a `git-history-ui presets list|delete` subcommand, all backed by
50
+ `~/.git-history-ui/presets.json`.
51
+ - **Embeddable distribution.** Scaffolds for a Chrome extension
52
+ (`apps/chrome-extension/`) that injects a "View in git-history-ui" button
53
+ on GitHub PR / commit pages, a placeholder GitHub App
54
+ (`apps/github-app/`), and an opportunistic `git-history-ui://` protocol
55
+ registration script (`scripts/register-protocol.js`, opt-in).
56
+
57
+ ### Changed
58
+
59
+ - Bumped to `v3.2.0`.
60
+
61
+ ### Notes
62
+
63
+ - `better-sqlite3` is declared as an `optionalDependency`; install failures
64
+ are silent and the server continues to operate with the git-shelling path.
65
+
7
66
  ## [3.1.0] - 2026-05-02
8
67
 
9
68
  Visual polish on top of v3.0 — d3 visualizations replace the placeholder
package/README.md CHANGED
@@ -5,117 +5,175 @@
5
5
  [![npm license](https://img.shields.io/npm/l/git-history-ui.svg)](https://www.npmjs.com/package/git-history-ui)
6
6
  [![npm bundle size](https://img.shields.io/bundlephobia/min/git-history-ui.svg)](https://bundlephobia.com/result?p=git-history-ui)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/beingmartinbmc/git-history-ui.svg)](https://github.com/beingmartinbmc/git-history-ui)
8
- [![GitHub issues](https://img.shields.io/github/issues/beingmartinbmc/git-history-ui.svg)](https://github.com/beingmartinbmc/git-history-ui/issues)
9
8
 
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.
9
+ **Git Intelligence in your browser.**
10
+
11
+ Turn your git history into something you can actually understand:
12
+
13
+ - 🔎 Ask questions in plain English
14
+ - 📦 See commits grouped by feature or PR
15
+ - 🕰️ Travel through time and diff any state
16
+ - 🎯 Understand impact, not just changes
17
+
18
+ Zero setup. Runs locally. Your code never leaves your machine unless you opt in.
19
+
20
+ ```bash
21
+ npx git-history-ui@latest
22
+ ```
23
+
24
+ ## ⚡ 10-second workflow
25
+
26
+ 1. Run `npx git-history-ui` inside any git repo
27
+ 2. Search: *"login bug last month"*
28
+ 3. Jump to the commit
29
+ 4. Inspect the diff and what it impacted
30
+
31
+ Done.
15
32
 
16
33
  ## 👀 Preview
17
34
 
18
35
  ![Git History UI screenshot](./docs/screenshot.png)
19
36
 
37
+ > A demo GIF showing NL search, the timeline slider, and grouped view is on
38
+ > the way. In the meantime, `npx git-history-ui@latest` is the fastest way
39
+ > to see it for yourself — it opens in your browser in under a second.
40
+
41
+ ## 🤔 Why this exists
42
+
43
+ Git history is hard to understand:
44
+
45
+ - commits are flat
46
+ - context is missing
47
+ - debugging across branches is painful
48
+ - GitHub's UI hides your local and unpushed work
49
+
50
+ `git-history-ui` turns that history into something **searchable, grouped,
51
+ explorable, and explainable** — without a desktop install or a cloud
52
+ account.
53
+
54
+ ## ✨ What makes it different
55
+
56
+ Four things you don't get from `git log`, GitHub, or most desktop clients:
57
+
58
+ - **Natural-language search.** "login bug last month", "payments by alice".
59
+ A heuristic intent parser handles dates, authors, and keyword synonyms;
60
+ optional Anthropic / OpenAI key adds semantic re-ranking on top.
61
+ - **PR & feature grouping.** Switch the commit list to *Grouped* mode to
62
+ see commits clustered by pull request or Conventional Commits scope.
63
+ - **Time-travel timeline.** A horizontal slider that scrubs the repo state
64
+ at any point in time and live-diffs it against HEAD.
65
+ - **Commit impact analysis.** One click reveals which files, modules, and
66
+ related commits a change actually touches — not just the diff.
67
+
68
+ ## 🤝 AI is optional, opt-in, and on your key
69
+
70
+ - Heuristic mode works out of the box, no key required.
71
+ - Set `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` to upgrade NL search ranking
72
+ and unlock "Explain change" / "Summarize diff" actions.
73
+ - Prompts run from your machine to your provider. Your repo, your key,
74
+ your call.
75
+
20
76
  ## 🚀 Quick Start
21
77
 
22
78
  ```bash
23
- # Go to the git repository you want to inspect
24
79
  cd /path/to/your/project
25
-
26
- # Run directly with npx (no installation needed)
27
80
  npx git-history-ui@latest
28
81
  ```
29
82
 
30
- That's it! The application will start on `http://localhost:3000` and open your browser automatically.
31
- It reads history from the current working directory, so run it inside the project whose commits you want to visualize.
32
-
33
- No installs. No config. Just your commits, visualized.
34
-
35
- ## 🤔 Why use this?
36
-
37
- - `git log` is powerful, but hard to scan when branches, merges, and long-lived work overlap.
38
- - GitHub's commit UI does not show your local or unpushed commits.
39
- - Desktop clients can be heavy when you just want a quick read on one repo.
40
- - `git-history-ui` gives you a fast, local, visual way to explore history from any git repository.
41
-
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
83
+ That's it. The app starts on `http://localhost:3000` and opens your
84
+ browser automatically. It reads history from the current working
85
+ directory — no installs, no config, no account.
74
86
 
75
87
  ## ⚖️ How it compares
76
88
 
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
89
+ - **vs GitHub UI:** NL search and PR grouping work *with* your unpushed
90
+ commits. Time travel and impact analysis aren't on GitHub at all.
91
+ - **vs `tig` / `git log`:** visual lanes, browser diffs, optional AI
92
+ explanations, insights dashboard.
93
+ - **vs desktop clients (GitKraken, SourceTree, Fork):** starts on demand,
82
94
  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.
95
+ pay-as-you-go on *your* key — nothing about your code leaves your
96
+ machine unless you opt in.
85
97
 
86
- ## 📖 Usage
98
+ ## 📦 All features
87
99
 
88
- ### CLI Options
100
+ <details>
101
+ <summary><strong>Click to expand the full feature list</strong></summary>
89
102
 
90
- Run these commands from inside the git repository you want to inspect.
103
+ ### Exploration
91
104
 
92
- ```bash
93
- # Custom port
94
- npx git-history-ui@latest --port 8080
105
+ - **Canvas commit graph** with branch lanes, ref pills, hover/selected
106
+ states; viewport-virtualized so 50k-commit histories stay smooth.
107
+ - **Real-time filtering** by author, date, text, file path.
108
+ - **Unified & split diffs** with `highlight.js`, collapse-unchanged
109
+ blocks, side-by-side scroll-sync, and intra-line word highlighting.
110
+ - **Dark / light / system theme** with single-click toggle.
95
111
 
96
- # Filter by specific file
97
- npx git-history-ui@latest --file src/app.js
112
+ ### Code understanding
98
113
 
99
- # Filter by author
100
- npx git-history-ui@latest --author "your-name"
114
+ - **File-level history.** Click any file in a commit to see every commit
115
+ that touched it.
116
+ - **Blame view** powered by `highlight.js`, tabbed inside file history.
117
+ - **Insights dashboard.** Top contributors, hotspots (treemap), churn
118
+ over time (d3 area chart), heuristic risky-files score.
119
+ - **Commit impact card.** Files touched, modules affected, dependency
120
+ ripple parsed from JS/TS imports, related commits — including a d3
121
+ force-directed graph view.
101
122
 
102
- # Filter by date range
103
- npx git-history-ui@latest --since 2024-01-01
123
+ ### Collaboration
104
124
 
105
- # Don't auto-open browser
106
- npx git-history-ui@latest --no-open
125
+ - **Local-first annotations.** Per-commit comment threads stored in
126
+ `~/.git-history-ui/<repo>/annotations.json`.
127
+ - **Shareable URLs.** `POST /api/share` returns a deep link with the
128
+ current view-state encoded in the query string — no relay server
129
+ required for the common case.
130
+ - **"Explain this change"** AI card on the commit detail panel (opt-in).
107
131
 
108
- # Show help
109
- npx git-history-ui@latest --help
132
+ ### Performance & scale
133
+
134
+ - **SQLite indexer (optional).** Install `better-sqlite3` and large
135
+ repos get an FTS5-backed index in `~/.git-history-ui/`. Silent
136
+ fallback to git-shelling when the native module isn't available.
137
+ Endpoints: `GET /api/index/stats`, `POST /api/index/build`.
138
+ - **Streaming commits.** `GET /api/commits/stream` (SSE) pushes commits
139
+ as `git log` produces them.
140
+ - **Virtualized commit graph.** Only the visible viewport is painted;
141
+ scrolling is `requestAnimationFrame`-throttled.
142
+
143
+ ### CLI
144
+
145
+ - **Presets.** `--preset <name>` / `--save-preset <name>` and a
146
+ `git-history-ui presets list|delete` subcommand, stored in
147
+ `~/.git-history-ui/presets.json`.
148
+ - **Standard filters.** `--file`, `--author`, `--since`, `--port`,
149
+ `--no-open`, `--cwd`, `--llm <provider>`.
150
+
151
+ ### Embeds (experimental scaffolds)
152
+
153
+ - **Chrome extension** (`apps/chrome-extension/`) injects a "View in
154
+ git-history-ui" button on github.com PR / commit pages.
155
+ - **GitHub App** (`apps/github-app/`) scaffold for the same deep-link
156
+ strategy at the org level.
157
+
158
+ See [`CHANGELOG.md`](./CHANGELOG.md) for per-version detail.
159
+
160
+ </details>
161
+
162
+ ## 📖 Usage
163
+
164
+ Run from inside the git repository you want to inspect.
165
+
166
+ ```bash
167
+ npx git-history-ui@latest --port 8080 # custom port
168
+ npx git-history-ui@latest --file src/app.js # filter by file
169
+ npx git-history-ui@latest --author "alice" # filter by author
170
+ npx git-history-ui@latest --since 2024-01-01 # filter by date
171
+ npx git-history-ui@latest --no-open # don't open the browser
172
+ npx git-history-ui@latest --help # full flag list
110
173
  ```
111
174
 
112
175
  ### Optional: bring your own AI key
113
176
 
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
177
  ```bash
120
178
  # Anthropic (recommended; uses claude-3-5-haiku by default)
121
179
  export ANTHROPIC_API_KEY=sk-ant-...
@@ -124,65 +182,46 @@ export ANTHROPIC_API_KEY=sk-ant-...
124
182
  export OPENAI_API_KEY=sk-...
125
183
 
126
184
  # 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
185
+ export GHUI_LLM_PROVIDER=anthropic # anthropic | openai | heuristic
130
186
  ```
131
187
 
132
188
  ### Optional: GitHub PR enrichment
133
189
 
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
190
  ```bash
138
- export GITHUB_TOKEN=ghp_...
139
- npx git-history-ui@latest
191
+ export GITHUB_TOKEN=ghp_... # fine-grained PAT, read-only on the repo
140
192
  ```
141
193
 
194
+ This hydrates the *Grouped* view with PR titles, authors, and labels.
195
+
142
196
  ## 🏭 Production
143
197
 
144
- ### Build for Production
145
198
  ```bash
146
- # Build both backend and frontend
147
- npm run build:production
148
-
149
- # Start production server
150
- npm run start:production
199
+ npm run build:production # build backend + frontend
200
+ npm run start:production # start the production server
151
201
  ```
152
202
 
153
203
  ### Docker
204
+
154
205
  ```bash
155
- # Build and run with Docker
156
206
  docker build -t git-history-ui .
157
207
  docker run -p 3000:3000 git-history-ui
158
208
  ```
159
209
 
160
210
  ## 🛠️ Development
161
211
 
162
- ### Setup
163
212
  ```bash
164
- # Clone and install
165
213
  git clone https://github.com/beingmartinbmc/git-history-ui.git
166
214
  cd git-history-ui
167
215
  npm install
168
-
169
- # Start development servers
170
- npm run dev
171
- ```
172
-
173
- ### Testing
174
- ```bash
175
- # Run backend tests
176
- npm test
177
-
178
- # Run frontend tests
216
+ npm run dev # runs backend + frontend with hot reload
217
+ npm test # backend tests
179
218
  cd frontend && npm test
180
219
  ```
181
220
 
182
221
  ## 📋 Requirements
183
222
 
184
- - **Node.js**: 20.19.0 or higher, or 22.12.0 or higher
185
- - **Git**: Any version (must be in a git repository)
223
+ - **Node.js**: 20.19.0+ or 22.12.0+
224
+ - **Git**: any version (must be in a git repository)
186
225
 
187
226
  ## 🤝 Contributing
188
227
 
@@ -194,8 +233,12 @@ cd frontend && npm test
194
233
 
195
234
  ## 📄 License
196
235
 
197
- MIT License - see [LICENSE](LICENSE) file for details
236
+ MIT see [LICENSE](LICENSE).
198
237
 
199
238
  ---
200
239
 
201
- Made with ❤️ for developers who love beautiful git visualizations
240
+ ## If this saved you time
241
+
242
+ [Star the repo](https://github.com/beingmartinbmc/git-history-ui) — it
243
+ helps more developers discover it, and it tells me which features to
244
+ double down on.
@@ -0,0 +1,2 @@
1
+ import{a as Jt,b as en,c as pn}from"./chunk-36NFLS3P.js";import{a as on}from"./chunk-R33W2FKN.js";import{a as Yt,b as qt,c as Kt,h as Xt}from"./chunk-YSTG766K.js";import{a as tn}from"./chunk-NUMLL3OZ.js";import{a as G}from"./chunk-N7UHDKJ7.js";import{e as rn,f as an,g as sn,h as ln,i as cn,j as dn,k as mn}from"./chunk-QUDEGJKI.js";import{d as Qt}from"./chunk-3FFYILBL.js";import{a as nn}from"./chunk-ITIFFECZ.js";import{$ as f,$a as Nt,Ab as _,B as _t,Bb as B,C as Me,Cb as Ne,D as vt,Db as jt,Ea as Pe,Fb as nt,Ga as l,Gb as it,Hb as ot,I as Ct,Ia as kt,Ja as et,Jb as ue,Ka as Pt,Lb as he,Ma as Rt,Mb as _e,N as xt,Ob as Ve,P as bt,Pa as E,Q as Je,Qa as pe,R as Oe,Ra as ie,Rb as Gt,S as Ee,Sa as tt,Sb as re,Ta as v,Tb as j,V as wt,Va as It,W as V,Wa as fe,Wb as Ht,X as de,Xa as Tt,Xb as Wt,Z as Y,Zb as $t,ab as ge,db as g,dc as U,e as ft,ea as S,eb as s,ec as R,fa as M,fb as a,fc as Fe,g as Xe,ga as yt,gb as b,hb as Vt,hc as I,i as gt,ib as Ft,j as N,ja as St,jb as zt,ka as De,kb as Re,la as Mt,lb as Ie,lc as Ut,m as ut,mb as F,mc as Zt,n as ht,nb as y,nc as ze,oa as x,ob as C,pb as Lt,q as $,qa as Ot,qb as At,ra as Et,s as Se,sa as ke,sb as q,tb as K,ub as X,va as me,vb as Te,wb as oe,xb as z,yb as Bt,za as Dt,zb as d}from"./chunk-TQE5NWMZ.js";var Le=class i{http=f(ze);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 On=["svg"];function En(i,n){i&1&&(s(0,"div",8),d(1,"No graph data \u2014 changed files have no detectable internal imports."),a())}var Ae=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=rn(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,m=new Map,u=(p,W,ee)=>{let k=m.get(p);return k||(k={id:p,group:W,label:ee},m.set(p,k)),k};for(let p of t.files)u(`f:${p}`,"changed",rt(p));for(let p of t.modules)u(`m:${p}`,"module",p);for(let p of t.dependencyRipple)u(`f:${p.from}`,"changed",rt(p.from)),u(`f:${p.to}`,"imported",rt(p.to));let c=[];for(let p of t.dependencyRipple)c.push({source:`f:${p.from}`,target:`f:${p.to}`,type:"imports"});for(let p of t.files){let W=Dn(p);t.modules.includes(W)&&c.push({source:`f:${p}`,target:`m:${W}`,type:"in-module"})}let h=Array.from(m.values()),O=e.append("g").attr("stroke","var(--border-strong)").attr("stroke-opacity",.5).selectAll("line").data(c).join("line").attr("stroke-dasharray",p=>p.type==="in-module"?"2,3":null),D=e.append("g").selectAll("circle").data(h).join("circle").attr("r",p=>p.group==="module"?7:5).attr("fill",p=>p.group==="changed"?"var(--accent)":p.group==="imported"?"#f59e0b":"#8b5cf6").attr("stroke","var(--bg-app)").attr("stroke-width",1.5).call(Z());D.append("title").text(p=>p.id.slice(2));let ye=e.append("g").selectAll("text").data(h).join("text").attr("class","node-label").attr("dx",8).attr("dy",3).text(p=>p.label);this.simulation?.stop(),this.simulation=dn(h).force("link",cn(c).id(p=>p.id).distance(60).strength(.4)).force("charge",mn().strength(-160)).force("center",sn(o/2,r/2)).force("collide",ln(14)).on("tick",()=>{O.attr("x1",p=>p.source.x??0).attr("y1",p=>p.source.y??0).attr("x2",p=>p.target.x??0).attr("y2",p=>p.target.y??0),D.attr("cx",p=>p.x??0).attr("cy",p=>p.y??0),ye.attr("x",p=>p.x??0).attr("y",p=>p.y??0)});function Z(){function p(k,P){k.active||window.__impactSim?.alphaTarget?.(.3).restart(),P.fx=P.x,P.fy=P.y}function W(k,P){P.fx=k.x,P.fy=k.y}function ee(k,P){k.active||window.__impactSim?.alphaTarget?.(0),P.fx=null,P.fy=null}return an().on("start",p).on("drag",W).on("end",ee)}window.__impactSim=this.simulation}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=E({type:i,selectors:[["app-impact-graph"]],viewQuery:function(e,t){if(e&1&&q(On,7),e&2){let o;K(o=X())&&(t.svgRef=o.first)}},inputs:{impact:"impact"},features:[ke],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),d(2," changed "),b(3,"span",3),d(4," imports "),b(5,"span",4),d(6," module "),a(),s(7,"div",5),yt(),b(8,"svg",6,0),v(10,En,2,0,"div",7),a()),e&2&&(l(10),g("ngIf",!t.hasData))},dependencies:[I,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 rt(i){let n=i.split("/");return n[n.length-1]}function Dn(i){let n=i.split("/");return n.length===1?"(root)":n.slice(0,Math.min(n.length-1,3)).join("/")}function kn(i,n){if(i&1&&(s(0,"span",36),d(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Pn(i,n){if(i&1&&(s(0,"span",37),d(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Rn(i,n){i&1&&(s(0,"span",38),d(1,"merge"),a())}function In(i,n){if(i&1&&(s(0,"pre",39),d(1),a()),i&2){let e=C().ngIf;l(),_(e.body)}}function Tn(i,n){if(i&1){let e=F();s(0,"div",40)(1,"span",41),d(2,"AI"),a(),s(3,"span",42),d(4),a(),s(5,"button",43),y("click",function(){S(e);let o=C(2);return M(o.explanation.set(null))}),d(6,"\xD7"),a()()}if(i&2){let e=n.ngIf;l(4),_(e)}}function Nn(i,n){if(i&1&&(s(0,"div",44),d(1),a()),i&2){let e=n.ngIf;l(),_(e)}}function Vn(i,n){if(i&1&&(s(0,"li"),d(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function Fn(i,n){if(i&1){let e=F();s(0,"li",28),y("click",function(){let o=S(e).$implicit,r=C(3);return M(r.state.selectHash(o.hash))}),s(1,"code"),d(2),a(),s(3,"span"),d(4),a()()}if(i&2){let e=n.$implicit;l(2),_(e.hash.slice(0,7)),l(2),_(e.subject)}}function zn(i,n){if(i&1&&(s(0,"div",45)(1,"div",46)(2,"span"),d(3,"Impact"),a(),s(4,"span",47),d(5),a()(),b(6,"app-impact-graph",48),s(7,"div",49)(8,"div")(9,"h4"),d(10,"Modules"),a(),s(11,"ul",50),v(12,Vn,2,1,"li",51),a()(),s(13,"div")(14,"h4"),d(15,"Related commits"),a(),s(16,"ul",52),v(17,Fn,5,2,"li",53),a()()()()),i&2){let e=n.ngIf;l(5),jt(" ",e.files.length," files \xB7 ",e.modules.length," modules \xB7 ",e.relatedCommits.length," related commits "),l(),g("impact",e),l(6),g("ngForOf",e.modules),l(5),g("ngForOf",e.relatedCommits)}}function Ln(i,n){if(i&1&&(s(0,"span",54),d(1),a()),i&2){let e=C(2);l(),_(e.files().length)}}function An(i,n){if(i&1&&(s(0,"span",63),d(1),a()),i&2){let e=C().$implicit;l(),B("+",e.additions)}}function Bn(i,n){if(i&1&&(s(0,"span",64),d(1),a()),i&2){let e=C().$implicit;l(),B("\u2212",e.deletions)}}function jn(i,n){if(i&1){let e=F();s(0,"div",55)(1,"button",56),y("click",function(){let o=S(e).$implicit,r=C(2);return M(r.selectFile(o))}),b(2,"span",57),s(3,"span",58),d(4),a(),s(5,"span",59),v(6,An,2,1,"span",60)(7,Bn,2,1,"span",61),a()(),s(8,"button",62),y("click",function(){let o=S(e).$implicit,r=C(2);return M(r.openFileHistory(o.file))}),d(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(),ge("data-status",t.status),l(),g("title",t.file),l(),_(t.file),l(2),g("ngIf",t.additions),l(),g("ngIf",t.deletions)}}function Gn(i,n){i&1&&(s(0,"div",65),d(1," No files changed. "),a())}function Hn(i,n){i&1&&(s(0,"div",65),d(1,"Loading\u2026"),a())}function Wn(i,n){if(i&1){let e=F();s(0,"div",66)(1,"div",67)(2,"strong"),d(3),a(),s(4,"span",68),d(5),he(6,"date"),a(),s(7,"button",69),y("click",function(){let o=S(e).$implicit,r=C(2);return M(r.deleteComment(o.id))}),d(8,"\xD7"),a()(),s(9,"p",70),d(10),a()()}if(i&2){let e=n.$implicit;l(3),_(e.author),l(2),_(_e(6,3,e.createdAt,"short")),l(5),_(e.body)}}function $n(i,n){if(i&1){let e=F();Re(0),s(1,"header",2)(2,"div",3)(3,"span",4),d(4),a(),s(5,"span",5),v(6,kn,2,1,"span",6)(7,Pn,2,1,"span",7)(8,Rn,2,0,"span",8),a()(),s(9,"h2",9),d(10),a(),s(11,"div",10)(12,"span"),d(13),a(),s(14,"span",11),d(15,"\u2022"),a(),s(16,"span"),d(17),he(18,"date"),a()(),v(19,In,2,1,"pre",12),s(20,"div",13)(21,"button",14),y("click",function(){S(e);let o=C();return M(o.onExplain())}),d(22),a(),s(23,"button",14),y("click",function(){S(e);let o=C();return M(o.onLoadImpact())}),d(24),a(),s(25,"button",15),y("click",function(){S(e);let o=C();return M(o.copyShareLink())}),d(26),a()(),v(27,Tn,7,1,"div",16)(28,Nn,2,1,"div",17),a(),v(29,zn,18,6,"div",18),s(30,"div",19)(31,"aside",20)(32,"div",21)(33,"span"),d(34,"Files"),a(),v(35,Ln,2,1,"span",22),a(),s(36,"div",23),v(37,jn,10,7,"div",24)(38,Gn,2,0,"div",25)(39,Hn,2,0,"div",25),a()(),s(40,"section",26)(41,"details",27)(42,"summary",28),y("click",function(o){S(e);let r=C();return M(r.toggleAnnotations(o))}),d(43),a(),s(44,"div",29),v(45,Wn,11,6,"div",30),s(46,"div",31)(47,"input",32),ot("ngModelChange",function(o){S(e);let r=C();return it(r.commentAuthor,o)||(r.commentAuthor=o),M(o)}),a(),s(48,"textarea",33),ot("ngModelChange",function(o){S(e);let r=C();return it(r.commentDraft,o)||(r.commentDraft=o),M(o)}),a(),s(49,"button",34),y("click",function(){S(e);let o=C();return M(o.addComment())}),d(50,"Post"),a()()()(),b(51,"app-diff-viewer",35),a()(),Ie()}if(i&2){let e=n.ngIf,t=C();l(4),_(e.shortHash),l(2),g("ngForOf",e.tags),l(),g("ngForOf",e.branches),l(),g("ngIf",e.isMerge),l(2),_(e.subject),l(3),Ne("",e.author," <",e.authorEmail,">"),l(4),_(_e(18,29,e.date,"medium")),l(2),g("ngIf",e.body),l(2),g("disabled",t.explaining()),l(),B(" ",t.explaining()?"...":"\u2728 Explain change"," "),l(),g("disabled",t.loadingImpact()),l(),B(" ",t.loadingImpact()?"...":t.impact()?"Refresh impact":"Show impact"," "),l(2),B(" ",t.shareCopied()?"Copied!":"\u{1F517} Share"," "),l(),g("ngIf",t.explanation()),l(),g("ngIf",t.explainError()),l(),g("ngIf",t.impact()),l(6),g("ngIf",t.files().length),l(2),g("ngForOf",t.files())("ngForTrackBy",t.trackByFile),l(),g("ngIf",!t.files().length&&!t.loading()),l(),g("ngIf",t.loading()),l(2),g("open",t.annotationsOpen()),l(2),B(" \u{1F4AC} Notes (",t.comments().length,") "),l(2),g("ngForOf",t.comments()),l(2),nt("ngModel",t.commentAuthor),l(),nt("ngModel",t.commentDraft),l(),g("disabled",!t.commentDraft.trim()),l(2),g("fileInput",t.activeFile())}}function Un(i,n){i&1&&(s(0,"div",71)(1,"p",72),d(2,"No commit selected"),a(),s(3,"p",73),d(4," Pick a commit from the list, or press "),s(5,"kbd",74),d(6,"\u2318K"),a(),d(7," to open the command palette. "),a()())}var Be=class i{state=f(G);git=f(tn);insightsApi=f(nn);annotationsApi=f(Le);router=f(Qt);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=en(Jt(this.commit).pipe(Oe(n=>n?(this.loading.set(!0),this.git.getDiff(n.hash).pipe(vt(()=>$([])))):(this.loading.set(!1),$([])))),{initialValue:[]});activeFileIndex=x(0);activeFile=re(()=>{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=E({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,$n,52,32,"ng-container",1)(1,Un,8,0,"ng-template",null,0,Ve),e&2){let o=Te(2);g("ngIf",t.commit())("ngIfElse",o)}},dependencies:[I,U,R,Xt,Yt,qt,Kt,on,Ae,Fe],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 Zn=["canvas"],Qn=["scroll"];function Yn(i,n){if(i&1&&b(0,"span",10),i&2){let e=n.$implicit;oe("background",e)}}function qn(i,n){i&1&&(s(0,"div",11),d(1," No commits to draw. "),a())}var T=34,xe=24,je=5.5,at=16,ae=16,st=["#4f46e5","#06b6d4","#f59e0b","#ef4444","#10b981","#8b5cf6","#ec4899","#0ea5e9"],Ge=class i{state=f(G);theme=f(pn);legendColors=st.slice(0,5);graphSummary=re(()=>{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();scrollRaf=0;onScroll=()=>{this.scrollRaf||(this.scrollRaf=requestAnimationFrame(()=>{this.scrollRaf=0,this.draw()}))};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.scrollRef?.nativeElement.addEventListener("scroll",this.onScroll,{passive:!0}),this.draw()}ngOnDestroy(){let n=this.canvasRef?.nativeElement;n?.removeEventListener("click",this.onCanvasClick),n?.removeEventListener("mousemove",this.onCanvasMove),n?.removeEventListener("mouseleave",this.onCanvasLeave),this.scrollRef?.nativeElement.removeEventListener("scroll",this.onScroll),this.scrollRaf&&cancelAnimationFrame(this.scrollRaf)}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 m=e.indexOf(null);return m>=0?(e[m]=o,m):(e.push(o),e.length-1)};for(let o=0;o<n.length;o++){let r=n[o],m=t(r.hash),u={commit:r,row:o,lane:m};this.nodes.push(u),this.rowByHash.set(r.hash,u);let[c,...h]=r.parents;e[m]=c??null;for(let O of h)if(e.indexOf(O)===-1){let D=e.indexOf(null);D>=0?e[D]=O:e.push(O)}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=at*2+this.laneCount*xe,r=ae*2+Math.max(this.nodes.length,1)*T,m=Math.max(o,e?.clientWidth??o),u=Math.max(r,e?.clientHeight??r);n.width=Math.floor(m*t),n.height=Math.floor(u*t),n.style.width=`${m}px`,n.style.height=`${u}px`;let c=n.getContext("2d"),h=this.readTheme(n);if(c.setTransform(t,0,0,t,0,0),c.clearRect(0,0,m,u),c.fillStyle=h.surface,c.fillRect(0,0,m,u),!this.nodes.length)return;let O=w=>at+w*xe+xe/2,D=w=>ae+w*T+T/2,ye=w=>st[w%st.length],Z=this.state.selectedHash(),p=e?.scrollTop??0,W=e?.clientHeight??u,ee=T*4,k=Math.max(0,Math.floor((p-ae-ee)/T)),P=Math.min(this.nodes.length-1,Math.ceil((p+W-ae+ee)/T)),Ke=this.nodes.slice(k,P+1);this.drawRows(c,m,O,D,h,Z,Ke),this.drawGuides(c,D,h),c.lineCap="round",c.lineJoin="round";for(let w of Ke)for(let le of w.commit.parents){let H=this.rowByHash.get(le);if(!H)continue;let te=O(w.lane),Q=D(w.row),ce=O(H.lane),ne=D(H.row);c.strokeStyle=h.shadow,c.lineWidth=4,c.globalAlpha=.35,this.drawEdge(c,te,Q,ce,ne),c.strokeStyle=ye(H.lane),c.lineWidth=w.commit.isMerge?2.6:2.2,c.globalAlpha=Z&&Z!==w.commit.hash&&Z!==H.commit.hash?.55:.9,this.drawEdge(c,te,Q,ce,ne),c.globalAlpha=1}for(let w of Ke){let le=O(w.lane),H=D(w.row),te=ye(w.lane),Q=w.commit.hash===Z,ce=w.row===this.hoverRow,ne=Q?je+2:ce?je+1:je;c.beginPath(),c.arc(le,H,ne+3,0,Math.PI*2),c.fillStyle=h.nodeRing,c.fill(),c.beginPath(),c.arc(le,H,ne,0,Math.PI*2),c.fillStyle=w.commit.isMerge?h.surface:te,c.fill(),c.lineWidth=w.commit.isMerge||Q?2.5:1.75,c.strokeStyle=te,c.stroke(),(Q||ce)&&(c.beginPath(),c.arc(le,H,ne+5,0,Math.PI*2),c.strokeStyle=te,c.globalAlpha=Q?.42:.24,c.lineWidth=2,c.stroke(),c.globalAlpha=1)}}drawRows(n,e,t,o,r,m,u=this.nodes){for(let c of u){let h=o(c.row)-T/2;if(c.row%2===1&&(n.fillStyle=r.rowAlt,n.fillRect(0,h,e,T)),(c.row===this.hoverRow||c.commit.hash===m)&&(n.fillStyle=c.commit.hash===m?r.rowSelected:r.rowHover,this.roundRect(n,6,h+3,e-12,T-6,8),n.fill()),c.commit.branches.length||c.commit.tags.length){let O=t(c.lane)+je+8;this.drawRefPill(n,O,o(c.row),c.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=at+o*xe+xe/2;n.beginPath(),n.moveTo(r,ae/2),n.lineTo(r,e(this.nodes.length-1)+T/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 m=t+Math.min(T*.75,Math.max(12,(r-t)*.36));n.bezierCurveTo(e,m,o,m,o,r)}n.stroke()}drawRefPill(n,e,t,o,r){let m=o.tags[0]??o.branches[0];if(!m)return;let u=m.length>16?`${m.slice(0,15)}...`:m;n.font="600 10px ui-sans-serif, system-ui, sans-serif";let c=Math.min(96,n.measureText(u).width+14),h=18;n.fillStyle=o.tags.length?r.warningSoft:r.accentSoft,this.roundRect(n,e,t-h/2,c,h,999),n.fill(),n.fillStyle=o.tags.length?r.warning:r.accent,n.fillText(u,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-ae)/T);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,m){let u=Math.min(m,o/2,r/2);n.beginPath(),n.moveTo(e+u,t),n.arcTo(e+o,t,e+o,t+r,u),n.arcTo(e+o,t+r,e,t+r,u),n.arcTo(e,t+r,e,t,u),n.arcTo(e,t,e+o,t,u),n.closePath()}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=E({type:i,selectors:[["app-commit-graph"]],viewQuery:function(e,t){if(e&1&&(q(Zn,7),q(Qn,7)),e&2){let o;K(o=X())&&(t.canvasRef=o.first),K(o=X())&&(t.scrollRef=o.first)}},hostBindings:function(e,t){e&1&&y("resize",function(){return t.onResize()},Pe)},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"),d(3,"Graph"),a(),s(4,"span",4),d(5),a()(),s(6,"div",5),v(7,Yn,1,2,"span",6),a()(),s(8,"div",7,0),b(10,"canvas",8,1),v(12,qn,2,0,"div",9),a()),e&2&&(l(5),_(t.graphSummary()),l(2),g("ngForOf",t.legendColors),l(5),g("ngIf",!t.state.commits().length&&!t.state.loading()))},dependencies:[I,U,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 be(i,n=0){return Kn(i)?Number(i):arguments.length===2?n:0}function Kn(i){return!isNaN(parseFloat(i))&&!isNaN(Number(i))}function fn(i){return i instanceof me?i.nativeElement:i}var lt;try{lt=typeof Intl<"u"&&Intl.v8BreakIterator}catch{lt=!1}var He=(()=>{class i{_platformId=f(Dt);isBrowser=this._platformId?Ut(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||lt)&&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 Xn=new Y("cdk-dir-doc",{providedIn:"root",factory:Jn});function Jn(){return f(De)}var ei=/^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i;function ti(i){let n=i?.toLowerCase()||"";return n==="auto"&&typeof navigator<"u"&&navigator?.language?ei.test(navigator.language)?"rtl":"ltr":n==="rtl"?"rtl":"ltr"}var gn=(()=>{class i{get value(){return this.valueSignal()}valueSignal=x("ltr");change=new It;constructor(){let e=f(Xn,{optional:!0});if(e){let t=e.body?e.body.dir:null,o=e.documentElement?e.documentElement.dir:null;this.valueSignal.set(ti(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||{}),We,J;function un(){if(J==null){if(typeof document!="object"||!document||typeof Element!="function"||!Element)return J=!1,J;if(document.documentElement?.style&&"scrollBehavior"in document.documentElement.style)J=!0;else{let i=Element.prototype.scrollTo;i?J=!/\{\s*\[native code\]\s*\}/.test(i.toString()):J=!1}}return J}function se(){if(typeof document!="object"||!document)return A.NORMAL;if(We==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),We=A.NORMAL,i.scrollLeft===0&&(i.scrollLeft=1,We=i.scrollLeft===0?A.NEGATED:A.INVERTED),i.remove()}return We}var ct=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=pe({type:i});static \u0275inj=de({})}return i})();var $e=class{};function hn(i){return i&&typeof i.connect=="function"&&!(i instanceof gt)}var Ue=class extends $e{_data;constructor(n){super(),this._data=n}connect(){return Se(this._data)?this._data:$(this._data)}disconnect(){}},we=(function(i){return i[i.REPLACED=0]="REPLACED",i[i.INSERTED=1]="INSERTED",i[i.MOVED=2]="MOVED",i[i.REMOVED=3]="REMOVED",i})(we||{}),dt=new Y("_ViewRepeater"),Ze=class{viewCacheSize=20;_viewCache=[];applyChanges(n,e,t,o,r){n.forEachOperation((m,u,c)=>{let h,O;if(m.previousIndex==null){let D=()=>t(m,u,c);h=this._insertView(D,c,e,o(m)),O=h?we.INSERTED:we.REPLACED}else c==null?(this._detachAndCacheView(u,e),O=we.REMOVED):(h=this._moveView(u,c,e,o(m)),O=we.MOVED);r&&r({context:h?.context,operation:O,record:m})})}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 m=n();return t.createEmbeddedView(m.templateRef,m.context,m.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 ni=["contentWrapper"],ii=["*"],xn=new Y("VIRTUAL_SCROLL_STRATEGY"),mt=class{_scrolledIndexChange=new N;scrolledIndexChange=this._scrolledIndexChange.pipe(Ct());_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(),m=this._itemSize>0?r/this._itemSize:0;if(e.end>o){let c=Math.ceil(t/this._itemSize),h=Math.max(0,Math.min(m,o-c));m!=h&&(m=h,r=h*this._itemSize,e.start=Math.floor(m)),e.end=Math.max(0,Math.min(o,e.start+c))}let u=r-e.start*this._itemSize;if(u<this._minBufferPx&&e.start!=0){let c=Math.ceil((this._maxBufferPx-u)/this._itemSize);e.start=Math.max(0,e.start-c),e.end=Math.min(o,Math.ceil(m+(t+this._minBufferPx)/this._itemSize))}else{let c=e.end*this._itemSize-(r+t);if(c<this._minBufferPx&&e.end!=o){let h=Math.ceil((this._maxBufferPx-c)/this._itemSize);h>0&&(e.end=Math.min(o,e.end+h),e.start=Math.max(0,Math.floor(m-this._minBufferPx/this._itemSize)))}}this._viewport.setRenderedRange(e),this._viewport.setRenderedContentOffset(Math.round(this._itemSize*e.start)),this._scrolledIndexChange.next(Math.floor(m))}};function oi(i){return i._scrollStrategy}var bn=(()=>{class i{get itemSize(){return this._itemSize}set itemSize(e){this._itemSize=be(e)}_itemSize=20;get minBufferPx(){return this._minBufferPx}set minBufferPx(e){this._minBufferPx=be(e)}_minBufferPx=100;get maxBufferPx(){return this._maxBufferPx}set maxBufferPx(e){this._maxBufferPx=be(e)}_maxBufferPx=200;_scrollStrategy=new mt(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=ie({type:i,selectors:[["cdk-virtual-scroll-viewport","itemSize",""]],inputs:{itemSize:"itemSize",minBufferPx:"minBufferPx",maxBufferPx:"maxBufferPx"},features:[ue([{provide:xn,useFactory:oi,deps:[wt(()=>i)]}]),ke]})}return i})(),ri=20,ai=(()=>{class i{_ngZone=f(fe);_platform=f(He);_renderer=f(et).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=ri){return this._platform.isBrowser?new Xe(t=>{this._cleanupGlobalListener||(this._cleanupGlobalListener=this._ngZone.runOutsideAngular(()=>this._renderer.listen("document","scroll",()=>this._scrolled.next())));let o=e>0?this._scrolled.pipe(Me(e)).subscribe(t):this._scrolled.subscribe(t);return this._scrolledCount++,()=>{o.unsubscribe(),this._scrolledCount--,this._scrolledCount||(this._cleanupGlobalListener?.(),this._cleanupGlobalListener=void 0)}}):$()}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(_t(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=fn(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})(),wn=(()=>{class i{elementRef=f(me);scrollDispatcher=f(ai);ngZone=f(fe);dir=f(gn,{optional:!0});_scrollElement=this.elementRef.nativeElement;_destroyed=new N;_renderer=f(Pt);_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&&se()!=A.NORMAL?(e.left!=null&&(e.right=t.scrollWidth-t.clientWidth-e.left),se()==A.INVERTED?e.left=e.right:se()==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;un()?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 m=this.dir&&this.dir.value=="rtl";return e=="start"?e=m?o:t:e=="end"&&(e=m?t:o),m&&se()==A.INVERTED?e==t?r.scrollWidth-r.clientWidth-r.scrollLeft:r.scrollLeft:m&&se()==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=ie({type:i,selectors:[["","cdk-scrollable",""],["","cdkScrollable",""]]})}return i})(),si=20,li=(()=>{class i{_platform=f(He);_listeners;_viewportSize;_change=new N;_document=f(De);constructor(){let e=f(fe),t=f(et).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(),m=-r.top||e.body.scrollTop||t.scrollY||o.scrollTop||0,u=-r.left||e.body.scrollLeft||t.scrollX||o.scrollLeft||0;return{top:m,left:u}}change(e=si){return e>0?this._change.pipe(Me(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})(),_n=new Y("VIRTUAL_SCROLLABLE"),ci=(()=>{class i extends wn{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=ie({type:i,features:[tt]})}return i})();function di(i,n){return i.start==n.start&&i.end==n.end}var mi=typeof requestAnimationFrame<"u"?ht:ut,pt=(()=>{class i extends ci{elementRef=f(me);_changeDetectorRef=f(Ht);_scrollStrategy=f(xn,{optional:!0});scrollable=f(_n,{optional:!0});_platform=f(He);_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 Xe(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=ft.EMPTY;_injector=f(St);_isDestroyed=!1;constructor(){super();let e=f(li);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(Nt).injector});f(Mt).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(Je(null),Me(0,mi),Ee(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(Ee(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){di(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",m=r?"X":"Y",c=`translate${m}(${Number((r&&o?-1:1)*e)}px)`;this._renderedContentOffset=e,t==="to-end"&&(c+=` translate${m}(-100%)`,this._renderedContentOffsetNeedsRewrite=!0),this._renderedContentTransform!=c&&(this._renderedContentTransform=c,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",m=this.dir?.value=="rtl";e=="start"?t=m?r:o:e=="end"?t=m?o:r:e?t=e:t=this.orientation==="horizontal"?"left":"top";let u=this.scrollable.measureBoundingClientRectWithScrollOffset(t);return this.elementRef.nativeElement.getBoundingClientRect()[t]-u}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),!Gt(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,Tt(()=>{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=E({type:i,selectors:[["cdk-virtual-scroll-viewport"]],viewQuery:function(t,o){if(t&1&&q(ni,7),t&2){let r;K(r=X())&&(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",$t]},outputs:{scrolledIndexChange:"scrolledIndexChange"},features:[ue([{provide:wn,useFactory:(e,t)=>e||t,deps:[[new Et,new Ot(_n)],i]}]),tt],ngContentSelectors:ii,decls:4,vars:4,consts:[["contentWrapper",""],[1,"cdk-virtual-scroll-content-wrapper"],[1,"cdk-virtual-scroll-spacer"]],template:function(t,o){t&1&&(Lt(),Vt(0,"div",1,0),At(2),Ft(),zt(3,"div",2)),t&2&&(l(3),oe("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 vn(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 yn=(()=>{class i{_viewContainerRef=f(Rt);_template=f(kt);_differs=f(Wt);_viewRepeater=f(dt);_viewport=f(pt,{skipSelf:!0});viewChange=new N;_dataSourceChanges=new N;get cdkVirtualForOf(){return this._cdkVirtualForOf}set cdkVirtualForOf(e){this._cdkVirtualForOf=e,hn(e)?this._dataSourceChanges.next(e):this._dataSourceChanges.next(new Ue(Se(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=be(e)}dataStream=this._dataSourceChanges.pipe(Je(null),xt(),Oe(([e,t])=>this._changeDataSource(e,t)),bt(1));_differ=null;_data;_renderedItems;_renderedRange;_needsUpdate=!1;_destroyed=new N;constructor(){let e=f(fe);this.dataStream.subscribe(t=>{this._data=t,this._onRenderedDataChange()}),this._viewport.renderedRangeStream.pipe(Ee(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,m,u;for(let c=0;c<r;c++){let h=this._viewContainerRef.get(c+o);if(h&&h.rootNodes.length){m=u=h.rootNodes[0];break}}for(let c=r-1;c>-1;c--){let h=this._viewContainerRef.get(c+o);if(h&&h.rootNodes.length){u=h.rootNodes[h.rootNodes.length-1];break}}return m&&u?vn(t,"end",u)-vn(t,"start",m):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):$()}_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,m,u)=>this._getEmbeddedViewArgs(r,u),r=>r.item),e.forEachIdentityChange(r=>{let m=this._viewContainerRef.get(r.currentIndex);m.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=ie({type:i,selectors:[["","cdkVirtualFor","","cdkVirtualForOf",""]],inputs:{cdkVirtualForOf:"cdkVirtualForOf",cdkVirtualForTrackBy:"cdkVirtualForTrackBy",cdkVirtualForTemplate:"cdkVirtualForTemplate",cdkVirtualForTemplateCacheSize:"cdkVirtualForTemplateCacheSize"},features:[ue([{provide:dt,useClass:Ze}])]})}return i})();var Cn=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=pe({type:i});static \u0275inj=de({})}return i})(),Sn=(()=>{class i{static \u0275fac=function(t){return new(t||i)};static \u0275mod=pe({type:i});static \u0275inj=de({imports:[ct,Cn,ct,Cn]})}return i})();function fi(i,n){i&1&&b(0,"span",19)}function gi(i,n){if(i&1&&(s(0,"span",23),d(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function ui(i,n){if(i&1&&(s(0,"span",24),d(1),a()),i&2){let e=n.$implicit;l(),_(e)}}function hi(i,n){if(i&1&&(s(0,"span",20),v(1,gi,2,1,"span",21)(2,ui,2,1,"span",22),a()),i&2){let e=C().$implicit;l(),g("ngForOf",e.tags),l(),g("ngForOf",e.branches)}}function _i(i,n){if(i&1){let e=F();s(0,"button",7),y("click",function(){let o=S(e).$implicit,r=C();return M(r.select(o))}),s(1,"span",8),b(2,"span",9),v(3,fi,1,0,"span",10),a(),s(4,"span",11)(5,"span",12),d(6),a(),s(7,"span",13)(8,"span",14),d(9),a(),s(10,"span",15),d(11,"\u2022"),a(),s(12,"span",16),d(13),a(),s(14,"span",15),d(15,"\u2022"),a(),s(16,"span",17),d(17),he(18,"date"),a()()(),v(19,hi,3,2,"span",18),a()}if(i&2){let e=n.$implicit,t=n.index,o=C();z("selected",e.hash===o.selectedHash()),ge("aria-current",e.hash===o.selectedHash()?"true":null),l(2),oe("background",o.laneColor(e)),z("merge",e.isMerge),l(),g("ngIf",t<o.commits().length-1),l(2),g("title",e.subject),l(),_(e.subject),l(3),_(e.shortHash),l(4),_(e.author),l(4),_(_e(18,14,e.date,"MMM d, y, h:mm a")),l(2),g("ngIf",e.branches.length||e.tags.length)}}function vi(i,n){i&1&&(s(0,"div",25)(1,"p"),d(2,"No commits match your filters."),a()())}var Qe=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=E({type:i,selectors:[["app-commit-list"]],hostBindings:function(e,t){e&1&&y("keydown",function(r){return t.onKey(r)},Pe)},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),d(2),a(),s(3,"span",2)(4,"kbd",3),d(5,"j"),a(),d(6,"/"),s(7,"kbd",3),d(8,"k"),a(),d(9," navigate "),a()(),s(10,"cdk-virtual-scroll-viewport",4),v(11,_i,20,17,"button",5)(12,vi,3,0,"div",6),a()),e&2&&(l(2),Ne("",t.commits().length," of ",t.state.total()),l(8),g("itemSize",64),l(),g("cdkVirtualForOf",t.commits())("cdkVirtualForTrackBy",t.trackByHash),l(),g("ngIf",!t.commits().length&&!t.state.loading()))},dependencies:[I,U,R,Sn,bn,yn,pt,Fe],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 Ye=class i{http=f(ze);base="/api";list(n={}){let e=new Zt;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 Ci(i,n){if(i&1&&(s(0,"span",7),d(1),a()),i&2){let e=n.ngIf;l(),B("",e," groups")}}function xi(i,n){i&1&&(s(0,"div",8),d(1,"Loading groups\u2026"),a())}function bi(i,n){if(i&1&&(s(0,"div",9),d(1),a()),i&2){let e=n.ngIf;l(),_(e)}}function wi(i,n){i&1&&(s(0,"div",8),d(1," No groups detected. Try the flat view. "),a())}function yi(i,n){if(i&1&&(s(0,"span",18),d(1),a()),i&2){let e=C().$implicit;l(),B("#",e.prNumber)}}function Si(i,n){if(i&1){let e=F();s(0,"li",21),y("click",function(){let o=S(e).$implicit,r=C(3);return M(r.state.selectHash(o))}),s(1,"code",22),d(2),a(),s(3,"span",23),d(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 Mi(i,n){if(i&1&&(s(0,"ul",19),v(1,Si,5,4,"li",20),a()),i&2){let e=C().$implicit;l(),g("ngForOf",e.commits)}}function Oi(i,n){if(i&1){let e=F();s(0,"li",10)(1,"button",11),y("click",function(){let o=S(e).$implicit,r=C();return M(r.toggle(o.id))}),s(2,"span",12),d(3,"\u25B8"),a(),s(4,"span",13),d(5),a(),v(6,yi,2,1,"span",14),s(7,"span",15),d(8),a(),s(9,"span",16),d(10),a()(),v(11,Mi,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),Bt("src-"+e.source),l(),_(t.sourceLabel(e.source)),l(),g("ngIf",e.prNumber),l(2),_(e.title),l(2),_(e.commits.length),l(),g("ngIf",t.isExpanded(e.id))}}var qe=class i{state=f(G);groupsApi=f(Ye);groups=x(null);loading=x(!1);error=x(null);expanded=x(new Set);subjectMap=re(()=>{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(m=>m.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=E({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),d(2,"PR / feature groups"),a(),v(3,Ci,2,1,"span",2),a(),v(4,xi,2,0,"div",3)(5,bi,2,1,"div",4)(6,wi,2,0,"div",3),s(7,"ul",5),v(8,Oi,12,11,"li",6),a()),e&2){let o,r;l(3),g("ngIf",(o=t.groups())==null?null:o.length),l(),g("ngIf",t.loading()),l(),g("ngIf",t.error()),l(),g("ngIf",!t.loading()&&!t.error()&&(((r=t.groups())==null?null:r.length)??0)===0),l(2),g("ngForOf",t.groups())}},dependencies:[I,U,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 Ei(i,n){i&1&&(Re(0),b(1,"app-grouped-list"),Ie())}function Di(i,n){i&1&&b(0,"app-commit-list")}var Mn=class i{state=f(G);static \u0275fac=function(e){return new(e||i)};static \u0275cmp=E({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,Ei,2,0,"ng-container",4)(5,Di,1,0,"ng-template",null,0,Ve),a(),s(7,"section",5),b(8,"app-commit-detail"),a()()),e&2){let o=Te(6);l(4),g("ngIf",t.state.viewMode()==="grouped")("ngIfElse",o)}},dependencies:[I,R,Ge,Qe,Be,qe],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{Mn as HomeShellComponent};
@@ -12,5 +12,5 @@
12
12
  <style>*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}:root{color-scheme:light;--bg-app:#f7f8fa;--bg-surface:#ffffff;--bg-surface-2:#f3f4f6;--bg-elevated:#ffffff;--bg-hover:#eef2ff;--bg-selected:#e0e7ff;--bg-overlay:rgba(15, 23, 42, .45);--fg-primary:#0f172a;--fg-secondary:#475569;--fg-muted:#64748b;--fg-subtle:#94a3b8;--fg-inverted:#ffffff;--border-soft:#e2e8f0;--border-strong:#cbd5e1;--border-focus:#6366f1;--accent:#4f46e5;--accent-hover:#4338ca;--accent-soft:#eef2ff;--accent-fg:#ffffff;--success:#16a34a;--warning:#d97706;--danger:#dc2626;--diff-add-bg:#dcfce7;--diff-add-fg:#14532d;--diff-add-gutter:#86efac;--diff-del-bg:#fee2e2;--diff-del-fg:#7f1d1d;--diff-del-gutter:#fca5a5;--diff-hunk-bg:#f1f5f9;--diff-hunk-fg:#475569;--graph-guide:rgba(148, 163, 184, .28);--graph-row-alt:rgba(15, 23, 42, .025);--graph-row-hover:rgba(79, 70, 229, .08);--graph-row-selected:rgba(79, 70, 229, .14);--graph-node-ring:#ffffff;--graph-shadow:rgba(15, 23, 42, .12);--shadow-sm:0 1px 2px rgba(15, 23, 42, .06);--shadow-md:0 4px 12px rgba(15, 23, 42, .08);--shadow-lg:0 12px 32px rgba(15, 23, 42, .12);--radius-sm:6px;--radius-md:10px;--radius-lg:14px;--font-sans:ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Liberation Mono", "Courier New", monospace}*,*:before,*:after{box-sizing:border-box}html,body{height:100%}body{margin:0;font-family:var(--font-sans);font-size:14px;line-height:1.5;color:var(--fg-primary);background:var(--bg-app);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}</style><link rel="stylesheet" href="styles-BOEVKAI2.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-BOEVKAI2.css"></noscript></head>
13
13
  <body>
14
14
  <app-root></app-root>
15
- <link rel="modulepreload" href="chunk-36NFLS3P.js"><link rel="modulepreload" href="chunk-YSTG766K.js"><link rel="modulepreload" href="chunk-NUMLL3OZ.js"><link rel="modulepreload" href="chunk-N7UHDKJ7.js"><link rel="modulepreload" href="chunk-3FFYILBL.js"><link rel="modulepreload" href="chunk-TQE5NWMZ.js"><script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-6LGPRO6C.js" type="module"></script></body>
15
+ <link rel="modulepreload" href="chunk-36NFLS3P.js"><link rel="modulepreload" href="chunk-YSTG766K.js"><link rel="modulepreload" href="chunk-NUMLL3OZ.js"><link rel="modulepreload" href="chunk-N7UHDKJ7.js"><link rel="modulepreload" href="chunk-3FFYILBL.js"><link rel="modulepreload" href="chunk-TQE5NWMZ.js"><script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-AW2YOC32.js" type="module"></script></body>
16
16
  </html>