github-search-cli 9.1.0 → 9.1.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/README.md +6 -1
- package/completions/ghfind.bash +5 -1
- package/completions/ghfind.fish +2 -0
- package/completions/ghfind.zsh +2 -0
- package/demo.svg +51 -46
- package/dist/{chunk-TUMYR2IC.js → chunk-IVUU2KGR.js} +1 -2
- package/dist/cli.js +976 -208
- package/dist/{config-DFL44OVL.js → config-GNZOQRTH.js} +1 -1
- package/package.json +1 -1
- package/src/cli.ts +88 -6
- package/src/config.ts +1 -1
- package/src/help.ts +142 -112
- package/src/init.ts +1 -1
- package/src/package-browser.ts +305 -0
- package/src/package.ts +137 -0
- package/src/themes.ts +60 -36
- package/src/tui.ts +403 -52
- package/src/types.ts +16 -2
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import {
|
|
3
2
|
loadConfig,
|
|
4
3
|
saveConfig,
|
|
5
4
|
stateDir
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IVUU2KGR.js";
|
|
7
6
|
|
|
8
7
|
// src/errors.ts
|
|
9
8
|
var SearchCliError = class extends Error {
|
|
@@ -513,8 +512,101 @@ function createTrendingSearch(logger2 = noopLogger) {
|
|
|
513
512
|
return new SearchModule(new TrendingAdapter(logger2), logger2);
|
|
514
513
|
}
|
|
515
514
|
|
|
516
|
-
// src/
|
|
517
|
-
|
|
515
|
+
// src/package.ts
|
|
516
|
+
function normalizeNpmObject(obj) {
|
|
517
|
+
const p = obj.package;
|
|
518
|
+
const name = p.name ?? "";
|
|
519
|
+
return {
|
|
520
|
+
name,
|
|
521
|
+
version: p.version ?? "",
|
|
522
|
+
description: p.description ?? null,
|
|
523
|
+
url: p.links?.npm ?? `https://www.npmjs.com/package/${name}`,
|
|
524
|
+
downloads: obj.downloads?.monthly ?? 0,
|
|
525
|
+
score: obj.score?.final ?? obj.searchScore ?? 0,
|
|
526
|
+
publisher: p.publisher?.username ?? p.author?.name ?? null,
|
|
527
|
+
keywords: p.keywords ?? [],
|
|
528
|
+
registry: "npm"
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function createPackageSearch() {
|
|
532
|
+
return {
|
|
533
|
+
async searchPackage(rawQuery, limit) {
|
|
534
|
+
const size = Math.min(Math.max(Math.trunc(limit) || 50, 1), 250);
|
|
535
|
+
const url = `https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(rawQuery)}&size=${size}`;
|
|
536
|
+
const res = await fetch(url, {
|
|
537
|
+
headers: { "User-Agent": "ghfind", Accept: "application/json" }
|
|
538
|
+
});
|
|
539
|
+
if (!res.ok) {
|
|
540
|
+
throw new Error(`npm search failed: HTTP ${res.status}`);
|
|
541
|
+
}
|
|
542
|
+
const data = await res.json();
|
|
543
|
+
const packages = (data.objects ?? []).map(normalizeNpmObject);
|
|
544
|
+
return { totalCount: data.total ?? packages.length, packages };
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
var PACKAGE_SORT_MODES = [
|
|
549
|
+
{ key: "best-match", label: "best-match" },
|
|
550
|
+
{ key: "downloads", label: "downloads" },
|
|
551
|
+
{ key: "score", label: "score" },
|
|
552
|
+
{ key: "name", label: "name" }
|
|
553
|
+
];
|
|
554
|
+
function sortPackages(pkgs, sort) {
|
|
555
|
+
if (sort === "best-match" || pkgs.length < 2) return pkgs;
|
|
556
|
+
const arr = [...pkgs];
|
|
557
|
+
if (sort === "score") {
|
|
558
|
+
arr.sort((a, b) => b.score - a.score);
|
|
559
|
+
} else if (sort === "downloads") {
|
|
560
|
+
arr.sort((a, b) => b.downloads - a.downloads);
|
|
561
|
+
} else {
|
|
562
|
+
arr.sort((a, b) => a.name.localeCompare(b.name));
|
|
563
|
+
}
|
|
564
|
+
return arr;
|
|
565
|
+
}
|
|
566
|
+
function csvEscape(val) {
|
|
567
|
+
const str = String(val ?? "");
|
|
568
|
+
if (str.includes('"') || str.includes(",") || str.includes("\n")) {
|
|
569
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
570
|
+
}
|
|
571
|
+
return str;
|
|
572
|
+
}
|
|
573
|
+
function formatPackagesJson(pkgs) {
|
|
574
|
+
return JSON.stringify(pkgs, null, 2);
|
|
575
|
+
}
|
|
576
|
+
function formatPackagesCsv(pkgs) {
|
|
577
|
+
const header = "rank,name,version,downloads,score,url";
|
|
578
|
+
const rows = pkgs.map(
|
|
579
|
+
(p, i) => `${i + 1},${csvEscape(p.name)},${p.version},${p.downloads},${p.score.toFixed(2)},${csvEscape(p.url)}`
|
|
580
|
+
);
|
|
581
|
+
return [header, ...rows].join("\n");
|
|
582
|
+
}
|
|
583
|
+
function formatPackagesMarkdown(pkgs) {
|
|
584
|
+
const header = "| # | Package | Version | Downloads | Score |";
|
|
585
|
+
const sep = "|---|---------|---------|-----------|-------|";
|
|
586
|
+
const rows = pkgs.map(
|
|
587
|
+
(p, i) => `| ${i + 1} | [${p.name}@${p.version}](${p.url}) | ${p.version} | ${p.downloads.toLocaleString()} | ${p.score.toFixed(2)} |`
|
|
588
|
+
);
|
|
589
|
+
return [header, sep, ...rows].join("\n");
|
|
590
|
+
}
|
|
591
|
+
function formatPackagesUrls(pkgs) {
|
|
592
|
+
return pkgs.map((p) => p.url).join("\n");
|
|
593
|
+
}
|
|
594
|
+
function formatPackagesNames(pkgs) {
|
|
595
|
+
return pkgs.map((p) => p.name).join("\n");
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/package-browser.ts
|
|
599
|
+
import {
|
|
600
|
+
createCliRenderer,
|
|
601
|
+
BoxRenderable,
|
|
602
|
+
TextRenderable,
|
|
603
|
+
ScrollBoxRenderable,
|
|
604
|
+
StyledText,
|
|
605
|
+
bold,
|
|
606
|
+
fg,
|
|
607
|
+
dim,
|
|
608
|
+
t
|
|
609
|
+
} from "@opentui/core";
|
|
518
610
|
|
|
519
611
|
// src/open-url.ts
|
|
520
612
|
function openUrl(url) {
|
|
@@ -527,7 +619,282 @@ function openUrl(url) {
|
|
|
527
619
|
}
|
|
528
620
|
}
|
|
529
621
|
|
|
622
|
+
// src/package-browser.ts
|
|
623
|
+
var C = {
|
|
624
|
+
bg: "#1a1b26",
|
|
625
|
+
surface: "#16161e",
|
|
626
|
+
text: "#a9b1d6",
|
|
627
|
+
muted: "#565f89",
|
|
628
|
+
cyan: "#7dcfff",
|
|
629
|
+
purple: "#bb9af7",
|
|
630
|
+
green: "#9ece6a",
|
|
631
|
+
gold: "#e0af68",
|
|
632
|
+
orange: "#ff9e64",
|
|
633
|
+
red: "#f7768e",
|
|
634
|
+
blue: "#7aa2f7",
|
|
635
|
+
lightBlue: "#b4f9f8",
|
|
636
|
+
selectionBg: "#3b6ce8",
|
|
637
|
+
selectionText: "#c0caf5",
|
|
638
|
+
border: "#363b54",
|
|
639
|
+
descText: "#6c7086",
|
|
640
|
+
// lighter muted for descriptions
|
|
641
|
+
nameText: "#c0caf5"
|
|
642
|
+
// bright for package names
|
|
643
|
+
};
|
|
644
|
+
var INT_FMT = new Intl.NumberFormat("en-US");
|
|
645
|
+
function fmtCount(n) {
|
|
646
|
+
return INT_FMT.format(Math.trunc(n));
|
|
647
|
+
}
|
|
648
|
+
function formatPackageLine(pack) {
|
|
649
|
+
const nameStr = `${pack.name}@${pack.version}`.padEnd(28).slice(0, 28);
|
|
650
|
+
const dlStr = `\u2193 ${fmtCount(pack.downloads)}`.padEnd(20).slice(0, 20);
|
|
651
|
+
const scoreStr = `score ${pack.score.toFixed(2)}`.padEnd(12).slice(0, 12);
|
|
652
|
+
const desc = (pack.description ?? "").length > 60 ? (pack.description ?? "").slice(0, 57) + "..." : pack.description ?? "";
|
|
653
|
+
const line1 = t`${bold(fg(C.nameText)(nameStr))}${fg(C.cyan)(dlStr)}${fg(C.gold)(scoreStr)}`;
|
|
654
|
+
const line2 = t`${dim(fg(C.descText)(` ${desc}`))}`;
|
|
655
|
+
const newline = { text: "\n", __isChunk: true };
|
|
656
|
+
const chunks = [...line1.chunks, newline, ...line2.chunks];
|
|
657
|
+
return new StyledText(chunks);
|
|
658
|
+
}
|
|
659
|
+
async function launchPackageBrowser() {
|
|
660
|
+
let renderer;
|
|
661
|
+
try {
|
|
662
|
+
renderer = await createCliRenderer();
|
|
663
|
+
} catch (err) {
|
|
664
|
+
console.error(
|
|
665
|
+
"Failed to start the interactive browser.\n",
|
|
666
|
+
`Reason: ${err instanceof Error ? err.message : String(err)}
|
|
667
|
+
`
|
|
668
|
+
);
|
|
669
|
+
process.exit(1);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const root = renderer.root;
|
|
673
|
+
root.flexDirection = "column";
|
|
674
|
+
let query = "";
|
|
675
|
+
let dirty = false;
|
|
676
|
+
let packages = [];
|
|
677
|
+
let selectedIdx = 0;
|
|
678
|
+
let isLoading = false;
|
|
679
|
+
const outerBox = new BoxRenderable(renderer, {
|
|
680
|
+
flexGrow: 1,
|
|
681
|
+
flexDirection: "column",
|
|
682
|
+
backgroundColor: C.bg,
|
|
683
|
+
border: true,
|
|
684
|
+
borderColor: C.border,
|
|
685
|
+
borderStyle: "rounded",
|
|
686
|
+
paddingX: 0,
|
|
687
|
+
paddingY: 0
|
|
688
|
+
});
|
|
689
|
+
const headerBox = new BoxRenderable(renderer, {
|
|
690
|
+
height: 1,
|
|
691
|
+
backgroundColor: C.bg,
|
|
692
|
+
paddingX: 1
|
|
693
|
+
});
|
|
694
|
+
const headerText = new TextRenderable(renderer, {
|
|
695
|
+
content: t`${bold(fg(C.purple)("\u27E0 tulipsearch"))}${fg(C.muted)(" \u2014 Packages")}`,
|
|
696
|
+
fg: C.purple,
|
|
697
|
+
bg: C.bg,
|
|
698
|
+
height: 1
|
|
699
|
+
});
|
|
700
|
+
headerBox.add(headerText);
|
|
701
|
+
outerBox.add(headerBox);
|
|
702
|
+
const inputBox = new BoxRenderable(renderer, {
|
|
703
|
+
height: 1,
|
|
704
|
+
flexDirection: "row",
|
|
705
|
+
backgroundColor: C.surface,
|
|
706
|
+
paddingX: 1
|
|
707
|
+
});
|
|
708
|
+
let inputText = null;
|
|
709
|
+
function renderInput() {
|
|
710
|
+
if (inputText) inputBox.remove(inputText);
|
|
711
|
+
const cursor = isLoading ? " " : "\u258A";
|
|
712
|
+
inputText = new TextRenderable(renderer, {
|
|
713
|
+
content: t`${fg(C.cyan)("\u276F ")}${query}${dim(fg(C.muted)(cursor))}`,
|
|
714
|
+
fg: C.text,
|
|
715
|
+
bg: C.surface,
|
|
716
|
+
height: 1
|
|
717
|
+
});
|
|
718
|
+
inputBox.add(inputText);
|
|
719
|
+
}
|
|
720
|
+
const statusBox = new BoxRenderable(renderer, {
|
|
721
|
+
height: 1,
|
|
722
|
+
backgroundColor: C.bg,
|
|
723
|
+
paddingX: 1
|
|
724
|
+
});
|
|
725
|
+
let statusText = null;
|
|
726
|
+
function setStatus(cfg) {
|
|
727
|
+
if (statusText) statusBox.remove(statusText);
|
|
728
|
+
statusText = new TextRenderable(renderer, {
|
|
729
|
+
content: cfg.content,
|
|
730
|
+
fg: cfg.color ?? C.text,
|
|
731
|
+
bg: C.bg,
|
|
732
|
+
height: 1
|
|
733
|
+
});
|
|
734
|
+
statusBox.add(statusText);
|
|
735
|
+
}
|
|
736
|
+
const scrollBox = new ScrollBoxRenderable(renderer, {
|
|
737
|
+
flexGrow: 1,
|
|
738
|
+
backgroundColor: C.bg,
|
|
739
|
+
scrollY: true,
|
|
740
|
+
scrollX: false,
|
|
741
|
+
viewportCulling: true
|
|
742
|
+
});
|
|
743
|
+
const rowBoxes = [];
|
|
744
|
+
const rowTexts = [];
|
|
745
|
+
function clearRows() {
|
|
746
|
+
for (const rb of rowBoxes) scrollBox.remove(rb);
|
|
747
|
+
rowBoxes.length = 0;
|
|
748
|
+
rowTexts.length = 0;
|
|
749
|
+
}
|
|
750
|
+
function rebuildList() {
|
|
751
|
+
clearRows();
|
|
752
|
+
packages.forEach((p, i) => {
|
|
753
|
+
const isEven = i % 2 === 0;
|
|
754
|
+
const isSelected = i === selectedIdx;
|
|
755
|
+
const rowBg = isSelected ? C.selectionBg : isEven ? C.bg : C.surface;
|
|
756
|
+
const rowText = new TextRenderable(renderer, {
|
|
757
|
+
content: formatPackageLine(p),
|
|
758
|
+
bg: rowBg,
|
|
759
|
+
height: 2
|
|
760
|
+
});
|
|
761
|
+
const rowBox = new BoxRenderable(renderer, {
|
|
762
|
+
flexDirection: "row",
|
|
763
|
+
backgroundColor: rowBg
|
|
764
|
+
});
|
|
765
|
+
rowBox.add(rowText);
|
|
766
|
+
rowBoxes.push(rowBox);
|
|
767
|
+
rowTexts.push(rowText);
|
|
768
|
+
scrollBox.add(rowBox);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
const footerBox = new BoxRenderable(renderer, {
|
|
772
|
+
height: 1,
|
|
773
|
+
backgroundColor: C.bg,
|
|
774
|
+
paddingX: 1
|
|
775
|
+
});
|
|
776
|
+
const footerText = new TextRenderable(renderer, {
|
|
777
|
+
content: " type query \u21B5 search \u2191\u2193 navigate \u21B5 open q/esc quit",
|
|
778
|
+
fg: C.muted,
|
|
779
|
+
bg: C.bg,
|
|
780
|
+
height: 1
|
|
781
|
+
});
|
|
782
|
+
footerBox.add(footerText);
|
|
783
|
+
outerBox.add(footerBox);
|
|
784
|
+
root.add(outerBox);
|
|
785
|
+
outerBox.add(inputBox);
|
|
786
|
+
outerBox.add(statusBox);
|
|
787
|
+
const divider = new TextRenderable(renderer, {
|
|
788
|
+
content: "\u2500".repeat(80),
|
|
789
|
+
fg: C.border,
|
|
790
|
+
bg: C.bg,
|
|
791
|
+
height: 1
|
|
792
|
+
});
|
|
793
|
+
outerBox.add(divider);
|
|
794
|
+
outerBox.add(scrollBox);
|
|
795
|
+
async function runSearch() {
|
|
796
|
+
const q = query.trim();
|
|
797
|
+
if (!q) return;
|
|
798
|
+
isLoading = true;
|
|
799
|
+
dirty = false;
|
|
800
|
+
clearRows();
|
|
801
|
+
setStatus({ content: t`${dim(fg(C.muted)(` Loading packages for "${q}"...`))}` });
|
|
802
|
+
renderer.requestRender();
|
|
803
|
+
try {
|
|
804
|
+
const searcher = createPackageSearch();
|
|
805
|
+
const res = await searcher.searchPackage(q, 25);
|
|
806
|
+
packages = res.packages;
|
|
807
|
+
selectedIdx = 0;
|
|
808
|
+
rebuildList();
|
|
809
|
+
setStatus({
|
|
810
|
+
content: t`${fg(C.muted)(` ${packages.length} of ${fmtCount(res.totalCount)} results for `)}${fg(C.cyan)(`"${q}"`)}`
|
|
811
|
+
});
|
|
812
|
+
} catch (err) {
|
|
813
|
+
packages = [];
|
|
814
|
+
clearRows();
|
|
815
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
816
|
+
setStatus({
|
|
817
|
+
content: t`${fg(C.red)(` Error: ${msg.slice(0, 70)} \u2014 press \u21B5 to retry`)}`
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
isLoading = false;
|
|
821
|
+
renderInput();
|
|
822
|
+
renderer.requestRender();
|
|
823
|
+
}
|
|
824
|
+
function openSelectedPackage() {
|
|
825
|
+
if (selectedIdx < 0 || selectedIdx >= packages.length) return;
|
|
826
|
+
const p = packages[selectedIdx];
|
|
827
|
+
openUrl(p.url);
|
|
828
|
+
}
|
|
829
|
+
function moveSelection(delta) {
|
|
830
|
+
const oldIdx = selectedIdx;
|
|
831
|
+
const newIdx = Math.max(0, Math.min(packages.length - 1, oldIdx + delta));
|
|
832
|
+
if (newIdx === oldIdx) return;
|
|
833
|
+
selectedIdx = newIdx;
|
|
834
|
+
const setBg = (idx, selected) => {
|
|
835
|
+
if (idx < 0 || idx >= rowBoxes.length) return;
|
|
836
|
+
const bg = selected ? C.selectionBg : idx % 2 === 0 ? C.bg : C.surface;
|
|
837
|
+
rowBoxes[idx].backgroundColor = bg;
|
|
838
|
+
if (rowTexts[idx]) rowTexts[idx].bg = bg;
|
|
839
|
+
};
|
|
840
|
+
setBg(oldIdx, false);
|
|
841
|
+
setBg(newIdx, true);
|
|
842
|
+
renderer.requestRender();
|
|
843
|
+
}
|
|
844
|
+
renderer.keyInput.on("keypress", (key) => {
|
|
845
|
+
const name = key.name || "";
|
|
846
|
+
if (name === "q" || name === "escape") {
|
|
847
|
+
process.exit(0);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (name === "backspace") {
|
|
851
|
+
query = query.slice(0, -1);
|
|
852
|
+
dirty = true;
|
|
853
|
+
renderInput();
|
|
854
|
+
renderer.requestRender();
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (name === "space") {
|
|
858
|
+
query += " ";
|
|
859
|
+
dirty = true;
|
|
860
|
+
renderInput();
|
|
861
|
+
renderer.requestRender();
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (name.length === 1) {
|
|
865
|
+
query += name;
|
|
866
|
+
dirty = true;
|
|
867
|
+
renderInput();
|
|
868
|
+
renderer.requestRender();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (name === "up" || name === "k") {
|
|
872
|
+
moveSelection(-1);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
if (name === "down" || name === "j") {
|
|
876
|
+
moveSelection(1);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (name === "enter" || name === "return") {
|
|
880
|
+
if (dirty || packages.length === 0) {
|
|
881
|
+
runSearch();
|
|
882
|
+
} else {
|
|
883
|
+
openSelectedPackage();
|
|
884
|
+
}
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
renderer.start();
|
|
889
|
+
renderInput();
|
|
890
|
+
setStatus({
|
|
891
|
+
content: t`${fg(C.muted)(" Type a package query above and press \u21B5 to search")}`
|
|
892
|
+
});
|
|
893
|
+
renderer.requestRender();
|
|
894
|
+
}
|
|
895
|
+
|
|
530
896
|
// src/output.ts
|
|
897
|
+
import { writeFileSync } from "fs";
|
|
531
898
|
var FORMATS = /* @__PURE__ */ new Map([
|
|
532
899
|
// Export formats
|
|
533
900
|
["json", formatJson],
|
|
@@ -544,7 +911,7 @@ var FORMATS = /* @__PURE__ */ new Map([
|
|
|
544
911
|
function formatJson(repos) {
|
|
545
912
|
return JSON.stringify(repos, null, 2);
|
|
546
913
|
}
|
|
547
|
-
function
|
|
914
|
+
function csvEscape2(val) {
|
|
548
915
|
const str = String(val ?? "");
|
|
549
916
|
if (str.includes('"') || str.includes(",") || str.includes("\n")) {
|
|
550
917
|
return `"${str.replace(/"/g, '""')}"`;
|
|
@@ -554,7 +921,7 @@ function csvEscape(val) {
|
|
|
554
921
|
function formatCsv(repos) {
|
|
555
922
|
const header = "rank,full_name,stars,forks,language,url";
|
|
556
923
|
const rows = repos.map(
|
|
557
|
-
(r, i) => `${i + 1},${
|
|
924
|
+
(r, i) => `${i + 1},${csvEscape2(r.fullName)},${r.stars},${r.forks},${csvEscape2(r.language ?? "")},${csvEscape2(r.url)}`
|
|
558
925
|
);
|
|
559
926
|
return [header, ...rows].join("\n");
|
|
560
927
|
}
|
|
@@ -664,36 +1031,42 @@ import { createInterface } from "readline";
|
|
|
664
1031
|
|
|
665
1032
|
// src/themes.ts
|
|
666
1033
|
var THEMES = {
|
|
667
|
-
"premium-dark": {
|
|
668
|
-
name: "premium-dark",
|
|
669
|
-
bg: "#3D3B3B",
|
|
670
|
-
surface: "#4a4848",
|
|
671
|
-
surfaceAlt: "#525050",
|
|
672
|
-
text: "#e0e4f0",
|
|
673
|
-
muted: "#a8a6a6",
|
|
674
|
-
blue: "#89b4fa",
|
|
675
|
-
green: "#a6e3a1",
|
|
676
|
-
yellow: "#f9e2af",
|
|
677
|
-
red: "#f38ba8",
|
|
678
|
-
teal: "#94e2d5",
|
|
679
|
-
purple: "#cba6f7",
|
|
680
|
-
orange: "#fab387",
|
|
681
|
-
border: "#1C1C1C",
|
|
682
|
-
borderAlt: "#2a2a2a",
|
|
683
|
-
separator: "#1C1C1C",
|
|
684
|
-
selectionBg: "#2A2A9C",
|
|
685
|
-
selectionText: "#ffffff",
|
|
686
|
-
success: "#a6e3a1",
|
|
687
|
-
warning: "#f9e2af",
|
|
688
|
-
error: "#f38ba8"
|
|
689
|
-
},
|
|
690
1034
|
"tokyo-night": {
|
|
691
1035
|
name: "tokyo-night",
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1036
|
+
// Genuine Tokyo Night palette — cool blue-violet mood
|
|
1037
|
+
bg: "#1a1b26",
|
|
1038
|
+
surface: "#24283b",
|
|
1039
|
+
surfaceAlt: "#292e42",
|
|
1040
|
+
text: "#c0caf5",
|
|
1041
|
+
muted: "#565f89",
|
|
1042
|
+
blue: "#7aa2f7",
|
|
1043
|
+
green: "#9ece6a",
|
|
1044
|
+
yellow: "#e0af68",
|
|
1045
|
+
red: "#f7768e",
|
|
1046
|
+
teal: "#73daca",
|
|
1047
|
+
purple: "#bb9af7",
|
|
1048
|
+
orange: "#ff9e64",
|
|
1049
|
+
border: "#24283b",
|
|
1050
|
+
borderAlt: "#1f2335",
|
|
1051
|
+
separator: "#1f2335",
|
|
1052
|
+
selectionBg: "#2d3f76",
|
|
1053
|
+
selectionText: "#c0caf5",
|
|
1054
|
+
success: "#9ece6a",
|
|
1055
|
+
warning: "#e0af68",
|
|
1056
|
+
error: "#f7768e",
|
|
1057
|
+
surfaceDim: "#16161e",
|
|
1058
|
+
accent: "#7aa2f7",
|
|
1059
|
+
accentDim: "#5d7abf",
|
|
1060
|
+
borderAccent: "#1f2335"
|
|
1061
|
+
},
|
|
1062
|
+
"premium-dark": {
|
|
1063
|
+
name: "premium-dark",
|
|
1064
|
+
// Warm counter-mood to Tokyo Night — amber/rust accent
|
|
1065
|
+
bg: "#1e1e2e",
|
|
1066
|
+
surface: "#262637",
|
|
1067
|
+
surfaceAlt: "#2e2e42",
|
|
1068
|
+
text: "#cdd6f4",
|
|
1069
|
+
muted: "#6c7086",
|
|
697
1070
|
blue: "#89b4fa",
|
|
698
1071
|
green: "#a6e3a1",
|
|
699
1072
|
yellow: "#f9e2af",
|
|
@@ -701,60 +1074,18 @@ var THEMES = {
|
|
|
701
1074
|
teal: "#94e2d5",
|
|
702
1075
|
purple: "#cba6f7",
|
|
703
1076
|
orange: "#fab387",
|
|
704
|
-
border: "#
|
|
705
|
-
borderAlt: "#
|
|
706
|
-
separator: "#
|
|
707
|
-
selectionBg: "#
|
|
708
|
-
selectionText: "#
|
|
1077
|
+
border: "#313244",
|
|
1078
|
+
borderAlt: "#2a2a3d",
|
|
1079
|
+
separator: "#2a2a3d",
|
|
1080
|
+
selectionBg: "#45475a",
|
|
1081
|
+
selectionText: "#cdd6f4",
|
|
709
1082
|
success: "#a6e3a1",
|
|
710
1083
|
warning: "#f9e2af",
|
|
711
|
-
error: "#f38ba8"
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
surface: "#4a4848",
|
|
717
|
-
surfaceAlt: "#525050",
|
|
718
|
-
text: "#f8f8f2",
|
|
719
|
-
muted: "#a8a6a6",
|
|
720
|
-
blue: "#8be9fd",
|
|
721
|
-
green: "#50fa7b",
|
|
722
|
-
yellow: "#f1fa8c",
|
|
723
|
-
red: "#ff5555",
|
|
724
|
-
teal: "#50fa7b",
|
|
725
|
-
purple: "#bd93f9",
|
|
726
|
-
orange: "#ffb86c",
|
|
727
|
-
border: "#1C1C1C",
|
|
728
|
-
borderAlt: "#2a2a2a",
|
|
729
|
-
separator: "#1C1C1C",
|
|
730
|
-
selectionBg: "#2A2A9C",
|
|
731
|
-
selectionText: "#ffffff",
|
|
732
|
-
success: "#50fa7b",
|
|
733
|
-
warning: "#f1fa8c",
|
|
734
|
-
error: "#ff5555"
|
|
735
|
-
},
|
|
736
|
-
monokai: {
|
|
737
|
-
name: "monokai",
|
|
738
|
-
bg: "#3D3B3B",
|
|
739
|
-
surface: "#4a4848",
|
|
740
|
-
surfaceAlt: "#525050",
|
|
741
|
-
text: "#f8f8f2",
|
|
742
|
-
muted: "#a8a6a6",
|
|
743
|
-
blue: "#66d9ef",
|
|
744
|
-
green: "#a6e22e",
|
|
745
|
-
yellow: "#e6db74",
|
|
746
|
-
red: "#f92672",
|
|
747
|
-
teal: "#a6e22e",
|
|
748
|
-
purple: "#ae81ff",
|
|
749
|
-
orange: "#fd971f",
|
|
750
|
-
border: "#1C1C1C",
|
|
751
|
-
borderAlt: "#2a2a2a",
|
|
752
|
-
separator: "#1C1C1C",
|
|
753
|
-
selectionBg: "#2A2A9C",
|
|
754
|
-
selectionText: "#f8f8f2",
|
|
755
|
-
success: "#a6e22e",
|
|
756
|
-
warning: "#e6db74",
|
|
757
|
-
error: "#f92672"
|
|
1084
|
+
error: "#f38ba8",
|
|
1085
|
+
surfaceDim: "#181825",
|
|
1086
|
+
accent: "#f9e2af",
|
|
1087
|
+
accentDim: "#c9a87a",
|
|
1088
|
+
borderAccent: "#313244"
|
|
758
1089
|
}
|
|
759
1090
|
};
|
|
760
1091
|
function loadTheme(name) {
|
|
@@ -790,13 +1121,13 @@ async function runInitWizard() {
|
|
|
790
1121
|
const config = {
|
|
791
1122
|
defaultSort: ["best-match", "stars", "updated", "forks"].includes(sortStr) ? sortStr : "best-match",
|
|
792
1123
|
defaultLimit: Math.min(100, Math.max(10, parseInt(limitStr) || 50)),
|
|
793
|
-
theme: themes.includes(themeStr) ? themeStr :
|
|
1124
|
+
theme: themes.includes(themeStr) ? themeStr : themes[0],
|
|
794
1125
|
cacheTtlSeconds: 300,
|
|
795
1126
|
defaultTab: "search"
|
|
796
1127
|
};
|
|
797
1128
|
if (token) config.githubToken = token;
|
|
798
1129
|
saveConfig(config);
|
|
799
|
-
const { configPath } = await import("./config-
|
|
1130
|
+
const { configPath } = await import("./config-GNZOQRTH.js");
|
|
800
1131
|
console.log(` \u2713 Config saved to ${configPath()}`);
|
|
801
1132
|
console.log(" Run 'ghfind' to start browsing!");
|
|
802
1133
|
console.log("");
|
|
@@ -809,25 +1140,25 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
809
1140
|
|
|
810
1141
|
// src/tui.ts
|
|
811
1142
|
import {
|
|
812
|
-
createCliRenderer as
|
|
813
|
-
BoxRenderable as
|
|
814
|
-
TextRenderable as
|
|
1143
|
+
createCliRenderer as createCliRenderer3,
|
|
1144
|
+
BoxRenderable as BoxRenderable3,
|
|
1145
|
+
TextRenderable as TextRenderable3,
|
|
815
1146
|
InputRenderable,
|
|
816
1147
|
SelectRenderable,
|
|
817
|
-
ScrollBoxRenderable as
|
|
1148
|
+
ScrollBoxRenderable as ScrollBoxRenderable3
|
|
818
1149
|
} from "@opentui/core";
|
|
819
1150
|
|
|
820
1151
|
// src/trending.ts
|
|
821
1152
|
import {
|
|
822
|
-
createCliRenderer,
|
|
823
|
-
BoxRenderable,
|
|
824
|
-
TextRenderable,
|
|
825
|
-
ScrollBoxRenderable,
|
|
826
|
-
StyledText,
|
|
827
|
-
bold,
|
|
828
|
-
fg,
|
|
829
|
-
dim,
|
|
830
|
-
t
|
|
1153
|
+
createCliRenderer as createCliRenderer2,
|
|
1154
|
+
BoxRenderable as BoxRenderable2,
|
|
1155
|
+
TextRenderable as TextRenderable2,
|
|
1156
|
+
ScrollBoxRenderable as ScrollBoxRenderable2,
|
|
1157
|
+
StyledText as StyledText2,
|
|
1158
|
+
bold as bold2,
|
|
1159
|
+
fg as fg2,
|
|
1160
|
+
dim as dim2,
|
|
1161
|
+
t as t2
|
|
831
1162
|
} from "@opentui/core";
|
|
832
1163
|
var TAB_NAMES = ["Today", "This Week", "This Month"];
|
|
833
1164
|
function tabSince(tab) {
|
|
@@ -853,6 +1184,16 @@ if (import.meta.main && false) {
|
|
|
853
1184
|
// src/help.ts
|
|
854
1185
|
function buildHelpSections() {
|
|
855
1186
|
return [
|
|
1187
|
+
{
|
|
1188
|
+
title: " Landing ",
|
|
1189
|
+
titleColor: "blue",
|
|
1190
|
+
rows: [
|
|
1191
|
+
{ keys: "\u2191 \u2193 / j k", action: "Navigate menu cards" },
|
|
1192
|
+
{ keys: "Enter", action: "Select mode" },
|
|
1193
|
+
{ keys: "? / h", action: "Toggle this help screen" },
|
|
1194
|
+
{ keys: "q", action: "Quit" }
|
|
1195
|
+
]
|
|
1196
|
+
},
|
|
856
1197
|
{
|
|
857
1198
|
title: " Navigation ",
|
|
858
1199
|
titleColor: "blue",
|
|
@@ -882,6 +1223,17 @@ function buildHelpSections() {
|
|
|
882
1223
|
{ keys: "\u2190 \u2192 / h l", action: "Navigate tabs" }
|
|
883
1224
|
]
|
|
884
1225
|
},
|
|
1226
|
+
{
|
|
1227
|
+
title: " Packages Mode ",
|
|
1228
|
+
titleColor: "cyan",
|
|
1229
|
+
rows: [
|
|
1230
|
+
{ keys: "/", action: "Search npm packages" },
|
|
1231
|
+
{ keys: "\u2191 \u2193 / j k", action: "Move selection" },
|
|
1232
|
+
{ keys: "Enter", action: "View package details" },
|
|
1233
|
+
{ keys: "Space", action: "Open command menu" }
|
|
1234
|
+
],
|
|
1235
|
+
note: "Search by name, description, or tags. Sort by downloads or score."
|
|
1236
|
+
},
|
|
885
1237
|
{
|
|
886
1238
|
title: " Command Menu (Space)",
|
|
887
1239
|
titleColor: "purple",
|
|
@@ -894,9 +1246,17 @@ function buildHelpSections() {
|
|
|
894
1246
|
{ keys: "Deep-dive", action: "Languages + contributors + excerpt" },
|
|
895
1247
|
{ keys: "Bookmark", action: "Save / unsave selected repo" },
|
|
896
1248
|
{ keys: "Compare", action: "Add to / remove from comparison" },
|
|
897
|
-
{ keys: "Trending", action: "Switch to trending browser" }
|
|
1249
|
+
{ keys: "Trending", action: "Switch to trending browser" },
|
|
1250
|
+
{ keys: "Packages", action: "Switch to npm packages" },
|
|
1251
|
+
{ keys: "Export", action: "Export results to file" },
|
|
1252
|
+
{ keys: "Share", action: "Share repo as text" },
|
|
1253
|
+
{ keys: "Topics", action: "Browse GitHub topics" },
|
|
1254
|
+
{ keys: "Saved", action: "View saved searches" },
|
|
1255
|
+
{ keys: "Notifs", action: "Check GitHub notifications" },
|
|
1256
|
+
{ keys: "History", action: "View search history" },
|
|
1257
|
+
{ keys: "Help", action: "Show this help screen" }
|
|
898
1258
|
],
|
|
899
|
-
note: "
|
|
1259
|
+
note: "Navigate menu with \u2191 \u2193, select with Enter, back with \u2190 / Esc"
|
|
900
1260
|
},
|
|
901
1261
|
{
|
|
902
1262
|
title: " Overlay Keys ",
|
|
@@ -905,7 +1265,8 @@ function buildHelpSections() {
|
|
|
905
1265
|
{ keys: "Esc / q", action: "Close any overlay" },
|
|
906
1266
|
{ keys: "Enter", action: "Confirm selection / run action" },
|
|
907
1267
|
{ keys: "d", action: "Delete entry (history, bookmarks, saved)" },
|
|
908
|
-
{ keys: "\u2191 \u2193 / j k", action: "Navigate overlay list" }
|
|
1268
|
+
{ keys: "\u2191 \u2193 / j k", action: "Navigate overlay list" },
|
|
1269
|
+
{ keys: "\u2190 \u2192 / h l", action: "Navigate tabs / panels" }
|
|
909
1270
|
]
|
|
910
1271
|
},
|
|
911
1272
|
{
|
|
@@ -915,23 +1276,26 @@ function buildHelpSections() {
|
|
|
915
1276
|
{ keys: "keywords", action: "Free-text search" },
|
|
916
1277
|
{ keys: "key:value", action: "Qualifier filter" },
|
|
917
1278
|
{ keys: "-key:value", action: "Negated qualifier" },
|
|
918
|
-
{ keys: '"phrase"', action: "Exact phrase match" }
|
|
1279
|
+
{ keys: '"phrase"', action: "Exact phrase match" },
|
|
1280
|
+
{ keys: "language:TypeScript", action: "Filter by language" },
|
|
1281
|
+
{ keys: "stars:>1000", action: "Minimum stars" },
|
|
1282
|
+
{ keys: "fork:true", action: "Include forks" },
|
|
1283
|
+
{ keys: "user:octocat", action: "User's repos" },
|
|
1284
|
+
{ keys: "org:facebook", action: "Organization repos" },
|
|
1285
|
+
{ keys: "repo:facebook/react", action: "Specific repo" },
|
|
1286
|
+
{ keys: "topic:react", action: "Filter by topic" },
|
|
1287
|
+
{ keys: "license:mit", action: "License type" },
|
|
1288
|
+
{ keys: "pushed:>2024-01-01", action: "Last push date" },
|
|
1289
|
+
{ keys: "archived:false", action: "Exclude archived" }
|
|
919
1290
|
]
|
|
920
1291
|
},
|
|
921
1292
|
{
|
|
922
|
-
title: "
|
|
1293
|
+
title: " Utility Keys ",
|
|
923
1294
|
titleColor: "muted",
|
|
924
1295
|
rows: [
|
|
925
|
-
{ keys: "
|
|
926
|
-
{ keys: "
|
|
927
|
-
{ keys: "
|
|
928
|
-
{ keys: "user:", action: "User's repos" },
|
|
929
|
-
{ keys: "org:", action: "Organization repos" },
|
|
930
|
-
{ keys: "repo:", action: "Specific repo" },
|
|
931
|
-
{ keys: "topic:", action: "Filter by topic" },
|
|
932
|
-
{ keys: "license:", action: "License type" },
|
|
933
|
-
{ keys: "pushed:>YYYY-MM-DD", action: "Last push date" },
|
|
934
|
-
{ keys: "archived:false", action: "Exclude archived" }
|
|
1296
|
+
{ keys: "t", action: "Cycle theme (light / dark / solarized)" },
|
|
1297
|
+
{ keys: "Ctrl+X", action: "Copy repo URL to clipboard" },
|
|
1298
|
+
{ keys: "Ctrl+C", action: "Copy search query to clipboard" }
|
|
935
1299
|
]
|
|
936
1300
|
},
|
|
937
1301
|
{
|
|
@@ -1465,7 +1829,6 @@ var colors = {
|
|
|
1465
1829
|
surfaceAlt: "#525050",
|
|
1466
1830
|
text: "#e0e4f0",
|
|
1467
1831
|
muted: "#a8a6a6",
|
|
1468
|
-
blue: "#89b4fa",
|
|
1469
1832
|
green: "#a6e3a1",
|
|
1470
1833
|
yellow: "#f9e2af",
|
|
1471
1834
|
red: "#f38ba8",
|
|
@@ -1500,10 +1863,20 @@ var SORT_MODES = [
|
|
|
1500
1863
|
{ key: "updated", label: "updated" },
|
|
1501
1864
|
{ key: "forks", label: "forks" }
|
|
1502
1865
|
];
|
|
1503
|
-
async function launchBrowser() {
|
|
1866
|
+
async function launchBrowser(theme_override) {
|
|
1504
1867
|
let renderer;
|
|
1505
1868
|
try {
|
|
1506
|
-
|
|
1869
|
+
const isTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
1870
|
+
if (isTTY) {
|
|
1871
|
+
renderer = await createCliRenderer3();
|
|
1872
|
+
} else {
|
|
1873
|
+
renderer = await createCliRenderer3({
|
|
1874
|
+
width: 80,
|
|
1875
|
+
height: 24,
|
|
1876
|
+
screenMode: "main-screen",
|
|
1877
|
+
autoFocus: false
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1507
1880
|
} catch (err) {
|
|
1508
1881
|
const isNode = process.versions.bun === void 0;
|
|
1509
1882
|
if (isNode) {
|
|
@@ -1526,10 +1899,16 @@ async function launchBrowser() {
|
|
|
1526
1899
|
}
|
|
1527
1900
|
const root = renderer.root;
|
|
1528
1901
|
root.flexDirection = "column";
|
|
1902
|
+
root.border = true;
|
|
1903
|
+
root.borderTop = true;
|
|
1904
|
+
root.borderBottom = true;
|
|
1905
|
+
root.borderLeft = true;
|
|
1906
|
+
root.borderRight = true;
|
|
1907
|
+
root.borderColor = colors.borderAccent;
|
|
1529
1908
|
root.backgroundColor = colors.bg;
|
|
1530
1909
|
const config = loadConfig();
|
|
1531
1910
|
const githubToken = config.githubToken || process.env.GITHUB_TOKEN;
|
|
1532
|
-
const theme = loadTheme(config.theme);
|
|
1911
|
+
const theme = loadTheme(theme_override || config.theme);
|
|
1533
1912
|
Object.assign(colors, theme);
|
|
1534
1913
|
const session = restoreSession();
|
|
1535
1914
|
let currentRepos = [];
|
|
@@ -1538,6 +1917,9 @@ async function launchBrowser() {
|
|
|
1538
1917
|
let currentQueryInput = session?.query ?? "";
|
|
1539
1918
|
let isLoading = false;
|
|
1540
1919
|
let currentMode = session?.mode ?? "search";
|
|
1920
|
+
let packages = [];
|
|
1921
|
+
let currentPackageSort = "best-match";
|
|
1922
|
+
let packageResultsRaw = [];
|
|
1541
1923
|
let trendingTab = session?.trendingTab ?? "This Week";
|
|
1542
1924
|
let trendingPeriod = "this week";
|
|
1543
1925
|
let graphFullscreen = false;
|
|
@@ -1548,7 +1930,7 @@ async function launchBrowser() {
|
|
|
1548
1930
|
let deepDiveActive = false;
|
|
1549
1931
|
let compareList = [];
|
|
1550
1932
|
let quitArmed = false;
|
|
1551
|
-
const header = new
|
|
1933
|
+
const header = new TextRenderable3(renderer, {
|
|
1552
1934
|
content: " ghfind \u2014 GitHub repo browser [/]search [Space]menu [\u2192]open [?]help [q]uit",
|
|
1553
1935
|
backgroundColor: colors.bg,
|
|
1554
1936
|
color: colors.muted,
|
|
@@ -1556,7 +1938,7 @@ async function launchBrowser() {
|
|
|
1556
1938
|
borderBottom: true,
|
|
1557
1939
|
borderBottomColor: colors.border
|
|
1558
1940
|
});
|
|
1559
|
-
const trendingTabBox = new
|
|
1941
|
+
const trendingTabBox = new BoxRenderable3(renderer, {
|
|
1560
1942
|
flexDirection: "row",
|
|
1561
1943
|
height: 1,
|
|
1562
1944
|
backgroundColor: colors.surface,
|
|
@@ -1566,12 +1948,12 @@ async function launchBrowser() {
|
|
|
1566
1948
|
});
|
|
1567
1949
|
const trendingTabTexts = [];
|
|
1568
1950
|
function renderTrendingTabs() {
|
|
1569
|
-
for (const
|
|
1951
|
+
for (const t3 of trendingTabTexts) trendingTabBox.remove(t3);
|
|
1570
1952
|
trendingTabTexts.length = 0;
|
|
1571
1953
|
TAB_NAMES.forEach((name, i) => {
|
|
1572
1954
|
const isActive = name === trendingTab;
|
|
1573
1955
|
const label = ` ${name} `;
|
|
1574
|
-
const tt = new
|
|
1956
|
+
const tt = new TextRenderable3(renderer, {
|
|
1575
1957
|
content: label,
|
|
1576
1958
|
color: isActive ? colors.bg : colors.muted,
|
|
1577
1959
|
backgroundColor: isActive ? colors.blue : colors.bg,
|
|
@@ -1580,7 +1962,7 @@ async function launchBrowser() {
|
|
|
1580
1962
|
trendingTabTexts.push(tt);
|
|
1581
1963
|
trendingTabBox.add(tt);
|
|
1582
1964
|
if (i < TAB_NAMES.length - 1) {
|
|
1583
|
-
const sp = new
|
|
1965
|
+
const sp = new TextRenderable3(renderer, {
|
|
1584
1966
|
content: " ",
|
|
1585
1967
|
color: colors.muted,
|
|
1586
1968
|
backgroundColor: colors.bg,
|
|
@@ -1593,16 +1975,20 @@ async function launchBrowser() {
|
|
|
1593
1975
|
}
|
|
1594
1976
|
root.add(header);
|
|
1595
1977
|
root.add(trendingTabBox);
|
|
1596
|
-
const
|
|
1978
|
+
const gapBox = new BoxRenderable3(renderer, {
|
|
1979
|
+
height: 1,
|
|
1980
|
+
visible: true
|
|
1981
|
+
});
|
|
1982
|
+
root.add(gapBox);
|
|
1983
|
+
const searchBox = new BoxRenderable3(renderer, {
|
|
1597
1984
|
visible: true,
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
title: "
|
|
1985
|
+
bordered: true,
|
|
1986
|
+
borderColor: colors.border,
|
|
1987
|
+
title: "",
|
|
1601
1988
|
titleColor: colors.blue,
|
|
1602
1989
|
width: "100%",
|
|
1603
1990
|
flexDirection: "row",
|
|
1604
|
-
|
|
1605
|
-
paddingBottom: 0,
|
|
1991
|
+
marginTop: 1,
|
|
1606
1992
|
backgroundColor: colors.surface
|
|
1607
1993
|
});
|
|
1608
1994
|
const searchInput = new InputRenderable(renderer, {
|
|
@@ -1617,7 +2003,7 @@ async function launchBrowser() {
|
|
|
1617
2003
|
});
|
|
1618
2004
|
searchBox.add(searchInput);
|
|
1619
2005
|
root.add(searchBox);
|
|
1620
|
-
const toolbarText = new
|
|
2006
|
+
const toolbarText = new TextRenderable3(renderer, {
|
|
1621
2007
|
content: formatToolbar(currentSort, currentLimit, totalCount),
|
|
1622
2008
|
visible: true,
|
|
1623
2009
|
// hidden in trending mode
|
|
@@ -1630,15 +2016,16 @@ async function launchBrowser() {
|
|
|
1630
2016
|
paddingX: 1
|
|
1631
2017
|
});
|
|
1632
2018
|
root.add(toolbarText);
|
|
1633
|
-
const body = new
|
|
2019
|
+
const body = new BoxRenderable3(renderer, {
|
|
1634
2020
|
flexDirection: "row",
|
|
1635
2021
|
flexGrow: 1,
|
|
1636
2022
|
gap: 1,
|
|
1637
2023
|
paddingY: 1,
|
|
1638
2024
|
paddingX: 1
|
|
1639
2025
|
});
|
|
1640
|
-
const resultsBox = new
|
|
1641
|
-
|
|
2026
|
+
const resultsBox = new BoxRenderable3(renderer, {
|
|
2027
|
+
bordered: true,
|
|
2028
|
+
borderColor: colors.border,
|
|
1642
2029
|
borderBottomColor: colors.border,
|
|
1643
2030
|
title: " Results ",
|
|
1644
2031
|
titleColor: colors.blue,
|
|
@@ -1666,8 +2053,9 @@ async function launchBrowser() {
|
|
|
1666
2053
|
itemSpacing: 0
|
|
1667
2054
|
});
|
|
1668
2055
|
resultsBox.add(resultsSelect);
|
|
1669
|
-
const detailBox = new
|
|
1670
|
-
|
|
2056
|
+
const detailBox = new BoxRenderable3(renderer, {
|
|
2057
|
+
bordered: true,
|
|
2058
|
+
borderColor: colors.border,
|
|
1671
2059
|
borderBottomColor: colors.border,
|
|
1672
2060
|
title: " Details ",
|
|
1673
2061
|
titleColor: colors.green,
|
|
@@ -1678,16 +2066,174 @@ async function launchBrowser() {
|
|
|
1678
2066
|
focusable: false,
|
|
1679
2067
|
backgroundColor: colors.surface
|
|
1680
2068
|
});
|
|
1681
|
-
const detailText = new
|
|
2069
|
+
const detailText = new TextRenderable3(renderer, {
|
|
1682
2070
|
content: "",
|
|
1683
|
-
color: colors.text
|
|
2071
|
+
color: colors.text,
|
|
2072
|
+
wrapMode: "none"
|
|
1684
2073
|
});
|
|
1685
2074
|
detailBox.add(detailText);
|
|
1686
2075
|
body.add(resultsBox);
|
|
1687
2076
|
body.add(detailBox);
|
|
1688
2077
|
root.add(body);
|
|
2078
|
+
const landingBox = new BoxRenderable3(renderer, {
|
|
2079
|
+
visible: false,
|
|
2080
|
+
flexGrow: 1,
|
|
2081
|
+
flexDirection: "column",
|
|
2082
|
+
alignItems: "center",
|
|
2083
|
+
justifyContent: "center"
|
|
2084
|
+
});
|
|
2085
|
+
root.add(landingBox);
|
|
2086
|
+
const landingBanner = new TextRenderable3(renderer, {
|
|
2087
|
+
content: [
|
|
2088
|
+
" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
2089
|
+
"\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
2090
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
|
|
2091
|
+
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
|
|
2092
|
+
"\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554",
|
|
2093
|
+
" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
2094
|
+
].join("\n"),
|
|
2095
|
+
color: colors.accent,
|
|
2096
|
+
backgroundColor: colors.bg,
|
|
2097
|
+
height: 6
|
|
2098
|
+
});
|
|
2099
|
+
landingBox.add(landingBanner);
|
|
2100
|
+
const landingOptions = [
|
|
2101
|
+
{
|
|
2102
|
+
icon: "\u{1F50D}",
|
|
2103
|
+
title: "Repo Search",
|
|
2104
|
+
desc: "Search GitHub repositories by query, language, stars, and more",
|
|
2105
|
+
action: () => {
|
|
2106
|
+
currentMode = "search";
|
|
2107
|
+
showLanding(false);
|
|
2108
|
+
showSearchMode();
|
|
2109
|
+
}
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
icon: "\u{1F4E6}",
|
|
2113
|
+
title: "Package Search",
|
|
2114
|
+
desc: "Search npm packages by name, description, or tags",
|
|
2115
|
+
action: () => {
|
|
2116
|
+
currentMode = "packages";
|
|
2117
|
+
showLanding(false);
|
|
2118
|
+
showPackagesMode();
|
|
2119
|
+
}
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
icon: "\u{1F525}",
|
|
2123
|
+
title: "Trending",
|
|
2124
|
+
desc: "Browse the hottest repos on GitHub right now",
|
|
2125
|
+
action: () => {
|
|
2126
|
+
currentMode = "trending";
|
|
2127
|
+
showLanding(false);
|
|
2128
|
+
loadTrending();
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
];
|
|
2132
|
+
let landingSelected = 0;
|
|
2133
|
+
const landingCards = [];
|
|
2134
|
+
for (let i = 0; i < landingOptions.length; i++) {
|
|
2135
|
+
const opt = landingOptions[i];
|
|
2136
|
+
const card = new BoxRenderable3(renderer, {
|
|
2137
|
+
flexDirection: "row",
|
|
2138
|
+
height: 1,
|
|
2139
|
+
width: "60%",
|
|
2140
|
+
marginTop: 1,
|
|
2141
|
+
backgroundColor: void 0,
|
|
2142
|
+
borderColor: i === 0 ? colors.accent : colors.border,
|
|
2143
|
+
borderWidth: 1,
|
|
2144
|
+
paddingX: 1,
|
|
2145
|
+
alignItems: "center",
|
|
2146
|
+
focusable: false
|
|
2147
|
+
});
|
|
2148
|
+
landingCards.push(card);
|
|
2149
|
+
landingBox.add(card);
|
|
2150
|
+
const titleLine = new TextRenderable3(renderer, {
|
|
2151
|
+
content: opt.title,
|
|
2152
|
+
color: i === 0 ? colors.accent : colors.text,
|
|
2153
|
+
backgroundColor: void 0,
|
|
2154
|
+
height: 1,
|
|
2155
|
+
flexGrow: 1
|
|
2156
|
+
});
|
|
2157
|
+
card.add(titleLine);
|
|
2158
|
+
}
|
|
2159
|
+
const landingHint = new TextRenderable3(renderer, {
|
|
2160
|
+
content: " \u2191\u2193 Navigate \u21E9 Select [?]help [q]uit",
|
|
2161
|
+
color: colors.muted,
|
|
2162
|
+
backgroundColor: colors.bg,
|
|
2163
|
+
height: 1,
|
|
2164
|
+
marginTop: 1
|
|
2165
|
+
});
|
|
2166
|
+
landingBox.add(landingHint);
|
|
2167
|
+
function showLanding(visible) {
|
|
2168
|
+
landingBox.visible = visible;
|
|
2169
|
+
if (visible) {
|
|
2170
|
+
currentMode = "landing";
|
|
2171
|
+
hideMainContent();
|
|
2172
|
+
landingSelected = 0;
|
|
2173
|
+
updateLandingCards();
|
|
2174
|
+
renderer.requestRender();
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function updateLandingCards() {
|
|
2178
|
+
for (let i = 0; i < landingCards.length; i++) {
|
|
2179
|
+
const opt = landingOptions[i];
|
|
2180
|
+
const isSel = i === landingSelected;
|
|
2181
|
+
landingCards[i].backgroundColor = isSel ? colors.selectionBg : colors.surface;
|
|
2182
|
+
landingCards[i].borderColor = isSel ? colors.accent : colors.border;
|
|
2183
|
+
landingCards[i].borderWidth = isSel ? 1 : 0;
|
|
2184
|
+
const children = landingCards[i].children ?? [];
|
|
2185
|
+
if (children[0]) {
|
|
2186
|
+
const iconCol = children[0];
|
|
2187
|
+
const iconChildren = iconCol.children ?? [];
|
|
2188
|
+
const iconTextChild = iconChildren[0];
|
|
2189
|
+
if (iconTextChild) {
|
|
2190
|
+
iconTextChild.color = isSel ? colors.accent : colors.text;
|
|
2191
|
+
iconTextChild.backgroundColor = isSel ? colors.selectionBg : colors.surface;
|
|
2192
|
+
}
|
|
2193
|
+
const iconSpacerChild = iconChildren[1];
|
|
2194
|
+
if (iconSpacerChild) {
|
|
2195
|
+
iconSpacerChild.backgroundColor = isSel ? colors.selectionBg : colors.surface;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (children[1]) {
|
|
2199
|
+
const contentCol = children[1];
|
|
2200
|
+
const contentChildren = contentCol.children ?? [];
|
|
2201
|
+
const titleChild = contentChildren[0];
|
|
2202
|
+
if (titleChild) {
|
|
2203
|
+
titleChild.color = isSel ? colors.accent : colors.text;
|
|
2204
|
+
titleChild.backgroundColor = isSel ? colors.selectionBg : colors.surface;
|
|
2205
|
+
}
|
|
2206
|
+
const descChild = contentChildren[1];
|
|
2207
|
+
if (descChild) {
|
|
2208
|
+
descChild.backgroundColor = isSel ? colors.selectionBg : colors.surface;
|
|
2209
|
+
}
|
|
2210
|
+
const hintChild = contentChildren[2];
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
renderer.keyInput.on("keypress", (key) => {
|
|
2215
|
+
if (currentMode === "landing") {
|
|
2216
|
+
if (key.name === "up" || key.name === "k") {
|
|
2217
|
+
landingSelected = Math.max(0, landingSelected - 1);
|
|
2218
|
+
updateLandingCards();
|
|
2219
|
+
renderer.requestRender();
|
|
2220
|
+
} else if (key.name === "down" || key.name === "j") {
|
|
2221
|
+
landingSelected = Math.min(landingOptions.length - 1, landingSelected + 1);
|
|
2222
|
+
updateLandingCards();
|
|
2223
|
+
renderer.requestRender();
|
|
2224
|
+
} else if (key.name === "enter" || key.name === "return") {
|
|
2225
|
+
landingOptions[landingSelected].action();
|
|
2226
|
+
} else if (key.name === "?" || key.name === "h") {
|
|
2227
|
+
showOverlay(currentOverlay === "help" ? "none" : "help");
|
|
2228
|
+
} else if (key.name === "q") {
|
|
2229
|
+
cleanup();
|
|
2230
|
+
}
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
1689
2234
|
function hideMainContent() {
|
|
1690
2235
|
trendingTabBox.visible = false;
|
|
2236
|
+
gapBox.visible = false;
|
|
1691
2237
|
searchBox.visible = false;
|
|
1692
2238
|
toolbarText.visible = false;
|
|
1693
2239
|
body.visible = false;
|
|
@@ -1697,6 +2243,7 @@ async function launchBrowser() {
|
|
|
1697
2243
|
trendingTabBox.visible = true;
|
|
1698
2244
|
} else {
|
|
1699
2245
|
searchBox.visible = true;
|
|
2246
|
+
gapBox.visible = true;
|
|
1700
2247
|
toolbarText.visible = true;
|
|
1701
2248
|
}
|
|
1702
2249
|
body.visible = true;
|
|
@@ -1718,7 +2265,16 @@ async function launchBrowser() {
|
|
|
1718
2265
|
updateDim.visible = false;
|
|
1719
2266
|
if (type === "none") {
|
|
1720
2267
|
currentOverlay = "none";
|
|
1721
|
-
|
|
2268
|
+
if (currentMode === "landing") {
|
|
2269
|
+
landingBox.visible = true;
|
|
2270
|
+
body.visible = false;
|
|
2271
|
+
trendingTabBox.visible = false;
|
|
2272
|
+
gapBox.visible = false;
|
|
2273
|
+
searchBox.visible = false;
|
|
2274
|
+
toolbarText.visible = false;
|
|
2275
|
+
} else {
|
|
2276
|
+
showMainContent();
|
|
2277
|
+
}
|
|
1722
2278
|
renderer.requestRender();
|
|
1723
2279
|
return;
|
|
1724
2280
|
}
|
|
@@ -1757,14 +2313,14 @@ async function launchBrowser() {
|
|
|
1757
2313
|
renderer.requestRender();
|
|
1758
2314
|
}
|
|
1759
2315
|
const helpSections = buildHelpSections();
|
|
1760
|
-
const helpBox = new
|
|
2316
|
+
const helpBox = new BoxRenderable3(renderer, {
|
|
1761
2317
|
visible: false,
|
|
1762
2318
|
flexGrow: 1,
|
|
1763
2319
|
backgroundColor: colors.bg,
|
|
1764
2320
|
borderBottom: true,
|
|
1765
2321
|
borderBottomColor: colors.border
|
|
1766
2322
|
});
|
|
1767
|
-
const helpScroll = new
|
|
2323
|
+
const helpScroll = new ScrollBoxRenderable3(renderer, {
|
|
1768
2324
|
flexGrow: 1,
|
|
1769
2325
|
backgroundColor: colors.bg,
|
|
1770
2326
|
scrollY: true,
|
|
@@ -1779,7 +2335,7 @@ async function launchBrowser() {
|
|
|
1779
2335
|
}
|
|
1780
2336
|
});
|
|
1781
2337
|
for (const section of helpSections) {
|
|
1782
|
-
const sectionBox = new
|
|
2338
|
+
const sectionBox = new BoxRenderable3(renderer, {
|
|
1783
2339
|
borderBottom: true,
|
|
1784
2340
|
borderBottomColor: colors.border,
|
|
1785
2341
|
title: section.title,
|
|
@@ -1793,7 +2349,7 @@ async function launchBrowser() {
|
|
|
1793
2349
|
for (const row of section.rows) {
|
|
1794
2350
|
const keys = row.keys.padEnd(HELP_KEYS_COLUMN);
|
|
1795
2351
|
sectionBox.add(
|
|
1796
|
-
new
|
|
2352
|
+
new TextRenderable3(renderer, {
|
|
1797
2353
|
content: ` ${keys} ${row.action}`,
|
|
1798
2354
|
color: colors.text
|
|
1799
2355
|
})
|
|
@@ -1801,7 +2357,7 @@ async function launchBrowser() {
|
|
|
1801
2357
|
}
|
|
1802
2358
|
if (section.note) {
|
|
1803
2359
|
sectionBox.add(
|
|
1804
|
-
new
|
|
2360
|
+
new TextRenderable3(renderer, {
|
|
1805
2361
|
content: ` ${section.note}`,
|
|
1806
2362
|
color: colors.muted
|
|
1807
2363
|
})
|
|
@@ -1811,7 +2367,7 @@ async function launchBrowser() {
|
|
|
1811
2367
|
}
|
|
1812
2368
|
helpBox.add(helpScroll);
|
|
1813
2369
|
root.add(helpBox);
|
|
1814
|
-
const historyBox = new
|
|
2370
|
+
const historyBox = new BoxRenderable3(renderer, {
|
|
1815
2371
|
visible: false,
|
|
1816
2372
|
flexGrow: 1,
|
|
1817
2373
|
backgroundColor: colors.bg,
|
|
@@ -1848,7 +2404,7 @@ async function launchBrowser() {
|
|
|
1848
2404
|
}
|
|
1849
2405
|
historySelect.setSelectedIndex(0);
|
|
1850
2406
|
}
|
|
1851
|
-
const bookmarksBox = new
|
|
2407
|
+
const bookmarksBox = new BoxRenderable3(renderer, {
|
|
1852
2408
|
visible: false,
|
|
1853
2409
|
flexGrow: 1,
|
|
1854
2410
|
backgroundColor: colors.bg,
|
|
@@ -1885,7 +2441,7 @@ async function launchBrowser() {
|
|
|
1885
2441
|
}
|
|
1886
2442
|
bookmarksSelect.setSelectedIndex(0);
|
|
1887
2443
|
}
|
|
1888
|
-
const savedBox = new
|
|
2444
|
+
const savedBox = new BoxRenderable3(renderer, {
|
|
1889
2445
|
visible: false,
|
|
1890
2446
|
flexGrow: 1,
|
|
1891
2447
|
backgroundColor: colors.bg,
|
|
@@ -1907,7 +2463,7 @@ async function launchBrowser() {
|
|
|
1907
2463
|
});
|
|
1908
2464
|
savedBox.add(savedSelect);
|
|
1909
2465
|
root.add(savedBox);
|
|
1910
|
-
const topicsBox = new
|
|
2466
|
+
const topicsBox = new BoxRenderable3(renderer, {
|
|
1911
2467
|
visible: false,
|
|
1912
2468
|
flexGrow: 1,
|
|
1913
2469
|
backgroundColor: colors.bg,
|
|
@@ -1932,10 +2488,10 @@ async function launchBrowser() {
|
|
|
1932
2488
|
async function refreshTopics() {
|
|
1933
2489
|
try {
|
|
1934
2490
|
const topics = await fetchTopics();
|
|
1935
|
-
topicsSelect.options = topics.map((
|
|
1936
|
-
name: `${
|
|
1937
|
-
description:
|
|
1938
|
-
value:
|
|
2491
|
+
topicsSelect.options = topics.map((t3) => ({
|
|
2492
|
+
name: `${t3.name} (${t3.repoCount.toLocaleString()} repos)`,
|
|
2493
|
+
description: t3.description.slice(0, 80),
|
|
2494
|
+
value: t3
|
|
1939
2495
|
}));
|
|
1940
2496
|
} catch {
|
|
1941
2497
|
topicsSelect.options = [
|
|
@@ -1944,7 +2500,7 @@ async function launchBrowser() {
|
|
|
1944
2500
|
}
|
|
1945
2501
|
topicsSelect.setSelectedIndex(0);
|
|
1946
2502
|
}
|
|
1947
|
-
const exportBox = new
|
|
2503
|
+
const exportBox = new BoxRenderable3(renderer, {
|
|
1948
2504
|
visible: false,
|
|
1949
2505
|
flexGrow: 1,
|
|
1950
2506
|
backgroundColor: colors.bg,
|
|
@@ -1983,7 +2539,7 @@ async function launchBrowser() {
|
|
|
1983
2539
|
});
|
|
1984
2540
|
exportBox.add(exportSelect);
|
|
1985
2541
|
root.add(exportBox);
|
|
1986
|
-
const compareBox = new
|
|
2542
|
+
const compareBox = new BoxRenderable3(renderer, {
|
|
1987
2543
|
visible: false,
|
|
1988
2544
|
flexGrow: 1,
|
|
1989
2545
|
backgroundColor: colors.bg,
|
|
@@ -1992,14 +2548,14 @@ async function launchBrowser() {
|
|
|
1992
2548
|
title: " Comparison ",
|
|
1993
2549
|
titleColor: colors.yellow
|
|
1994
2550
|
});
|
|
1995
|
-
const compareText = new
|
|
2551
|
+
const compareText = new TextRenderable3(renderer, {
|
|
1996
2552
|
content: "",
|
|
1997
2553
|
color: colors.text,
|
|
1998
2554
|
backgroundColor: colors.bg
|
|
1999
2555
|
});
|
|
2000
2556
|
compareBox.add(compareText);
|
|
2001
2557
|
root.add(compareBox);
|
|
2002
|
-
const notifsBox = new
|
|
2558
|
+
const notifsBox = new BoxRenderable3(renderer, {
|
|
2003
2559
|
visible: false,
|
|
2004
2560
|
flexGrow: 1,
|
|
2005
2561
|
backgroundColor: colors.bg,
|
|
@@ -2008,7 +2564,7 @@ async function launchBrowser() {
|
|
|
2008
2564
|
title: " Notifications ",
|
|
2009
2565
|
titleColor: colors.yellow
|
|
2010
2566
|
});
|
|
2011
|
-
const notifsText = new
|
|
2567
|
+
const notifsText = new TextRenderable3(renderer, {
|
|
2012
2568
|
content: "(no notifications)",
|
|
2013
2569
|
color: colors.text,
|
|
2014
2570
|
backgroundColor: colors.bg
|
|
@@ -2023,7 +2579,7 @@ async function launchBrowser() {
|
|
|
2023
2579
|
notifsText.content = notifs2.map((n) => ` ${n.icon} ${n.message}`).join("\n");
|
|
2024
2580
|
}
|
|
2025
2581
|
}
|
|
2026
|
-
const shareBox = new
|
|
2582
|
+
const shareBox = new BoxRenderable3(renderer, {
|
|
2027
2583
|
visible: false,
|
|
2028
2584
|
flexGrow: 1,
|
|
2029
2585
|
backgroundColor: colors.bg,
|
|
@@ -2084,7 +2640,7 @@ async function launchBrowser() {
|
|
|
2084
2640
|
}
|
|
2085
2641
|
savedSelect.setSelectedIndex(0);
|
|
2086
2642
|
}
|
|
2087
|
-
const leaderDim = new
|
|
2643
|
+
const leaderDim = new BoxRenderable3(renderer, {
|
|
2088
2644
|
visible: false,
|
|
2089
2645
|
position: "absolute",
|
|
2090
2646
|
top: 0,
|
|
@@ -2095,7 +2651,7 @@ async function launchBrowser() {
|
|
|
2095
2651
|
// 60% opacity black (hex alpha: 0x99 ≈ 153/255)
|
|
2096
2652
|
});
|
|
2097
2653
|
root.add(leaderDim);
|
|
2098
|
-
const leaderBox = new
|
|
2654
|
+
const leaderBox = new BoxRenderable3(renderer, {
|
|
2099
2655
|
visible: false,
|
|
2100
2656
|
position: "absolute",
|
|
2101
2657
|
width: "80%",
|
|
@@ -2127,7 +2683,7 @@ async function launchBrowser() {
|
|
|
2127
2683
|
selectedDescriptionColor: colors.selectionText,
|
|
2128
2684
|
itemSpacing: 0
|
|
2129
2685
|
});
|
|
2130
|
-
const leaderFooter = new
|
|
2686
|
+
const leaderFooter = new TextRenderable3(renderer, {
|
|
2131
2687
|
content: " \u2191\u2193 select Enter run Esc/q close ",
|
|
2132
2688
|
color: colors.muted,
|
|
2133
2689
|
backgroundColor: colors.surfaceDim,
|
|
@@ -2140,7 +2696,7 @@ async function launchBrowser() {
|
|
|
2140
2696
|
leaderBox.add(leaderFooter);
|
|
2141
2697
|
root.add(leaderDim);
|
|
2142
2698
|
root.add(leaderBox);
|
|
2143
|
-
const updateDim = new
|
|
2699
|
+
const updateDim = new BoxRenderable3(renderer, {
|
|
2144
2700
|
visible: false,
|
|
2145
2701
|
position: "absolute",
|
|
2146
2702
|
top: 0,
|
|
@@ -2150,7 +2706,7 @@ async function launchBrowser() {
|
|
|
2150
2706
|
backgroundColor: "#00000088"
|
|
2151
2707
|
});
|
|
2152
2708
|
root.add(updateDim);
|
|
2153
|
-
const updateBox = new
|
|
2709
|
+
const updateBox = new BoxRenderable3(renderer, {
|
|
2154
2710
|
visible: false,
|
|
2155
2711
|
position: "absolute",
|
|
2156
2712
|
width: "55%",
|
|
@@ -2169,20 +2725,20 @@ async function launchBrowser() {
|
|
|
2169
2725
|
paddingLeft: 2,
|
|
2170
2726
|
paddingRight: 2
|
|
2171
2727
|
});
|
|
2172
|
-
const updateText = new
|
|
2728
|
+
const updateText = new TextRenderable3(renderer, {
|
|
2173
2729
|
content: "",
|
|
2174
2730
|
color: colors.accent,
|
|
2175
2731
|
backgroundColor: colors.surfaceDim
|
|
2176
2732
|
});
|
|
2177
2733
|
let updateSelectedOption = 0;
|
|
2178
|
-
const updateOptionsRow = new
|
|
2734
|
+
const updateOptionsRow = new BoxRenderable3(renderer, {
|
|
2179
2735
|
flexDirection: "row",
|
|
2180
2736
|
gap: 1,
|
|
2181
2737
|
backgroundColor: colors.surfaceDim,
|
|
2182
2738
|
height: 3,
|
|
2183
2739
|
width: "100%"
|
|
2184
2740
|
});
|
|
2185
|
-
const updateNowBox = new
|
|
2741
|
+
const updateNowBox = new BoxRenderable3(renderer, {
|
|
2186
2742
|
border: true,
|
|
2187
2743
|
borderColor: updateSelectedOption === 0 ? colors.yellow : colors.border,
|
|
2188
2744
|
backgroundColor: colors.surfaceDim,
|
|
@@ -2191,13 +2747,13 @@ async function launchBrowser() {
|
|
|
2191
2747
|
paddingLeft: 1,
|
|
2192
2748
|
paddingRight: 1
|
|
2193
2749
|
});
|
|
2194
|
-
const updateNowText = new
|
|
2750
|
+
const updateNowText = new TextRenderable3(renderer, {
|
|
2195
2751
|
content: " Update Now ",
|
|
2196
2752
|
color: updateSelectedOption === 0 ? colors.yellow : colors.text,
|
|
2197
2753
|
backgroundColor: colors.surfaceDim
|
|
2198
2754
|
});
|
|
2199
2755
|
updateNowBox.add(updateNowText);
|
|
2200
|
-
const laterBox = new
|
|
2756
|
+
const laterBox = new BoxRenderable3(renderer, {
|
|
2201
2757
|
border: true,
|
|
2202
2758
|
borderColor: updateSelectedOption === 1 ? colors.yellow : colors.border,
|
|
2203
2759
|
backgroundColor: colors.surfaceDim,
|
|
@@ -2206,13 +2762,13 @@ async function launchBrowser() {
|
|
|
2206
2762
|
paddingLeft: 1,
|
|
2207
2763
|
paddingRight: 1
|
|
2208
2764
|
});
|
|
2209
|
-
const laterText = new
|
|
2765
|
+
const laterText = new TextRenderable3(renderer, {
|
|
2210
2766
|
content: " Later",
|
|
2211
2767
|
color: updateSelectedOption === 1 ? colors.yellow : colors.text,
|
|
2212
2768
|
backgroundColor: colors.surfaceDim
|
|
2213
2769
|
});
|
|
2214
2770
|
laterBox.add(laterText);
|
|
2215
|
-
const neverBox = new
|
|
2771
|
+
const neverBox = new BoxRenderable3(renderer, {
|
|
2216
2772
|
border: true,
|
|
2217
2773
|
borderColor: updateSelectedOption === 2 ? colors.yellow : colors.border,
|
|
2218
2774
|
backgroundColor: colors.surfaceDim,
|
|
@@ -2221,7 +2777,7 @@ async function launchBrowser() {
|
|
|
2221
2777
|
paddingLeft: 1,
|
|
2222
2778
|
paddingRight: 1
|
|
2223
2779
|
});
|
|
2224
|
-
const neverText = new
|
|
2780
|
+
const neverText = new TextRenderable3(renderer, {
|
|
2225
2781
|
content: " Don't show again",
|
|
2226
2782
|
color: updateSelectedOption === 2 ? colors.yellow : colors.text,
|
|
2227
2783
|
backgroundColor: colors.surfaceDim
|
|
@@ -2238,7 +2794,7 @@ async function launchBrowser() {
|
|
|
2238
2794
|
}
|
|
2239
2795
|
updateOptionsRow.add(updateNowBox);
|
|
2240
2796
|
updateOptionsRow.add(laterBox);
|
|
2241
|
-
const updateFooter = new
|
|
2797
|
+
const updateFooter = new TextRenderable3(renderer, {
|
|
2242
2798
|
content: " \u2190\u2192 select Enter Esc/q close",
|
|
2243
2799
|
color: colors.muted,
|
|
2244
2800
|
backgroundColor: colors.surfaceDim,
|
|
@@ -2331,6 +2887,29 @@ async function launchBrowser() {
|
|
|
2331
2887
|
}
|
|
2332
2888
|
}
|
|
2333
2889
|
);
|
|
2890
|
+
} else if (currentMode === "packages") {
|
|
2891
|
+
searchItems.push(
|
|
2892
|
+
{
|
|
2893
|
+
type: "action",
|
|
2894
|
+
name: "Sort",
|
|
2895
|
+
description: `Cycle package sort (current: ${currentPackageSort})`,
|
|
2896
|
+
action: () => changePackageSort()
|
|
2897
|
+
},
|
|
2898
|
+
{
|
|
2899
|
+
type: "action",
|
|
2900
|
+
name: "Limit",
|
|
2901
|
+
description: `Cycle result limit (current: ${currentLimit})`,
|
|
2902
|
+
action: () => changeLimit()
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
type: "action",
|
|
2906
|
+
name: "Refresh",
|
|
2907
|
+
description: "Re-run current package search",
|
|
2908
|
+
action: () => {
|
|
2909
|
+
if (currentQueryInput) doPackageSearch(currentQueryInput);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
);
|
|
2334
2913
|
}
|
|
2335
2914
|
if (searchItems.length > 0) {
|
|
2336
2915
|
groups.push({
|
|
@@ -2407,6 +2986,15 @@ async function launchBrowser() {
|
|
|
2407
2986
|
});
|
|
2408
2987
|
}
|
|
2409
2988
|
const navItems = [];
|
|
2989
|
+
navItems.push({
|
|
2990
|
+
type: "action",
|
|
2991
|
+
name: "Packages",
|
|
2992
|
+
description: "Search npm packages",
|
|
2993
|
+
action: () => {
|
|
2994
|
+
showOverlay("none");
|
|
2995
|
+
showPackagesMode();
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2410
2998
|
if (currentMode === "search") {
|
|
2411
2999
|
navItems.push({
|
|
2412
3000
|
type: "action",
|
|
@@ -2634,7 +3222,7 @@ async function launchBrowser() {
|
|
|
2634
3222
|
renderer.requestRender();
|
|
2635
3223
|
}
|
|
2636
3224
|
}
|
|
2637
|
-
const readmeBox = new
|
|
3225
|
+
const readmeBox = new BoxRenderable3(renderer, {
|
|
2638
3226
|
visible: false,
|
|
2639
3227
|
flexGrow: 1,
|
|
2640
3228
|
backgroundColor: colors.surface,
|
|
@@ -2643,7 +3231,7 @@ async function launchBrowser() {
|
|
|
2643
3231
|
title: " README ",
|
|
2644
3232
|
titleColor: colors.green
|
|
2645
3233
|
});
|
|
2646
|
-
const readmeScroll = new
|
|
3234
|
+
const readmeScroll = new ScrollBoxRenderable3(renderer, {
|
|
2647
3235
|
flexGrow: 1,
|
|
2648
3236
|
backgroundColor: colors.bg,
|
|
2649
3237
|
scrollY: true,
|
|
@@ -2657,14 +3245,14 @@ async function launchBrowser() {
|
|
|
2657
3245
|
width: 1
|
|
2658
3246
|
}
|
|
2659
3247
|
});
|
|
2660
|
-
const readmeText = new
|
|
3248
|
+
const readmeText = new TextRenderable3(renderer, {
|
|
2661
3249
|
content: "",
|
|
2662
3250
|
color: colors.text,
|
|
2663
3251
|
backgroundColor: colors.bg
|
|
2664
3252
|
});
|
|
2665
3253
|
readmeScroll.add(readmeText);
|
|
2666
3254
|
readmeBox.add(readmeScroll);
|
|
2667
|
-
const readmeFooter = new
|
|
3255
|
+
const readmeFooter = new TextRenderable3(renderer, {
|
|
2668
3256
|
content: " \u2191\u2193/jk scroll Esc/q close",
|
|
2669
3257
|
color: colors.muted,
|
|
2670
3258
|
backgroundColor: colors.bg,
|
|
@@ -2710,7 +3298,7 @@ async function launchBrowser() {
|
|
|
2710
3298
|
}
|
|
2711
3299
|
renderer.requestRender();
|
|
2712
3300
|
}
|
|
2713
|
-
const statusBar = new
|
|
3301
|
+
const statusBar = new TextRenderable3(renderer, {
|
|
2714
3302
|
content: " Ready. Press Enter to search.",
|
|
2715
3303
|
backgroundColor: colors.surface,
|
|
2716
3304
|
color: colors.muted,
|
|
@@ -2735,7 +3323,8 @@ async function launchBrowser() {
|
|
|
2735
3323
|
statusMgr.set("idle", msg);
|
|
2736
3324
|
}
|
|
2737
3325
|
function setToolbar() {
|
|
2738
|
-
|
|
3326
|
+
const sort = currentMode === "packages" ? currentPackageSort : currentSort;
|
|
3327
|
+
toolbarText.content = formatToolbar(sort, currentLimit, totalCount);
|
|
2739
3328
|
renderer.requestRender();
|
|
2740
3329
|
}
|
|
2741
3330
|
function updateDetail(repo) {
|
|
@@ -2845,33 +3434,38 @@ async function launchBrowser() {
|
|
|
2845
3434
|
}
|
|
2846
3435
|
const xAxis = ` \u2506${"\u2500".repeat(termW)}`;
|
|
2847
3436
|
const weekLabels = ` \u2506 ${"1w".padEnd(Math.floor(termW / 4))} ${"13w".padEnd(Math.floor(termW / 4))} ${"26w".padEnd(Math.floor(termW / 4))} ${"52w"}`;
|
|
2848
|
-
return [xAxis, weekLabels];
|
|
3437
|
+
return [...lines, xAxis, weekLabels];
|
|
2849
3438
|
}
|
|
2850
3439
|
async function loadChart(repo) {
|
|
2851
3440
|
detailText.content = " Loading chart...";
|
|
2852
3441
|
renderer.requestRender();
|
|
2853
3442
|
try {
|
|
3443
|
+
const headers = { "User-Agent": "ghfind/1.0" };
|
|
3444
|
+
if (githubToken) headers.Authorization = `Bearer ${githubToken}`;
|
|
2854
3445
|
const [chartRes, repoRes] = await Promise.all([
|
|
2855
3446
|
fetch(
|
|
2856
3447
|
`https://api.github.com/repos/${repo.owner}/${repo.name}/stats/participation`,
|
|
2857
|
-
{ headers
|
|
3448
|
+
{ headers }
|
|
2858
3449
|
),
|
|
2859
3450
|
fetch(`https://api.github.com/repos/${repo.owner}/${repo.name}`, {
|
|
2860
|
-
headers
|
|
2861
|
-
}).then((r) => r.ok ? r.json() : null)
|
|
3451
|
+
headers
|
|
3452
|
+
}).then((r) => r.ok ? r.clone().json() : null)
|
|
2862
3453
|
]);
|
|
2863
3454
|
let chartSection = "";
|
|
2864
3455
|
if (chartRes.ok) {
|
|
2865
3456
|
const data = await chartRes.json();
|
|
2866
3457
|
if (data?.all && data.all.length >= 2) {
|
|
2867
3458
|
chartCommitData = data.all;
|
|
2868
|
-
const
|
|
3459
|
+
const termW = graphFullscreen ? (process.stdout.columns || 120) - 12 : Math.floor((process.stdout.columns || 120) / 2) - 12;
|
|
3460
|
+
const chartW = Math.max(20, Math.min(60, termW));
|
|
2869
3461
|
const chartH = 10;
|
|
2870
3462
|
const chartLines = buildChartString(chartCommitData, chartW, chartH);
|
|
2871
|
-
chartSection = ["", "Weekly commits (52 weeks)", ...chartLines].join(
|
|
2872
|
-
|
|
2873
|
-
);
|
|
3463
|
+
chartSection = ["", "Weekly commits (52 weeks)", ...chartLines].join("\n");
|
|
3464
|
+
} else if (data?.message) {
|
|
3465
|
+
chartSection = ["", `Chart unavailable: ${data.message}`].join("\n");
|
|
2874
3466
|
}
|
|
3467
|
+
} else {
|
|
3468
|
+
chartSection = ["", `Chart unavailable (API ${chartRes.status})`].join("\n");
|
|
2875
3469
|
}
|
|
2876
3470
|
const desc = repo.description ?? "";
|
|
2877
3471
|
const lang = repo.language ?? "\u2014";
|
|
@@ -2982,12 +3576,69 @@ async function launchBrowser() {
|
|
|
2982
3576
|
function showSearchMode() {
|
|
2983
3577
|
currentMode = "search";
|
|
2984
3578
|
trendingTabBox.visible = false;
|
|
3579
|
+
gapBox.visible = false;
|
|
2985
3580
|
searchBox.visible = true;
|
|
2986
3581
|
toolbarText.visible = true;
|
|
3582
|
+
body.visible = true;
|
|
2987
3583
|
searchInput.placeholder = "Search GitHub repos (e.g. rust cli, or language:Rust stars:>100)";
|
|
2988
3584
|
searchInput.focus();
|
|
2989
3585
|
renderer.requestRender();
|
|
2990
3586
|
}
|
|
3587
|
+
function showPackagesMode() {
|
|
3588
|
+
currentMode = "packages";
|
|
3589
|
+
trendingTabBox.visible = false;
|
|
3590
|
+
gapBox.visible = false;
|
|
3591
|
+
searchBox.visible = true;
|
|
3592
|
+
toolbarText.visible = true;
|
|
3593
|
+
body.visible = true;
|
|
3594
|
+
searchInput.placeholder = "Search npm packages (e.g. react, vue, typescript)";
|
|
3595
|
+
searchInput.focus();
|
|
3596
|
+
packages = [];
|
|
3597
|
+
resultsSelect.options = [
|
|
3598
|
+
{ name: "", description: "Type a query and press Enter to search", value: null }
|
|
3599
|
+
];
|
|
3600
|
+
detailText.content = "";
|
|
3601
|
+
renderer.requestRender();
|
|
3602
|
+
}
|
|
3603
|
+
async function doPackageSearch(queryText) {
|
|
3604
|
+
const q = queryText.trim();
|
|
3605
|
+
if (q === "" || isLoading) return;
|
|
3606
|
+
currentQueryInput = q;
|
|
3607
|
+
isLoading = true;
|
|
3608
|
+
resultsSelect.options = [
|
|
3609
|
+
{ name: " Searching packages...", description: "", value: null }
|
|
3610
|
+
];
|
|
3611
|
+
detailText.content = "";
|
|
3612
|
+
statusMgr.set("searching", `Searching npm for "${q}"`);
|
|
3613
|
+
renderer.requestRender();
|
|
3614
|
+
try {
|
|
3615
|
+
const search = createPackageSearch();
|
|
3616
|
+
const result = await search.searchPackage(q, currentLimit);
|
|
3617
|
+
totalCount = result.totalCount;
|
|
3618
|
+
packageResultsRaw = result.packages;
|
|
3619
|
+
packages = sortPackages(packageResultsRaw, currentPackageSort);
|
|
3620
|
+
setToolbar();
|
|
3621
|
+
if (packages.length > 0) {
|
|
3622
|
+
resultsSelect.options = packages.map((p) => ({
|
|
3623
|
+
name: `${p.name}@${p.version}`,
|
|
3624
|
+
description: p.description ?? "",
|
|
3625
|
+
value: p
|
|
3626
|
+
}));
|
|
3627
|
+
resultsSelect.setSelectedIndex(0);
|
|
3628
|
+
statusMgr.set("success", `${packages.length} packages found`);
|
|
3629
|
+
} else {
|
|
3630
|
+
throw new NoResultsError(q);
|
|
3631
|
+
}
|
|
3632
|
+
} catch (err) {
|
|
3633
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3634
|
+
resultsSelect.options = [
|
|
3635
|
+
{ name: " (error)", description: "", value: null }
|
|
3636
|
+
];
|
|
3637
|
+
statusMgr.set("error", msg.slice(0, 60));
|
|
3638
|
+
}
|
|
3639
|
+
isLoading = false;
|
|
3640
|
+
renderer.requestRender();
|
|
3641
|
+
}
|
|
2991
3642
|
async function doSearch(queryText, append = false) {
|
|
2992
3643
|
const q = queryText.trim();
|
|
2993
3644
|
if (q === "" || isLoading) return;
|
|
@@ -3074,6 +3725,22 @@ async function launchBrowser() {
|
|
|
3074
3725
|
renderer.requestRender();
|
|
3075
3726
|
}
|
|
3076
3727
|
}
|
|
3728
|
+
function changePackageSort() {
|
|
3729
|
+
const idx = PACKAGE_SORT_MODES.findIndex((m) => m.key === currentPackageSort);
|
|
3730
|
+
const next = PACKAGE_SORT_MODES[(idx + 1) % PACKAGE_SORT_MODES.length];
|
|
3731
|
+
currentPackageSort = next.key;
|
|
3732
|
+
setToolbar();
|
|
3733
|
+
if (packageResultsRaw.length > 0) {
|
|
3734
|
+
packages = sortPackages(packageResultsRaw, currentPackageSort);
|
|
3735
|
+
resultsSelect.options = packages.map((p) => ({
|
|
3736
|
+
name: `${p.name}@${p.version}`,
|
|
3737
|
+
description: p.description ?? "",
|
|
3738
|
+
value: p
|
|
3739
|
+
}));
|
|
3740
|
+
resultsSelect.setSelectedIndex(0);
|
|
3741
|
+
renderer.requestRender();
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3077
3744
|
function changeLimit() {
|
|
3078
3745
|
const limits = [10, 25, 50, 100];
|
|
3079
3746
|
const idx = limits.indexOf(currentLimit);
|
|
@@ -3084,22 +3751,40 @@ async function launchBrowser() {
|
|
|
3084
3751
|
}
|
|
3085
3752
|
searchInput.on("enter", () => {
|
|
3086
3753
|
if (searchInput.value.trim() === "" || isLoading) return;
|
|
3087
|
-
|
|
3088
|
-
|
|
3754
|
+
if (currentMode === "packages") {
|
|
3755
|
+
doPackageSearch(searchInput.value);
|
|
3756
|
+
} else {
|
|
3757
|
+
doSearch(searchInput.value);
|
|
3758
|
+
}
|
|
3089
3759
|
});
|
|
3090
3760
|
resultsSelect.on("selectionChanged", () => {
|
|
3091
3761
|
const opt = resultsSelect.getSelectedOption();
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
if (
|
|
3095
|
-
|
|
3762
|
+
if (currentMode === "packages") {
|
|
3763
|
+
const pack = opt?.value;
|
|
3764
|
+
if (pack) {
|
|
3765
|
+
detailText.content = `Package: ${pack.name}@${pack.version}
|
|
3766
|
+
Downloads: ${pack.downloads.toLocaleString()}
|
|
3767
|
+
Score: ${pack.score.toFixed(2)}
|
|
3768
|
+
${pack.description ?? ""}`;
|
|
3769
|
+
}
|
|
3770
|
+
} else {
|
|
3771
|
+
const repo = opt?.value;
|
|
3772
|
+
if (repo) {
|
|
3773
|
+
if (graphFullscreen) loadChart(repo);
|
|
3774
|
+
else updateDetail(repo);
|
|
3775
|
+
}
|
|
3096
3776
|
}
|
|
3097
3777
|
renderer.requestRender();
|
|
3098
3778
|
});
|
|
3099
3779
|
resultsSelect.on("itemSelected", () => {
|
|
3100
3780
|
const opt = resultsSelect.getSelectedOption();
|
|
3101
|
-
|
|
3102
|
-
|
|
3781
|
+
if (currentMode === "packages") {
|
|
3782
|
+
const pack = opt?.value;
|
|
3783
|
+
if (pack?.url) openUrl(pack.url);
|
|
3784
|
+
} else {
|
|
3785
|
+
const repo = opt?.value;
|
|
3786
|
+
if (repo) openUrl(repo.url);
|
|
3787
|
+
}
|
|
3103
3788
|
});
|
|
3104
3789
|
renderer.keyInput.on("keypress", (key) => {
|
|
3105
3790
|
if (currentOverlay !== "none") {
|
|
@@ -3389,6 +4074,18 @@ async function launchBrowser() {
|
|
|
3389
4074
|
showLeaderMenu();
|
|
3390
4075
|
}
|
|
3391
4076
|
return;
|
|
4077
|
+
if (key.name === "t" && !searchInput.focused) {
|
|
4078
|
+
const current = config.theme;
|
|
4079
|
+
const themes = listThemes();
|
|
4080
|
+
const idx = themes.indexOf(current);
|
|
4081
|
+
const next = themes[(idx + 1) % themes.length];
|
|
4082
|
+
const nextTheme = loadTheme(next);
|
|
4083
|
+
Object.assign(colors, nextTheme);
|
|
4084
|
+
config.theme = next;
|
|
4085
|
+
saveConfig(config);
|
|
4086
|
+
renderer.requestRender();
|
|
4087
|
+
return;
|
|
4088
|
+
}
|
|
3392
4089
|
}
|
|
3393
4090
|
if (key.name === "?" || key.name === "h" && key.ctrl) {
|
|
3394
4091
|
showOverlay("help");
|
|
@@ -3471,18 +4168,23 @@ async function launchBrowser() {
|
|
|
3471
4168
|
currentVersion = pkg.version;
|
|
3472
4169
|
} catch {
|
|
3473
4170
|
}
|
|
4171
|
+
if (!session || !session.mode) {
|
|
4172
|
+
showLanding(true);
|
|
4173
|
+
}
|
|
3474
4174
|
checkForUpdateAndShow();
|
|
3475
4175
|
renderer.start();
|
|
3476
|
-
if (
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
searchInput.value = currentQueryInput;
|
|
3480
|
-
if (currentQueryInput) {
|
|
3481
|
-
doSearch(currentQueryInput);
|
|
4176
|
+
if (session && session.mode) {
|
|
4177
|
+
if (currentMode === "trending") {
|
|
4178
|
+
loadTrending();
|
|
3482
4179
|
} else {
|
|
3483
|
-
|
|
4180
|
+
searchInput.value = currentQueryInput;
|
|
4181
|
+
if (currentQueryInput) {
|
|
4182
|
+
doSearch(currentQueryInput);
|
|
4183
|
+
} else {
|
|
4184
|
+
showSearchMode();
|
|
4185
|
+
}
|
|
4186
|
+
searchInput.focus();
|
|
3484
4187
|
}
|
|
3485
|
-
searchInput.focus();
|
|
3486
4188
|
}
|
|
3487
4189
|
renderer.requestRender();
|
|
3488
4190
|
}
|
|
@@ -3513,6 +4215,7 @@ if (import.meta.main && false) {
|
|
|
3513
4215
|
}
|
|
3514
4216
|
|
|
3515
4217
|
// src/cli.ts
|
|
4218
|
+
process.env.GHFIND_CLI_RUN = "1";
|
|
3516
4219
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
3517
4220
|
var PKG_DIR2 = join3(__dirname2, "..");
|
|
3518
4221
|
function parseArgs(args) {
|
|
@@ -3531,7 +4234,9 @@ function parseArgs(args) {
|
|
|
3531
4234
|
interval: 300,
|
|
3532
4235
|
init: false,
|
|
3533
4236
|
version: false,
|
|
3534
|
-
help: false
|
|
4237
|
+
help: false,
|
|
4238
|
+
pkg: false,
|
|
4239
|
+
registry: "npm"
|
|
3535
4240
|
};
|
|
3536
4241
|
let queryParts = [];
|
|
3537
4242
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -3591,8 +4296,11 @@ function parseArgs(args) {
|
|
|
3591
4296
|
case "--format":
|
|
3592
4297
|
flags.format = args[++i];
|
|
3593
4298
|
break;
|
|
3594
|
-
case "--
|
|
3595
|
-
flags.
|
|
4299
|
+
case "--registry":
|
|
4300
|
+
flags.registry = args[++i] ?? "npm";
|
|
4301
|
+
break;
|
|
4302
|
+
case "--theme":
|
|
4303
|
+
flags.theme = args[++i] ?? "tokyo-night";
|
|
3596
4304
|
break;
|
|
3597
4305
|
case "--completion":
|
|
3598
4306
|
flags.completion = args[++i];
|
|
@@ -3601,6 +4309,10 @@ function parseArgs(args) {
|
|
|
3601
4309
|
if (!arg.startsWith("-")) queryParts.push(arg);
|
|
3602
4310
|
}
|
|
3603
4311
|
}
|
|
4312
|
+
if (queryParts[0] === "pkg") {
|
|
4313
|
+
flags.pkg = true;
|
|
4314
|
+
queryParts.shift();
|
|
4315
|
+
}
|
|
3604
4316
|
flags.query = queryParts.join(" ");
|
|
3605
4317
|
return flags;
|
|
3606
4318
|
}
|
|
@@ -3624,6 +4336,8 @@ Usage:
|
|
|
3624
4336
|
ghfind <query> --format <fmt> Format lines (urls|names|ssh-urls|clone-commands|ids)
|
|
3625
4337
|
ghfind <query> --pipe <target> Pipe to clone/open
|
|
3626
4338
|
ghfind --trending --json Trending repos as JSON
|
|
4339
|
+
ghfind pkg <query> --json Search npm packages, output JSON
|
|
4340
|
+
ghfind pkg <query> Search npm packages, text list
|
|
3627
4341
|
ghfind --watch <query> Watch mode (poll every Ns)
|
|
3628
4342
|
ghfind init Run setup wizard
|
|
3629
4343
|
ghfind --completion <shell> Print completion script (bash|zsh|fish)
|
|
@@ -3634,11 +4348,13 @@ Options:
|
|
|
3634
4348
|
--json, --csv, --markdown, --count Output format
|
|
3635
4349
|
--limit <n> Max results (default: 50)
|
|
3636
4350
|
--sort <strategy> Sort: best-match|stars|updated|forks
|
|
3637
|
-
--
|
|
4351
|
+
--theme <name> Theme: tokyo-night|premium-dark (default: tokyo-night)
|
|
3638
4352
|
--trending Trending mode
|
|
3639
4353
|
--since <period> Trending period: daily|weekly|monthly
|
|
3640
4354
|
--pipe <target> Pipe target: clone|open
|
|
3641
4355
|
--format <fmt> Line format: urls|names|ssh-urls|...
|
|
4356
|
+
--registry <name> Registry for pkg: npm (default) | others TBD
|
|
4357
|
+
|
|
3642
4358
|
--watch Watch mode (re-run periodically)
|
|
3643
4359
|
--interval <s> Watch interval in seconds (default: 300)
|
|
3644
4360
|
--completion <shell> Generate completions
|
|
@@ -3660,13 +4376,22 @@ Options:
|
|
|
3660
4376
|
await runInitWizard();
|
|
3661
4377
|
return;
|
|
3662
4378
|
}
|
|
4379
|
+
if (flags.pkg) {
|
|
4380
|
+
const interactive = !flags.query && !flags.json && !flags.csv && !flags.markdown && !flags.count && !flags.format;
|
|
4381
|
+
if (interactive) {
|
|
4382
|
+
await launchPackageBrowser();
|
|
4383
|
+
} else {
|
|
4384
|
+
await runPackageSearch(flags);
|
|
4385
|
+
}
|
|
4386
|
+
return;
|
|
4387
|
+
}
|
|
3663
4388
|
const outputFormat = flags.json ? "json" : flags.csv ? "csv" : flags.markdown ? "markdown" : void 0;
|
|
3664
4389
|
const isNonInteractive = outputFormat || flags.count || flags.format || flags.pipe || flags.watch;
|
|
3665
4390
|
if (isNonInteractive) {
|
|
3666
4391
|
await runNonInteractive(flags, outputFormat);
|
|
3667
4392
|
return;
|
|
3668
4393
|
}
|
|
3669
|
-
await launchBrowser();
|
|
4394
|
+
await launchBrowser(flags.theme);
|
|
3670
4395
|
}
|
|
3671
4396
|
function buildSearchContext(flags) {
|
|
3672
4397
|
const parsed = applyFlagFilters(parseQuery(flags.query), {});
|
|
@@ -3676,6 +4401,48 @@ function buildSearchContext(flags) {
|
|
|
3676
4401
|
provider: createGitHubSearch(void 0, flags.token ? [flags.token] : [])
|
|
3677
4402
|
};
|
|
3678
4403
|
}
|
|
4404
|
+
async function runPackageSearch(flags) {
|
|
4405
|
+
if (flags.registry !== "npm") {
|
|
4406
|
+
console.error(`Registry "${flags.registry}" not yet supported (only npm).`);
|
|
4407
|
+
process.exit(1);
|
|
4408
|
+
}
|
|
4409
|
+
const PKG_SORTS = ["best-match", "score", "downloads", "name"];
|
|
4410
|
+
const pkgSort = flags.sort;
|
|
4411
|
+
if (!PKG_SORTS.includes(pkgSort)) {
|
|
4412
|
+
console.error(`Invalid sort for pkg: "${flags.sort}" (use best-match|score|downloads|name).`);
|
|
4413
|
+
process.exit(1);
|
|
4414
|
+
}
|
|
4415
|
+
const search = createPackageSearch();
|
|
4416
|
+
const query = flags.query.trim();
|
|
4417
|
+
if (!query) {
|
|
4418
|
+
console.error("Usage: ghfind pkg <query> [--json|--csv|--markdown|--count|--format urls|names] [--limit <n>]");
|
|
4419
|
+
process.exit(1);
|
|
4420
|
+
}
|
|
4421
|
+
try {
|
|
4422
|
+
const { totalCount, packages } = await search.searchPackage(query, flags.limit);
|
|
4423
|
+
const sorted = sortPackages(packages, pkgSort);
|
|
4424
|
+
if (flags.count) {
|
|
4425
|
+
console.log(totalCount);
|
|
4426
|
+
} else if (flags.json) {
|
|
4427
|
+
console.log(formatPackagesJson(sorted));
|
|
4428
|
+
} else if (flags.csv) {
|
|
4429
|
+
console.log(formatPackagesCsv(sorted));
|
|
4430
|
+
} else if (flags.markdown) {
|
|
4431
|
+
console.log(formatPackagesMarkdown(sorted));
|
|
4432
|
+
} else if (flags.format === "urls") {
|
|
4433
|
+
console.log(formatPackagesUrls(sorted));
|
|
4434
|
+
} else if (flags.format === "names") {
|
|
4435
|
+
console.log(formatPackagesNames(sorted));
|
|
4436
|
+
} else {
|
|
4437
|
+
for (const p of sorted) {
|
|
4438
|
+
console.log(`${p.name}@${p.version} \u2193 ${p.downloads.toLocaleString()} ${p.score.toFixed(2)} ${p.description ?? ""}`);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
} catch (err) {
|
|
4442
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
4443
|
+
process.exit(1);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
3679
4446
|
async function runNonInteractive(flags, outputFormat) {
|
|
3680
4447
|
const handlers = {
|
|
3681
4448
|
trending: async (ctx2) => {
|
|
@@ -3756,6 +4523,7 @@ async function runNonInteractive(flags, outputFormat) {
|
|
|
3756
4523
|
await handlers[key](ctx);
|
|
3757
4524
|
}
|
|
3758
4525
|
main().catch((err) => {
|
|
4526
|
+
console.error("DEBUG: uncaught error:", err.message || err);
|
|
3759
4527
|
console.error(err instanceof Error ? err.message : String(err));
|
|
3760
4528
|
process.exit(1);
|
|
3761
4529
|
});
|