@meebs/meeb 1.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/README.md +158 -0
- package/animals/alien.js +34 -0
- package/animals/axolotl.js +38 -0
- package/animals/bat.js +34 -0
- package/animals/bear.js +41 -0
- package/animals/bee.js +32 -0
- package/animals/bunny.js +45 -0
- package/animals/butterfly.js +36 -0
- package/animals/capybara.js +36 -0
- package/animals/cat.js +44 -0
- package/animals/chameleon.js +44 -0
- package/animals/chick.js +27 -0
- package/animals/cookie.js +30 -0
- package/animals/crab.js +32 -0
- package/animals/crocodile.js +32 -0
- package/animals/deer.js +35 -0
- package/animals/dino.js +37 -0
- package/animals/dog.js +44 -0
- package/animals/dragon.js +44 -0
- package/animals/duck.js +45 -0
- package/animals/eagle.js +37 -0
- package/animals/elephant.js +33 -0
- package/animals/firefly.js +37 -0
- package/animals/fish.js +32 -0
- package/animals/flamingo.js +36 -0
- package/animals/fox.js +43 -0
- package/animals/frog.js +46 -0
- package/animals/ghost.js +32 -0
- package/animals/giraffe.js +37 -0
- package/animals/gorilla.js +37 -0
- package/animals/hamster.js +35 -0
- package/animals/hedgehog.js +32 -0
- package/animals/hippo.js +34 -0
- package/animals/index.js +87 -0
- package/animals/jellyfish.js +34 -0
- package/animals/koala.js +34 -0
- package/animals/ladybug.js +34 -0
- package/animals/lion.js +36 -0
- package/animals/mantisshrimp.js +49 -0
- package/animals/monkey.js +35 -0
- package/animals/moth.js +41 -0
- package/animals/mouse.js +33 -0
- package/animals/narwhal.js +38 -0
- package/animals/octopus.js +31 -0
- package/animals/otter.js +43 -0
- package/animals/owl.js +38 -0
- package/animals/panda.js +42 -0
- package/animals/parrot.js +39 -0
- package/animals/peacock.js +42 -0
- package/animals/penguin.js +47 -0
- package/animals/penguin2.js +38 -0
- package/animals/pig.js +33 -0
- package/animals/raccoon.js +35 -0
- package/animals/redpanda.js +39 -0
- package/animals/robot.js +37 -0
- package/animals/scorpion.js +33 -0
- package/animals/seahorse.js +39 -0
- package/animals/shark.js +33 -0
- package/animals/shit.js +33 -0
- package/animals/sloth.js +34 -0
- package/animals/snail.js +34 -0
- package/animals/snake.js +36 -0
- package/animals/spider.js +33 -0
- package/animals/squid.js +33 -0
- package/animals/starfish.js +32 -0
- package/animals/stingray.js +41 -0
- package/animals/swan.js +34 -0
- package/animals/toucan.js +41 -0
- package/animals/turtle.js +35 -0
- package/animals/unicorn.js +40 -0
- package/animals/walrus.js +35 -0
- package/animals/whale.js +42 -0
- package/animals/wolf.js +36 -0
- package/animals/yak.js +36 -0
- package/animals/zebra.js +34 -0
- package/color.js +9 -0
- package/completions/_meeb +41 -0
- package/completions/meeb.bash +16 -0
- package/emoji.js +30 -0
- package/fortune.js +41 -0
- package/gif.js +435 -0
- package/index.js +915 -0
- package/info.js +80 -0
- package/package.json +39 -0
- package/png.js +88 -0
- package/svg.js +43 -0
- package/theme.js +80 -0
- package/tui.js +76 -0
- package/util.js +313 -0
package/info.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Fun facts about animals — used by --info flag
|
|
2
|
+
const facts = {
|
|
3
|
+
penguin: "Emperor penguins can dive over 1,800 feet deep and hold their breath for 20+ minutes.",
|
|
4
|
+
cat: "Cats spend 70% of their lives sleeping. Living the dream.",
|
|
5
|
+
dog: "A dog's nose print is as unique as a human fingerprint.",
|
|
6
|
+
fox: "Arctic foxes can survive temperatures as low as -58°F (-50°C).",
|
|
7
|
+
bear: "Polar bears have black skin under their white fur to absorb heat.",
|
|
8
|
+
owl: "Owls can rotate their heads 270° but can't move their eyeballs.",
|
|
9
|
+
bunny: "Rabbits can't vomit. Once it's eaten, it's committed.",
|
|
10
|
+
frog: "The golden poison frog has enough toxin to kill 10 grown adults.",
|
|
11
|
+
panda: "Pandas spend up to 14 hours a day eating bamboo. Relatable.",
|
|
12
|
+
snake: "Snakes smell with their tongues and can feel vibrations through their jaw bones.",
|
|
13
|
+
duck: "Ducks have three eyelids and can sleep with one eye open.",
|
|
14
|
+
mouse: "A mouse's heart beats about 632 times per minute.",
|
|
15
|
+
whale: "Blue whale hearts are so big a small child could crawl through the arteries.",
|
|
16
|
+
octopus: "Octopuses have three hearts, blue blood, and can edit their own RNA.",
|
|
17
|
+
bat: "Bats are the only mammals that can truly fly. Not just glide — fly.",
|
|
18
|
+
fish: "Goldfish can distinguish between Bach and Stravinsky. Cultured.",
|
|
19
|
+
turtle: "Some turtles can breathe through their butts. It's called cloacal respiration.",
|
|
20
|
+
crab: "Japanese spider crabs can have a leg span of 12 feet.",
|
|
21
|
+
monkey: "Capuchin monkeys wash their hands and feet in their own urine as perfume.",
|
|
22
|
+
pig: "Pigs can play video games with joysticks. Seriously.",
|
|
23
|
+
koala: "Koalas sleep 22 hours a day and have fingerprints nearly identical to humans.",
|
|
24
|
+
lion: "A lion's roar can be heard from 5 miles away.",
|
|
25
|
+
shark: "Sharks have been around longer than trees. 400 million years.",
|
|
26
|
+
elephant: "Elephants are the only animals that can't jump. But they can paint.",
|
|
27
|
+
unicorn: "The national animal of Scotland is the unicorn. Officially.",
|
|
28
|
+
jellyfish: "The immortal jellyfish (Turritopsis dohrnii) can revert to its juvenile state indefinitely.",
|
|
29
|
+
sloth: "Sloths can hold their breath longer than dolphins — up to 40 minutes.",
|
|
30
|
+
wolf: "Wolves can eat up to 20 pounds of meat in a single meal.",
|
|
31
|
+
parrot: "African grey parrots can learn over 1,000 words and understand context.",
|
|
32
|
+
hedgehog: "Hedgehogs are immune to many snake venoms and poisons.",
|
|
33
|
+
giraffe: "Giraffes only need 30 minutes of sleep per day, in 5-minute naps.",
|
|
34
|
+
flamingo: "Flamingos are born grey/white and turn pink from eating shrimp.",
|
|
35
|
+
raccoon: "Raccoons can remember solutions to tasks for up to 3 years.",
|
|
36
|
+
hippo: "Hippos secrete a red oily substance that acts as sunscreen and antibiotic.",
|
|
37
|
+
deer: "Reindeer eyes change color — from gold in summer to blue in winter.",
|
|
38
|
+
squid: "Giant squid have the largest eyes in the animal kingdom — the size of dinner plates.",
|
|
39
|
+
gorilla: "Gorillas share 98.3% of their DNA with humans and can learn sign language.",
|
|
40
|
+
dragon: "Komodo dragons have venom glands and can detect prey from 6 miles away.",
|
|
41
|
+
alien: "The Wow! signal was a 72-second radio burst from deep space. Still unexplained.",
|
|
42
|
+
robot: "The word 'robot' comes from Czech 'robota' meaning forced labor.",
|
|
43
|
+
ghost: "The fear of ghosts is called phasmophobia. The game knew.",
|
|
44
|
+
dino: "T-Rex had the strongest bite force of any land animal ever — 12,800 pounds.",
|
|
45
|
+
zebra: "No two zebras have the same stripe pattern. They're nature's fingerprints.",
|
|
46
|
+
snail: "Garden snails have about 14,000 teeth, all on their tongue.",
|
|
47
|
+
bee: "A single bee produces only 1/12th of a teaspoon of honey in its lifetime.",
|
|
48
|
+
butterfly: "Butterflies taste with their feet and can see ultraviolet light.",
|
|
49
|
+
scorpion: "Scorpions glow under UV light. Nobody knows why.",
|
|
50
|
+
crocodile: "Crocodiles have the strongest bite in the animal kingdom — 3,700 PSI.",
|
|
51
|
+
chameleon: "Chameleons don't change color for camouflage — they do it to communicate mood.",
|
|
52
|
+
seahorse: "Male seahorses get pregnant and give birth. Progressive kings.",
|
|
53
|
+
otter: "Sea otters hold hands while sleeping so they don't drift apart.",
|
|
54
|
+
mantisshrimp: "Mantis shrimp can punch at 50 mph — fast enough to boil the water around their fists.",
|
|
55
|
+
narwhal: "The narwhal's tusk is actually a giant tooth with 10 million nerve endings.",
|
|
56
|
+
peacock: "Peacock tail feathers can be 6 feet long — 60% of their total body length.",
|
|
57
|
+
spider: "Spider silk is 5x stronger than steel by weight.",
|
|
58
|
+
chick: "Chicks can communicate with each other while still inside their eggs.",
|
|
59
|
+
ladybug: "A ladybug can eat up to 5,000 aphids in its lifetime. Pest control legends.",
|
|
60
|
+
capybara: "Capybaras are the world's largest rodent and literally everyone likes them.",
|
|
61
|
+
axolotl: "Axolotls can regenerate their brain, heart, and limbs. Wolverine of the sea.",
|
|
62
|
+
hamster: "Hamsters can run up to 5 miles on their wheel in a single night.",
|
|
63
|
+
swan: "Swans mate for life and can remember people who've been kind — or mean — to them.",
|
|
64
|
+
eagle: "Bald eagles can see fish from over a mile away.",
|
|
65
|
+
walrus: "Walruses can slow their heartbeat to withstand frigid Arctic waters.",
|
|
66
|
+
toucan: "Toucans regulate body temperature through their enormous beaks.",
|
|
67
|
+
yak: "Yaks can survive at altitudes above 20,000 feet where most animals can't breathe.",
|
|
68
|
+
firefly: "Firefly light is the most efficient in the world — nearly 100% of the energy becomes light.",
|
|
69
|
+
cookie: "The chocolate chip cookie was invented by accident in 1938.",
|
|
70
|
+
starfish: "Starfish don't have blood — they pump filtered sea water through their bodies.",
|
|
71
|
+
caterpillar: "Caterpillars have 12 eyes but still have terrible vision.",
|
|
72
|
+
manta: "Manta rays have the largest brain-to-body ratio of any fish.",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getInfo(animalName) {
|
|
76
|
+
const key = animalName.toLowerCase().replace(/[^a-z]/g, '')
|
|
77
|
+
return facts[key] || `No fun fact for ${animalName} yet. Maybe submit one?`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { getInfo, facts }
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meebs/meeb",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Cute ASCII art animals for your terminal",
|
|
5
|
+
"bin": {
|
|
6
|
+
"meeb": "./index.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"cli",
|
|
10
|
+
"ascii-art",
|
|
11
|
+
"terminal",
|
|
12
|
+
"animals",
|
|
13
|
+
"cowsay",
|
|
14
|
+
"unicode",
|
|
15
|
+
"pixel-art",
|
|
16
|
+
"fun"
|
|
17
|
+
],
|
|
18
|
+
"license": "CC-BY-NC-4.0",
|
|
19
|
+
"author": "meeb",
|
|
20
|
+
"homepage": "https://meeb.sh",
|
|
21
|
+
"files": [
|
|
22
|
+
"index.js",
|
|
23
|
+
"color.js",
|
|
24
|
+
"emoji.js",
|
|
25
|
+
"fortune.js",
|
|
26
|
+
"gif.js",
|
|
27
|
+
"png.js",
|
|
28
|
+
"svg.js",
|
|
29
|
+
"theme.js",
|
|
30
|
+
"tui.js",
|
|
31
|
+
"util.js",
|
|
32
|
+
"info.js",
|
|
33
|
+
"animals/",
|
|
34
|
+
"completions/"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=14"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/png.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const zlib = require('zlib')
|
|
3
|
+
const { artToPixels, applyWhiteBg, correctAspect } = require('./gif')
|
|
4
|
+
|
|
5
|
+
// CRC32 lookup table
|
|
6
|
+
const crcTable = new Uint32Array(256)
|
|
7
|
+
for (let n = 0; n < 256; n++) {
|
|
8
|
+
let c = n
|
|
9
|
+
for (let k = 0; k < 8; k++) {
|
|
10
|
+
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1)
|
|
11
|
+
}
|
|
12
|
+
crcTable[n] = c
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function crc32(buf) {
|
|
16
|
+
let crc = 0xffffffff
|
|
17
|
+
for (let i = 0; i < buf.length; i++) {
|
|
18
|
+
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8)
|
|
19
|
+
}
|
|
20
|
+
return (crc ^ 0xffffffff) >>> 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeChunk(type, data) {
|
|
24
|
+
const typeBytes = Buffer.from(type)
|
|
25
|
+
const len = Buffer.alloc(4)
|
|
26
|
+
len.writeUInt32BE(data.length)
|
|
27
|
+
const body = Buffer.concat([typeBytes, data])
|
|
28
|
+
const checksum = Buffer.alloc(4)
|
|
29
|
+
checksum.writeUInt32BE(crc32(body))
|
|
30
|
+
return Buffer.concat([len, body, checksum])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function generatePng(art, outPath, bgColor) {
|
|
34
|
+
const bg = bgColor || [25, 25, 30]
|
|
35
|
+
let { pixels, w, h } = artToPixels(art, bg)
|
|
36
|
+
applyWhiteBg(pixels, w, h, bg)
|
|
37
|
+
const corrected = correctAspect(pixels, w, h)
|
|
38
|
+
const px = corrected.pixels
|
|
39
|
+
const pw = corrected.w, ph = corrected.h
|
|
40
|
+
if (pw === 0 || ph === 0) {
|
|
41
|
+
console.error(' empty art, cannot generate png')
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// PNG signature
|
|
46
|
+
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
|
|
47
|
+
|
|
48
|
+
// IHDR
|
|
49
|
+
const ihdr = Buffer.alloc(13)
|
|
50
|
+
ihdr.writeUInt32BE(pw, 0)
|
|
51
|
+
ihdr.writeUInt32BE(ph, 4)
|
|
52
|
+
ihdr[8] = 8 // bit depth
|
|
53
|
+
ihdr[9] = 2 // color type: RGB
|
|
54
|
+
ihdr[10] = 0 // compression
|
|
55
|
+
ihdr[11] = 0 // filter
|
|
56
|
+
ihdr[12] = 0 // interlace
|
|
57
|
+
|
|
58
|
+
// IDAT - raw pixel data with filter bytes
|
|
59
|
+
const rawSize = ph * (1 + pw * 3)
|
|
60
|
+
const raw = Buffer.alloc(rawSize)
|
|
61
|
+
let offset = 0
|
|
62
|
+
for (let y = 0; y < ph; y++) {
|
|
63
|
+
raw[offset++] = 0 // filter: none
|
|
64
|
+
for (let x = 0; x < pw; x++) {
|
|
65
|
+
const [r, g, b] = px[y][x]
|
|
66
|
+
raw[offset++] = r
|
|
67
|
+
raw[offset++] = g
|
|
68
|
+
raw[offset++] = b
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const compressed = zlib.deflateSync(raw, { level: 6 })
|
|
73
|
+
|
|
74
|
+
// IEND
|
|
75
|
+
const iend = Buffer.alloc(0)
|
|
76
|
+
|
|
77
|
+
const png = Buffer.concat([
|
|
78
|
+
sig,
|
|
79
|
+
makeChunk('IHDR', ihdr),
|
|
80
|
+
makeChunk('IDAT', compressed),
|
|
81
|
+
makeChunk('IEND', iend),
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(outPath, png)
|
|
85
|
+
return outPath
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { generatePng }
|
package/svg.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const { artToPixels, applyWhiteBg, correctAspect, CELL_W, CELL_H } = require('./gif')
|
|
3
|
+
|
|
4
|
+
function generateSvg(art, outPath, bgColor) {
|
|
5
|
+
const bg = bgColor || [25, 25, 30]
|
|
6
|
+
let { pixels, w, h } = artToPixels(art, bg)
|
|
7
|
+
applyWhiteBg(pixels, w, h, bg)
|
|
8
|
+
const corrected = correctAspect(pixels, w, h)
|
|
9
|
+
const px = corrected.pixels
|
|
10
|
+
const pw = corrected.w, ph = corrected.h
|
|
11
|
+
if (pw === 0 || ph === 0) {
|
|
12
|
+
console.error(' empty art, cannot generate svg')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const lines = []
|
|
17
|
+
lines.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pw} ${ph}" width="${pw}" height="${ph}" shape-rendering="crispEdges">`)
|
|
18
|
+
lines.push(`<rect width="${pw}" height="${ph}" fill="rgb(${bg[0]},${bg[1]},${bg[2]})"/>`)
|
|
19
|
+
|
|
20
|
+
for (let y = 0; y < ph; y++) {
|
|
21
|
+
let x = 0
|
|
22
|
+
while (x < pw) {
|
|
23
|
+
const [r, g, b] = px[y][x]
|
|
24
|
+
if (r === bg[0] && g === bg[1] && b === bg[2]) { x++; continue }
|
|
25
|
+
let runEnd = x + 1
|
|
26
|
+
while (runEnd < pw) {
|
|
27
|
+
const [nr, ng, nb] = px[y][runEnd]
|
|
28
|
+
if (nr !== r || ng !== g || nb !== b) break
|
|
29
|
+
runEnd++
|
|
30
|
+
}
|
|
31
|
+
lines.push(`<rect x="${x}" y="${y}" width="${runEnd - x}" height="1" fill="rgb(${r},${g},${b})"/>`)
|
|
32
|
+
x = runEnd
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
lines.push('</svg>')
|
|
37
|
+
|
|
38
|
+
const svg = lines.join('\n')
|
|
39
|
+
fs.writeFileSync(outPath, svg)
|
|
40
|
+
return outPath
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { generateSvg }
|
package/theme.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Color themes for --theme flag
|
|
2
|
+
// Each theme is a function that transforms rgb values
|
|
3
|
+
|
|
4
|
+
const themes = {
|
|
5
|
+
neon: (r, g, b) => {
|
|
6
|
+
// boost saturation and brightness, shift toward vivid neon
|
|
7
|
+
const max = Math.max(r, g, b)
|
|
8
|
+
const min = Math.min(r, g, b)
|
|
9
|
+
if (max - min < 30) return [r, g, b] // leave grays alone
|
|
10
|
+
// amplify the dominant channel
|
|
11
|
+
const nr = r === max ? Math.min(255, r + 80) : Math.max(0, r - 40)
|
|
12
|
+
const ng = g === max ? Math.min(255, g + 80) : Math.max(0, g - 40)
|
|
13
|
+
const nb = b === max ? Math.min(255, b + 80) : Math.max(0, b - 40)
|
|
14
|
+
return [nr, ng, nb]
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
pastel: (r, g, b) => {
|
|
18
|
+
// blend toward white for soft pastel look
|
|
19
|
+
return [
|
|
20
|
+
Math.round(r * 0.5 + 200 * 0.5),
|
|
21
|
+
Math.round(g * 0.5 + 200 * 0.5),
|
|
22
|
+
Math.round(b * 0.5 + 200 * 0.5),
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
retro: (r, g, b) => {
|
|
27
|
+
// warm sepia-like tones
|
|
28
|
+
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b)
|
|
29
|
+
return [
|
|
30
|
+
Math.min(255, Math.round(gray * 1.2 + 20)),
|
|
31
|
+
Math.min(255, Math.round(gray * 0.9 + 10)),
|
|
32
|
+
Math.round(gray * 0.6),
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
mono: (r, g, b) => {
|
|
37
|
+
// grayscale
|
|
38
|
+
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b)
|
|
39
|
+
return [gray, gray, gray]
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
matrix: (r, g, b) => {
|
|
43
|
+
// green terminal look
|
|
44
|
+
const lum = Math.round(0.299 * r + 0.587 * g + 0.114 * b)
|
|
45
|
+
return [0, Math.min(255, lum + 30), 0]
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const themeNames = Object.keys(themes)
|
|
50
|
+
|
|
51
|
+
// Apply a theme to an ANSI art string by transforming color codes
|
|
52
|
+
function applyTheme(art, themeName) {
|
|
53
|
+
const transform = themes[themeName]
|
|
54
|
+
if (!transform) return art
|
|
55
|
+
|
|
56
|
+
return art.replace(/\x1b\[([0-9;]*)m/g, (match, codes) => {
|
|
57
|
+
const parts = codes.split(';').map(Number)
|
|
58
|
+
const newParts = []
|
|
59
|
+
let i = 0
|
|
60
|
+
while (i < parts.length) {
|
|
61
|
+
if (parts[i] === 38 && parts[i + 1] === 2 && i + 4 < parts.length) {
|
|
62
|
+
// foreground color
|
|
63
|
+
const [nr, ng, nb] = transform(parts[i + 2], parts[i + 3], parts[i + 4])
|
|
64
|
+
newParts.push(38, 2, nr, ng, nb)
|
|
65
|
+
i += 5
|
|
66
|
+
} else if (parts[i] === 48 && parts[i + 1] === 2 && i + 4 < parts.length) {
|
|
67
|
+
// background color
|
|
68
|
+
const [nr, ng, nb] = transform(parts[i + 2], parts[i + 3], parts[i + 4])
|
|
69
|
+
newParts.push(48, 2, nr, ng, nb)
|
|
70
|
+
i += 5
|
|
71
|
+
} else {
|
|
72
|
+
newParts.push(parts[i])
|
|
73
|
+
i++
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return `\x1b[${newParts.join(';')}m`
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { applyTheme, themeNames }
|
package/tui.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { rgb } = require('./color')
|
|
2
|
+
|
|
3
|
+
function browse(animals) {
|
|
4
|
+
const keys = Object.keys(animals)
|
|
5
|
+
let idx = 0
|
|
6
|
+
const stdin = process.stdin
|
|
7
|
+
const stdout = process.stdout
|
|
8
|
+
|
|
9
|
+
if (!stdin.isTTY) {
|
|
10
|
+
console.error(' --browse requires an interactive terminal')
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
stdin.setRawMode(true)
|
|
14
|
+
stdin.resume()
|
|
15
|
+
stdin.setEncoding('utf8')
|
|
16
|
+
|
|
17
|
+
const hide = '\x1b[?25l'
|
|
18
|
+
const show = '\x1b[?25h'
|
|
19
|
+
const clear = '\x1b[2J\x1b[H'
|
|
20
|
+
|
|
21
|
+
function render() {
|
|
22
|
+
const animal = animals[keys[idx]]
|
|
23
|
+
const art = animal.render()
|
|
24
|
+
const lines = []
|
|
25
|
+
lines.push('')
|
|
26
|
+
lines.push(rgb(215, 119, 87, ` ── ${animal.name} ──`) + rgb(120, 120, 120, ` (${idx + 1}/${keys.length})`))
|
|
27
|
+
lines.push(art)
|
|
28
|
+
lines.push('')
|
|
29
|
+
lines.push(rgb(120, 120, 120, ' ← → navigate q quit r random'))
|
|
30
|
+
lines.push('')
|
|
31
|
+
stdout.write(clear + hide + lines.join('\n'))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cleanup() {
|
|
35
|
+
stdout.write(show + clear)
|
|
36
|
+
stdin.setRawMode(false)
|
|
37
|
+
stdin.pause()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render()
|
|
41
|
+
|
|
42
|
+
stdin.on('data', (key) => {
|
|
43
|
+
if (key === 'q' || key === '\x03') {
|
|
44
|
+
// q or ctrl-c
|
|
45
|
+
cleanup()
|
|
46
|
+
process.exit(0)
|
|
47
|
+
} else if (key === '\x1b[C' || key === 'l' || key === ' ') {
|
|
48
|
+
// right arrow, l, space
|
|
49
|
+
idx = (idx + 1) % keys.length
|
|
50
|
+
render()
|
|
51
|
+
} else if (key === '\x1b[D' || key === 'h') {
|
|
52
|
+
// left arrow, h
|
|
53
|
+
idx = (idx - 1 + keys.length) % keys.length
|
|
54
|
+
render()
|
|
55
|
+
} else if (key === '\x1b[A' || key === 'k') {
|
|
56
|
+
// up arrow, k — jump back 10
|
|
57
|
+
idx = (idx - 10 + keys.length) % keys.length
|
|
58
|
+
render()
|
|
59
|
+
} else if (key === '\x1b[B' || key === 'j') {
|
|
60
|
+
// down arrow, j — jump forward 10
|
|
61
|
+
idx = (idx + 10) % keys.length
|
|
62
|
+
render()
|
|
63
|
+
} else if (key === 'r') {
|
|
64
|
+
idx = Math.floor(Math.random() * keys.length)
|
|
65
|
+
render()
|
|
66
|
+
} else if (key === 'g') {
|
|
67
|
+
idx = 0
|
|
68
|
+
render()
|
|
69
|
+
} else if (key === 'G') {
|
|
70
|
+
idx = keys.length - 1
|
|
71
|
+
render()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = { browse }
|