github-search-cli 9.4.2 → 9.4.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.
- package/CHANGELOG.md +26 -4
- package/README.md +8 -3
- package/bin/ghfind.js +98 -89
- package/completions/ghfind.bash +1 -1
- package/completions/ghfind.fish +3 -1
- package/completions/ghfind.zsh +6 -0
- package/dist/{chunk-IVUU2KGR.js → chunk-GYIL4S5X.js} +4 -1
- package/dist/cli.js +761 -342
- package/dist/{config-GNZOQRTH.js → config-OCIPF2IF.js} +1 -1
- package/package.json +6 -3
- package/release-notes/9.3.0.md +32 -29
- package/scripts/build.ts +34 -19
- package/scripts/generate-svg-demo.js +218 -168
- package/scripts/postinstall.js +176 -176
- package/src/bookmarks.ts +69 -69
- package/src/cli.ts +17 -1
- package/src/compare.ts +69 -47
- package/src/config.ts +70 -65
- package/src/deepdive.ts +149 -118
- package/src/doctor.ts +287 -287
- package/src/errors.ts +62 -59
- package/src/explore.ts +34 -29
- package/src/export.ts +73 -63
- package/src/help.ts +157 -157
- package/src/history.ts +69 -55
- package/src/init.ts +60 -55
- package/src/landing.ts +106 -104
- package/src/login.ts +186 -0
- package/src/markdown-render.ts +1 -6
- package/src/notifications.ts +75 -59
- package/src/open-url.ts +16 -16
- package/src/org.ts +43 -15
- package/src/output.ts +153 -137
- package/src/package-browser.ts +317 -305
- package/src/package.ts +149 -137
- package/src/pipe.ts +60 -54
- package/src/releases.ts +182 -144
- package/src/saved-searches.ts +72 -50
- package/src/search-state.ts +283 -229
- package/src/search.ts +678 -653
- package/src/session.ts +26 -24
- package/src/share.ts +61 -54
- package/src/status.ts +68 -61
- package/src/storage.ts +136 -106
- package/src/themes.ts +259 -116
- package/src/tips.ts +42 -42
- package/src/toast.ts +70 -69
- package/src/trending-parser.ts +104 -0
- package/src/trending.ts +481 -462
- package/src/tui.ts +149 -153
- package/src/types.ts +160 -158
- package/src/watch.ts +95 -74
package/src/tui.ts
CHANGED
|
@@ -31,7 +31,14 @@ import {
|
|
|
31
31
|
SelectRenderable,
|
|
32
32
|
ScrollBoxRenderable,
|
|
33
33
|
} from "@opentui/core";
|
|
34
|
-
import type {
|
|
34
|
+
import type { CliRenderer } from "@opentui/core";
|
|
35
|
+
import type {
|
|
36
|
+
Repo,
|
|
37
|
+
SearchOptions,
|
|
38
|
+
SortStrategy,
|
|
39
|
+
Package,
|
|
40
|
+
SessionState,
|
|
41
|
+
} from "./types";
|
|
35
42
|
import { fetchOrgProfile, type OrgProfile } from "./org";
|
|
36
43
|
import {
|
|
37
44
|
parseQuery,
|
|
@@ -79,12 +86,16 @@ import { buildComparisonTable } from "./compare";
|
|
|
79
86
|
import { renderMarkdown } from "./markdown-render";
|
|
80
87
|
import { fetchTopics, type TopicItem } from "./explore";
|
|
81
88
|
import { exportToFile, type ExportFormat } from "./output";
|
|
82
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
listThemes,
|
|
91
|
+
loadTheme,
|
|
92
|
+
detectTerminalBackground,
|
|
93
|
+
deriveSurfaceLayers,
|
|
94
|
+
} from "./themes";
|
|
83
95
|
import {
|
|
84
96
|
createPackageSearch,
|
|
85
97
|
sortPackages,
|
|
86
98
|
PACKAGE_SORT_MODES,
|
|
87
|
-
type Package,
|
|
88
99
|
type PackageSortMode,
|
|
89
100
|
} from "./package";
|
|
90
101
|
import { StatusManager } from "./status";
|
|
@@ -111,7 +122,7 @@ import {
|
|
|
111
122
|
recordPreUpdateState,
|
|
112
123
|
} from "./update-check";
|
|
113
124
|
import { debugLog } from "./storage";
|
|
114
|
-
const colors = {
|
|
125
|
+
const colors: Record<string, string> = {
|
|
115
126
|
bg: "#3D3B3B",
|
|
116
127
|
surface: "#4a4848",
|
|
117
128
|
surfaceAlt: "#525050",
|
|
@@ -153,7 +164,11 @@ const SORT_MODES: { key: SortStrategy; label: string }[] = [
|
|
|
153
164
|
|
|
154
165
|
// ─── Main ─────────────────────────────────────────────────────────────
|
|
155
166
|
export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
156
|
-
|
|
167
|
+
// Ask the terminal for its real background color (OSC 11) BEFORE the
|
|
168
|
+
// renderer takes over stdin — after that, reading the reply gets messy.
|
|
169
|
+
// Used below so the app backdrop matches the user's terminal theme.
|
|
170
|
+
const terminalBg = await detectTerminalBackground();
|
|
171
|
+
let renderer: CliRenderer;
|
|
157
172
|
try {
|
|
158
173
|
const isTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
159
174
|
if (isTTY) {
|
|
@@ -188,13 +203,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
188
203
|
|
|
189
204
|
const root = renderer.root;
|
|
190
205
|
root.flexDirection = "column";
|
|
191
|
-
root.border = true;
|
|
192
|
-
root.borderTop = true;
|
|
193
|
-
root.borderBottom = true;
|
|
194
|
-
root.borderLeft = true;
|
|
195
|
-
root.borderRight = true;
|
|
196
|
-
root.borderColor = colors.borderAccent;
|
|
197
|
-
root.backgroundColor = colors.bg;
|
|
198
206
|
|
|
199
207
|
// ── Config ──
|
|
200
208
|
const config = loadConfig();
|
|
@@ -203,6 +211,18 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
203
211
|
// Apply theme
|
|
204
212
|
const theme = loadTheme(theme_override || config.theme);
|
|
205
213
|
Object.assign(colors, theme);
|
|
214
|
+
// Prefer the terminal's own background so the app blends into the terminal
|
|
215
|
+
// instead of painting a rectangle of theme-bg over it. Falls back to the
|
|
216
|
+
// theme bg when the terminal can't be queried (conhost, piped, etc.).
|
|
217
|
+
if (terminalBg) {
|
|
218
|
+
colors.bg = terminalBg;
|
|
219
|
+
// Blend card/surface/border layers toward that background so panels are
|
|
220
|
+
// subtle tints of the terminal color rather than foreign theme chips.
|
|
221
|
+
Object.assign(colors, deriveSurfaceLayers(terminalBg));
|
|
222
|
+
}
|
|
223
|
+
// Must come after the theme is applied — this paints the whole terminal
|
|
224
|
+
// backdrop, and doing it earlier bakes in the hardcoded default palette.
|
|
225
|
+
renderer.setBackgroundColor(colors.bg);
|
|
206
226
|
|
|
207
227
|
// ── Session restore ──
|
|
208
228
|
const session = restoreSession();
|
|
@@ -214,7 +234,9 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
214
234
|
let currentQueryInput = session?.query ?? "";
|
|
215
235
|
let isLoading = false;
|
|
216
236
|
let currentMode: "landing" | "search" | "trending" | "packages" =
|
|
217
|
-
session?.mode
|
|
237
|
+
session?.mode === "trending" || session?.mode === "packages"
|
|
238
|
+
? session.mode
|
|
239
|
+
: "search";
|
|
218
240
|
let packages: Package[] = [];
|
|
219
241
|
let currentPackageSort: PackageSortMode = "best-match";
|
|
220
242
|
let packageResultsRaw: Package[] = [];
|
|
@@ -248,11 +270,9 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
248
270
|
const header = new TextRenderable(renderer, {
|
|
249
271
|
content:
|
|
250
272
|
" ghfind — GitHub repo browser [/]search [Space]menu [\u2192]open [?]help [q]uit",
|
|
251
|
-
|
|
252
|
-
|
|
273
|
+
bg: colors.bg,
|
|
274
|
+
fg: colors.muted,
|
|
253
275
|
height: 1,
|
|
254
|
-
borderBottom: true,
|
|
255
|
-
borderBottomColor: colors.border,
|
|
256
276
|
});
|
|
257
277
|
|
|
258
278
|
// ── Trending tab bar (hidden in search mode) ────────────────────────
|
|
@@ -275,8 +295,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
275
295
|
const label = ` ${name} `;
|
|
276
296
|
const tt = new TextRenderable(renderer, {
|
|
277
297
|
content: label,
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
fg: isActive ? colors.bg : colors.muted,
|
|
299
|
+
bg: isActive ? colors.blue : colors.bg,
|
|
280
300
|
height: 1,
|
|
281
301
|
});
|
|
282
302
|
trendingTabTexts.push(tt);
|
|
@@ -284,8 +304,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
284
304
|
if (i < TAB_NAMES.length - 1) {
|
|
285
305
|
const sp = new TextRenderable(renderer, {
|
|
286
306
|
content: " ",
|
|
287
|
-
|
|
288
|
-
|
|
307
|
+
fg: colors.muted,
|
|
308
|
+
bg: colors.bg,
|
|
289
309
|
height: 1,
|
|
290
310
|
});
|
|
291
311
|
trendingTabTexts.push(sp);
|
|
@@ -295,23 +315,15 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
295
315
|
}
|
|
296
316
|
root.add(header);
|
|
297
317
|
root.add(trendingTabBox);
|
|
298
|
-
// ── Gap between header/tabs and search ────────────────────────────────
|
|
299
|
-
const gapBox = new BoxRenderable(renderer, {
|
|
300
|
-
height: 1,
|
|
301
|
-
visible: true,
|
|
302
|
-
});
|
|
303
|
-
root.add(gapBox);
|
|
304
318
|
|
|
305
319
|
// ── Search input row ────────────────────────────────────────────────
|
|
306
320
|
const searchBox = new BoxRenderable(renderer, {
|
|
307
321
|
visible: true,
|
|
308
|
-
bordered: true,
|
|
309
|
-
borderColor: colors.border,
|
|
310
|
-
title: "",
|
|
311
|
-
titleColor: colors.blue,
|
|
312
322
|
width: "100%",
|
|
313
323
|
flexDirection: "row",
|
|
314
324
|
marginTop: 1,
|
|
325
|
+
paddingY: 1,
|
|
326
|
+
paddingLeft: 1,
|
|
315
327
|
backgroundColor: colors.surface,
|
|
316
328
|
});
|
|
317
329
|
const searchInput = new InputRenderable(renderer, {
|
|
@@ -320,8 +332,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
320
332
|
value: "",
|
|
321
333
|
backgroundColor: colors.surface,
|
|
322
334
|
textColor: colors.text,
|
|
323
|
-
borderColor: colors.blue,
|
|
324
|
-
selectedBorderColor: colors.blue,
|
|
325
335
|
paddingX: 0,
|
|
326
336
|
flexGrow: 1,
|
|
327
337
|
});
|
|
@@ -332,11 +342,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
332
342
|
const toolbarText = new TextRenderable(renderer, {
|
|
333
343
|
content: formatToolbar(currentSort, currentLimit, totalCount),
|
|
334
344
|
visible: true, // hidden in trending mode
|
|
335
|
-
|
|
336
|
-
color: colors.muted,
|
|
345
|
+
fg: colors.muted,
|
|
337
346
|
height: 1,
|
|
338
|
-
borderBottom: true,
|
|
339
|
-
borderBottomColor: colors.border,
|
|
340
347
|
paddingX: 1,
|
|
341
348
|
});
|
|
342
349
|
root.add(toolbarText);
|
|
@@ -352,9 +359,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
352
359
|
|
|
353
360
|
// Results pane
|
|
354
361
|
const resultsBox = new BoxRenderable(renderer, {
|
|
355
|
-
|
|
362
|
+
border: true,
|
|
356
363
|
borderColor: colors.border,
|
|
357
|
-
borderBottomColor: colors.border,
|
|
358
364
|
title: " Results ",
|
|
359
365
|
titleColor: colors.blue,
|
|
360
366
|
width: "50%",
|
|
@@ -384,11 +390,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
384
390
|
|
|
385
391
|
// Detail / Graph pane
|
|
386
392
|
const detailBox = new BoxRenderable(renderer, {
|
|
387
|
-
bordered: true,
|
|
388
|
-
borderColor: colors.border,
|
|
389
|
-
borderBottomColor: colors.border,
|
|
390
|
-
title: " Details ",
|
|
391
|
-
titleColor: colors.green,
|
|
392
393
|
width: "50%",
|
|
393
394
|
flexDirection: "column",
|
|
394
395
|
paddingX: 1,
|
|
@@ -398,7 +399,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
398
399
|
});
|
|
399
400
|
const detailText = new TextRenderable(renderer, {
|
|
400
401
|
content: "",
|
|
401
|
-
|
|
402
|
+
fg: colors.text,
|
|
402
403
|
wrapMode: "none",
|
|
403
404
|
});
|
|
404
405
|
detailBox.add(detailText);
|
|
@@ -424,8 +425,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
424
425
|
"╚██████╔╝██║ ██║██║ ██║██║ ╚████║██████╔",
|
|
425
426
|
" ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝",
|
|
426
427
|
].join("\n"),
|
|
427
|
-
|
|
428
|
-
|
|
428
|
+
fg: colors.accent,
|
|
429
|
+
bg: colors.bg,
|
|
429
430
|
height: 6,
|
|
430
431
|
});
|
|
431
432
|
landingBox.add(landingBanner);
|
|
@@ -483,7 +484,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
483
484
|
const spacerTop = new TextRenderable(renderer, { content: "", height: 1 });
|
|
484
485
|
const titleLine = new TextRenderable(renderer, {
|
|
485
486
|
content: opt.title,
|
|
486
|
-
|
|
487
|
+
fg: i === 0 ? colors.accent : colors.text,
|
|
487
488
|
height: 1,
|
|
488
489
|
});
|
|
489
490
|
const spacerBot = new TextRenderable(renderer, { content: "", height: 1 });
|
|
@@ -494,8 +495,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
494
495
|
|
|
495
496
|
const landingHint = new TextRenderable(renderer, {
|
|
496
497
|
content: " \u2191\u2193 Navigate \u21E9 Select [?]help [q]uit",
|
|
497
|
-
|
|
498
|
-
|
|
498
|
+
fg: colors.muted,
|
|
499
|
+
bg: colors.bg,
|
|
499
500
|
height: 1,
|
|
500
501
|
marginTop: 1,
|
|
501
502
|
});
|
|
@@ -516,10 +517,10 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
516
517
|
for (let i = 0; i < landingCards.length; i++) {
|
|
517
518
|
const isSel = i === landingSelected;
|
|
518
519
|
landingCards[i].borderColor = isSel ? colors.accent : colors.border;
|
|
519
|
-
const children = landingCards[i].
|
|
520
|
+
const children = landingCards[i].getChildren();
|
|
520
521
|
const titleChild = children[1] as TextRenderable;
|
|
521
522
|
if (titleChild) {
|
|
522
|
-
titleChild.
|
|
523
|
+
titleChild.fg = isSel ? colors.accent : colors.text;
|
|
523
524
|
}
|
|
524
525
|
}
|
|
525
526
|
}
|
|
@@ -552,7 +553,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
552
553
|
// Hide main content so overlays can take 100% of the content area
|
|
553
554
|
function hideMainContent() {
|
|
554
555
|
trendingTabBox.visible = false;
|
|
555
|
-
gapBox.visible = false;
|
|
556
556
|
searchBox.visible = false;
|
|
557
557
|
toolbarText.visible = false;
|
|
558
558
|
body.visible = false;
|
|
@@ -563,7 +563,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
563
563
|
trendingTabBox.visible = true;
|
|
564
564
|
} else {
|
|
565
565
|
searchBox.visible = true;
|
|
566
|
-
gapBox.visible = true;
|
|
567
566
|
toolbarText.visible = true;
|
|
568
567
|
}
|
|
569
568
|
body.visible = true;
|
|
@@ -590,7 +589,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
590
589
|
landingBox.visible = true;
|
|
591
590
|
body.visible = false;
|
|
592
591
|
trendingTabBox.visible = false;
|
|
593
|
-
gapBox.visible = false;
|
|
594
592
|
searchBox.visible = false;
|
|
595
593
|
toolbarText.visible = false;
|
|
596
594
|
} else {
|
|
@@ -632,7 +630,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
632
630
|
} else if (type === "update") {
|
|
633
631
|
updateDim.visible = true;
|
|
634
632
|
updateBox.visible = true;
|
|
635
|
-
|
|
633
|
+
// Selection is driven by updateSelectedOption + the global key handler
|
|
634
|
+
// (see the "Update panel" block); there is no focusable Select here.
|
|
636
635
|
}
|
|
637
636
|
renderer.requestRender();
|
|
638
637
|
}
|
|
@@ -643,8 +642,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
643
642
|
visible: false,
|
|
644
643
|
flexGrow: 1,
|
|
645
644
|
backgroundColor: colors.bg,
|
|
646
|
-
borderBottom: true,
|
|
647
|
-
borderBottomColor: colors.border,
|
|
648
645
|
});
|
|
649
646
|
const helpScroll = new ScrollBoxRenderable(renderer, {
|
|
650
647
|
flexGrow: 1,
|
|
@@ -655,15 +652,15 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
655
652
|
viewportOptions: { backgroundColor: colors.bg },
|
|
656
653
|
contentOptions: { backgroundColor: colors.bg, flexDirection: "column" },
|
|
657
654
|
scrollbarOptions: {
|
|
658
|
-
backgroundColor: colors.bg,
|
|
659
|
-
foregroundColor: colors.muted,
|
|
660
655
|
width: 1,
|
|
656
|
+
trackOptions: {
|
|
657
|
+
backgroundColor: colors.bg,
|
|
658
|
+
foregroundColor: colors.muted,
|
|
659
|
+
},
|
|
661
660
|
},
|
|
662
661
|
});
|
|
663
662
|
for (const section of helpSections) {
|
|
664
663
|
const sectionBox = new BoxRenderable(renderer, {
|
|
665
|
-
borderBottom: true,
|
|
666
|
-
borderBottomColor: colors.border,
|
|
667
664
|
title: section.title,
|
|
668
665
|
titleColor:
|
|
669
666
|
(colors as Record<string, string>)[section.titleColor] ??
|
|
@@ -679,7 +676,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
679
676
|
sectionBox.add(
|
|
680
677
|
new TextRenderable(renderer, {
|
|
681
678
|
content: ` ${keys} ${row.action}`,
|
|
682
|
-
|
|
679
|
+
fg: colors.text,
|
|
683
680
|
}),
|
|
684
681
|
);
|
|
685
682
|
}
|
|
@@ -687,7 +684,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
687
684
|
sectionBox.add(
|
|
688
685
|
new TextRenderable(renderer, {
|
|
689
686
|
content: ` ${section.note}`,
|
|
690
|
-
|
|
687
|
+
fg: colors.muted,
|
|
691
688
|
}),
|
|
692
689
|
);
|
|
693
690
|
}
|
|
@@ -701,8 +698,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
701
698
|
visible: false,
|
|
702
699
|
flexGrow: 1,
|
|
703
700
|
backgroundColor: colors.bg,
|
|
704
|
-
borderBottom: true,
|
|
705
|
-
borderBottomColor: colors.border,
|
|
706
701
|
title: " Search History ",
|
|
707
702
|
titleColor: colors.blue,
|
|
708
703
|
});
|
|
@@ -741,8 +736,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
741
736
|
visible: false,
|
|
742
737
|
flexGrow: 1,
|
|
743
738
|
backgroundColor: colors.bg,
|
|
744
|
-
borderBottom: true,
|
|
745
|
-
borderBottomColor: colors.border,
|
|
746
739
|
title: " Bookmarks ",
|
|
747
740
|
titleColor: colors.green,
|
|
748
741
|
});
|
|
@@ -781,8 +774,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
781
774
|
visible: false,
|
|
782
775
|
flexGrow: 1,
|
|
783
776
|
backgroundColor: colors.bg,
|
|
784
|
-
borderBottom: true,
|
|
785
|
-
borderBottomColor: colors.border,
|
|
786
777
|
title: " Saved Searches ",
|
|
787
778
|
titleColor: colors.yellow,
|
|
788
779
|
});
|
|
@@ -805,8 +796,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
805
796
|
visible: false,
|
|
806
797
|
flexGrow: 1,
|
|
807
798
|
backgroundColor: colors.bg,
|
|
808
|
-
borderBottom: true,
|
|
809
|
-
borderBottomColor: colors.border,
|
|
810
799
|
title: " Topics ",
|
|
811
800
|
titleColor: colors.purple ?? colors.blue,
|
|
812
801
|
});
|
|
@@ -845,8 +834,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
845
834
|
visible: false,
|
|
846
835
|
flexGrow: 1,
|
|
847
836
|
backgroundColor: colors.bg,
|
|
848
|
-
borderBottom: true,
|
|
849
|
-
borderBottomColor: colors.border,
|
|
850
837
|
title: " Export ",
|
|
851
838
|
titleColor: colors.green,
|
|
852
839
|
});
|
|
@@ -886,15 +873,13 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
886
873
|
visible: false,
|
|
887
874
|
flexGrow: 1,
|
|
888
875
|
backgroundColor: colors.bg,
|
|
889
|
-
borderBottom: true,
|
|
890
|
-
borderBottomColor: colors.border,
|
|
891
876
|
title: " Comparison ",
|
|
892
877
|
titleColor: colors.yellow,
|
|
893
878
|
});
|
|
894
879
|
const compareText = new TextRenderable(renderer, {
|
|
895
880
|
content: "",
|
|
896
|
-
|
|
897
|
-
|
|
881
|
+
fg: colors.text,
|
|
882
|
+
bg: colors.bg,
|
|
898
883
|
});
|
|
899
884
|
compareBox.add(compareText);
|
|
900
885
|
root.add(compareBox);
|
|
@@ -904,15 +889,13 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
904
889
|
visible: false,
|
|
905
890
|
flexGrow: 1,
|
|
906
891
|
backgroundColor: colors.bg,
|
|
907
|
-
borderBottom: true,
|
|
908
|
-
borderBottomColor: colors.border,
|
|
909
892
|
title: " Notifications ",
|
|
910
893
|
titleColor: colors.yellow,
|
|
911
894
|
});
|
|
912
895
|
const notifsText = new TextRenderable(renderer, {
|
|
913
896
|
content: "(no notifications)",
|
|
914
|
-
|
|
915
|
-
|
|
897
|
+
fg: colors.text,
|
|
898
|
+
bg: colors.bg,
|
|
916
899
|
});
|
|
917
900
|
notifsBox.add(notifsText);
|
|
918
901
|
root.add(notifsBox);
|
|
@@ -933,8 +916,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
933
916
|
visible: false,
|
|
934
917
|
flexGrow: 1,
|
|
935
918
|
backgroundColor: colors.bg,
|
|
936
|
-
borderBottom: true,
|
|
937
|
-
borderBottomColor: colors.border,
|
|
938
919
|
title: " Share Repo ",
|
|
939
920
|
titleColor: colors.blue,
|
|
940
921
|
});
|
|
@@ -1041,11 +1022,9 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1041
1022
|
});
|
|
1042
1023
|
const leaderFooter = new TextRenderable(renderer, {
|
|
1043
1024
|
content: " ↑↓ select Enter run Esc/q close ",
|
|
1044
|
-
|
|
1045
|
-
|
|
1025
|
+
fg: colors.muted,
|
|
1026
|
+
bg: colors.surfaceDim,
|
|
1046
1027
|
height: 1,
|
|
1047
|
-
borderTop: true,
|
|
1048
|
-
borderTopColor: colors.border,
|
|
1049
1028
|
paddingX: 2,
|
|
1050
1029
|
});
|
|
1051
1030
|
leaderBox.add(leaderSelect);
|
|
@@ -1087,8 +1066,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1087
1066
|
});
|
|
1088
1067
|
const updateText = new TextRenderable(renderer, {
|
|
1089
1068
|
content: "",
|
|
1090
|
-
|
|
1091
|
-
|
|
1069
|
+
fg: colors.accent,
|
|
1070
|
+
bg: colors.surfaceDim,
|
|
1092
1071
|
});
|
|
1093
1072
|
|
|
1094
1073
|
// Horizontal option boxes
|
|
@@ -1113,8 +1092,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1113
1092
|
});
|
|
1114
1093
|
const updateNowText = new TextRenderable(renderer, {
|
|
1115
1094
|
content: " Update Now ",
|
|
1116
|
-
|
|
1117
|
-
|
|
1095
|
+
fg: updateSelectedOption === 0 ? colors.yellow : colors.text,
|
|
1096
|
+
bg: colors.surfaceDim,
|
|
1118
1097
|
});
|
|
1119
1098
|
updateNowBox.add(updateNowText);
|
|
1120
1099
|
|
|
@@ -1129,8 +1108,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1129
1108
|
});
|
|
1130
1109
|
const laterText = new TextRenderable(renderer, {
|
|
1131
1110
|
content: " Later",
|
|
1132
|
-
|
|
1133
|
-
|
|
1111
|
+
fg: updateSelectedOption === 1 ? colors.yellow : colors.text,
|
|
1112
|
+
bg: colors.surfaceDim,
|
|
1134
1113
|
});
|
|
1135
1114
|
laterBox.add(laterText);
|
|
1136
1115
|
|
|
@@ -1145,8 +1124,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1145
1124
|
});
|
|
1146
1125
|
const neverText = new TextRenderable(renderer, {
|
|
1147
1126
|
content: " Don't show again",
|
|
1148
|
-
|
|
1149
|
-
|
|
1127
|
+
fg: updateSelectedOption === 2 ? colors.yellow : colors.text,
|
|
1128
|
+
bg: colors.surfaceDim,
|
|
1150
1129
|
});
|
|
1151
1130
|
neverBox.add(neverText);
|
|
1152
1131
|
|
|
@@ -1157,10 +1136,9 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1157
1136
|
updateSelectedOption === 1 ? colors.yellow : colors.border;
|
|
1158
1137
|
neverBox.borderColor =
|
|
1159
1138
|
updateSelectedOption === 2 ? colors.yellow : colors.border;
|
|
1160
|
-
updateNowText.
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
neverText.color = updateSelectedOption === 2 ? colors.yellow : colors.text;
|
|
1139
|
+
updateNowText.fg = updateSelectedOption === 0 ? colors.yellow : colors.text;
|
|
1140
|
+
laterText.fg = updateSelectedOption === 1 ? colors.yellow : colors.text;
|
|
1141
|
+
neverText.fg = updateSelectedOption === 2 ? colors.yellow : colors.text;
|
|
1164
1142
|
renderer.requestRender();
|
|
1165
1143
|
}
|
|
1166
1144
|
|
|
@@ -1169,8 +1147,8 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1169
1147
|
|
|
1170
1148
|
const updateFooter = new TextRenderable(renderer, {
|
|
1171
1149
|
content: " ←→ select Enter Esc/q close",
|
|
1172
|
-
|
|
1173
|
-
|
|
1150
|
+
fg: colors.muted,
|
|
1151
|
+
bg: colors.surfaceDim,
|
|
1174
1152
|
height: 1,
|
|
1175
1153
|
paddingX: 1,
|
|
1176
1154
|
});
|
|
@@ -1543,13 +1521,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1543
1521
|
name: "Quit",
|
|
1544
1522
|
description: "Exit ghfind",
|
|
1545
1523
|
action: () => {
|
|
1546
|
-
saveSession(
|
|
1547
|
-
mode: currentMode,
|
|
1548
|
-
query: currentQueryInput,
|
|
1549
|
-
sort: currentSort,
|
|
1550
|
-
limit: currentLimit,
|
|
1551
|
-
trendingTab,
|
|
1552
|
-
});
|
|
1524
|
+
saveSession(buildSessionState());
|
|
1553
1525
|
cleanup();
|
|
1554
1526
|
},
|
|
1555
1527
|
},
|
|
@@ -1673,7 +1645,7 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1673
1645
|
saveSearch(
|
|
1674
1646
|
name,
|
|
1675
1647
|
currentQueryInput,
|
|
1676
|
-
currentMode,
|
|
1648
|
+
currentMode === "trending" ? "trending" : "search",
|
|
1677
1649
|
currentSort,
|
|
1678
1650
|
currentLimit,
|
|
1679
1651
|
currentMode === "trending" ? trendingTab : undefined,
|
|
@@ -1712,8 +1684,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1712
1684
|
visible: false,
|
|
1713
1685
|
flexGrow: 1,
|
|
1714
1686
|
backgroundColor: colors.surface,
|
|
1715
|
-
borderBottom: true,
|
|
1716
|
-
borderBottomColor: colors.border,
|
|
1717
1687
|
title: " Org profile ",
|
|
1718
1688
|
titleColor: colors.green,
|
|
1719
1689
|
});
|
|
@@ -1726,22 +1696,24 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1726
1696
|
viewportOptions: { backgroundColor: colors.bg },
|
|
1727
1697
|
contentOptions: { backgroundColor: colors.bg, flexDirection: "column" },
|
|
1728
1698
|
scrollbarOptions: {
|
|
1729
|
-
backgroundColor: colors.bg,
|
|
1730
|
-
foregroundColor: colors.muted,
|
|
1731
1699
|
width: 1,
|
|
1700
|
+
trackOptions: {
|
|
1701
|
+
backgroundColor: colors.bg,
|
|
1702
|
+
foregroundColor: colors.muted,
|
|
1703
|
+
},
|
|
1732
1704
|
},
|
|
1733
1705
|
});
|
|
1734
1706
|
const orgText = new TextRenderable(renderer, {
|
|
1735
1707
|
content: "",
|
|
1736
|
-
|
|
1737
|
-
|
|
1708
|
+
fg: colors.text,
|
|
1709
|
+
bg: colors.bg,
|
|
1738
1710
|
});
|
|
1739
1711
|
orgScroll.add(orgText);
|
|
1740
1712
|
orgBox.add(orgScroll);
|
|
1741
1713
|
const orgFooter = new TextRenderable(renderer, {
|
|
1742
1714
|
content: " ↑↓/jk scroll Esc/q close",
|
|
1743
|
-
|
|
1744
|
-
|
|
1715
|
+
fg: colors.muted,
|
|
1716
|
+
bg: colors.bg,
|
|
1745
1717
|
height: 1,
|
|
1746
1718
|
paddingX: 1,
|
|
1747
1719
|
});
|
|
@@ -1812,8 +1784,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1812
1784
|
visible: false,
|
|
1813
1785
|
flexGrow: 1,
|
|
1814
1786
|
backgroundColor: colors.surface,
|
|
1815
|
-
borderBottom: true,
|
|
1816
|
-
borderBottomColor: colors.border,
|
|
1817
1787
|
title: " README ",
|
|
1818
1788
|
titleColor: colors.green,
|
|
1819
1789
|
});
|
|
@@ -1826,22 +1796,24 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1826
1796
|
viewportOptions: { backgroundColor: colors.bg },
|
|
1827
1797
|
contentOptions: { backgroundColor: colors.bg, flexDirection: "column" },
|
|
1828
1798
|
scrollbarOptions: {
|
|
1829
|
-
backgroundColor: colors.bg,
|
|
1830
|
-
foregroundColor: colors.muted,
|
|
1831
1799
|
width: 1,
|
|
1800
|
+
trackOptions: {
|
|
1801
|
+
backgroundColor: colors.bg,
|
|
1802
|
+
foregroundColor: colors.muted,
|
|
1803
|
+
},
|
|
1832
1804
|
},
|
|
1833
1805
|
});
|
|
1834
1806
|
const readmeText = new TextRenderable(renderer, {
|
|
1835
1807
|
content: "",
|
|
1836
|
-
|
|
1837
|
-
|
|
1808
|
+
fg: colors.text,
|
|
1809
|
+
bg: colors.bg,
|
|
1838
1810
|
});
|
|
1839
1811
|
readmeScroll.add(readmeText);
|
|
1840
1812
|
readmeBox.add(readmeScroll);
|
|
1841
1813
|
const readmeFooter = new TextRenderable(renderer, {
|
|
1842
1814
|
content: " ↑↓/jk scroll Esc/q close",
|
|
1843
|
-
|
|
1844
|
-
|
|
1815
|
+
fg: colors.muted,
|
|
1816
|
+
bg: colors.bg,
|
|
1845
1817
|
height: 1,
|
|
1846
1818
|
paddingX: 1,
|
|
1847
1819
|
});
|
|
@@ -1889,12 +1861,9 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1889
1861
|
// ── Status bar ──────────────────────────────────────────────────────
|
|
1890
1862
|
const statusBar = new TextRenderable(renderer, {
|
|
1891
1863
|
content: " Ready. Press Enter to search.",
|
|
1892
|
-
|
|
1893
|
-
color: colors.muted,
|
|
1864
|
+
fg: colors.muted,
|
|
1894
1865
|
height: 1,
|
|
1895
1866
|
paddingX: 1,
|
|
1896
|
-
borderTop: true,
|
|
1897
|
-
borderTopColor: colors.border,
|
|
1898
1867
|
});
|
|
1899
1868
|
root.add(statusBar);
|
|
1900
1869
|
|
|
@@ -1919,6 +1888,23 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
1919
1888
|
statusMgr.set("idle", msg);
|
|
1920
1889
|
}
|
|
1921
1890
|
|
|
1891
|
+
/** Session snapshot; only restorable modes are persisted (see SessionState). */
|
|
1892
|
+
function buildSessionState(): SessionState {
|
|
1893
|
+
const mode: SessionState["mode"] =
|
|
1894
|
+
currentMode === "trending"
|
|
1895
|
+
? "trending"
|
|
1896
|
+
: currentMode === "packages"
|
|
1897
|
+
? "packages"
|
|
1898
|
+
: "search";
|
|
1899
|
+
return {
|
|
1900
|
+
mode,
|
|
1901
|
+
query: currentQueryInput,
|
|
1902
|
+
sort: currentSort,
|
|
1903
|
+
limit: currentLimit,
|
|
1904
|
+
trendingTab,
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1922
1908
|
function setToolbar() {
|
|
1923
1909
|
const sort = currentMode === "packages" ? currentPackageSort : currentSort;
|
|
1924
1910
|
toolbarText.content = formatToolbar(sort, currentLimit, totalCount);
|
|
@@ -2081,11 +2067,29 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
2081
2067
|
),
|
|
2082
2068
|
fetch(`https://api.github.com/repos/${repo.owner}/${repo.name}`, {
|
|
2083
2069
|
headers,
|
|
2084
|
-
}).then(
|
|
2070
|
+
}).then(
|
|
2071
|
+
(
|
|
2072
|
+
r,
|
|
2073
|
+
): Promise<{
|
|
2074
|
+
all?: number[];
|
|
2075
|
+
topics?: string[];
|
|
2076
|
+
message?: string;
|
|
2077
|
+
} | null> | null =>
|
|
2078
|
+
r.ok
|
|
2079
|
+
? (r.clone().json() as Promise<{
|
|
2080
|
+
all?: number[];
|
|
2081
|
+
topics?: string[];
|
|
2082
|
+
message?: string;
|
|
2083
|
+
}>)
|
|
2084
|
+
: null,
|
|
2085
|
+
),
|
|
2085
2086
|
]);
|
|
2086
2087
|
let chartSection = "";
|
|
2087
2088
|
if (chartRes.ok) {
|
|
2088
|
-
const data = await chartRes.json()
|
|
2089
|
+
const data = (await chartRes.json()) as {
|
|
2090
|
+
all?: number[];
|
|
2091
|
+
message?: string;
|
|
2092
|
+
};
|
|
2089
2093
|
if (data?.all && data.all.length >= 2) {
|
|
2090
2094
|
chartCommitData = data.all;
|
|
2091
2095
|
// Chart width: leave room for y-axis labels (6 chars) and padding.
|
|
@@ -2250,7 +2254,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
2250
2254
|
function showSearchMode() {
|
|
2251
2255
|
currentMode = "search";
|
|
2252
2256
|
trendingTabBox.visible = false;
|
|
2253
|
-
gapBox.visible = false;
|
|
2254
2257
|
searchBox.visible = true;
|
|
2255
2258
|
toolbarText.visible = true;
|
|
2256
2259
|
body.visible = true;
|
|
@@ -2263,7 +2266,6 @@ export async function launchBrowser(theme_override?: string): Promise<void> {
|
|
|
2263
2266
|
function showPackagesMode() {
|
|
2264
2267
|
currentMode = "packages";
|
|
2265
2268
|
trendingTabBox.visible = false;
|
|
2266
|
-
gapBox.visible = false;
|
|
2267
2269
|
searchBox.visible = true;
|
|
2268
2270
|
toolbarText.visible = true;
|
|
2269
2271
|
body.visible = true;
|
|
@@ -2807,13 +2809,7 @@ ${pack.description ?? ""}`;
|
|
|
2807
2809
|
toggleGraph();
|
|
2808
2810
|
return;
|
|
2809
2811
|
}
|
|
2810
|
-
saveSession(
|
|
2811
|
-
mode: currentMode,
|
|
2812
|
-
query: currentQueryInput,
|
|
2813
|
-
sort: currentSort,
|
|
2814
|
-
limit: currentLimit,
|
|
2815
|
-
trendingTab,
|
|
2816
|
-
});
|
|
2812
|
+
saveSession(buildSessionState());
|
|
2817
2813
|
cleanup();
|
|
2818
2814
|
return;
|
|
2819
2815
|
}
|
|
@@ -2826,12 +2822,9 @@ ${pack.description ?? ""}`;
|
|
|
2826
2822
|
|
|
2827
2823
|
// Space toggles leader menu (open if closed, close if open)
|
|
2828
2824
|
// Skip when the search input is focused so spaces can be typed in queries
|
|
2825
|
+
// (overlays were already handled above, so this is always "open" here)
|
|
2829
2826
|
if (key.name === "space" && !searchInput.focused) {
|
|
2830
|
-
|
|
2831
|
-
showOverlay("none");
|
|
2832
|
-
} else {
|
|
2833
|
-
showLeaderMenu();
|
|
2834
|
-
}
|
|
2827
|
+
showLeaderMenu();
|
|
2835
2828
|
return;
|
|
2836
2829
|
}
|
|
2837
2830
|
|
|
@@ -2843,6 +2836,13 @@ ${pack.description ?? ""}`;
|
|
|
2843
2836
|
const next = themes[(idx + 1) % themes.length];
|
|
2844
2837
|
const nextTheme = loadTheme(next);
|
|
2845
2838
|
Object.assign(colors, nextTheme);
|
|
2839
|
+
// Keep the terminal-matched backdrop and blended layers across theme
|
|
2840
|
+
// switches (fall back to the new theme's own layers when no detection).
|
|
2841
|
+
if (terminalBg) {
|
|
2842
|
+
colors.bg = terminalBg;
|
|
2843
|
+
Object.assign(colors, deriveSurfaceLayers(terminalBg));
|
|
2844
|
+
}
|
|
2845
|
+
renderer.setBackgroundColor(colors.bg);
|
|
2846
2846
|
config.theme = next;
|
|
2847
2847
|
saveConfig(config);
|
|
2848
2848
|
renderer.requestRender();
|
|
@@ -2851,13 +2851,9 @@ ${pack.description ?? ""}`;
|
|
|
2851
2851
|
|
|
2852
2852
|
// 'c' toggles compare on selected repo (when not typing in search)
|
|
2853
2853
|
if (key.name === "c" && !searchInput.focused) {
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
toggleCompareOnSelected();
|
|
2858
|
-
refreshCompare();
|
|
2859
|
-
showOverlay("compare");
|
|
2860
|
-
}
|
|
2854
|
+
toggleCompareOnSelected();
|
|
2855
|
+
refreshCompare();
|
|
2856
|
+
showOverlay("compare");
|
|
2861
2857
|
return;
|
|
2862
2858
|
}
|
|
2863
2859
|
|