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 +59 -0
- package/README.md +156 -113
- package/build/frontend/chunk-NDC3FSO4.js +2 -0
- package/build/frontend/index.html +1 -1
- package/build/frontend/{main-6LGPRO6C.js → main-AW2YOC32.js} +1 -1
- package/dist/backend/cache/sqliteIndex.d.ts +43 -0
- package/dist/backend/cache/sqliteIndex.js +221 -0
- package/dist/backend/cache/sqliteIndex.js.map +1 -0
- package/dist/backend/gitService.d.ts +10 -0
- package/dist/backend/gitService.js +35 -0
- package/dist/backend/gitService.js.map +1 -1
- package/dist/backend/presets.d.ts +18 -0
- package/dist/backend/presets.js +60 -0
- package/dist/backend/presets.js.map +1 -0
- package/dist/backend/server.js +80 -1
- package/dist/backend/server.js.map +1 -1
- package/dist/cli.js +96 -5
- package/dist/cli.js.map +1 -1
- package/package.json +4 -1
- package/build/frontend/chunk-3BORCJKI.js +0 -2
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
|
[](https://www.npmjs.com/package/git-history-ui)
|
|
6
6
|
[](https://bundlephobia.com/result?p=git-history-ui)
|
|
7
7
|
[](https://github.com/beingmartinbmc/git-history-ui)
|
|
8
|
-
[](https://github.com/beingmartinbmc/git-history-ui/issues)
|
|
9
8
|
|
|
10
|
-
**Git Intelligence in your browser.**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|

|
|
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
|
|
31
|
-
It reads history from the current working
|
|
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
|
|
78
|
-
commits
|
|
79
|
-
- **vs `tig`
|
|
80
|
-
insights dashboard.
|
|
81
|
-
- **vs desktop clients (GitKraken, SourceTree, Fork)
|
|
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
|
|
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
|
-
##
|
|
98
|
+
## 📦 All features
|
|
87
99
|
|
|
88
|
-
|
|
100
|
+
<details>
|
|
101
|
+
<summary><strong>Click to expand the full feature list</strong></summary>
|
|
89
102
|
|
|
90
|
-
|
|
103
|
+
### Exploration
|
|
91
104
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
npx git-history-ui@latest --file src/app.js
|
|
112
|
+
### Code understanding
|
|
98
113
|
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
npx git-history-ui@latest --since 2024-01-01
|
|
123
|
+
### Collaboration
|
|
104
124
|
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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 #
|
|
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
|
-
#
|
|
147
|
-
npm run
|
|
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
|
-
#
|
|
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
|
|
185
|
-
- **Git**:
|
|
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
|
|
236
|
+
MIT — see [LICENSE](LICENSE).
|
|
198
237
|
|
|
199
238
|
---
|
|
200
239
|
|
|
201
|
-
|
|
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-
|
|
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>
|