gemstar 1.2 → 1.3

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.
@@ -8,6 +8,7 @@
8
8
  --line: #ddcfbf;
9
9
  --accent: #b44d25;
10
10
  --accent-soft: rgba(180, 77, 37, 0.14);
11
+ --focus: #2563c9;
11
12
  --green: #2f8f5b;
12
13
  --green-soft: rgba(47, 143, 91, 0.12);
13
14
  --red: #a9473c;
@@ -182,6 +183,63 @@
182
183
  opacity: 0.55;
183
184
  cursor: not-allowed;
184
185
  }
186
+ .command-dialog-backdrop {
187
+ position: fixed;
188
+ inset: 0;
189
+ z-index: 20;
190
+ display: grid;
191
+ place-items: center;
192
+ padding: 1rem;
193
+ background: rgba(31, 27, 23, 0.42);
194
+ }
195
+ .command-dialog {
196
+ width: min(48rem, 100%);
197
+ max-height: min(42rem, calc(100vh - 2rem));
198
+ display: grid;
199
+ grid-template-rows: auto minmax(10rem, 1fr) auto;
200
+ overflow: hidden;
201
+ border: 1px solid var(--line);
202
+ border-radius: 0.45rem;
203
+ background: #fff;
204
+ box-shadow: 0 1.25rem 3rem rgba(31, 27, 23, 0.24);
205
+ }
206
+ .command-dialog-header {
207
+ display: grid;
208
+ gap: 0.3rem;
209
+ padding: 0.85rem 1rem 0.7rem;
210
+ border-bottom: 1px solid #ece8df;
211
+ }
212
+ .command-dialog-header h2 {
213
+ margin: 0;
214
+ font-size: 1rem;
215
+ line-height: 1.15;
216
+ }
217
+ .command-dialog-command {
218
+ margin: 0;
219
+ color: var(--muted);
220
+ font-family: "SFMono-Regular", "Cascadia Code", monospace;
221
+ font-size: 0.78rem;
222
+ overflow-wrap: anywhere;
223
+ }
224
+ .command-dialog-output {
225
+ min-height: 0;
226
+ margin: 0;
227
+ padding: 0.85rem 1rem;
228
+ overflow: auto;
229
+ white-space: pre-wrap;
230
+ background: #181614;
231
+ color: #f8efe4;
232
+ font-size: 0.76rem;
233
+ line-height: 1.45;
234
+ }
235
+ .command-dialog-actions {
236
+ display: flex;
237
+ justify-content: flex-end;
238
+ gap: 0.35rem;
239
+ padding: 0.65rem 0.75rem;
240
+ border-top: 1px solid #ece8df;
241
+ background: #fff;
242
+ }
185
243
  .toolbar-meta {
186
244
  color: var(--muted);
187
245
  display: flex;
@@ -358,6 +416,11 @@
358
416
  min-height: 0;
359
417
  overflow: auto;
360
418
  }
419
+ .detail:focus {
420
+ outline: none;
421
+ border-color: var(--focus);
422
+ box-shadow: 0 0 0 1px var(--focus);
423
+ }
361
424
  .detail-hero,
362
425
  .panel-heading {
363
426
  display: flex;
@@ -366,6 +429,16 @@
366
429
  align-items: start;
367
430
  flex-wrap: wrap;
368
431
  }
432
+ .detail-hero {
433
+ position: sticky;
434
+ top: -0.62rem;
435
+ z-index: 2;
436
+ margin: -0.62rem -0.8rem 0 -0.5rem;
437
+ padding: 0.62rem 0.8rem 0.5rem 0.5rem;
438
+ border-bottom: 1px solid #f0ede6;
439
+ background: rgba(255, 255, 255, 0.96);
440
+ backdrop-filter: blur(0.45rem);
441
+ }
369
442
  .detail-hero-copy {
370
443
  display: grid;
371
444
  gap: 0.2rem;
@@ -384,6 +457,16 @@
384
457
  min-width: 0;
385
458
  flex: 1 1 auto;
386
459
  }
460
+ .detail-heading {
461
+ display: flex;
462
+ align-items: center;
463
+ flex-wrap: wrap;
464
+ gap: 0.5rem;
465
+ min-width: 0;
466
+ }
467
+ .detail-hero-copy .detail-heading h2 {
468
+ flex: 0 1 auto;
469
+ }
387
470
  .detail-hero-copy h2 {
388
471
  margin: 0;
389
472
  font-size: 2.35rem;
@@ -396,6 +479,32 @@
396
479
  font-weight: 600;
397
480
  color: var(--muted);
398
481
  }
482
+ .detail-package-type {
483
+ display: inline-flex;
484
+ align-items: center;
485
+ gap: 0.28rem;
486
+ flex: 0 0 auto;
487
+ border: 1px solid var(--line);
488
+ border-radius: 999px;
489
+ background: #faf8f3;
490
+ color: var(--muted);
491
+ padding: 0.16rem 0.42rem 0.16rem 0.3rem;
492
+ font-size: 0.68rem;
493
+ font-weight: 700;
494
+ line-height: 1;
495
+ letter-spacing: 0.01em;
496
+ }
497
+ .detail-package-type-icon {
498
+ display: inline-flex;
499
+ width: 0.9rem;
500
+ height: 0.9rem;
501
+ color: var(--ink);
502
+ }
503
+ .detail-package-type-icon svg {
504
+ display: block;
505
+ width: 100%;
506
+ height: 100%;
507
+ }
399
508
  .detail-title-row .link-strip {
400
509
  margin-left: auto;
401
510
  align-self: start;
@@ -528,9 +637,18 @@
528
637
  overflow: hidden;
529
638
  }
530
639
  .revision-future {
531
- border-style: dashed;
532
- border-color: rgba(124, 124, 133, 0.55);
533
- background: linear-gradient(180deg, rgba(124, 124, 133, 0.07), rgba(255,255,255,0.45));
640
+ position: relative;
641
+ border: 2px solid rgba(37, 99, 201, 0.3);
642
+ background:
643
+ repeating-linear-gradient(
644
+ 135deg,
645
+ rgba(37, 99, 201, 0.018) 0,
646
+ rgba(37, 99, 201, 0.018) 0.5rem,
647
+ rgba(255, 255, 255, 0) 0.5rem,
648
+ rgba(255, 255, 255, 0) 1.2rem
649
+ ),
650
+ #fbfdff;
651
+ box-shadow: inset 0.24rem 0 0 rgba(37, 99, 201, 0.42);
534
652
  }
535
653
  .revision-current {
536
654
  border-color: rgba(47, 143, 91, 0.25);
@@ -563,7 +681,8 @@
563
681
  background: rgba(124, 124, 133, 0.09);
564
682
  }
565
683
  .revision-future .revision-card-titlebar {
566
- background: rgba(124, 124, 133, 0.07);
684
+ background: rgba(37, 99, 201, 0.07);
685
+ border-bottom-color: rgba(37, 99, 201, 0.14);
567
686
  }
568
687
  .revision-card-actions {
569
688
  display: inline-flex;
@@ -643,4 +762,12 @@
643
762
  .detail {
644
763
  overflow: visible;
645
764
  }
765
+ .detail-hero {
766
+ position: static;
767
+ margin: 0;
768
+ padding: 0;
769
+ border-bottom: 0;
770
+ background: transparent;
771
+ backdrop-filter: none;
772
+ }
646
773
  }
@@ -2,6 +2,7 @@
2
2
  const projectSelect = document.querySelector("[data-project-select]");
3
3
  const fromSelect = document.querySelector("[data-from-select]");
4
4
  const toSelect = document.querySelector("[data-to-select]");
5
+ const projectActionButtons = Array.from(document.querySelectorAll("[data-project-action]"));
5
6
  const sidebarPanel = document.querySelector("[data-sidebar-panel]");
6
7
  const filterButtons = Array.from(document.querySelectorAll("[data-filter-button]"));
7
8
  const ecosystemButtons = Array.from(document.querySelectorAll("[data-ecosystem-button]"));
@@ -34,7 +35,9 @@
34
35
  });
35
36
  };
36
37
  const packageNameParam = (url) => url.searchParams.get("package") || url.searchParams.get("gem");
38
+ const packageIdParam = (url) => url.searchParams.get("package_id");
37
39
  const requestedGemName = () => packageNameParam(new URL(window.location.href));
40
+ const requestedPackageId = () => packageIdParam(new URL(window.location.href));
38
41
  const currentDetailGemName = () => {
39
42
  if (!detailPanel || !detailPanel.dataset.detailUrl) return null;
40
43
 
@@ -44,6 +47,15 @@
44
47
  return null;
45
48
  }
46
49
  };
50
+ const currentDetailPackageId = () => {
51
+ if (!detailPanel || !detailPanel.dataset.detailUrl) return null;
52
+
53
+ try {
54
+ return packageIdParam(new URL(detailPanel.dataset.detailUrl, window.location.origin));
55
+ } catch (_error) {
56
+ return null;
57
+ }
58
+ };
47
59
  const detailNeedsInitialFetch = () => detailPanel && detailPanel.dataset.detailDeferred === "true" && !!detailPanel.dataset.detailUrl;
48
60
  const isSidebarFocused = () => document.activeElement === sidebarPanel;
49
61
  const isDetailFocused = () => detailPanel && document.activeElement === detailPanel;
@@ -53,6 +65,73 @@
53
65
  const focusDetail = () => {
54
66
  if (detailPanel) detailPanel.focus({ preventScroll: true });
55
67
  };
68
+ const closeCommandErrorDialog = () => {
69
+ const overlay = document.querySelector("[data-command-error-dialog]");
70
+ if (overlay) overlay.remove();
71
+ };
72
+ const showCommandErrorDialog = (command, output) => {
73
+ closeCommandErrorDialog();
74
+
75
+ const overlay = document.createElement("div");
76
+ overlay.className = "command-dialog-backdrop";
77
+ overlay.dataset.commandErrorDialog = "true";
78
+
79
+ const dialog = document.createElement("section");
80
+ dialog.className = "command-dialog";
81
+ dialog.setAttribute("role", "alertdialog");
82
+ dialog.setAttribute("aria-modal", "true");
83
+ dialog.setAttribute("aria-labelledby", "command-dialog-title");
84
+
85
+ const header = document.createElement("header");
86
+ header.className = "command-dialog-header";
87
+
88
+ const title = document.createElement("h2");
89
+ title.id = "command-dialog-title";
90
+ title.textContent = "Command failed";
91
+
92
+ const commandLine = document.createElement("p");
93
+ commandLine.className = "command-dialog-command";
94
+ commandLine.textContent = command;
95
+
96
+ header.append(title, commandLine);
97
+
98
+ const outputField = document.createElement("pre");
99
+ outputField.className = "command-dialog-output";
100
+ outputField.tabIndex = 0;
101
+ outputField.textContent = output || "No output captured.";
102
+
103
+ const actions = document.createElement("div");
104
+ actions.className = "command-dialog-actions";
105
+
106
+ const copyButton = document.createElement("button");
107
+ copyButton.type = "button";
108
+ copyButton.className = "action";
109
+ copyButton.textContent = "Copy";
110
+ copyButton.addEventListener("click", () => {
111
+ navigator.clipboard.writeText(`Command failed: ${command}\n\n${output || ""}`)
112
+ .then(() => {
113
+ copyButton.textContent = "Copied";
114
+ setTimeout(() => {
115
+ copyButton.textContent = "Copy";
116
+ }, 1400);
117
+ })
118
+ .catch(() => {
119
+ outputField.focus();
120
+ });
121
+ });
122
+
123
+ const closeButton = document.createElement("button");
124
+ closeButton.type = "button";
125
+ closeButton.className = "action action-primary";
126
+ closeButton.textContent = "Close";
127
+ closeButton.addEventListener("click", closeCommandErrorDialog);
128
+
129
+ actions.append(copyButton, closeButton);
130
+ dialog.append(header, outputField, actions);
131
+ overlay.append(dialog);
132
+ document.body.append(overlay);
133
+ closeButton.focus({ preventScroll: true });
134
+ };
56
135
  const storedDetailDisclosureOpen = () => {
57
136
  try {
58
137
  return window.localStorage.getItem(detailDisclosureStorageKey) === "true";
@@ -82,6 +161,7 @@
82
161
  const applyGemFilter = (filter) => {
83
162
  currentFilter = filter;
84
163
  const pinnedGemName = requestedGemName();
164
+ const pinnedPackageId = requestedPackageId();
85
165
  const searchTerm = currentSearch.trim().toLowerCase();
86
166
 
87
167
  filterButtons.forEach((button) => {
@@ -94,7 +174,7 @@
94
174
 
95
175
  gemLinks.forEach((link) => {
96
176
  const updated = link.dataset.gemUpdated === "true";
97
- const pinned = pinnedGemName && link.dataset.gemName === pinnedGemName;
177
+ const pinned = pinnedPackageId ? link.dataset.packageId === pinnedPackageId : pinnedGemName && link.dataset.gemName === pinnedGemName;
98
178
  const matchesScope = currentPackageScope === "all" || link.dataset.packageScope === currentPackageScope;
99
179
  const matchesSearch = searchTerm === "" || link.dataset.gemName.toLowerCase().includes(searchTerm);
100
180
  link.hidden = (!matchesScope || (filter === "updated" && !updated && !pinned) || !matchesSearch);
@@ -115,16 +195,34 @@
115
195
  }
116
196
  };
117
197
 
118
- const syncSidebarSelection = (gemName = null, keepVisible = false) => {
198
+ const keepSidebarLinkVisible = (link) => {
199
+ if (!sidebarPanel || !link) return;
200
+
201
+ const panelBounds = sidebarPanel.getBoundingClientRect();
202
+ const stickyHeader = sidebarPanel.querySelector(".sidebar-header");
203
+ const headerBounds = stickyHeader && stickyHeader.getBoundingClientRect();
204
+ const linkBounds = link.getBoundingClientRect();
205
+ const visibleTop = headerBounds ? Math.max(panelBounds.top, headerBounds.bottom) : panelBounds.top;
206
+
207
+ if (linkBounds.top < visibleTop) {
208
+ sidebarPanel.scrollTop -= visibleTop - linkBounds.top;
209
+ } else if (linkBounds.bottom > panelBounds.bottom) {
210
+ sidebarPanel.scrollTop += linkBounds.bottom - panelBounds.bottom;
211
+ }
212
+ };
213
+
214
+ const syncSidebarSelection = (packageId = null, gemName = null, keepVisible = false) => {
215
+ const effectivePackageId = packageId || requestedPackageId();
119
216
  const effectiveGemName = gemName || requestedGemName();
120
- if (!effectiveGemName) {
217
+ if (!effectivePackageId && !effectiveGemName) {
121
218
  clearSidebarSelection();
122
219
  return;
123
220
  }
124
221
 
125
222
  let selectedLink = null;
126
223
  gemLinks.forEach((link) => {
127
- const matches = link.dataset.gemName === effectiveGemName;
224
+ const identityMatches = effectivePackageId ? link.dataset.packageId === effectivePackageId : link.dataset.gemName === effectiveGemName;
225
+ const matches = !link.hidden && identityMatches;
128
226
  link.classList.toggle("is-selected", matches);
129
227
  if (matches) {
130
228
  selectedLink = link;
@@ -132,7 +230,7 @@
132
230
  });
133
231
 
134
232
  if (keepVisible && sidebarPanel && selectedLink) {
135
- selectedLink.scrollIntoView({ block: "nearest" });
233
+ keepSidebarLinkVisible(selectedLink);
136
234
  }
137
235
  };
138
236
 
@@ -211,7 +309,7 @@
211
309
  window.history[historyMethod]({}, "", pageUrl);
212
310
  }
213
311
  const detailUrl = new URL(url, window.location.origin);
214
- syncSidebarSelection(packageNameParam(detailUrl));
312
+ syncSidebarSelection(packageIdParam(detailUrl), packageNameParam(detailUrl));
215
313
  })
216
314
  .catch(() => {
217
315
  stopDetailLoading();
@@ -223,7 +321,7 @@
223
321
 
224
322
  stopDetailPoll();
225
323
  stopDetailLoading();
226
- syncSidebarSelection(link.dataset.gemName, keepVisible);
324
+ syncSidebarSelection(link.dataset.packageId, link.dataset.gemName, keepVisible);
227
325
  fetchDetail(link.dataset.detailUrl || link.href, historyMode);
228
326
 
229
327
  focusSidebar();
@@ -231,13 +329,17 @@
231
329
 
232
330
  const ensureInitialGemSelection = () => {
233
331
  const requested = requestedGemName();
332
+ const requestedId = requestedPackageId();
234
333
 
235
334
  const detailGemName = currentDetailGemName();
236
- const detailLink = detailGemName ? visibleGemLinks().find((link) => link.dataset.gemName === detailGemName) : null;
335
+ const detailPackageId = currentDetailPackageId();
336
+ const detailLink = detailPackageId ?
337
+ visibleGemLinks().find((link) => link.dataset.packageId === detailPackageId) :
338
+ (detailGemName ? visibleGemLinks().find((link) => link.dataset.gemName === detailGemName) : null);
237
339
 
238
340
  if (detailLink) {
239
- syncSidebarSelection(detailGemName, true);
240
- if (!requested) {
341
+ syncSidebarSelection(detailLink.dataset.packageId, detailLink.dataset.gemName, true);
342
+ if (!requested && !requestedId) {
241
343
  const pageUrl = new URL(window.location.href);
242
344
  const detailUrl = new URL(detailLink.dataset.detailUrl || detailLink.href, window.location.origin);
243
345
  pageUrl.search = detailUrl.search;
@@ -246,19 +348,19 @@
246
348
  window.history.replaceState({}, "", pageUrl);
247
349
  }
248
350
  if (detailNeedsInitialFetch()) {
249
- fetchDetail(detailLink.dataset.detailUrl || detailLink.href, requested ? "none" : "replace", { refresh: pendingManualDetailRefresh });
351
+ fetchDetail(detailLink.dataset.detailUrl || detailLink.href, requested || requestedId ? "none" : "replace", { refresh: pendingManualDetailRefresh });
250
352
  pendingManualDetailRefresh = false;
251
353
  }
252
354
  return;
253
355
  }
254
356
 
255
- if (requested) return;
357
+ if (requested || requestedId) return;
256
358
 
257
359
  const firstVisibleLink = visibleGemLinks()[0];
258
360
  if (firstVisibleLink) {
259
361
  const refresh = pendingManualDetailRefresh;
260
362
  pendingManualDetailRefresh = false;
261
- syncSidebarSelection(firstVisibleLink.dataset.gemName, true);
363
+ syncSidebarSelection(firstVisibleLink.dataset.packageId, firstVisibleLink.dataset.gemName, true);
262
364
  fetchDetail(firstVisibleLink.dataset.detailUrl || firstVisibleLink.href, "replace", { refresh });
263
365
  focusSidebar();
264
366
  }
@@ -279,7 +381,7 @@
279
381
  scheduleDetailPoll();
280
382
  }
281
383
 
282
- syncSidebarSelection(null, true);
384
+ syncSidebarSelection(null, null, true);
283
385
  applyGemFilter(currentFilter);
284
386
  ensureInitialGemSelection();
285
387
 
@@ -299,7 +401,7 @@
299
401
  const url = new URL(window.location.href);
300
402
  url.searchParams.set("filter", currentFilter);
301
403
  window.history.replaceState({}, "", url);
302
- syncSidebarSelection(null, true);
404
+ syncSidebarSelection(null, null, true);
303
405
  });
304
406
  });
305
407
 
@@ -313,7 +415,7 @@
313
415
  const url = new URL(window.location.href);
314
416
  url.searchParams.set("scope", currentPackageScope);
315
417
  window.history.replaceState({}, "", url);
316
- syncSidebarSelection(null, true);
418
+ syncSidebarSelection(null, null, true);
317
419
  });
318
420
  });
319
421
 
@@ -324,7 +426,7 @@
324
426
  if (sidebarPanel) {
325
427
  sidebarPanel.scrollTop = 0;
326
428
  }
327
- syncSidebarSelection(null, true);
429
+ syncSidebarSelection(null, null, true);
328
430
  });
329
431
  }
330
432
 
@@ -338,6 +440,55 @@
338
440
  fetchDetail(detailPanel.dataset.detailUrl, "none", { refresh: true, useGh: true });
339
441
  });
340
442
 
443
+ projectActionButtons.forEach((button) => {
444
+ button.addEventListener("click", () => {
445
+ const actionId = button.dataset.projectAction;
446
+ const command = button.dataset.projectActionCommand || actionId;
447
+ if (!actionId) return;
448
+ if (!window.confirm(`Run ${command}?`)) return;
449
+
450
+ const originalText = button.textContent;
451
+ projectActionButtons.forEach((actionButton) => {
452
+ actionButton.disabled = true;
453
+ });
454
+ button.textContent = "Running...";
455
+
456
+ const params = new URLSearchParams();
457
+ params.set("project", "<%= selected_project_index %>");
458
+
459
+ fetch(`/actions/${encodeURIComponent(actionId)}`, {
460
+ method: "POST",
461
+ headers: {
462
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
463
+ "X-Requested-With": "gemstar-action"
464
+ },
465
+ body: params.toString()
466
+ })
467
+ .then((response) => response.json().then((payload) => ({ response, payload })))
468
+ .then(({ response, payload }) => {
469
+ if (!response.ok || !payload.ok) {
470
+ const output = payload && payload.output ? payload.output : "";
471
+ const failedCommand = payload && payload.command ? payload.command : command;
472
+ showCommandErrorDialog(failedCommand, output);
473
+ button.textContent = originalText;
474
+ projectActionButtons.forEach((actionButton) => {
475
+ actionButton.disabled = false;
476
+ });
477
+ return;
478
+ }
479
+
480
+ window.location.reload();
481
+ })
482
+ .catch((error) => {
483
+ showCommandErrorDialog(command, error.message);
484
+ button.textContent = originalText;
485
+ projectActionButtons.forEach((actionButton) => {
486
+ actionButton.disabled = false;
487
+ });
488
+ });
489
+ });
490
+ });
491
+
341
492
  const navigate = (params) => {
342
493
  const url = new URL(window.location.href);
343
494
  Object.entries(params).forEach(([key, value]) => {
@@ -357,23 +508,29 @@
357
508
  event.target.value = "<%= selected_project_index %>";
358
509
  return;
359
510
  }
360
- navigate({ project: event.target.value, from: null, to: "worktree", filter: currentFilter, scope: currentPackageScope, package: null, gem: null });
511
+ navigate({ project: event.target.value, from: null, to: "worktree", filter: currentFilter, scope: currentPackageScope, package: null, package_id: null, gem: null });
361
512
  });
362
513
  }
363
514
 
364
515
  if (fromSelect) {
365
516
  fromSelect.addEventListener("change", (event) => {
366
- navigate({ from: event.target.value, filter: currentFilter, scope: currentPackageScope, package: null, gem: null });
517
+ navigate({ from: event.target.value, filter: currentFilter, scope: currentPackageScope, package: null, package_id: null, gem: null });
367
518
  });
368
519
  }
369
520
 
370
521
  if (toSelect) {
371
522
  toSelect.addEventListener("change", (event) => {
372
- navigate({ to: event.target.value, filter: currentFilter, scope: currentPackageScope, package: null, gem: null });
523
+ navigate({ to: event.target.value, filter: currentFilter, scope: currentPackageScope, package: null, package_id: null, gem: null });
373
524
  });
374
525
  }
375
526
 
376
527
  document.addEventListener("keydown", (event) => {
528
+ if (event.key === "Escape" && document.querySelector("[data-command-error-dialog]")) {
529
+ event.preventDefault();
530
+ closeCommandErrorDialog();
531
+ return;
532
+ }
533
+
377
534
  const tagName = document.activeElement && document.activeElement.tagName;
378
535
  if (tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT") return;
379
536
 
@@ -421,7 +578,7 @@
421
578
  if (!links.length) return;
422
579
 
423
580
  const selectedGem = currentSelectedIndex();
424
- const currentIndex = selectedGem >= 0 ? selectedGem : 0;
581
+ const currentIndex = selectedGem >= 0 ? selectedGem : (event.key === "ArrowUp" ? links.length : -1);
425
582
  let nextIndex = null;
426
583
 
427
584
  if (event.key === "ArrowDown") nextIndex = Math.min(currentIndex + 1, links.length - 1);
data/lib/gemstar.rb CHANGED
@@ -9,6 +9,8 @@ require "gemstar/commands/cache"
9
9
  require "gemstar/commands/diff"
10
10
  require "gemstar/commands/server"
11
11
  require "gemstar/config"
12
+ require "gemstar/cargo_lock_file"
13
+ require "gemstar/crates_io_metadata"
12
14
  require "gemstar/outputs/basic"
13
15
  require "gemstar/outputs/html"
14
16
  require "gemstar/outputs/markdown"
@@ -19,8 +21,10 @@ require "gemstar/importmap_file"
19
21
  require "gemstar/lock_file"
20
22
  require "gemstar/npm_metadata"
21
23
  require "gemstar/package_lock_file"
24
+ require "gemstar/pypi_metadata"
22
25
  require "gemstar/project"
23
26
  require "gemstar/remote_repository"
24
27
  require "gemstar/utils"
25
28
  require "gemstar/ruby_gems_metadata"
29
+ require "gemstar/uv_lock_file"
26
30
  require "gemstar/git_repo"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemstar
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.2'
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Dejako
@@ -219,8 +219,8 @@ dependencies:
219
219
  - - "~>"
220
220
  - !ruby/object:Gem::Version
221
221
  version: '1.9'
222
- description: Gem changelog viewer and more, but starting as a changelog viewer for
223
- bundled gems.
222
+ description: Browse and compare changelogs for Ruby gems, JavaScript packages, Python
223
+ packages, and Rust crates in your projects.
224
224
  email:
225
225
  - fdejako@gmail.com
226
226
  executables:
@@ -236,6 +236,7 @@ files:
236
236
  - lib/gemstar/cache.rb
237
237
  - lib/gemstar/cache_cli.rb
238
238
  - lib/gemstar/cache_warmer.rb
239
+ - lib/gemstar/cargo_lock_file.rb
239
240
  - lib/gemstar/change_log.rb
240
241
  - lib/gemstar/cli.rb
241
242
  - lib/gemstar/commands/cache.rb
@@ -243,6 +244,7 @@ files:
243
244
  - lib/gemstar/commands/diff.rb
244
245
  - lib/gemstar/commands/server.rb
245
246
  - lib/gemstar/config.rb
247
+ - lib/gemstar/crates_io_metadata.rb
246
248
  - lib/gemstar/data/importmap_package_metadata.json
247
249
  - lib/gemstar/data/ruby_gems_metadata.json
248
250
  - lib/gemstar/git_hub.rb
@@ -255,10 +257,12 @@ files:
255
257
  - lib/gemstar/outputs/markdown.rb
256
258
  - lib/gemstar/package_lock_file.rb
257
259
  - lib/gemstar/project.rb
260
+ - lib/gemstar/pypi_metadata.rb
258
261
  - lib/gemstar/remote_repository.rb
259
262
  - lib/gemstar/request_logger.rb
260
263
  - lib/gemstar/ruby_gems_metadata.rb
261
264
  - lib/gemstar/utils.rb
265
+ - lib/gemstar/uv_lock_file.rb
262
266
  - lib/gemstar/version.rb
263
267
  - lib/gemstar/web/app.rb
264
268
  - lib/gemstar/web/templates/app.css
@@ -288,5 +292,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
292
  requirements: []
289
293
  rubygems_version: 3.6.9
290
294
  specification_version: 4
291
- summary: Making sense of gems.
295
+ summary: Changelog viewer for Ruby, JavaScript, Python, and Rust dependencies.
292
296
  test_files: []