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