guito 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to Guito are documented in this file. The project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and this changelog is structured around [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## [0.5.4] - 2026-09-06
6
+
7
+ ### Changed
8
+
9
+ - The commit table now renders only the visible rows with virtual scrolling, so scrolling stays smooth no matter how much history is loaded.
10
+ - The commit graph is drawn as a viewport-sized overlay that tracks the scroll position, replacing the full-height SVG canvas.
11
+ - Lane assignment for large histories (over 2,000 commits) runs in a web worker, keeping the interface responsive after "Load all" on repositories with 10k+ commits.
12
+ - The commit list payload omits commit bodies, roughly halving its size; the detail pane fetches the body on demand when a commit is opened.
13
+ - Commit body search now runs on the server, preserving full-history body matching with the trimmed list payload.
14
+ - Working-tree actions (discard, reset, clean) refresh only the working-tree status instead of refetching the commit history, branches, and repository info.
15
+ - Refreshing or searching aborts superseded in-flight requests instead of letting stale responses race newer ones.
16
+
5
17
  ## [0.5.3] - 2026-09-06
6
18
 
7
19
  ### Changed
@@ -147,13 +147,15 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
147
147
  const skip = Number.parseInt(query.skip ?? '', 10);
148
148
  // Build simple-git options conditionally: an explicit key with an
149
149
  // undefined value would still be emitted as a bare command flag.
150
+ // The commit body is intentionally omitted: it roughly doubles the
151
+ // payload and is only needed when a commit is opened, so it is served
152
+ // on demand by /api/commit/detail instead.
150
153
  const options = {
151
154
  format: {
152
155
  hash: '%H',
153
156
  date: '%aI',
154
157
  message: '%s',
155
158
  refs: '%D',
156
- body: '%b',
157
159
  author_name: '%aN',
158
160
  author_email: '%aE',
159
161
  parents: '%P',
@@ -188,6 +190,35 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
188
190
  return resp.status(400).type('application/json').send({ error: err.message });
189
191
  }
190
192
  });
193
+ // Full-text commit search. The list payload omits commit bodies, so body
194
+ // matching runs here (git --grep covers subject and body together) and the
195
+ // client unions the resulting hashes with its local subject/author/hash
196
+ // matches over the loaded history.
197
+ app.get('/api/commits/search', async (req, resp) => {
198
+ const query = String((req.query ?? {}).query ?? '').trim();
199
+ if (!query) {
200
+ return resp.type('application/json').send({ hashes: [] });
201
+ }
202
+ try {
203
+ const raw = await git.raw([
204
+ 'log',
205
+ '--all',
206
+ '-i',
207
+ '--fixed-strings',
208
+ `--grep=${query}`,
209
+ '--format=%H',
210
+ ]);
211
+ return resp
212
+ .type('application/json')
213
+ .send({ hashes: raw.split('\n').map((hash) => hash.trim()).filter(Boolean) });
214
+ }
215
+ catch (err) {
216
+ if (/does not have any commits yet|unknown revision|bad default revision/i.test(err.message ?? '')) {
217
+ return resp.type('application/json').send({ hashes: [] });
218
+ }
219
+ return resp.status(400).type('application/json').send({ error: err.message });
220
+ }
221
+ });
191
222
  app.post('/api/commit', async (req, resp) => {
192
223
  try {
193
224
  const { message, description } = req.body;
@@ -574,6 +605,39 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
574
605
  return resp.status(400).type('application/json').send({ error: err.message });
575
606
  }
576
607
  });
608
+ // ==================== Commit Detail ====================
609
+ app.post('/api/commit/detail', async (req, resp) => {
610
+ try {
611
+ const { hash } = req.body;
612
+ if (!hash) {
613
+ return resp.status(400).type('application/json').send({ error: 'hash required' });
614
+ }
615
+ // Fields are NUL-separated; %b may span lines and contain anything
616
+ // except NUL, so it cannot be split naively by newline.
617
+ const raw = await git.raw([
618
+ 'show',
619
+ '-s',
620
+ '--format=%H%x00%aI%x00%s%x00%b%x00%aN%x00%aE%x00%P',
621
+ hash,
622
+ ]);
623
+ const [fullHash, date, message, body, authorName, authorEmail, parents] = raw.trimEnd().split('\u0000');
624
+ return resp.type('application/json').send({
625
+ hash: fullHash,
626
+ date,
627
+ message,
628
+ refs: '',
629
+ body: body ?? '',
630
+ author_name: authorName,
631
+ author_email: authorEmail,
632
+ parents: String(parents ?? '')
633
+ .split(' ')
634
+ .filter(Boolean),
635
+ });
636
+ }
637
+ catch (err) {
638
+ return resp.status(400).type('application/json').send({ error: err.message });
639
+ }
640
+ });
577
641
  // ==================== Diff Viewer ====================
578
642
  app.post('/api/commit/diff', async (req, resp) => {
579
643
  try {
@@ -300,6 +300,32 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
300
300
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
301
301
  THE SOFTWARE.
302
302
 
303
+ --------------------------------------------------------------------------------
304
+ Package: @angular/cdk
305
+ License: "MIT"
306
+
307
+ The MIT License
308
+
309
+ Copyright (c) 2025 Google LLC.
310
+
311
+ Permission is hereby granted, free of charge, to any person obtaining a copy
312
+ of this software and associated documentation files (the "Software"), to deal
313
+ in the Software without restriction, including without limitation the rights
314
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
315
+ copies of the Software, and to permit persons to whom the Software is
316
+ furnished to do so, subject to the following conditions:
317
+
318
+ The above copyright notice and this permission notice shall be included in
319
+ all copies or substantial portions of the Software.
320
+
321
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
322
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
323
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
324
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
325
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
326
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
327
+ THE SOFTWARE.
328
+
303
329
  --------------------------------------------------------------------------------
304
330
  Package: zone.js
305
331
  License: "MIT"
@@ -9,5 +9,5 @@
9
9
  <style>:root{--bg:#1e1e1e;--bg-alt:#232323;--bg-hover:#2a2d2e;--border:#3c3c3c;--text:#cccccc;--text-bright:#e8e8e8;--text-dim:#9d9d9d;--accent:#3794ff;--red:#f14c4c;--mono:ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace}*{box-sizing:border-box}html,body{height:100%;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:13px;color:var(--text);background:var(--bg);overflow:hidden}</style><link rel="stylesheet" href="styles-I7K2YZTW.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-I7K2YZTW.css"></noscript></head>
10
10
  <body>
11
11
  <app-root></app-root>
12
- <script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-A5ZTTMFS.js" type="module"></script></body>
12
+ <script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-3QYX4VGI.js" type="module"></script></body>
13
13
  </html>