🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

ecklf

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ecklf - npm Package Compare versions

Comparing version
1.0.2
to
1.0.3
+12
-18
dist/App.js

@@ -1,2 +0,2 @@

import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React, { useEffect, useState } from "react";

@@ -8,3 +8,3 @@ import { Box, Text, useApp, useInput } from "ink";

import { fetchSiteData, SITE_URL, } from "./data.js";
const ACCENT = "cyanBright";
const ACCENT = "#d2a6ff";
const PLATFORM_LABELS = {

@@ -63,3 +63,3 @@ ios: "iOS",

}
else if (input === "g") {
else if (input === "r") {
openUrl(catalog[selected].entry.url);

@@ -73,7 +73,3 @@ setOpened(catalog[selected].entry.url);

}
else if (key.leftArrow ||
key.rightArrow ||
input === "h" ||
input === "l" ||
key.tab) {
else if (key.leftArrow || key.rightArrow || input === "h" || input === "l" || key.tab) {
setActionChoice((choice) => (choice === 0 ? 1 : 0));

@@ -95,12 +91,10 @@ }

const current = catalog[selected];
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, minWidth: 0, children: [_jsx(Box, { height: 1, children: _jsx(Greeting, {}) }), _jsx(Text, { bold: true, color: "whiteBright", wrap: "truncate-end", children: data.profile.name }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: "gray", wrap: "truncate-end", children: [data.profile.role, " \u00B7 ", data.profile.location] }), _jsx(Text, { color: "gray", wrap: "truncate-end", children: data.profile.company })] })] }), terminalColumns >= 64 ? (_jsx(Box, { width: 28, height: 14, justifyContent: "flex-end", children: _jsx(SignalField, { columns: 26, rows: 14 }) })) : null] }), _jsxs(Box, { marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "whiteBright", children: "Portfolio" }), _jsxs(Text, { color: "gray", children: [" ", data.source === "live" ? "● live" : "○ offline"] })] }), _jsx(Box, { flexDirection: "column", children: catalog.map((item, index) => {
const active = index === selected;
const firstInCategory = index === 0 || catalog[index - 1].category !== item.category;
const metadata = item.category === "Apps"
? `${item.entry.platforms.map((platform) => PLATFORM_LABELS[platform]).join(" · ")} · ${item.entry.status === "coming-soon" ? "coming soon" : "available"}`
: item.entry.technologies.join(" · ");
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 ? "whiteBright" : "white", wrap: "truncate-end", children: item.entry.title })] }), active ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", minWidth: 0, children: [_jsx(Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: item.entry.description }), _jsxs(Text, { color: ACCENT, wrap: "truncate-end", children: [item.category, " \u00B7 ", metadata] }), _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: " Entry " })] })] })) : 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, _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 · g entry · q quit"
: "↑/↓ · ↵/g 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(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, minWidth: 0, children: [_jsx(Box, { height: 1, children: _jsx(Greeting, {}) }), _jsx(Text, { bold: true, color: "whiteBright", wrap: "truncate-end", children: data.profile.name }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: "gray", wrap: "truncate-end", children: ["Technical Staff \u25B2 ", data.profile.company] }), _jsxs(Text, { color: "gray", wrap: "truncate-end", children: [data.profile.location, " ", _jsx(Text, { color: data.source === "live" ? "#7ed962" : "gray", children: data.source === "live" ? "●" : "○" }), data.source === "live" ? " connected" : " offline"] })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: catalog.map((item, index) => {
const active = index === selected;
const firstInCategory = index === 0 || catalog[index - 1].category !== item.category;
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" })) })] }));
};
+3
-3

@@ -15,5 +15,5 @@ #!/usr/bin/env node

↑/↓ or j/k navigate apps and open source projects
↵ open menu (Website / Entry)
↵ open menu (Website / Repository)
w open ecklf.com
g open the selected entry URL
r open the selected repository URL
q quit

@@ -24,3 +24,3 @@ `);

if (args.includes("--version") || args.includes("-v")) {
console.log("1.0.1");
console.log("1.0.3");
process.exit(0);

@@ -27,0 +27,0 @@ }

@@ -11,3 +11,3 @@ const SITE_URL = "https://ecklf.com";

name: "Florentin Eckl",
role: "Full Stack Developer",
role: "Technical Staff",
location: "Germany",

@@ -48,3 +48,3 @@ company: "Vercel",

url: "https://github.com/ecklf/tailwindcss-radix",
technologies: ["TypeScript", "Tailwind CSS"],
stars: 2170,
},

@@ -56,3 +56,3 @@ {

url: "https://github.com/ecklf/tailwindcss-base-ui",
technologies: ["TypeScript", "Tailwind CSS"],
stars: 0,
},

@@ -64,3 +64,3 @@ {

url: "https://github.com/ecklf/tailwindcss-figma-kit",
technologies: ["Figma", "Tailwind CSS"],
stars: 737,
},

@@ -72,3 +72,3 @@ {

url: "https://github.com/ecklf/tailwindcss-figma-plugin",
technologies: ["Figma", "React", "Tailwind CSS"],
stars: 274,
},

@@ -80,3 +80,3 @@ {

url: "https://github.com/ecklf/flutters",
technologies: ["Flutter", "Game Development"],
stars: 241,
},

@@ -88,3 +88,3 @@ {

url: "https://github.com/ecklf/coingecko-rs",
technologies: ["Rust"],
stars: 25,
},

@@ -96,3 +96,3 @@ {

url: "https://github.com/ecklf/macos-tags",
technologies: ["Rust"],
stars: 3,
},

@@ -104,3 +104,3 @@ {

url: "https://github.com/ecklf/reddit-clawler",
technologies: ["Rust", "Reddit"],
stars: 4,
},

@@ -126,5 +126,2 @@ ],

}
function isStringArray(value) {
return Array.isArray(value) && value.every(isString);
}
function isProfile(value) {

@@ -154,3 +151,4 @@ return (isRecord(value) &&

isUrl(value.url) &&
isStringArray(value.technologies));
Number.isInteger(value.stars) &&
value.stars >= 0);
}

@@ -157,0 +155,0 @@ export function validatePortfolioData(value) {

@@ -60,3 +60,3 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";

const cursorOn = Math.floor(seconds * 2) % 2 === 0;
return (_jsxs(Text, { bold: true, color: "white", children: [text, _jsx(Text, { color: "cyanBright", children: cursorOn ? "▋" : " " })] }));
return (_jsxs(Text, { bold: true, color: "#ffffff", children: [text, _jsx(Text, { color: "cyanBright", children: cursorOn ? "▋" : " " })] }));
};
{
"name": "ecklf",
"version": "1.0.2",
"version": "1.0.3",
"description": "A terminal UI for ecklf.com: browse apps and open source projects with an animated Braille signal field.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -30,3 +30,3 @@ # ecklf

| `w` | Open ecklf.com in your browser |
| `g` | Open the selected entry URL |
| `r` | Open the selected repository URL |
| `←` / `→` | Switch choice (in the open menu) |

@@ -33,0 +33,0 @@ | `esc` | Back |

// 3D torus-knot wireframe renderer -> ASCII braille grid.
// Mirrors the spinning wireframe on ecklf.com.
// Generate vertices along a (p,q) torus knot.
function torusKnot(samples, p = 2, q = 3, R = 1, r = 0.4) {
const pts = [];
for (let i = 0; i < samples; i++) {
const u = (i / samples) * Math.PI * 2;
const cu = Math.cos(q * u);
const x = (R + r * cu) * Math.cos(p * u);
const y = (R + r * cu) * Math.sin(p * u);
const z = r * Math.sin(q * u);
pts.push({ x, y, z });
}
return pts;
}
function rotate(p, ax, ay, az) {
// X
let { x, y, z } = p;
let cy = Math.cos(ax), sy = Math.sin(ax);
let ny = y * cy - z * sy;
let nz = y * sy + z * cy;
y = ny;
z = nz;
// Y
cy = Math.cos(ay);
sy = Math.sin(ay);
let nx = x * cy + z * sy;
nz = -x * sy + z * cy;
x = nx;
z = nz;
// Z
cy = Math.cos(az);
sy = Math.sin(az);
nx = x * cy - y * sy;
ny = x * sy + y * cy;
x = nx;
y = ny;
return { x, y, z };
}
// Braille dot bit layout (2 cols x 4 rows per cell).
const BRAILLE_OFFSETS = [
[0x01, 0x08],
[0x02, 0x10],
[0x04, 0x20],
[0x40, 0x80],
];
const SAMPLES = 240;
const KNOT = torusKnot(SAMPLES);
/**
* Render the rotating knot to a string. `cols`/`rows` are character cell
* dimensions; the braille grid is 2x wider and 4x taller in dots.
*/
export function renderKnot(t, cols, rows) {
const dotW = cols * 2;
const dotH = rows * 4;
const grid = new Uint8Array(cols * rows);
const ax = t * 0.7;
const ay = t * 1.0;
const az = t * 0.3;
const scale = Math.min(dotW, dotH) * 0.32;
const cx = dotW / 2;
const cy = dotH / 2;
const persp = 3.2;
const projected = new Array(SAMPLES);
for (let i = 0; i < SAMPLES; i++) {
const r = rotate(KNOT[i], ax, ay, az);
const depth = persp / (persp + r.z);
const px = cx + r.x * scale * depth;
// terminal cells ~2x taller than wide -> compress vertical a touch
const py = cy + r.y * scale * depth * 1.0;
projected[i] = { x: px, y: py };
}
const plot = (x, y) => {
const xi = Math.round(x);
const yi = Math.round(y);
if (xi < 0 || xi >= dotW || yi < 0 || yi >= dotH)
return;
const cellX = xi >> 1;
const cellY = yi >> 2;
const idx = cellY * cols + cellX;
const bit = BRAILLE_OFFSETS[yi & 3][xi & 1];
grid[idx] |= bit;
};
// Draw line segments between consecutive (looping) knot points.
for (let i = 0; i < SAMPLES; i++) {
const a = projected[i];
const b = projected[(i + 1) % SAMPLES];
const steps = Math.max(1, Math.ceil(Math.hypot(b.x - a.x, b.y - a.y)));
for (let s = 0; s <= steps; s++) {
const f = s / steps;
plot(a.x + (b.x - a.x) * f, a.y + (b.y - a.y) * f);
}
}
let out = "";
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const v = grid[row * cols + col];
out += v === 0 ? " " : String.fromCharCode(0x2800 + v);
}
if (row < rows - 1)
out += "\n";
}
return out;
}
import { jsx as _jsx } from "react/jsx-runtime";
import { Text } from "ink";
import { useClock } from "./hooks.js";
import { renderKnot } from "./knot-renderer.js";
export const Knot = ({ cols = 26, rows = 14 }) => {
const t = useClock(24);
const frame = renderKnot(t, cols, rows);
return _jsx(Text, { color: "gray", children: frame });
};