+9
-39
@@ -7,3 +7,3 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; | ||
| import { openUrl } from "./open.js"; | ||
| import { fetchSiteData, SITE_URL, } from "./data.js"; | ||
| import { fetchSiteData, } from "./data.js"; | ||
| const ACCENT = "#d2a6ff"; | ||
@@ -21,5 +21,2 @@ const PLATFORM_LABELS = { | ||
| const [selected, setSelected] = useState(0); | ||
| const [mode, setMode] = useState("list"); | ||
| const [actionChoice, setActionChoice] = useState(1); | ||
| const [opened, setOpened] = useState(null); | ||
| const terminalColumns = process.stdout.columns ?? 80; | ||
@@ -51,36 +48,11 @@ useEffect(() => { | ||
| return; | ||
| if (mode === "list") { | ||
| if (key.upArrow || input === "k") { | ||
| setSelected((current) => (current - 1 + catalog.length) % catalog.length); | ||
| setOpened(null); | ||
| } | ||
| else if (key.downArrow || input === "j") { | ||
| setSelected((current) => (current + 1) % catalog.length); | ||
| setOpened(null); | ||
| } | ||
| else if (key.return || input === " ") { | ||
| setMode("action"); | ||
| setActionChoice(1); | ||
| } | ||
| else if (input === "r") { | ||
| openUrl(catalog[selected].entry.url); | ||
| setOpened(catalog[selected].entry.url); | ||
| } | ||
| else if (input === "w") { | ||
| openUrl(SITE_URL); | ||
| setOpened(SITE_URL); | ||
| } | ||
| if (key.upArrow || input === "k" || (key.ctrl && input === "p")) { | ||
| setSelected((current) => (current - 1 + catalog.length) % catalog.length); | ||
| } | ||
| else if (key.leftArrow || key.rightArrow || input === "h" || input === "l" || key.tab) { | ||
| setActionChoice((choice) => (choice === 0 ? 1 : 0)); | ||
| else if (key.downArrow || input === "j" || (key.ctrl && input === "n")) { | ||
| setSelected((current) => (current + 1) % catalog.length); | ||
| } | ||
| else if (key.return) { | ||
| const url = actionChoice === 0 ? SITE_URL : catalog[selected].entry.url; | ||
| openUrl(url); | ||
| setOpened(url); | ||
| setMode("list"); | ||
| else if (key.return || input === " ") { | ||
| openUrl(catalog[selected].entry.url); | ||
| } | ||
| else if (key.escape) { | ||
| setMode("list"); | ||
| } | ||
| }, { isActive: process.stdin.isTTY === true }); | ||
@@ -95,6 +67,4 @@ if (status === "loading") { | ||
| const metadata = item.category === "Apps" ? (_jsxs(_Fragment, { children: [item.entry.platforms.map((platform) => PLATFORM_LABELS[platform]).join(" · "), " ", "\u00B7", " ", _jsx(Text, { color: item.entry.status === "coming-soon" ? "#e5b674" : "#ffffff", children: item.entry.status === "coming-soon" ? "coming soon" : "available" })] })) : null; | ||
| return (_jsxs(React.Fragment, { children: [firstInCategory ? (_jsx(Box, { marginTop: index === 0 ? 0 : 1, children: _jsx(Text, { bold: true, color: "whiteBright", children: item.category }) })) : null, _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: active ? ACCENT : "gray", children: active ? "❯ " : " " }), _jsx(Text, { bold: true, color: active ? ACCENT : "gray", wrap: "truncate-end", children: item.entry.title }), item.category === "Open Source" ? (_jsxs(Text, { color: "gray", children: [" \u2605 ", item.entry.stars.toLocaleString("en-US")] })) : null] }), active ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", minWidth: 0, children: [_jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: item.entry.description }), metadata ? (_jsx(Text, { color: "#ffffff", wrap: "truncate-end", children: metadata })) : null, _jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: item.entry.url })] })) : null] })] }, `${item.category}:${item.entry.slug}`)); | ||
| }) }), mode === "action" ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: "gray", wrap: "truncate-end", children: ["Open ", _jsx(Text, { color: "whiteBright", children: current.entry.title }), " \u2192"] }), _jsxs(Box, { children: [_jsx(Text, { color: actionChoice === 0 ? "black" : ACCENT, backgroundColor: actionChoice === 0 ? ACCENT : undefined, children: " Website " }), _jsx(Text, { children: " " }), _jsx(Text, { color: actionChoice === 1 ? "black" : ACCENT, backgroundColor: actionChoice === 1 ? ACCENT : undefined, children: " Repository " })] })] })) : null, opened && mode === "list" ? (_jsxs(Box, { marginTop: 1, minWidth: 0, children: [_jsx(Text, { color: "greenBright", children: "\u2197 opened " }), _jsx(Text, { color: "gray", wrap: "truncate-end", children: opened })] })) : null] }), terminalColumns >= 64 ? (_jsx(Box, { width: 28, height: 14, justifyContent: "flex-end", children: _jsx(SignalField, { columns: 26, rows: 14 }) })) : null] }), _jsx(Box, { marginTop: 1, minWidth: 0, children: mode === "list" ? (_jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: terminalColumns >= 72 | ||
| ? "↑/↓ navigate · ↵ open... · w website · r repository · q quit" | ||
| : "↑/↓ · ↵/r open · w site · q quit" })) : (_jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: "\u2190/\u2192 choose \u00B7 \u21B5 confirm \u00B7 esc back" })) })] })); | ||
| return (_jsxs(React.Fragment, { children: [firstInCategory ? (_jsx(Box, { marginTop: index === 0 ? 0 : 1, children: _jsx(Text, { bold: true, color: "whiteBright", children: item.category }) })) : null, _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: active ? ACCENT : "gray", children: active ? "❯ " : " " }), _jsx(Text, { bold: true, color: active ? ACCENT : "gray", wrap: "truncate-end", children: item.entry.title }), item.category === "Open Source" ? (_jsxs(Text, { color: "gray", children: [" \u2605 ", item.entry.stars.toLocaleString("en-US")] })) : null] }), active ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", minWidth: 0, children: [_jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: item.entry.description }), metadata ? (_jsx(Text, { color: "#ffffff", wrap: "truncate-end", children: metadata })) : null] })) : null] })] }, `${item.category}:${item.entry.slug}`)); | ||
| }) })] }), terminalColumns >= 64 ? (_jsx(Box, { width: 28, height: 14, justifyContent: "flex-end", children: _jsx(SignalField, { columns: 26, rows: 14 }) })) : null] }), _jsx(Box, { marginTop: 1, minWidth: 0, children: _jsxs(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: ["\u2191/\u2193/ctrl+n/p navigate \u00B7 \u21B5 open ", current.entry.url, " \u00B7 q quit"] }) })] })); | ||
| }; |
+3
-4
@@ -14,6 +14,5 @@ #!/usr/bin/env node | ||
| Controls | ||
| ↑/↓ or j/k navigate apps and open source projects | ||
| ↵ open menu (Website / Repository) | ||
| w open ecklf.com | ||
| r open the selected repository URL | ||
| ↑/↓, j/k, or ctrl+n/p | ||
| navigate apps and open source projects | ||
| ↵ or space open the selected URL | ||
| q quit | ||
@@ -20,0 +19,0 @@ `); |
+1
-1
| { | ||
| "name": "ecklf", | ||
| "version": "1.0.3", | ||
| "version": "1.0.4", | ||
| "description": "A terminal UI for ecklf.com: browse apps and open source projects with an animated Braille signal field.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+2
-6
@@ -27,8 +27,4 @@ # ecklf | ||
| | -------------- | ---------------------------------------- | | ||
| | `↑` / `↓`, `j` / `k` | Navigate apps and open source entries | | ||
| | `↵` | Open menu to choose **Website** / **Entry** | | ||
| | `w` | Open ecklf.com in your browser | | ||
| | `r` | Open the selected repository URL | | ||
| | `←` / `→` | Switch choice (in the open menu) | | ||
| | `esc` | Back | | ||
| | `↑` / `↓`, `j` / `k`, `ctrl+n` / `ctrl+p` | Navigate apps and open source entries | | ||
| | `↵` / `space` | Open the selected URL | | ||
| | `q` | Quit | | ||
@@ -35,0 +31,0 @@ |
21572
-10.31%501
-6.18%54
-6.9%