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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
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
- // Some sweet ascii art
10
- import avatar from "../art/avatar.js";
11
- import banner from "../art/banner.js";
12
-
13
- // define custom colors
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
- // Text + chalk definitions
44
- const data = {
45
- name: yellowChalk.bold( " DAVID NEAL" ),
46
- handle: yellowChalk( "reverentgeek" ),
47
- work: gradient( "Developer Relations Engineer" ),
48
- workUrl: gradient( " https://plaid.com" ),
49
- twitter: yellowChalk( "https://x.com/reverentgeek" ),
50
- instagram: yellowChalk( "https://instagram.com/reverentgeek" ),
51
- mastodon: yellowChalk( "@reverentgeek@reverentgeek.com" ),
52
- bluesky: yellowChalk( "@reverentgeek.com" ),
53
- threads: yellowChalk( "@reverentgeek" ),
54
- github: yellowChalk( "https://github.com/reverentgeek" ),
55
- linkedin: yellowChalk( "https://linkedin.com/in/davidneal" ),
56
- web: yellowChalk( "https://reverentgeek.com" ),
57
- email: yellowChalk( "david@reverentgeek.com" ),
58
- npx: orangeChalk( "npx reverentgeek" ),
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 hr = gradient( "----------~~~~~~~~~==========~~~~~~~~~-----------" );
82
- const heading = data.name;
83
- const working = `\n${ data.labelWork } ${ data.work }`;
84
- const workingUrl = `${ data.labelWorkUrl } ${ data.workUrl }`;
85
- const twittering = `${ data.labelTwitter } ${ data.twitter }`;
86
- const instagramming = `${ data.labelInstagram } ${ data.instagram }`;
87
- const tooting = `${ data.labelMastodon } ${ data.mastodon }`;
88
- const skeeting = `${ data.labelBluesky } ${ data.bluesky }`;
89
- const threading = `${ data.labelThreads } ${ data.threads }`;
90
- const githubing = `${ data.labelGitHub } ${ data.github }`;
91
- const linkedining = `${ data.labelLinkedIn } ${ data.linkedin }`;
92
- const webing = `${ data.labelWeb } ${ data.web }`;
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
- gradient.multiline( avatar ), gradient.multiline( banner ),
62
+ ...art,
100
63
  hr, heading, hr,
101
- working, workingUrl,
102
- webing, emailing,
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": "2.2.1",
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.36.0",
31
- "eslint-config-reverentgeek": "^6.3.0"
30
+ "eslint": "^9.39.0",
31
+ "eslint-config-reverentgeek": "^6.3.2"
32
32
  }
33
- }
33
+ }