reverentgeek 2.2.1 → 3.0.0
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/AGENTS.md +16 -0
- package/README.md +10 -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 +1 -1
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/README.md
CHANGED
|
@@ -8,6 +8,15 @@ 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 `node bin/card.js` to preview the box output locally before publishing.
|
|
19
|
+
|
|
11
20
|
## Credit
|
|
12
21
|
|
|
13
|
-
Completely *borrowed* this idea from [Tierney](https://github.com/bnb/bitandbang) :)
|
|
22
|
+
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
|
+
};
|