milstead 0.1.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/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # milst
2
+
3
+ Michael Milstead's personal profile, as a terminal app.
4
+
5
+ ```bash
6
+ npx milst
7
+ ```
8
+
9
+ A full-screen terminal UI: a pixel wordmark, your name, an ANSI-art portrait,
10
+ and links you can open in your browser.
11
+
12
+ ## Keys
13
+
14
+ | Key | Action |
15
+ | -------------- | ------------------------------- |
16
+ | `s` | open personal site |
17
+ | `g` | open GitHub profile |
18
+ | `x` | open X profile |
19
+ | `q` / `Esc` | quit |
20
+
21
+ ## Make it yours
22
+
23
+ - **Content** — edit [`src/data/profile.ts`](src/data/profile.ts): wordmark,
24
+ name, and links. That's the only file you need to touch.
25
+ - **Portrait** — drop a photo at `assets/portrait.png` and run
26
+ `npm run portrait`. See [`assets/README.md`](assets/README.md).
27
+
28
+ ## Develop
29
+
30
+ ```bash
31
+ npm install
32
+ npm run portrait # generate the portrait (placeholder if no photo)
33
+ npm run build # compile src/ -> dist/
34
+ npm start # run the built CLI locally
35
+ npm test # vitest
36
+ ```
37
+
38
+ Built with [Ink](https://github.com/vadimdemedes/ink) (React for the terminal),
39
+ compiled with [tsup](https://tsup.egoist.dev/).
package/bin/milst.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Thin launcher: the real app is the compiled Ink bundle in dist/. Keeping this
4
+ // file dependency-free means `npx milst` starts the moment node is up.
5
+ import("../dist/cli.js");
package/dist/cli.js ADDED
@@ -0,0 +1,274 @@
1
+ // src/cli.tsx
2
+ import { render } from "ink";
3
+
4
+ // src/app.tsx
5
+ import { Box as Box5 } from "ink";
6
+
7
+ // src/data/profile.ts
8
+ var profile = {
9
+ wordmark: "milst",
10
+ name: "Michael Milstead",
11
+ tagline: "Building creative uses of AI",
12
+ githubUrl: "https://github.com/MichaelMilstead",
13
+ xUrl: "https://x.com/michael_milst",
14
+ siteUrl: "https://milste.ad",
15
+ githubLabel: "github.com/MichaelMilstead",
16
+ xLabel: "x.com/michael_milst",
17
+ siteLabel: "milste.ad"
18
+ };
19
+
20
+ // src/data/portrait.ts
21
+ var portraitFrames = [" \n \n \n \n :=+***+-:. \n .=#####%###%#*=. \n :*****#%%%%%%%%##. \n ==:.::-+**#####%#- \n . .---=---+*####+ \n :-.=+====---+++=+= \n :.--....:---=+-= \n .===. .:----=-= \n .-=-:::------:. \n .-=----=====-: \n .::-=++++++=- \n :-+******+=-::=- \n .==+**+===-*%%%*-. \n :=+#%%+------+#%%%%%%%##*-. \n .=*#%%%%%%%==+*#%@@%%%%%%%%%%%##*: \n +%#%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%%#*. \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%##: \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@%@%%%%%%%# \n .#%%%%%%%%%%@@@%@@@@@@@@@@@@@%%@@%%@@%%%%%+ \n +%%%%@%%%%%@@@@@@@@@@@@%@@@@@%@@@@@@%%%%%%#: ....", " \n \n \n \n :=+***+-:. \n .+#####%###%#*=. \n :*****#%%%%%%%%##. \n +=:.::-+**#####%#- \n .---=---**####+ \n :-.=+====---+++=+- \n :.--....:---=+-= \n .===. .:----=-= \n .-=-:::------: \n .-=----=====-: \n .::-=++++++=- \n :-+******+=-::=- \n .==+**+===-*%%%*-. \n :=+#%%+------+#%%%%%%%##*-. \n .=*#%%%%%%%==+*#%@@%%%%%%%%%%%##*: \n +%#%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%%#*. \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%##. \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%# \n .#%%%%%%%%%%@@@%@@@@@@@%@@@@@%%@@%%@@%%%%%+ \n *%%%%@%%%%@@@@@@@@@@@@@%@@@@@%@@@@@@%%%%%%#: ....", " \n \n \n \n :=+***+-:. \n .+#####%###%#*=. \n -#****#%%%%%%%%#*. \n ==:.::-+**#####%#- \n .---=---**####+ \n :-.=+====---+++=+- \n :.--....----=+-= \n :===. .:----=-= \n .-=-:::------: \n .-=----====--: \n .::-=++++++=- \n :-+******+=-::=- \n .==+**+===-*%%%*-. \n :=+#%%+------+#%%%%%%%##*-. \n .=#%%%%%%%%==+*#@@@%%%@%%%%%%%##*: \n *%#%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%%#*. \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%##. \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%# \n .#%%%%%%%%%%@@@%@@@@@@@%@@@@@%%@@%%@@%%%%%+ \n *%%@%@%%%%@@@@@@@@@@@@@%@@@@@%@@@@@@@%%%%%#: .....", " \n \n \n \n :=+***+=:. \n .+#####%###%##+. \n -##**##%%%%%%%%%#. \n ==:.::-+**#####%#- \n .---=---**####+ \n :-.=+====--=+++=+= \n :.--....----=+-= \n :===. .:----=-= \n :-=-:::------:. \n .-==---=====-: \n .::-=+*++++=- \n :=+******+=-::=- \n .==+**+===-*%%%*-. \n :=+#%%+------+#%%%%%%%##*-. \n .=##%%%%%%%==+*#%@@%%%@%%%%%%%##*: \n *%#%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%%#*. \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%##. \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%# \n .#%%%%%%%%%%@@@%@@@@@@@%@@@@@%%@@%%@@%%%%%+ \n *%%@%@%%%%@@@@@@@@@@@@@%@@@@@%@@@@@@@%%%%%#: .....", " \n \n \n \n :=+****=:. \n .+#####%###%##+. \n -*****#%%%%%%%%%#. \n ==:.::-+**#####%#= \n .---=---+*####* \n :-.=+==+=---+++=+= \n :.--....:---=+-= \n .===. .:----=-= \n .-=-:::------:. \n .-==---=====-: \n .::-=++++++=-. \n :-+******+=-::=- \n .==+**+===-*%%%*-: \n :=*#%%+-=----+#%%%%%%%##*-. \n .=#%%%%%%%%==+*#%@@%%%%%%%%%%%##*: \n +%#%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%%#*: \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%##: \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%# \n .#%%%%%%%%%%@@@%@@@@@@@%@@@@@%%@@%%@@%%%%%* \n +%%%%@%%%%@@@@@@@@@@@@@%@@@@@%@@@@@@@%%%%%#- .....", " \n \n \n \n :=**##*+-: \n -*#####%%####+: \n +*****#%%%%%%###- \n :=-:..:--++*#####+ \n ....:====--=****= \n :--:+==++=--=++=: \n :.:--::::----+- \n :===..:----=- \n .--==--------. \n .:-==--====-- \n .::-=++++++-. \n .-+******+=-:+= \n .=-+***++==*%%%#=: \n :=+#%%+:-====+#%%%%%%%##*=. \n .=#%%%%%%%%==+*#%@@%%%@%%%%%%%##*: \n *%#%%%%%%%%%%%%@@@@@@%%@@%%%%%%%%%%##: \n =%%%%%%%%%%%%%%%@@@@@@%@@%%%%%@%%%%%%%#- \n -#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%#. \n .#%%%%%%%%%%@@@%@@@@@@@@@@@@@%%@@%%@@%%%%%* \n *%%%%@%%%%@@@@@@@@@@@@@%@@@@@%@@@@@@@%%%%%%- .....", " \n \n \n ... \n :+**###*=: \n =######%%%##*- \n .**+++++*#%%%###+ \n .*:.::..:--=+*##*. \n =.::..-=+==::**- \n -=+::+*+++=:=+- \n ... .------::+= \n :-==-:--:-- \n .:=++==---: \n .:-=+++===-. \n :::--=+++=: \n .=+*****+=:+= \n .=:-+****++*%%%#=: \n :=*#%%+:-===+*#%%%%%%%##*=. \n :=##%%%%%%%+=++#%@@%%%%%%%%%%%##*- \n *%#%%%%%%%%%%%%@@@@@@%%@@%%%%%%%%%%##- \n +%%%%%%%%%%%%%%%@@@@@@%%@@%%%%@%%%%%%%#- \n =#%%%@%%%%%%@@%%@@@@@@@%@@%%%%@@@%%%%%%%#: \n -#%%%%%%%%%%@@@%@@@@@@@@%@@@@%%@@%%@@%%%%%# \n #%%%%@%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%= .....", " \n \n \n .:-.. \n :+**####*+: \n :+#######%%##*= \n =*#+==++**##%%#*- \n .*+...:--:--=+**- \n +:.:::::-===::* \n :.:=++::+**+=:= \n : .:. .:===-:-. \n ..:-----:: \n ..-=++=--: \n .:--=+++=-. \n .:::-===+= \n :-==++*+=-=+. \n .=::=+****+*%%%%+-. \n .-+*##%*::===+*#@@@%%%%%#*=: \n -+#%%%%%%%%+=++*%@@@%%%%%%%%%%%#*- \n #%%%%%%%%%%%%%%@@@@@@@%%@%%%%%%%%%%##= \n *%%%%%%%%%%%%%%%@@@@@@@%@@%%%%%@%%%%%%#+ \n +#%%%@%%%%%%@@%%@@@@@@@%@@@%%%%@%%%%%%%%#- \n =#%%%%%%%%%%@@@@@@@@@@@@%@@@@%%@@@%%@@%%%%# \n #%%@%@%%%%@@@@@@@@@@@@@@%@@@@@%@@@@@@%%%%%%+ .....", " \n \n \n .--=:. \n -*#######*=. \n =#######%%%%##*: \n -###++=+**##%%##* \n :*#+:..:-====++*- \n ++: .::-----::. \n -- :=+*+:-+**+: \n :: .-=: .=*+-: \n .. . .:---: \n .:.==+==:. \n ..:--=++++- \n .:-::=++==. \n :=---==+=-+: \n :=-.:=*****+#%@%+-. \n :=*#%#%*::-===+#@@@@%%%%#*=: \n .=*#%%%%%%%%*-+=+#@@@@@%@%%%%%%%#*= \n .#%#%%%%%%%%%%%%@@@@@@@%%@@%%%%%%%%%##+ \n #%%%%%%%%%%%%%%%@@@@@@@%@@%%%%%@%%%%%%#* \n *#%%%@%%%%%%@@%%@@@@@@@@%@@%%%%@@%%%%%%%#+ \n *#%%%%%%%%%%@@@@@@@@@@@@@@@@@%%%@@%%@@%%%%#: \n .%%%@%@%%%%@@@@@@@@@@@@@@%@@@@@%@@@@@@%%%%%%# .....", " \n \n \n .:-=+=:. \n .-*########*- \n +########%%%%#*= \n :#*#++=+*###%%%#*- \n .*#+:..:-===++*#+ \n =+: ..:------:- \n -: :=+*+--+**+:. \n :. .:==: :+**=: \n .. .. .:---: \n .:.--==-: \n ..:--=+++=- \n ::-:-+++++. \n .--::-==+=== \n .=+.:-+****++%%%#-. \n -+*#%##%=.:-==+*%@@@@%%%%*=. \n -+##%%%%%%%%%====+%@@@@@%%%%%%%##+- \n #%%%%%%%%%%%%%%%@@@@@@@%%@%%%%%%%%%%#+ \n *%%%%%%%%%%%%%%%@@@@@@@@%@@%%%%%@%%%%%##. \n *#%%%@%%%%%%@@@%%@@@@@@@@@@@%%%%@%%%%%%%## \n *#%%@%@%%%%%@@@@%@@@@@@@@%@@@@%%@@@%%%@%%%%+ \n .%%@@@@%%%%@@@@@@@@@@@@@@@@@@@@@%@@@@@@%%%%%#: .....", " \n \n \n .:-=++-:. \n :*#%######*+. \n =########%%%%#**: \n **#*===+*###%%#*+ \n =**-...-===++*##- \n :+- .:-------= \n .- :-+*+--=+*+-- \n :. .-=-..+**+-. \n . .. .:=--: \n .:.--==-:. \n ..:--=+++=- \n ::-:-+++++: \n .--::-==+==: \n .=*+.:-++***++%%%+. \n -+##%%%%#:.:===+*%@@@%%%%#=: \n -*##%%%%%%%%%#-===*%@@@@@%%%%%%%#*- \n =%%%%%%%%%%%%%%%@@@@@@@@%%@%%%%%%%%%#*: \n +#%%%%%%%%%%%%%%@@@@@@@@@%@@%%%%%@%%%%%#+ \n +#%%%%@%%%%%@@@@%@@@@@@@@%@@@%%%%@@%%%%%%#= \n -#%%@%@%%%%%@@@@@@@@@@@@@@%@@@@%%@@@%%%%%%%#: \n #%%@@%@%%%@@@@@@@@@@@@@@@@@@@@@@%@@@@@@%%%%%# ......", " \n \n \n ..:-==: \n .-*########*: \n +########%%###*= \n .*##++++*###%%##*- \n **=...:===+**##*: \n += .:------== \n = .-=++--=+++-:. \n .: .-=-..+##+-: \n . . .:===-. \n .:.::-=--: \n .::-=+++=- \n ::-:=++++=: \n .-:::==++=- \n .-+#+.:-=++**++%%#: \n .:+##%%%%%#:.-=+++*#@@@%%%#=. \n *##%%%%%%%%%%#==-=*%@@@@%%%%%%%#+: \n *%%%%%%%%%%%%%%%@@@@@@@@%%@%%%%%%%%#*: \n :##%%%@%%%%%%@%%%@@@@@@@@%%@@%%%%%%%%%%#* \n :#%%@%@@%%%%@@@@%%@@@@@@@@%@@%%%%%@%%%%%%#* \n *%%%@%@@%%@@@@@@@@@@@@@@@@@@@@@%%@@@%%%%%%#* \n +%%@@@%@%%@@@@@@@@@@@@@@@@%@@@@@%@@@%%@@@%%%#- ......", " \n \n \n ..:. \n :=*######*- \n =#######%###**-. \n :*#*++**###%%##*= \n. .*+::.-=+++*#%#*+. \n:.. +. .:----==+*- \n-::... .:.-=+-:--=+=-.+ \n--::.. : :=+- -*#*+=:= \n=--:.. :====-:: \n---::. ..:.--=--:. \n----:. .::-=+++=-. \n----:. .:::=++++=- \n-::::. .:. -::-==++=. \n:........:-: .:--+#=:==++**+=*%#- \n. ..::---: =##%%%%%%%%#::=+++*#%@@@%%*- \n::--=++: :+#%%@@%@@%@%%%#+++*#@@@@@%%%%%%#=. \n=+++=. .+%%%%@@@%@@%@@@%%@@@@@@@@%%@%%%%%%%%#+. \n+**= .#%@@@@@@@@@@@@@@@@@@@@@@@@%@@%%%%%%%%%%#+ \n**+..+%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%#* \n**++#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%#+ \n**=::-*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%#- ......", " \n \n \n .:: \n -*##*####=. \n -*###########*+: \n =+*==+**###%%#*+. \n -. .-=:-..-===+*###*- \n - .-:::-:...:::---=+= \n :. :-:.-::::--:=+++=:=. \n :::::::-. .:-::-+**+=:- \n :---:::.::-:..:---==--:- \n :--::..:--: .--==+==---. \n .:......:: .-==+**+*==: \n :. ...::: ...:=*++++- \n .:--=:---==-:---===++++= \n .:-=+**#*#####%%+.-+****+=*%*: \n ..-==+**%%@@@%@@@%%%::=+++*#%@@@%#=. \n ..:=++**%@@@@@@@@@@@%%%#%#%@@@@@@%%%%%#+: \n .::-=+**#%@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%#*- \n::-=+**#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%##: \n--=+**++#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%@@%%%%%#= \n=+*******%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@%%%%%%%#: \n+*******#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%@@@%%%%%%%%#.......", " \n \n \n .. :. ... \n . -..-. . -+##*###*- \n :- :- .- .-. -*##########**-. \n :- :- :: :: *#*===**###%##*+ \n : .:..: :- *+.. .---=+*##*+. \n ..:.....:- =....::::----=*- \n ::-:....-. :.:=+=:-=+++-.+ \n .:-:. .: . .:-:.:+**+-:- \n .:: .: :::..:::::----::. \n .:. .::..:: :.---==+==--: \n :::::::-==-. .:====++++=- \n .:-=++=: ----=+==+=. \n .:-==+=. :==-===++: \n .-=+++: .+*=+**##%%*.:=+****==#+. \n .:=++*+::+%%%%@@%%@%@%%%--=+++**#@%%%+: \n.:-=+**+=#%%@@%@@@%@@%@@%%%%%%%%@@@@@%%%%#*- \n:-=+***#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%#*: \n-=+**+==*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%#+ \n++****+++*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%##. \n**********#@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%@%%%%%%#* \n*********#@@*@@@@@@@@@@@@@@@@@@@@@@@@@@%%@%%%%%%%%#+......", " \n \n \n \n -+***#*+-. \n . -*##########*=. \n - .: .: :+*+==+**#####*+: \n :: .: : ::...:---+*###*= \n :. :. : :-.....::::--=++. \n ......: .-- :-++-:-+++=:-- \n .:::.. .:: . .--..=**+=:- \n :::.. .:. :::--:----:- \n .::. .: .:--=++===-:. \n .. ... .:-==+*++==: \n ... ...:-:.:. :=--=++=+=- \n :--===-::. -=====++= \n ..--=++=+=-++**###:.-=****+=**: \n :-=++*#%%%@@%@@%@%%*::=+=+*#%%%%#- \n ..-=+**#%@@%@@@@@@@@%%%%%##%%@@@@%%%%%#=. \n .::-++*#%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%##= \n.::-=+***%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%#*. \n:--=+**++*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%##= \n-=+*******#@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%@%%%%%%##- \n=+*******#%@%@@@@@@@@@@@@@@@@@@@@@@@@@@%%@%%%%%%%%%#:.....", " \n \n \n \n .=******=: \n .=*#########*+: \n .. : +#*==+**#####**= \n : :-.-. +*:. .:--=+*##*+. \n :- :- :: :: =......::::--+*= \n :. .-..: :: : :=+=::=++=-.+ \n .....:: :: . .:. :+*++-:- \n ::::.. .:-. ....:----:: \n .:::. .- .:::==+==-:: \n .:. .. .:=-=+++++=- \n .. .: .::. ----++==== \n .:.....:--::. .======+= \n .:--+=::=**=+++**##=.:=+***+=+#: \n .:-=+++++#%%%%%%@%@%%#::=++=+*#%%%#- \n :-=++*#%%%%@@@@%@@%@%%%%%###%%@@@%%%%%#=. \n .-=+***%%%@@%@@@@@@@@@@@@@@@@@@@@@@%%%%%%###=. \n.:-=+**#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%##: \n:-++**==*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%##+ \n=+********%@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%@%%%%%%##+ \n+********#%@@@@@@@@@@@@@@@@@@@@@@%@@@@@%%@@%%%%%%%%#-.....", " \n \n \n \n .=*#****+- \n .=*####*#####*-. \n =**===+***####*+. \n =*- .:---+**#**- \n -: ....::::--=+= \n : :-+=:.-=++-.=. \n .:. .=*++=:= \n .::. .:---::: \n.:-+=-: ...--===-::. \n--=++=---:. .:::-=+++=-. \n==++++===: :...-=====: \n=+++++===. .-::--===: \n=====---: .. -+=***##=..-+***+==#- \n-=--. .:. :-+%%%%%%%%%%%#..:===+*#%%%#=. \n--: .:..=*#%%%@%%@%%%%%%#**+*#%@@%%%%%%#=. \n--:...:--=+#%%@%@@%@%%@%%%%%@@@@@@@%%%%%%%%###=. \n-=-===+*#%%@@%%@@@@@%%@@@%%@@@@@@@@%%@@%%%%%%%%##- \n-=+***#%%%@@@%@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%##+ \n=+**+#%%%@@@@%@@@@@@@@@@@@@@@@@@@@@@@@@@%%%@%%%%%##+ \n+**=.:=*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%@%%%%%%%%#=.....", " \n \n \n .:: \n -+##**#**=. \n -*#####*#####*=. \n *#*=-=++**####*+: \n *+. .:---=+*#*+: \n =.......::::-=+= \n .. :-+=..-+++-.= \n . .:. .+*++-.- \n .:---::. \n ...--===-:: \n .-::-=+++=- \n :. .-=====. \n .-:::-===. \n .--++*###. .-+***+=+#- \n =##%%%%%%%%##- .-===+*#%%%#-. \n .*%%%%%%%%%%%%%#++==+#%@@%%%%%##=. \n =##%%%%%%%%%%%%%%%%@@@@@%%%%%%%%%###+. \n *#%%%%@%%%%%%%%%%%%@@@@%%%%%@%%%%%%%%%##= \n *%%%%%%@%%%%%%@@@%%@@@@@%%%%@@%%%%%%%%%%##* \n *#%%@%%%%%%%%%@@@@%@@@@%@@@%@@%%%%%%@%%%%%##* \n *#%%%@%%%%%%%@@@@@@@@@@@@@@%%@@%%%%%@%%%%%%%%#+.....", " \n \n \n :-=++=:. \n :+###*####*+: \n -*##*#########*+: \n -#+---=++*##%##*+ \n -+. :-::-=+##*+. \n :..:::::::---:=+: \n . :-+=.:=**+=::= \n . .. :=+++=::- \n . ::--:::- \n .:.-=+==--:. \n .:::-=+++==- \n :..-====== \n ::::-==+=:. \n .-=+*##..:-+***+++%#= \n :=*###%%%%%%+ .:===+*#@@%%#*- \n -#%#%%%%%%%%%%%+====*%@@@%%%%%%##=. \n :#%%%%%%%%%%%%%%%%%@@@@@%%%%%%%%%%###=. \n ##%%%%%%%%%%%%%%%%@@@@%%%%%@%%%%%%%%%##* \n *#%%%%%%%%%%@@@%%%@@@@@%@%%@@%%%%%%%%%%%##. \n +#%%@%%%%%%@@@@@%%@@@@@@@@%@@@%%%%@@%%%%%%## \n +##%@%%%%%%%@@@@@@@@@@@@@@%%@@@@%%@@%%%%%%%%#*......", " \n \n \n :-+**+=-. \n .=*#########+- \n +#########%%##**. \n *+=-==++*##%%##**. \n *. .::::-=+****+ \n :.:::..:----:-** \n .:=+=.-+***+-:+= \n ... -=====-.=- \n .:---::::-. \n .:.-=+++=--:. \n .-:-=++++==: \n ..:=====+= \n :::-==+++=:-. \n :-=*#+.-=****++*%%#- \n :=*##%%%%%#:.-===+#%@@%%%%#=. \n *##%%%%%%%%%%*===+#%@@@%%%%%%%##+. \n +%%%%%%%%%%%%%%%@@@@@@%%%@%%%%%%%%##+ \n -#%%%%%%%%%%%%%%%@@@@@@%%@@%%%%%%%%%%##+ \n #%%%%%%%%%%%@%%%@@@@@@%%@@@%%%%%@%%%%%%#* \n =#%%%%%%%%@@@@@%@@@@@@@@%@@%%%%%@%%%%%%%%#+ \n :#%%%%%%%%@@@@@@%@@@@@@@%@@@@@%%@@@%%%%%%%%#: ......", " \n \n \n :-+++=-. \n .=**#######+-. \n .+#######%%%###*= \n +*+=+++**#%%%##**= \n --...:.:--=+*****- \n .:.....:----::+*= \n :-+=.-+***+-:-+- \n .:: -=====-::=- \n :---::-::- \n ..:=+++=---:. \n :---=+++===: \n ...-====++=: \n .::-=++*++--=. \n .=#=-=+****++#%%#: \n -+###%%%#.:=+=+*#@@@%%%%#+: \n :*##%%%%%%%%%+-==*%@@@%%%%%%%###*- \n :#%%%%%%%%%%%%%%@@@@@@%%%@%%%%%%%%##*: \n #%%%%%%%%%%%%%%%@@@@@@%%@@%%%%%%%%%%##* \n *#%%%%%%%%%%@@%%@@@@@@@%@@%%%%%@%%%%%%%#* \n -#%%%%%%%%%%@@@%@@@@@@@%@@@%%%%@%%%%%%%%%#+ \n .#%%%%%%%%@@@@@@@@@@@@@@%@@@@@%@@@@%@@@%%%%#: .....", " \n \n \n :-==-:. \n :+########+-. \n :*#######%%%###+ \n :**+++***#%%%%##*+ \n *:..::::-=+*****= \n = ....:---::-** \n :=+=.-+***+-:+= \n .:-: -+===--:-= \n :---::-:-: \n ..:-+++=---: \n :---=+++===. \n ...:====++=. \n ::--=++++--=. \n :+--=+****++#%%%=. \n :=*#%%#*::=+++*#%@@%%%%%#+: \n :=*#%%%%%%%%*:-=+#%@@%%%%%%%%####=. \n #%#%%%%%%%%%%%%%@@@@@%%%@%%%%%%%%%##*- \n *%%%%%%%%%%%%%%%%@@@@@@%@@%%%%%%%%%%%##+ \n =#%%%%%%%%%%%@%%%@@@@@@%%@@%%%%@%%%%%%%%#= \n #%%%%%%%%%%%@@@%@@@@@@@%@@@%%%@@%%%%%%%%%#: \n +%%%%%%%%%@@@@@@@@@@@@@@%@@@@@%@@@%@@%%%%%%* ....", " \n \n \n .-=-:: \n :+#*######=-. \n .+#######%%%##*- \n -**++****##%%%##*= \n *-:.:-::-==+****- \n =. ...:----::++ \n :-==::=**+=-:== \n :--..=+++=-::= \n .:--:::::: \n ..-=++==--: \n .---==+++==- \n :..-====+=- \n .::-==+++-:+- \n --:=+****++*%%%*-. \n .=*#%##:.-++++*%@@@%%%%##+- \n .-*##%%%%%%#::-=*%@@@%%%%%%%%####+: \n =##%%%%%%%%%%%%%@@@@@@%%%@%%%%%%%%%###: \n :#%%%%%%%%%%%%%%%@@@@@@%%@@%%%%%@%%%%%##- \n ##%%%%%%%%%%%@%%@@@@@@@%@@@%%%%@%%%%%%%%#: \n +#%%%@%%%%%%@@@%%@@@@@@@%@@%%%%@@@%%%%%%%%# \n #%%@%%%%%%%@@@@@@@@@@@@@%@@@@@%@@%@@@%%%%%%+ ....", " \n \n \n :-=-:. \n :+#*#####*=: \n :*#######%%###*: \n =**+++***##%%##**. \n .*-..:-::-==+***+. \n + ...:----.:+- \n .:-=+-:=**++-.=- \n :--. -+=+=-::= \n ::-:::::. \n ..:=+++=--: \n .---==+++==: \n :..-======. \n :::-==++=-=+. \n :=::=+****++#%%%+-. \n .-+####*..-++++#%@@%%%%%##+- \n :+##%%%%%%%*:--=*%@@@%%%%%%%%%###+. \n ###%%%%%%%%%%%%%@@@@@%%%%@%%%%%%%%%##* \n *%%%%%%%%%%%%%%%@@@@@@@%%@%%%%%@%%%%%%##. \n =#%%%%%%%%%%%%@%%@@@@@@@%@@%%%%%@%%%%%%%#* \n :#%%%%@%%%%%%@@@%@@@@@@@@%@@%%%%@@%%%@%%%%#+ \n *#%%@%%%%%%@@@@@%@@@@@@@@%@@@@%%@@@@@%%%%%%#: ....", " \n \n \n :-=-:. \n -*#*####**-: \n -*#######%%##*+ \n +**+++***##%%##*+ \n :*-..:-::-==+***+ \n + ..::---:.-*. \n ::-=+-:=***+-.+: \n :--. -+++=-.-: \n ::-::::: \n ..:=+++=--: \n .---==+++=-. \n .:..-====== \n :::-=+++=:+= \n -+.-=+***+++%%%#=-. \n :=*#%##=.:=+++*#@@@%%%%%##+: \n :=*#%%%%%%%%+--=+#%@@%%%%%%%%%####= \n :#%%%%%%%%%%%%%%%@@@@@%%%@@%%%%%%%%###+ \n -#%%%%%%%%%%%%%%%@@@@@@@%%@%%%%%@%%%%%##* \n :##%@%@%%%%%%%@@%%@@@@@@@%@@%%%%@%%%%%%%%#+ \n :##%%@%@%%%%%@@@@%@@@@@@@@%@@%%%%@@%%%%%%%%#: \n +%%%%@@@%%%@@@@@@%@@@@@@@@@@@@@%@@%@@@%%%%%%* ....", " \n \n \n .:-=:: \n .=*#*####*+:. \n .+########%%##*- \n .***+=++**##%%##*- \n =*:. .--:-==+***: \n .= ..:::---:.-+ \n ::-++-.=***+-:= \n . .--. :==+=-.- \n .:-:::.: \n ..:=++==--. \n .----=+++=- \n .:..-=====- \n .:::-=+++-=+: \n -+-.-=****+=#%%%*=- \n .-=##%%##:.:=+++*%@@@%%%%%##+: \n -+#%%%%%%%%%#====*%@@@%%%%%%%%%####= \n .*%%%%%%@%%%%%%%%@@@@@@%%%@%%%%%%%%%###- \n =##%@%@%%@%%%%%%%%@@@@@@@%@@%%%%%@%%%%%##= \n +#%%%@@@%%@%%@@@@%%@@@@@@@%@@%%%%@%%%%%%%%#: \n +#%%%@@@@%%%%%@@@@%@@@@@@@@%@@%%%%@%%@@%%%%%# \n :#%%%@@%@@%%%@@@@@@@@@@@@@@@@@@@@%@@@@@%%%%%%#= ....", " \n \n \n .-==:: \n :+##*####*+: \n :*########%%#**: \n :*#+==++**##%%##*. \n +*:. .:---==+**+ \n :- .::::---:.== \n ..:-++=.=***+-:- \n .:-. :=++=:.- \n .:-:::. \n ..:=++==-: \n .----=+++=- \n .:.:-=====. \n :-::-=++=:*=. \n -**.:=+***+++%%%#+=: \n .=+##%%%#+:.-++++#@@@%%%%%##*=. \n =*#%%%%%%%%%%#=+++#%@@@%%%%%%%%%###*: \n :*#%%%%%@@%%%%%%%%@@@@@@%%%@%%%%%%%%%##* \n +#%%%%%@%@@%@@%@%%%@@@@@@@%@@%%%%%%%%%%%##. \n ##%%%@%@@%@@%@@@@%%@@@@@@@%%@@%%%%@%%%%%%%#* \n =#%%%@@@@@%@@%@@@@@@@@@@@@@%@@@%%%@@%%@@%%%%#= \n:#%%%@@@@@@%@@@@@@@@@@@@@@@@%@@@@@%@@@@%%%%%%%# ....", " \n \n \n .:-==-: \n -*#**####*+: \n =*########%%#**: \n -*#+===+**###%##+ \n *+. .-----==+*+ \n -: .:::::-==:.=: \n : :-++-.=***=:-: \n . .:: :===-::: \n ::-:::. \n :.-=++==-: \n ..=:-==+++=: \n ::.:-===== \n :---=+++=-#=. \n .=#+.:=+***++*%%%#*=: \n -+*##%%%#+.:-==+*%@@@%%%%%##*=. \n *##%%%%%%%%%%*+++*%@@@@%%%%%%%%%%##*: \n =##%%%%%@@%@%%%%%@@@@@@@%%@@%%%%%%%%%##+ \n .*#%%%%@@%@@%@@@@%%@@@@@@@%%@@%%%%@%%%%%##* \n ##%%%@%@@%@@%@@@@%%@@@@@@@%@@%%%%@%%%%%%%%#+ \n =#%%%@@@@@%@@%@@@@@@@@@@@@@%@@%%%%@@%@@%%%%%#: \n-#%%%@@@@@@%@@@@@@@@@@@@@@@@%@@@@@%@@@@%%%%%%%* ....."];
22
+
23
+ // src/components/portrait.tsx
24
+ import { Box, Text } from "ink";
25
+ import { useEffect, useMemo, useState } from "react";
26
+ import { jsx } from "react/jsx-runtime";
27
+ var FRAME_MS = 120;
28
+ function pingPongOrder(n) {
29
+ if (n <= 1) return [0];
30
+ const forward = Array.from({ length: n }, (_, i) => i);
31
+ const backward = Array.from({ length: n - 2 }, (_, i) => n - 2 - i);
32
+ return forward.concat(backward);
33
+ }
34
+ function Portrait() {
35
+ const order = useMemo(() => pingPongOrder(portraitFrames.length), []);
36
+ const [step, setStep] = useState(0);
37
+ useEffect(() => {
38
+ if (portraitFrames.length <= 1) return;
39
+ const id = setInterval(() => setStep((s) => (s + 1) % order.length), FRAME_MS);
40
+ id.unref?.();
41
+ return () => clearInterval(id);
42
+ }, [order.length]);
43
+ const frame = portraitFrames[order[step]] ?? portraitFrames[0] ?? "";
44
+ if (!frame) return null;
45
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexShrink: 0, children: frame.split("\n").map((line, i) => /* @__PURE__ */ jsx(Text, { wrap: "truncate", children: line }, i)) });
46
+ }
47
+
48
+ // src/components/header.tsx
49
+ import { Box as Box3, Text as Text3 } from "ink";
50
+
51
+ // src/components/wordmark.tsx
52
+ import { Box as Box2, Text as Text2 } from "ink";
53
+
54
+ // src/components/pixel-font.ts
55
+ var GLYPHS = {
56
+ A: [".###.", "#...#", "#####", "#...#", "#...#"],
57
+ B: ["####.", "#...#", "####.", "#...#", "####."],
58
+ C: [".####", "#....", "#....", "#....", ".####"],
59
+ D: ["####.", "#...#", "#...#", "#...#", "####."],
60
+ E: ["#####", "#....", "###..", "#....", "#####"],
61
+ F: ["#####", "#....", "###..", "#....", "#...."],
62
+ G: [".####", "#....", "#..##", "#...#", ".###."],
63
+ H: ["#...#", "#...#", "#####", "#...#", "#...#"],
64
+ I: ["#####", "..#..", "..#..", "..#..", "#####"],
65
+ J: ["..###", "...#.", "...#.", "#..#.", ".##.."],
66
+ K: ["#...#", "#..#.", "###..", "#..#.", "#...#"],
67
+ L: ["#....", "#....", "#....", "#....", "#####"],
68
+ M: ["#...#", "##.##", "#.#.#", "#...#", "#...#"],
69
+ N: ["#...#", "##..#", "#.#.#", "#..##", "#...#"],
70
+ O: [".###.", "#...#", "#...#", "#...#", ".###."],
71
+ P: ["####.", "#...#", "####.", "#....", "#...."],
72
+ Q: [".###.", "#...#", "#.#.#", "#..#.", ".##.#"],
73
+ R: ["####.", "#...#", "####.", "#..#.", "#...#"],
74
+ S: [".####", "#....", ".###.", "....#", "####."],
75
+ T: ["#####", "..#..", "..#..", "..#..", "..#.."],
76
+ U: ["#...#", "#...#", "#...#", "#...#", ".###."],
77
+ V: ["#...#", "#...#", "#...#", ".#.#.", "..#.."],
78
+ W: ["#...#", "#...#", "#.#.#", "##.##", "#...#"],
79
+ X: ["#...#", ".#.#.", "..#..", ".#.#.", "#...#"],
80
+ Y: ["#...#", ".#.#.", "..#..", "..#..", "..#.."],
81
+ Z: ["#####", "...#.", "..#..", ".#...", "#####"],
82
+ "0": [".###.", "#..##", "#.#.#", "##..#", ".###."],
83
+ "1": ["..#..", ".##..", "..#..", "..#..", ".###."],
84
+ "2": [".###.", "#...#", "..##.", ".#...", "#####"],
85
+ "3": ["####.", "....#", ".###.", "....#", "####."],
86
+ "4": ["#..#.", "#..#.", "#####", "...#.", "...#."],
87
+ "5": ["#####", "#....", "####.", "....#", "####."],
88
+ "6": [".###.", "#....", "####.", "#...#", ".###."],
89
+ "7": ["#####", "...#.", "..#..", ".#...", ".#..."],
90
+ "8": [".###.", "#...#", ".###.", "#...#", ".###."],
91
+ "9": [".###.", "#...#", ".####", "....#", ".###."],
92
+ " ": [".....", ".....", ".....", ".....", "....."],
93
+ // Lowercase forms for the letters used in the wordmark. Letters without a
94
+ // lowercase glyph fall back to their uppercase form (see renderWordmark).
95
+ m: [".....", ".....", "#####", "#.#.#", "#.#.#"],
96
+ i: ["..#..", ".....", "..#..", "..#..", "..#.."],
97
+ l: ["..#..", "..#..", "..#..", "..#..", "..#.."],
98
+ s: [".....", ".####", "###..", "..###", "####."],
99
+ t: [".#...", "####.", ".#...", ".#...", ".###."]
100
+ };
101
+ var FALLBACK = ["#####", "#####", "#####", "#####", "#####"];
102
+ var GLYPH_HEIGHT = 5;
103
+ var ON = "\u2588\u2588";
104
+ var OFF = " ";
105
+ var GLYPH_GAP = " ";
106
+ function trimGlyph(rows) {
107
+ let min = Infinity;
108
+ let max = -1;
109
+ for (const row of rows) {
110
+ for (let c = 0; c < row.length; c++) {
111
+ if (row[c] === "#") {
112
+ if (c < min) min = c;
113
+ if (c > max) max = c;
114
+ }
115
+ }
116
+ }
117
+ if (max < 0) return rows.map(() => "..");
118
+ return rows.map((r) => r.slice(min, max + 1));
119
+ }
120
+ function renderWordmark(text) {
121
+ const glyphs = [...text].map(
122
+ (c) => trimGlyph(GLYPHS[c] ?? GLYPHS[c.toUpperCase()] ?? FALLBACK)
123
+ );
124
+ const rows = [];
125
+ for (let r = 0; r < GLYPH_HEIGHT; r++) {
126
+ const row = glyphs.map((g) => [...g[r]].map((p) => p === "#" ? ON : OFF).join("")).join(GLYPH_GAP);
127
+ rows.push(row);
128
+ }
129
+ return rows;
130
+ }
131
+
132
+ // src/components/wordmark.tsx
133
+ import { jsx as jsx2 } from "react/jsx-runtime";
134
+ function Wordmark({ text }) {
135
+ const rows = renderWordmark(text);
136
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: rows.map((row, i) => /* @__PURE__ */ jsx2(Text2, { color: "white", children: row }, i)) });
137
+ }
138
+
139
+ // src/components/header.tsx
140
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
141
+ function Header({ wordmark, name, tagline, siteLabel, githubLabel, xLabel }) {
142
+ return /* @__PURE__ */ jsxs(Box3, { flexDirection: "column", alignItems: "center", marginTop: 1, children: [
143
+ /* @__PURE__ */ jsx3(Wordmark, { text: wordmark }),
144
+ /* @__PURE__ */ jsxs(Box3, { flexDirection: "column", alignItems: "center", marginTop: 1, children: [
145
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: name }),
146
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: tagline }),
147
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text3, { color: "gray", children: [
148
+ siteLabel,
149
+ " ",
150
+ githubLabel,
151
+ " ",
152
+ xLabel
153
+ ] }) })
154
+ ] })
155
+ ] });
156
+ }
157
+
158
+ // src/components/footer.tsx
159
+ import { Box as Box4, Text as Text4 } from "ink";
160
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
161
+ var HINTS = [
162
+ { key: "s", label: "site" },
163
+ { key: "g", label: "github" },
164
+ { key: "x", label: "x.com" },
165
+ { key: "q", label: "quit" }
166
+ ];
167
+ function Footer() {
168
+ return /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: HINTS.map((hint, i) => /* @__PURE__ */ jsxs2(Text4, { children: [
169
+ i > 0 ? " " : "",
170
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: hint.key }),
171
+ " ",
172
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", children: hint.label })
173
+ ] }, hint.key)) });
174
+ }
175
+
176
+ // src/hooks/use-keyboard-actions.ts
177
+ import { useApp, useInput } from "ink";
178
+
179
+ // src/lib/open-url.ts
180
+ import open from "open";
181
+ async function openUrl(url) {
182
+ try {
183
+ await open(url);
184
+ } catch {
185
+ }
186
+ }
187
+
188
+ // src/hooks/use-keyboard-actions.ts
189
+ function useKeyboardActions({ githubUrl, xUrl, siteUrl }) {
190
+ const { exit } = useApp();
191
+ useInput((input, key) => {
192
+ if (input === "g") void openUrl(githubUrl);
193
+ else if (input === "x") void openUrl(xUrl);
194
+ else if (input === "s") void openUrl(siteUrl);
195
+ else if (input === "q" || key.escape || key.ctrl && input === "c") exit();
196
+ });
197
+ }
198
+
199
+ // src/hooks/use-terminal-size.ts
200
+ import { useStdout } from "ink";
201
+ import { useEffect as useEffect2, useState as useState2 } from "react";
202
+ function useTerminalSize() {
203
+ const { stdout } = useStdout();
204
+ const [size, setSize] = useState2({
205
+ columns: stdout?.columns ?? 80,
206
+ rows: stdout?.rows ?? 24
207
+ });
208
+ useEffect2(() => {
209
+ if (!stdout) return;
210
+ const onResize = () => {
211
+ setSize({ columns: stdout.columns, rows: stdout.rows });
212
+ };
213
+ stdout.on("resize", onResize);
214
+ return () => {
215
+ stdout.off("resize", onResize);
216
+ };
217
+ }, [stdout]);
218
+ return size;
219
+ }
220
+
221
+ // src/app.tsx
222
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
223
+ var TEXT_BLOCK_ROWS = 11;
224
+ var FIXED_CHROME_ROWS = 4;
225
+ var MAX_WIDTH = 62;
226
+ function App() {
227
+ const { columns, rows } = useTerminalSize();
228
+ useKeyboardActions({
229
+ githubUrl: profile.githubUrl,
230
+ xUrl: profile.xUrl,
231
+ siteUrl: profile.siteUrl
232
+ });
233
+ const portraitRows = portraitFrames[0] ? portraitFrames[0].split("\n").length : 0;
234
+ const frameWidth = Math.min(MAX_WIDTH, columns);
235
+ const contentRows = portraitRows + TEXT_BLOCK_ROWS + FIXED_CHROME_ROWS;
236
+ const canCenterVertically = rows > contentRows;
237
+ return /* @__PURE__ */ jsx5(
238
+ Box5,
239
+ {
240
+ width: columns,
241
+ height: canCenterVertically ? rows : void 0,
242
+ flexDirection: "column",
243
+ alignItems: "center",
244
+ justifyContent: canCenterVertically ? "center" : "flex-start",
245
+ children: /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", width: frameWidth, paddingX: 1, paddingY: 1, alignItems: "center", children: [
246
+ /* @__PURE__ */ jsx5(Portrait, {}),
247
+ /* @__PURE__ */ jsx5(
248
+ Header,
249
+ {
250
+ wordmark: profile.wordmark,
251
+ name: profile.name,
252
+ tagline: profile.tagline,
253
+ siteLabel: profile.siteLabel,
254
+ githubLabel: profile.githubLabel,
255
+ xLabel: profile.xLabel
256
+ }
257
+ ),
258
+ /* @__PURE__ */ jsx5(Footer, {})
259
+ ] })
260
+ }
261
+ );
262
+ }
263
+
264
+ // src/cli.tsx
265
+ import { jsx as jsx6 } from "react/jsx-runtime";
266
+ var ENTER_ALT_SCREEN = "\x1B[?1049h\x1B[H";
267
+ var EXIT_ALT_SCREEN = "\x1B[?1049l";
268
+ process.stdout.write(ENTER_ALT_SCREEN);
269
+ var { waitUntilExit } = render(/* @__PURE__ */ jsx6(App, {}));
270
+ try {
271
+ await waitUntilExit();
272
+ } finally {
273
+ process.stdout.write(EXIT_ALT_SCREEN);
274
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "milstead",
3
+ "version": "0.1.0",
4
+ "description": "Michael Milstead's personal profile, as a terminal app. Run: npx milstead",
5
+ "type": "module",
6
+ "bin": {
7
+ "milst": "bin/milst.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "bin"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "start": "node bin/milst.js",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "portrait": "tsx scripts/generate-portrait.ts",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "cli",
27
+ "tui",
28
+ "ink",
29
+ "profile",
30
+ "npx"
31
+ ],
32
+ "author": "Michael Milstead",
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "ink": "^5.0.1",
36
+ "open": "^10.1.0",
37
+ "react": "^18.3.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/react": "^18.3.12",
41
+ "ink-testing-library": "^4.0.0",
42
+ "jimp": "^1.6.0",
43
+ "tsup": "^8.3.5",
44
+ "tsx": "^4.19.2",
45
+ "typescript": "^5.7.2",
46
+ "vitest": "^2.1.8"
47
+ }
48
+ }