github-search-cli 9.4.2 → 9.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-search-cli",
|
|
3
|
-
"version": "9.4.
|
|
3
|
+
"version": "9.4.4",
|
|
4
4
|
"description": "Search GitHub from your terminal — repo search, trending repos, npm package search, bookmarks, deep-dive, compare, export. Keyboard-driven TUI + pipeable CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1",
|
|
@@ -34,8 +34,11 @@
|
|
|
34
34
|
"test": "bun test",
|
|
35
35
|
"dev": "bun run --watch src/tui.ts",
|
|
36
36
|
"postinstall": "node scripts/postinstall.js",
|
|
37
|
-
"format:check": "
|
|
38
|
-
"format": "
|
|
37
|
+
"format:check": "prettier --check .",
|
|
38
|
+
"format": "prettier --write .",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "bunx biome@2.5.13 check .",
|
|
41
|
+
"check": "prettier --check . && bun test"
|
|
39
42
|
},
|
|
40
43
|
"dependencies": {
|
|
41
44
|
"@opentui/core": "^0.4.3"
|
package/release-notes/9.3.0.md
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
# What's New in ghfind 9.3.0
|
|
2
|
-
|
|
3
|
-
Release tracking for bookmarked repos, plus a bookmarks page and toast notifications in the TUI.
|
|
4
|
-
|
|
5
|
-
### Added
|
|
6
|
-
|
|
7
|
-
**Release tracker** — follow releases of repos you've bookmarked
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
# What's New in ghfind 9.3.0
|
|
2
|
+
|
|
3
|
+
Release tracking for bookmarked repos, plus a bookmarks page and toast notifications in the TUI.
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
**Release tracker** — follow releases of repos you've bookmarked
|
|
8
|
+
|
|
9
|
+
- `src/releases.ts` — checks each bookmarked repo against `GET /repos/{owner}/{repo}/releases?per_page=5`
|
|
10
|
+
- Seen state is `lastSeenAt` per bookmark; "new" means published after that timestamp
|
|
11
|
+
- ETag conditional requests + `GITHUB_TOKEN` when available to conserve rate budget
|
|
12
|
+
- Prereleases shown and tagged `[pre]`; drafts never shown
|
|
13
|
+
- `ghfind --releases` prints the new-release feed, then marks everything seen; `--releases --json` for machine output
|
|
14
|
+
- Opening the Notifications panel (Space → System → Notifications) now runs a check in the background and files each new release as a `🚀` notification
|
|
15
|
+
|
|
16
|
+
**Bookmarks page** — bookmarks are now a first-class mode, not an overlay
|
|
17
|
+
|
|
18
|
+
- Reachable from the landing screen (`b`, or `Enter` on the new button) and from Space → Navigate → Bookmarks
|
|
19
|
+
- `Enter` opens the repo in a browser, `d` removes, `Esc` / `←` returns to landing
|
|
20
|
+
- `SessionState.mode` accepts `"bookmarks"`, so a restart restores you onto the page
|
|
21
|
+
|
|
22
|
+
**Landing bookmarks button** — fourth selectable row below the three mode cards, with a live count (`4 saved` / `none yet`) refreshed on every visit
|
|
23
|
+
|
|
24
|
+
**Toast popup** — top-right rounded card confirming bookmark changes; green border for bookmarked, yellow for unbookmarked, auto-hides after 2s
|
|
25
|
+
|
|
26
|
+
**`b` keybinding** — toggles bookmark on the selected repo from the main view (previously only reachable via Space → Repo → Bookmark)
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Landing screen vanished after the bookmarks page landed — `showLanding()` revealed `landingBox` before `hideMainContainer()`
|
|
31
|
+
- `--releases` failed when no bookmarks existed — now returns empty feed instead
|
|
32
|
+
- Toast stayed visible during TUI resize — now dismisses on `<Resize>`
|
package/scripts/build.ts
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build script — compile ghfind for distribution.
|
|
3
|
-
* Usage: npm run build
|
|
4
|
-
*/
|
|
5
|
-
import { execFileSync } from "child_process";
|
|
6
|
-
|
|
7
|
-
async function main() {
|
|
8
|
-
console.log("Building ghfind...");
|
|
9
|
-
|
|
10
|
-
// ESM bundle via tsup
|
|
11
|
-
execFileSync(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Build script — compile ghfind for distribution.
|
|
3
|
+
* Usage: npm run build
|
|
4
|
+
*/
|
|
5
|
+
import { execFileSync } from "child_process";
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
console.log("Building ghfind...");
|
|
9
|
+
|
|
10
|
+
// ESM bundle via tsup
|
|
11
|
+
execFileSync(
|
|
12
|
+
"npx",
|
|
13
|
+
[
|
|
14
|
+
"tsup",
|
|
15
|
+
"src/cli.ts",
|
|
16
|
+
"--format",
|
|
17
|
+
"esm",
|
|
18
|
+
"--target",
|
|
19
|
+
"node20",
|
|
20
|
+
"--out-dir",
|
|
21
|
+
"dist",
|
|
22
|
+
"--external",
|
|
23
|
+
"@opentui/core",
|
|
24
|
+
],
|
|
25
|
+
{ stdio: "inherit" },
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
console.log("✓ dist/cli.js (Node.js ESM bundle)");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main().catch((err) => {
|
|
32
|
+
console.error("Build failed:", err.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
@@ -1,168 +1,218 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Generate an SVG screenshot of ghfind matching the actual TUI layout.
|
|
4
|
-
* Single frame with blinking cursor — honest and accurate.
|
|
5
|
-
*
|
|
6
|
-
* Usage: node scripts/generate-svg-demo.js > demo.svg
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const C = {
|
|
10
|
-
bg: "#1e1e2e",
|
|
11
|
-
surface: "#181825",
|
|
12
|
-
text: "#cdd6f4",
|
|
13
|
-
muted: "#6c7086",
|
|
14
|
-
blue: "#89b4fa",
|
|
15
|
-
green: "#a6e3a1",
|
|
16
|
-
yellow: "#f9e2af",
|
|
17
|
-
red: "#f38ba8",
|
|
18
|
-
teal: "#94e2d5",
|
|
19
|
-
border: "#45475a",
|
|
20
|
-
selBg: "#2d5bcf",
|
|
21
|
-
selText: "#ffffff",
|
|
22
|
-
star: "#f9e2af",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const FW = 9.6,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{fn:"
|
|
44
|
-
{fn:"
|
|
45
|
-
{fn:"
|
|
46
|
-
{fn:"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
svg
|
|
75
|
-
|
|
76
|
-
// ───
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate an SVG screenshot of ghfind matching the actual TUI layout.
|
|
4
|
+
* Single frame with blinking cursor — honest and accurate.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node scripts/generate-svg-demo.js > demo.svg
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const C = {
|
|
10
|
+
bg: "#1e1e2e",
|
|
11
|
+
surface: "#181825",
|
|
12
|
+
text: "#cdd6f4",
|
|
13
|
+
muted: "#6c7086",
|
|
14
|
+
blue: "#89b4fa",
|
|
15
|
+
green: "#a6e3a1",
|
|
16
|
+
yellow: "#f9e2af",
|
|
17
|
+
red: "#f38ba8",
|
|
18
|
+
teal: "#94e2d5",
|
|
19
|
+
border: "#45475a",
|
|
20
|
+
selBg: "#2d5bcf",
|
|
21
|
+
selText: "#ffffff",
|
|
22
|
+
star: "#f9e2af",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const FW = 9.6,
|
|
26
|
+
FH = 21,
|
|
27
|
+
PX = 14,
|
|
28
|
+
PY = 12,
|
|
29
|
+
WIN_H = 36;
|
|
30
|
+
const W = 90,
|
|
31
|
+
H = 35;
|
|
32
|
+
const SW = Math.round(W * FW + PX * 2 + 8);
|
|
33
|
+
const SH = Math.round(H * FH + PY * 2 + WIN_H + 8);
|
|
34
|
+
|
|
35
|
+
const esc = (s) =>
|
|
36
|
+
String(s)
|
|
37
|
+
.replace(/&/g, "&")
|
|
38
|
+
.replace(/</g, "<")
|
|
39
|
+
.replace(/>/g, ">")
|
|
40
|
+
.replace(/"/g, """);
|
|
41
|
+
|
|
42
|
+
const repos = [
|
|
43
|
+
{ fn: "shadcn/ui", s: 78500, l: "TypeScript" },
|
|
44
|
+
{ fn: "vercel/next.js", s: 129000, l: "TypeScript" },
|
|
45
|
+
{ fn: "tailwindlabs/tailwindcss", s: 84200, l: "CSS" },
|
|
46
|
+
{ fn: "tauri-apps/tauri", s: 84100, l: "Rust" },
|
|
47
|
+
{ fn: "oven-sh/bun", s: 74500, l: "Zig" },
|
|
48
|
+
{ fn: "n8n-io/n8n", s: 54400, l: "TypeScript" },
|
|
49
|
+
{ fn: "astral-sh/ruff", s: 36100, l: "Rust" },
|
|
50
|
+
{ fn: "missive/emoji-mart", s: 8800, l: "TypeScript" },
|
|
51
|
+
{ fn: "sharkdp/bat", s: 50300, l: "Rust" },
|
|
52
|
+
{ fn: "BurntSushi/ripgrep", s: 48800, l: "Rust" },
|
|
53
|
+
{ fn: "jdx/mise", s: 14300, l: "Rust" },
|
|
54
|
+
{ fn: "nektos/act", s: 56100, l: "Go" },
|
|
55
|
+
{ fn: "astral-sh/uv", s: 40200, l: "Rust" },
|
|
56
|
+
{ fn: "zed-industries/zed", s: 52000, l: "Rust" },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const f = (n) =>
|
|
60
|
+
n >= 1000 ? `★ ${(n / 1000).toFixed(1).replace(/\.0$/, "")}k` : `★ ${n}`;
|
|
61
|
+
const num = (n) => n.toLocaleString();
|
|
62
|
+
|
|
63
|
+
function tx(x, y, text, fill, cls = "") {
|
|
64
|
+
const c = cls ? ` class="${cls}"` : "";
|
|
65
|
+
return `<text x="${x}" y="${y}" fill="${fill}" font-family="'JetBrains Mono','Fira Code','Cascadia Code',monospace" font-size="14"${c}>${esc(text)}</text>\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function rx(x, y, w, h, fill, stroke = "", r = 0) {
|
|
69
|
+
const s = stroke ? ` stroke="${stroke}" stroke-width="1"` : "";
|
|
70
|
+
const rc = r ? ` rx="${r}"` : "";
|
|
71
|
+
return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}"${s}${rc}/>\n`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let svg = "";
|
|
75
|
+
|
|
76
|
+
// ─── Header ──
|
|
77
|
+
svg += tx(
|
|
78
|
+
PX,
|
|
79
|
+
PY + WIN_H + 14,
|
|
80
|
+
"ghfind — GitHub repo browser [t]rending [/]search [g]raph [r]efresh [q]uit",
|
|
81
|
+
C.muted,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// ─── Search box (blue border, 3 lines height) ──
|
|
85
|
+
const sbY = PY + WIN_H + 1 * FH;
|
|
86
|
+
svg += rx(PX, sbY, W * FW - 4, FH * 2.5 + 2, C.bg, C.blue, 3);
|
|
87
|
+
svg += tx(PX + 8, sbY + FH - 2, "> language:TypeScript stars:>1000", C.blue);
|
|
88
|
+
|
|
89
|
+
// ─── Toolbar ──
|
|
90
|
+
svg += tx(
|
|
91
|
+
PX,
|
|
92
|
+
PY + WIN_H + 4.2 * FH + 14,
|
|
93
|
+
"sort: stars limit: 50 (s=cycle sort l=cycle limit)",
|
|
94
|
+
C.muted,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// ─── Body: Results (left) + Detail (right) ──
|
|
98
|
+
const bY = 5.5;
|
|
99
|
+
const paneW = (W * FW - 8) / 2;
|
|
100
|
+
const pH = 18;
|
|
101
|
+
|
|
102
|
+
// Left pane background
|
|
103
|
+
svg += rx(
|
|
104
|
+
PX,
|
|
105
|
+
PY + WIN_H + (bY - 0.2) * FH,
|
|
106
|
+
paneW + 4,
|
|
107
|
+
pH * FH,
|
|
108
|
+
C.bg,
|
|
109
|
+
C.border,
|
|
110
|
+
3,
|
|
111
|
+
);
|
|
112
|
+
svg += tx(PX + 8, PY + WIN_H + bY * FH + 14, " Results ", C.blue);
|
|
113
|
+
|
|
114
|
+
// Results list
|
|
115
|
+
repos.forEach((r, i) => {
|
|
116
|
+
if (i >= 14) return;
|
|
117
|
+
const ry = bY + 1 + i * 1.1;
|
|
118
|
+
const isSel = i === 0;
|
|
119
|
+
if (isSel) {
|
|
120
|
+
svg += rx(
|
|
121
|
+
PX + 4,
|
|
122
|
+
PY + WIN_H + (ry - 0.3) * FH,
|
|
123
|
+
paneW - 4,
|
|
124
|
+
FH - 1,
|
|
125
|
+
C.selBg,
|
|
126
|
+
"",
|
|
127
|
+
3,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const fgc = isSel ? C.selText : C.text;
|
|
131
|
+
const line = `${r.fn} ${f(r.s)}`;
|
|
132
|
+
svg += tx(PX + 10, PY + WIN_H + ry * FH + 14, line, fgc);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Right pane background
|
|
136
|
+
svg += rx(
|
|
137
|
+
PX + paneW + 8,
|
|
138
|
+
PY + WIN_H + (bY - 0.2) * FH,
|
|
139
|
+
paneW + 4,
|
|
140
|
+
pH * FH,
|
|
141
|
+
C.bg,
|
|
142
|
+
C.border,
|
|
143
|
+
3,
|
|
144
|
+
);
|
|
145
|
+
svg += tx(PX + paneW + 14, PY + WIN_H + bY * FH + 14, " Details ", C.green);
|
|
146
|
+
|
|
147
|
+
// Detail content (matches actual format from tui.ts line ~685)
|
|
148
|
+
const detail = [
|
|
149
|
+
`Name shadcn/ui`,
|
|
150
|
+
`Stars ${num(78500)}`,
|
|
151
|
+
`Forks ${num(4200)}`,
|
|
152
|
+
`Lang TypeScript`,
|
|
153
|
+
`Updated 2026-07-12`,
|
|
154
|
+
`Topics react, ui, components, shadcn`,
|
|
155
|
+
``,
|
|
156
|
+
`Beautifully designed components built with Radix UI and`,
|
|
157
|
+
`Tailwind CSS.`,
|
|
158
|
+
``,
|
|
159
|
+
`https://github.com/shadcn/ui`,
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
detail.forEach((line, i) => {
|
|
163
|
+
if (line === "") return;
|
|
164
|
+
const ry = bY + 1 + i * 1.1;
|
|
165
|
+
let fgc = C.text;
|
|
166
|
+
if (i <= 5) fgc = C.muted;
|
|
167
|
+
if (line.startsWith("http")) fgc = C.blue;
|
|
168
|
+
svg += tx(PX + paneW + 16, PY + WIN_H + ry * FH + 14, line, fgc);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// ─── Bottom status bar ──
|
|
172
|
+
svg += rx(PX, PY + WIN_H + 21 * FH, W * FW - 4, FH + 4, C.surface, "", 3);
|
|
173
|
+
svg += rx(PX, PY + WIN_H + 21 * FH, W * FW - 4, 2, C.border);
|
|
174
|
+
svg += tx(
|
|
175
|
+
PX + 4,
|
|
176
|
+
PY + WIN_H + 21.9 * FH + 14,
|
|
177
|
+
"15,842 results (↑↓ nav Enter open o browser Ctrl+R history)",
|
|
178
|
+
C.muted,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// ─── Cursor blinking after query text ──
|
|
182
|
+
const queryText = "language:TypeScript stars:>1000";
|
|
183
|
+
const cursorX = PX + 8 + 2 * FW + queryText.length * FW;
|
|
184
|
+
svg += `<rect x="${cursorX}" y="${PY + WIN_H + 1 * FH + 1}" width="${FW * 0.6}" height="16" fill="${C.blue}" opacity="0.8">
|
|
185
|
+
<animate attributeName="opacity" values="0.8;0.1;0.8" dur="1s" repeatCount="indefinite"/>
|
|
186
|
+
</rect>\n`;
|
|
187
|
+
|
|
188
|
+
// ─── Assemble ──
|
|
189
|
+
const output = `<?xml version="1.0" encoding="UTF-8"?>
|
|
190
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${SW} ${SH}" width="${SW}" height="${SH}">
|
|
191
|
+
<defs>
|
|
192
|
+
<style>
|
|
193
|
+
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
|
|
194
|
+
</style>
|
|
195
|
+
<filter id="shadow" x="-5%" y="-5%" width="115%" height="115%">
|
|
196
|
+
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#000" flood-opacity="0.4"/>
|
|
197
|
+
</filter>
|
|
198
|
+
</defs>
|
|
199
|
+
|
|
200
|
+
<rect width="100%" height="100%" fill="#0d0d1a"/>
|
|
201
|
+
|
|
202
|
+
<!-- Window -->
|
|
203
|
+
<rect x="${PX - 2}" y="${PY - 2}" width="${SW - PX * 2 + 4}" height="${SH - PY * 2 + 4}" fill="${C.bg}" rx="8" filter="url(#shadow)"/>
|
|
204
|
+
<rect x="${PX - 2}" y="${PY - 2}" width="${SW - PX * 2 + 4}" height="36" fill="${C.surface}" rx="8"/>
|
|
205
|
+
<rect x="${PX - 2}" y="${PY + 34}" width="${SW - PX * 2 + 4}" height="2" fill="#313244"/>
|
|
206
|
+
|
|
207
|
+
<!-- Traffic lights -->
|
|
208
|
+
<circle cx="${PX + 10}" cy="${PY + 16}" r="5" fill="#f38ba8"/>
|
|
209
|
+
<circle cx="${PX + 28}" cy="${PY + 16}" r="5" fill="#f9e2af"/>
|
|
210
|
+
<circle cx="${PX + 46}" cy="${PY + 16}" r="5" fill="#a6e3a1"/>
|
|
211
|
+
<text x="${SW / 2}" y="${PY + 22}" fill="${C.muted}" font-family="'JetBrains Mono','Fira Code',monospace" font-size="13" text-anchor="middle">ghfind — terminal GitHub browser</text>
|
|
212
|
+
|
|
213
|
+
<g font-family="'JetBrains Mono','Fira Code','Cascadia Code',monospace" font-size="14">
|
|
214
|
+
${svg}
|
|
215
|
+
</g>
|
|
216
|
+
</svg>`;
|
|
217
|
+
|
|
218
|
+
process.stdout.write(output);
|