reverentgeek 2.2.1 → 3.0.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/.claude/settings.local.json +9 -0
- package/AGENTS.md +16 -0
- package/CLAUDE.md +47 -0
- package/README.md +20 -1
- package/art/index.js +19 -0
- package/bin/card.js +41 -85
- package/bin/colors.js +22 -0
- package/bin/render-info.js +31 -0
- package/bin/text.js +35 -0
- package/config/profile.js +21 -0
- package/package.json +4 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
The CLI entry point remains `bin/card.js`, now focused on orchestration: it imports palette helpers (`bin/colors.js`), layout formatters (`bin/render-info.js`, `bin/text.js`), and profile data in `config/profile.js`. Adjust CLI content by editing the profile file; the renderer already handles centered headings, wrapped bios, and multi-line values. ASCII art lives in `art/` and is addressed through `art/index.js`, keeping individual assets (`avatar.js`, `banner.js`) easy to swap. Root-level configs (`package.json`, `eslint.config.js`) continue to hold metadata, lint rules, and script aliases.
|
|
5
|
+
|
|
6
|
+
## Build, Test, and Development Commands
|
|
7
|
+
Install dependencies with `npm install` (or `pnpm install` if you prefer pnpm). Run `npm run lint` to apply the repository ESLint profile across `bin/*.js` and `art/*.js`. During manual checks, execute `node bin/card.js` to preview the card locally, or `npx .` to simulate the published package without installing it globally.
|
|
8
|
+
|
|
9
|
+
## Coding Style & Naming Conventions
|
|
10
|
+
We use modern ESM everywhere. Keep indentation as tabs, match the existing double-quote style, and favor `const`. New helpers should sit with their peers (`bin/` for runtime logic, `config/` for data). If you extend value styling, add the formatter in `bin/colors.js` and reference it by name in `config/profile.js`. When profile entries need multiple lines, prefer arrays (`value: ["Role", "URL"]`) instead of embedding `\n`.
|
|
11
|
+
|
|
12
|
+
## Testing Guidelines
|
|
13
|
+
There is no automated test suite today. Before opening a PR, run `npm run lint` and execute `node bin/card.js` to confirm the output renders without ANSI errors. Validate spacing and color choices in a terminal that supports truecolor, and capture a screenshot or paste output snippets in the PR if you alter visual elements. If you add automated checks, colocate them with the feature and document the new command.
|
|
14
|
+
|
|
15
|
+
## Commit & Pull Request Guidelines
|
|
16
|
+
Existing commits favor short, present-tense summaries such as `updates dependencies`. Follow that style: a single concise line describing what changes, optionally followed by wrapped details. PRs should explain the intent, call out impacted modules (`bin/card.js`, `art/banner.js`, etc.), and mention manual verification steps taken. Link issues when applicable and include visuals whenever you change the rendered card.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is a terminal calling card application built with Node.js that displays formatted personal information in a terminal box. Users run `npx reverentgeek` to see the author's contact info and bio in an ASCII art-styled terminal card.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
- **Development preview**: `node bin/card.js` - Run the card locally to preview output
|
|
12
|
+
- **Linting**: `npm run lint` - Run ESLint on all JavaScript files in bin/ and art/ directories
|
|
13
|
+
- **Installation**: Users install/run via `npx reverentgeek`
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
### Core Components
|
|
18
|
+
|
|
19
|
+
- **bin/card.js**: Main entry point that orchestrates the entire card rendering
|
|
20
|
+
- **config/profile.js**: Contains all personal information, contact links, and art rendering order
|
|
21
|
+
- **bin/colors.js**: Defines color palette and gradient configurations using chalk and gradient-string
|
|
22
|
+
- **bin/text.js**: Text utilities for centering and wrapping text within specified widths
|
|
23
|
+
- **bin/render-info.js**: Handles formatting of info sections with labels and values
|
|
24
|
+
- **art/**: Directory containing ASCII art modules (avatar, banner, etc.)
|
|
25
|
+
|
|
26
|
+
### Data Flow
|
|
27
|
+
|
|
28
|
+
1. **card.js** imports profile data and art assets
|
|
29
|
+
2. Applies color styling to profile sections using colors defined in **colors.js**
|
|
30
|
+
3. Uses **text.js** utilities to format bio text and center headings
|
|
31
|
+
4. Uses **render-info.js** to format contact information sections
|
|
32
|
+
5. Loads and applies gradients to ASCII art from **art/** directory
|
|
33
|
+
6. Combines all elements and renders in a boxen-styled terminal box
|
|
34
|
+
|
|
35
|
+
### Customization Points
|
|
36
|
+
|
|
37
|
+
- **Profile data**: Edit `config/profile.js` to change name, bio, contact info, and art order
|
|
38
|
+
- **Colors/gradients**: Modify `bin/colors.js` for different color schemes
|
|
39
|
+
- **ASCII art**: Add new art files in `art/` directory and register in `art/index.js`
|
|
40
|
+
- **Styling**: Each info section supports style properties (orange, gradient, lightBlue, yellow)
|
|
41
|
+
|
|
42
|
+
### Key Patterns
|
|
43
|
+
|
|
44
|
+
- Profile sections can have string or array values for multi-line entries
|
|
45
|
+
- Art assets are loaded dynamically based on `artOrder` array in profile config
|
|
46
|
+
- Color styling is applied through a centralized `styleValue` function
|
|
47
|
+
- Text wrapping respects the horizontal rule width for consistent formatting
|
package/README.md
CHANGED
|
@@ -8,6 +8,25 @@ In case you ever need my information and all you have is a terminal and `npm`.
|
|
|
8
8
|
npx reverentgeek
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Make It Yours
|
|
12
|
+
|
|
13
|
+
Clone this repo (or create a new one) and adjust the profile and art assets to build your own terminal calling card:
|
|
14
|
+
|
|
15
|
+
- Edit `config/profile.js` with your name, biography, and contact links. Each entry in `infoSections` can be a string or an array if you want multiple lines under one label.
|
|
16
|
+
- Update colors or gradients by tweaking `bin/colors.js`, then reference any new styles by name from the profile file.
|
|
17
|
+
- Swap ASCII art by editing the files under `art/` or adding new modules and registering them in `art/index.js`. The `artOrder` export in `config/profile.js` controls the render sequence.
|
|
18
|
+
- Run `npm run lint` for style checks and `node bin/card.js` to preview the box output locally before publishing.
|
|
19
|
+
|
|
20
|
+
Want to publish it so others can view it with `npx`?
|
|
21
|
+
|
|
22
|
+
- Update `package.json` and change the name, version, and other fields to make it yours.
|
|
23
|
+
- Push your code to a new public repository.
|
|
24
|
+
- [Sign up for an npm account](https://www.npmjs.com/signup), if you don't have one.
|
|
25
|
+
- Authenticate with `npm login`.
|
|
26
|
+
- Publish your package using `npm publish` from the project root.
|
|
27
|
+
|
|
28
|
+
For more info, check out the [official npm publish guide](https://docs.npmjs.com/cli/v11/commands/npm-publish).
|
|
29
|
+
|
|
11
30
|
## Credit
|
|
12
31
|
|
|
13
|
-
Completely *borrowed* this idea from [Tierney](https://github.com/bnb/bitandbang) :)
|
|
32
|
+
Completely *borrowed* this idea from [Tierney](https://github.com/bnb/bitandbang) :)
|
package/art/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import avatar from "./avatar.js";
|
|
2
|
+
import banner from "./banner.js";
|
|
3
|
+
|
|
4
|
+
const assets = {
|
|
5
|
+
avatar,
|
|
6
|
+
banner
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const loadArt = ( name ) => {
|
|
10
|
+
const art = assets[name];
|
|
11
|
+
|
|
12
|
+
if ( !art ) {
|
|
13
|
+
throw new Error( `Unknown art asset: ${ name }` );
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return art;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const availableArt = () => Object.keys( assets );
|
package/bin/card.js
CHANGED
|
@@ -2,32 +2,13 @@
|
|
|
2
2
|
// 👆 Used to tell Node.js that this is a CLI tool
|
|
3
3
|
|
|
4
4
|
// Pull in our modules
|
|
5
|
-
import chalk from "chalk";
|
|
6
5
|
import boxen from "boxen";
|
|
7
|
-
import gs from "gradient-string";
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const colors = {
|
|
15
|
-
blue: "#00A3FF",
|
|
16
|
-
blueSky: "#51D2FB",
|
|
17
|
-
mintGreen: "#42F0CD",
|
|
18
|
-
inkyBlue: "#130f25",
|
|
19
|
-
lightBlue: "#99DAFF",
|
|
20
|
-
orange: "#ff7b01",
|
|
21
|
-
pink: "#ff1675",
|
|
22
|
-
yellow: "#ffc942"
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// set up chalk and gradients
|
|
26
|
-
const yellowChalk = chalk.hex( colors.yellow );
|
|
27
|
-
const lbChalk = chalk.hex( colors.lightBlue );
|
|
28
|
-
const blueChalk = chalk.hex( colors.blue );
|
|
29
|
-
const orangeChalk = chalk.hex( colors.orange );
|
|
30
|
-
const gradient = gs( [ colors.mintGreen, colors.blueSky ] );
|
|
7
|
+
import { availableArt, loadArt } from "../art/index.js";
|
|
8
|
+
import { artOrder, profile } from "../config/profile.js";
|
|
9
|
+
import { colors, gradient, palette } from "./colors.js";
|
|
10
|
+
import { renderInfoSections } from "./render-info.js";
|
|
11
|
+
import { centerText, wrapText } from "./text.js";
|
|
31
12
|
|
|
32
13
|
const newline = "\n";
|
|
33
14
|
|
|
@@ -40,73 +21,48 @@ const options = {
|
|
|
40
21
|
backgroundColor: colors.inkyBlue
|
|
41
22
|
};
|
|
42
23
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
labelWork: blueChalk( " Work:" ),
|
|
60
|
-
labelWorkUrl: blueChalk( " " ),
|
|
61
|
-
labelTwitter: blueChalk( " X:" ),
|
|
62
|
-
labelInstagram: blueChalk( " Instagram:" ),
|
|
63
|
-
labelMastodon: blueChalk( " Mastodon:" ),
|
|
64
|
-
labelBluesky: blueChalk( " BlueSky:" ),
|
|
65
|
-
labelThreads: blueChalk( " Threads:" ),
|
|
66
|
-
labelGitHub: blueChalk( " GitHub:" ),
|
|
67
|
-
labelLinkedIn: blueChalk( " LinkedIn:" ),
|
|
68
|
-
labelWeb: blueChalk( " Web:" ),
|
|
69
|
-
labelCard: blueChalk( " Card:" ),
|
|
70
|
-
labelEmail: blueChalk( " Email:" ),
|
|
71
|
-
bio: lbChalk( `David is a family man, geek, musician, illustrator,
|
|
72
|
-
speaker, software developer, and Microsoft MVP
|
|
73
|
-
living in North GA. He runs on a high-octane
|
|
74
|
-
mixture of caffeine and JavaScript, and
|
|
75
|
-
is entirely made of bacon.` )
|
|
76
|
-
// msg: greenChalk( `If there's anything I can help you with,
|
|
77
|
-
// reach out anytime!` )
|
|
24
|
+
const valueStylers = {
|
|
25
|
+
orange: value => palette.orange( value ),
|
|
26
|
+
gradient: value => gradient( value ),
|
|
27
|
+
lightBlue: value => palette.lightBlue( value ),
|
|
28
|
+
yellow: value => palette.yellow( value )
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const styleValue = ( { value, style } ) => {
|
|
32
|
+
const values = Array.isArray( value ) ? value : [ value ];
|
|
33
|
+
const formatter = valueStylers[style];
|
|
34
|
+
|
|
35
|
+
if ( !formatter ) {
|
|
36
|
+
return values;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return values.map( entry => formatter( entry ) );
|
|
78
40
|
};
|
|
79
41
|
|
|
42
|
+
const styledInfoSections = profile.infoSections.map( section => ( {
|
|
43
|
+
...section,
|
|
44
|
+
value: styleValue( section )
|
|
45
|
+
} ) );
|
|
46
|
+
|
|
80
47
|
// Actual strings we're going to output
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const emailing = `${ data.labelEmail } ${ data.email }`;
|
|
94
|
-
const carding = `\n${ data.labelCard } ${ data.npx }`;
|
|
95
|
-
const bio = `\n${ data.bio }`;
|
|
96
|
-
// const msg = `\n${ data.msg }`;
|
|
48
|
+
const hrText = "----------~~~~~~~~~==========~~~~~~~~~-----------";
|
|
49
|
+
const hr = gradient( hrText );
|
|
50
|
+
const heading = palette.yellow.bold( centerText( profile.name, hrText.length ) );
|
|
51
|
+
const formattedInfo = renderInfoSections( styledInfoSections, {
|
|
52
|
+
labelColor: palette.blue
|
|
53
|
+
} );
|
|
54
|
+
|
|
55
|
+
const bioLines = wrapText( profile.bio, hrText.length );
|
|
56
|
+
const bio = `\n${ bioLines.map( line => palette.lightBlue( line ) ).join( newline ) }`;
|
|
57
|
+
|
|
58
|
+
const artAssets = artOrder.length > 0 ? artOrder : availableArt();
|
|
59
|
+
const art = artAssets.map( name => gradient.multiline( loadArt( name ) ) );
|
|
97
60
|
|
|
98
61
|
const card = [
|
|
99
|
-
|
|
62
|
+
...art,
|
|
100
63
|
hr, heading, hr,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
linkedining,
|
|
104
|
-
githubing,
|
|
105
|
-
instagramming,
|
|
106
|
-
tooting, skeeting, threading,
|
|
107
|
-
twittering,
|
|
108
|
-
carding, bio
|
|
109
|
-
// , msg
|
|
64
|
+
...formattedInfo,
|
|
65
|
+
bio
|
|
110
66
|
];
|
|
111
67
|
|
|
112
68
|
// Put all our output together into a single variable so we can use boxen effectively
|
package/bin/colors.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import gs from "gradient-string";
|
|
3
|
+
|
|
4
|
+
export const colors = {
|
|
5
|
+
blue: "#00A3FF",
|
|
6
|
+
blueSky: "#51D2FB",
|
|
7
|
+
mintGreen: "#42F0CD",
|
|
8
|
+
inkyBlue: "#130f25",
|
|
9
|
+
lightBlue: "#99DAFF",
|
|
10
|
+
orange: "#ff7b01",
|
|
11
|
+
pink: "#ff1675",
|
|
12
|
+
yellow: "#ffc942"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const palette = {
|
|
16
|
+
blue: chalk.hex( colors.blue ),
|
|
17
|
+
lightBlue: chalk.hex( colors.lightBlue ),
|
|
18
|
+
orange: chalk.hex( colors.orange ),
|
|
19
|
+
yellow: chalk.hex( colors.yellow )
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const gradient = gs( [ colors.mintGreen, colors.blueSky ] );
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const defaultOptions = {
|
|
2
|
+
labelColor: text => text,
|
|
3
|
+
leadingNewlineChar: "\n"
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const renderInfoSections = ( sections, options = defaultOptions ) => {
|
|
7
|
+
const { labelColor, leadingNewlineChar } = { ...defaultOptions, ...options };
|
|
8
|
+
|
|
9
|
+
const labelWidth = sections.reduce( ( max, { label } ) => Math.max( max, label.length ), 0 );
|
|
10
|
+
|
|
11
|
+
const formatLabel = ( label ) => {
|
|
12
|
+
if ( label.length === 0 ) {
|
|
13
|
+
return labelColor( "".padStart( labelWidth + 1 ) );
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const baseLabel = `${ label.padStart( labelWidth ) }:`;
|
|
17
|
+
|
|
18
|
+
return labelColor( baseLabel );
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return sections.flatMap( ( { label, value, leadingNewline = false } ) => {
|
|
22
|
+
const values = Array.isArray( value ) ? value : [ value ];
|
|
23
|
+
|
|
24
|
+
return values.map( ( entry, index ) => {
|
|
25
|
+
const prefix = leadingNewline && index === 0 ? leadingNewlineChar : "";
|
|
26
|
+
const formattedLabel = formatLabel( index === 0 ? label : "" );
|
|
27
|
+
|
|
28
|
+
return `${ prefix }${ formattedLabel } ${ entry }`;
|
|
29
|
+
} );
|
|
30
|
+
} );
|
|
31
|
+
};
|
package/bin/text.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const centerText = ( text, width ) => {
|
|
2
|
+
const textLength = text.length;
|
|
3
|
+
const paddingTotal = Math.max( width - textLength, 0 );
|
|
4
|
+
const paddingLeft = Math.floor( paddingTotal / 2 );
|
|
5
|
+
const paddingRight = paddingTotal - paddingLeft;
|
|
6
|
+
|
|
7
|
+
return `${ " ".repeat( paddingLeft ) }${ text }${ " ".repeat( paddingRight ) }`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const wrapText = ( text, width ) => {
|
|
11
|
+
const words = text.split( /\s+/ );
|
|
12
|
+
const lines = [];
|
|
13
|
+
let currentLine = "";
|
|
14
|
+
|
|
15
|
+
words.forEach( ( word ) => {
|
|
16
|
+
const candidate = currentLine.length === 0 ? word : `${ currentLine } ${ word }`;
|
|
17
|
+
|
|
18
|
+
if ( candidate.length <= width ) {
|
|
19
|
+
currentLine = candidate;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if ( currentLine.length > 0 ) {
|
|
24
|
+
lines.push( currentLine );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
currentLine = word;
|
|
28
|
+
} );
|
|
29
|
+
|
|
30
|
+
if ( currentLine.length > 0 ) {
|
|
31
|
+
lines.push( currentLine );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return lines;
|
|
35
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const artOrder = [ "avatar", "banner" ];
|
|
2
|
+
|
|
3
|
+
export const profile = {
|
|
4
|
+
name: "DAVID NEAL",
|
|
5
|
+
handle: "reverentgeek",
|
|
6
|
+
bio: "David is a family man, geek, musician, illustrator, speaker, software developer, and Microsoft MVP living in North GA. He runs on a high-octane mixture of caffeine and JavaScript, and is entirely made of bacon.",
|
|
7
|
+
infoSections: [
|
|
8
|
+
{ label: "Work", value: [ "Developer Relations Engineer", "https://plaid.com" ], style: "gradient", leadingNewline: true },
|
|
9
|
+
{ label: "Handle", value: "reverentgeek", style: "yellow" },
|
|
10
|
+
{ label: "Portfolio", value: "https://reverentgeek.com", style: "yellow" },
|
|
11
|
+
{ label: "Email", value: "david@reverentgeek.com", style: "yellow" },
|
|
12
|
+
{ label: "LinkedIn", value: "https://linkedin.com/in/davidneal", style: "yellow" },
|
|
13
|
+
{ label: "GitHub", value: "https://github.com/reverentgeek", style: "yellow" },
|
|
14
|
+
{ label: "Instagram", value: "https://instagram.com/reverentgeek", style: "yellow" },
|
|
15
|
+
{ label: "Mastodon", value: "@reverentgeek@reverentgeek.com", style: "yellow" },
|
|
16
|
+
{ label: "BlueSky", value: "@reverentgeek.com", style: "yellow" },
|
|
17
|
+
{ label: "Threads", value: "@reverentgeek", style: "yellow" },
|
|
18
|
+
{ label: "X", value: "https://x.com/reverentgeek", style: "yellow" },
|
|
19
|
+
{ label: "Card", value: "npx reverentgeek", style: "orange", leadingNewline: true }
|
|
20
|
+
]
|
|
21
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reverentgeek",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "My personal calling card.",
|
|
5
5
|
"main": "./bin/card.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"gradient-string": "^3.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"eslint": "^9.
|
|
31
|
-
"eslint-config-reverentgeek": "^6.3.
|
|
30
|
+
"eslint": "^9.39.0",
|
|
31
|
+
"eslint-config-reverentgeek": "^6.3.2"
|
|
32
32
|
}
|
|
33
|
-
}
|
|
33
|
+
}
|