@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.
Files changed (89) hide show
  1. package/README.md +158 -0
  2. package/animals/alien.js +34 -0
  3. package/animals/axolotl.js +38 -0
  4. package/animals/bat.js +34 -0
  5. package/animals/bear.js +41 -0
  6. package/animals/bee.js +32 -0
  7. package/animals/bunny.js +45 -0
  8. package/animals/butterfly.js +36 -0
  9. package/animals/capybara.js +36 -0
  10. package/animals/cat.js +44 -0
  11. package/animals/chameleon.js +44 -0
  12. package/animals/chick.js +27 -0
  13. package/animals/cookie.js +30 -0
  14. package/animals/crab.js +32 -0
  15. package/animals/crocodile.js +32 -0
  16. package/animals/deer.js +35 -0
  17. package/animals/dino.js +37 -0
  18. package/animals/dog.js +44 -0
  19. package/animals/dragon.js +44 -0
  20. package/animals/duck.js +45 -0
  21. package/animals/eagle.js +37 -0
  22. package/animals/elephant.js +33 -0
  23. package/animals/firefly.js +37 -0
  24. package/animals/fish.js +32 -0
  25. package/animals/flamingo.js +36 -0
  26. package/animals/fox.js +43 -0
  27. package/animals/frog.js +46 -0
  28. package/animals/ghost.js +32 -0
  29. package/animals/giraffe.js +37 -0
  30. package/animals/gorilla.js +37 -0
  31. package/animals/hamster.js +35 -0
  32. package/animals/hedgehog.js +32 -0
  33. package/animals/hippo.js +34 -0
  34. package/animals/index.js +87 -0
  35. package/animals/jellyfish.js +34 -0
  36. package/animals/koala.js +34 -0
  37. package/animals/ladybug.js +34 -0
  38. package/animals/lion.js +36 -0
  39. package/animals/mantisshrimp.js +49 -0
  40. package/animals/monkey.js +35 -0
  41. package/animals/moth.js +41 -0
  42. package/animals/mouse.js +33 -0
  43. package/animals/narwhal.js +38 -0
  44. package/animals/octopus.js +31 -0
  45. package/animals/otter.js +43 -0
  46. package/animals/owl.js +38 -0
  47. package/animals/panda.js +42 -0
  48. package/animals/parrot.js +39 -0
  49. package/animals/peacock.js +42 -0
  50. package/animals/penguin.js +47 -0
  51. package/animals/penguin2.js +38 -0
  52. package/animals/pig.js +33 -0
  53. package/animals/raccoon.js +35 -0
  54. package/animals/redpanda.js +39 -0
  55. package/animals/robot.js +37 -0
  56. package/animals/scorpion.js +33 -0
  57. package/animals/seahorse.js +39 -0
  58. package/animals/shark.js +33 -0
  59. package/animals/shit.js +33 -0
  60. package/animals/sloth.js +34 -0
  61. package/animals/snail.js +34 -0
  62. package/animals/snake.js +36 -0
  63. package/animals/spider.js +33 -0
  64. package/animals/squid.js +33 -0
  65. package/animals/starfish.js +32 -0
  66. package/animals/stingray.js +41 -0
  67. package/animals/swan.js +34 -0
  68. package/animals/toucan.js +41 -0
  69. package/animals/turtle.js +35 -0
  70. package/animals/unicorn.js +40 -0
  71. package/animals/walrus.js +35 -0
  72. package/animals/whale.js +42 -0
  73. package/animals/wolf.js +36 -0
  74. package/animals/yak.js +36 -0
  75. package/animals/zebra.js +34 -0
  76. package/color.js +9 -0
  77. package/completions/_meeb +41 -0
  78. package/completions/meeb.bash +16 -0
  79. package/emoji.js +30 -0
  80. package/fortune.js +41 -0
  81. package/gif.js +435 -0
  82. package/index.js +915 -0
  83. package/info.js +80 -0
  84. package/package.json +39 -0
  85. package/png.js +88 -0
  86. package/svg.js +43 -0
  87. package/theme.js +80 -0
  88. package/tui.js +76 -0
  89. package/util.js +313 -0
package/util.js ADDED
@@ -0,0 +1,313 @@
1
+ const { rgb, fgBg } = require('./color')
2
+
3
+ const ANSI_RE = /\x1b\[[0-9;]*m/g
4
+
5
+ function stripAnsi(str) {
6
+ return str.replace(ANSI_RE, '')
7
+ }
8
+
9
+ function visLen(str) {
10
+ return stripAnsi(str).length
11
+ }
12
+
13
+ function wrapText(text, maxWidth) {
14
+ const words = text.replace(/\n/g, ' ').split(/\s+/).filter(Boolean)
15
+ const lines = []
16
+ let current = ''
17
+ for (const word of words) {
18
+ if (current && (current.length + 1 + word.length) > maxWidth) {
19
+ lines.push(current)
20
+ current = word
21
+ } else {
22
+ current = current ? current + ' ' + word : word
23
+ }
24
+ }
25
+ if (current) lines.push(current)
26
+ return lines.length ? lines : ['']
27
+ }
28
+
29
+ function addSay(art, msg) {
30
+ const artLines = art.split('\n')
31
+ const artWidth = Math.max(...artLines.map(l => visLen(l)))
32
+ const maxBubble = 60
33
+ const msgLines = wrapText(msg, maxBubble)
34
+ const innerWidth = Math.max(...msgLines.map(l => l.length), 8)
35
+ const bubbleWidth = innerWidth + 2
36
+ const pad = Math.max(0, Math.floor((artWidth - bubbleWidth - 2) / 2))
37
+ const sp = ' '.repeat(pad)
38
+
39
+ const top = `${sp} ${'_'.repeat(bubbleWidth)}`
40
+ const mid = msgLines.map(l =>
41
+ `${sp}| ${l}${' '.repeat(innerWidth - l.length)} |`
42
+ )
43
+ if (mid.length === 1) {
44
+ mid[0] = `${sp}< ${msgLines[0]}${' '.repeat(innerWidth - msgLines[0].length)} >`
45
+ }
46
+ const bot = `${sp} ${'‾'.repeat(bubbleWidth)}`
47
+ const ptr = `${sp}${' '.repeat(Math.floor(bubbleWidth / 2))}${mid.length === 1 ? '|' : '╱'}`
48
+
49
+ return [top, ...mid, bot, ptr, ...artLines].join('\n')
50
+ }
51
+
52
+ function addHat(art) {
53
+ const month = new Date().getMonth() // 0-indexed
54
+ const lines = art.split('\n')
55
+
56
+ // find the first non-empty line
57
+ let firstIdx = lines.findIndex(l => l.trim().length > 0)
58
+ if (firstIdx < 0) firstIdx = 0
59
+ const firstLine = lines[firstIdx]
60
+ const contentStart = visLen(firstLine) - visLen(firstLine.trimStart())
61
+
62
+ let hat
63
+ if (month === 11 || month === 0) {
64
+ // Dec-Jan: santa hat
65
+ hat = [
66
+ ' '.repeat(contentStart) + rgb(220, 50, 40, ' ▄') + rgb(240, 240, 240, '●'),
67
+ ' '.repeat(contentStart) + rgb(220, 50, 40, ' ▄██▀'),
68
+ ' '.repeat(contentStart) + rgb(240, 240, 240, '▀████▀'),
69
+ ]
70
+ } else if (month === 1) {
71
+ // Feb: heart
72
+ hat = [
73
+ ' '.repeat(contentStart) + rgb(220, 50, 80, ' ▄▀▄ ▄▀▄'),
74
+ ' '.repeat(contentStart) + rgb(220, 50, 80, ' ▀▄███▄▀'),
75
+ ' '.repeat(contentStart) + rgb(220, 50, 80, ' ▀█▀'),
76
+ ]
77
+ } else if (month === 2) {
78
+ // Mar: shamrock
79
+ hat = [
80
+ ' '.repeat(contentStart) + rgb(40, 180, 60, ' ▄█▄█▄'),
81
+ ' '.repeat(contentStart) + rgb(40, 180, 60, ' ▀███▀'),
82
+ ' '.repeat(contentStart) + rgb(40, 180, 60, ' █'),
83
+ ]
84
+ } else if (month === 3) {
85
+ // Apr: rain cloud
86
+ hat = [
87
+ ' '.repeat(contentStart) + rgb(180, 180, 190, ' ▄███▄'),
88
+ ' '.repeat(contentStart) + rgb(180, 180, 190, ' ▀█████▀'),
89
+ ' '.repeat(contentStart) + rgb(100, 160, 240, ' ░ ░ ░'),
90
+ ]
91
+ } else if (month === 4) {
92
+ // May: flower
93
+ hat = [
94
+ ' '.repeat(contentStart) + rgb(240, 100, 150, ' ▄█▄'),
95
+ ' '.repeat(contentStart) + rgb(240, 100, 150, ' █') + rgb(240, 220, 60, '▀█▀') + rgb(240, 100, 150, '█'),
96
+ ' '.repeat(contentStart) + rgb(240, 100, 150, ' ▀█▀'),
97
+ ]
98
+ } else if (month === 5) {
99
+ // Jun: sun
100
+ hat = [
101
+ ' '.repeat(contentStart) + rgb(240, 200, 50, ' ╲ ▄▄ ╱'),
102
+ ' '.repeat(contentStart) + rgb(240, 200, 50, ' ─████─'),
103
+ ' '.repeat(contentStart) + rgb(240, 200, 50, ' ╱ ▀▀ ╲'),
104
+ ]
105
+ } else if (month === 6) {
106
+ // Jul: firework
107
+ hat = [
108
+ ' '.repeat(contentStart) + rgb(220, 50, 40, ' ·') + rgb(240, 240, 240, '✦') + rgb(50, 80, 220, '· '),
109
+ ' '.repeat(contentStart) + rgb(50, 80, 220, ' · ') + rgb(220, 50, 40, '✦') + rgb(240, 240, 240, ' ·'),
110
+ ' '.repeat(contentStart) + rgb(240, 240, 240, ' · ') + rgb(50, 80, 220, '·'),
111
+ ]
112
+ } else if (month === 7) {
113
+ // Aug: sunglasses
114
+ hat = [
115
+ ' '.repeat(contentStart) + rgb(30, 30, 30, ' ▄████████▄'),
116
+ ' '.repeat(contentStart) + rgb(30, 30, 30, ' █▀▀██▀▀██▀'),
117
+ ]
118
+ } else if (month === 8) {
119
+ // Sep: falling leaf
120
+ hat = [
121
+ ' '.repeat(contentStart) + rgb(220, 140, 40, ' ▄▖'),
122
+ ' '.repeat(contentStart) + rgb(220, 140, 40, ' ▄██▘'),
123
+ ' '.repeat(contentStart) + rgb(220, 140, 40, ' ▀▀'),
124
+ ]
125
+ } else if (month === 9) {
126
+ // Oct: jack-o-lantern
127
+ hat = [
128
+ ' '.repeat(contentStart) + rgb(40, 140, 40, ' █'),
129
+ ' '.repeat(contentStart) + rgb(230, 150, 30, ' ▄███▄'),
130
+ ' '.repeat(contentStart) + rgb(230, 150, 30, ' █') + rgb(30, 30, 30, '▀█▀') + rgb(230, 150, 30, '█'),
131
+ ' '.repeat(contentStart) + rgb(230, 150, 30, ' ▀███▀'),
132
+ ]
133
+ } else if (month === 10) {
134
+ // Nov: pilgrim hat
135
+ hat = [
136
+ ' '.repeat(contentStart) + rgb(40, 40, 40, ' ▄██▄'),
137
+ ' '.repeat(contentStart) + rgb(40, 40, 40, ' ▐') + rgb(220, 180, 50, '█') + rgb(40, 40, 40, '██') + rgb(220, 180, 50, '█') + rgb(40, 40, 40, '▌'),
138
+ ' '.repeat(contentStart) + rgb(40, 40, 40, ' ▀██████▀'),
139
+ ]
140
+ }
141
+
142
+ if (hat) {
143
+ lines.splice(firstIdx, 0, ...hat)
144
+ }
145
+ return lines.join('\n')
146
+ }
147
+
148
+ const FLIP_MAP = {
149
+ '▐': '▌', '▌': '▐', '▛': '▜', '▜': '▛', '▙': '▟', '▟': '▙',
150
+ '▗': '▖', '▖': '▗', '▝': '▘', '▘': '▝', '▄': '▄', '▀': '▀',
151
+ '█': '█', '░': '░', '▓': '▓', '◉': '◉', '●': '●', '◆': '◆',
152
+ '▼': '▼', '▽': '▽', '▶': '◀', '◀': '▶', '▬': '▬',
153
+ '═': '═', '─': '─', '╲': '╱', '╱': '╲',
154
+ '▀▄': '▄▀', '(': ')', ')': '(',
155
+ '/': '\\', '\\': '/', '<': '>', '>': '<',
156
+ }
157
+
158
+ function flipLine(line) {
159
+ // Parse into styled characters, tracking cumulative ANSI state
160
+ const cells = [] // { style: string, char: string }
161
+ let currentStyle = '' // accumulated ANSI codes that define current style
162
+ let i = 0
163
+ while (i < line.length) {
164
+ if (line[i] === '\x1b' && i + 1 < line.length && line[i + 1] === '[') {
165
+ let j = i + 2
166
+ while (j < line.length && line[j] !== 'm') j++
167
+ const code = line.slice(i, j + 1)
168
+ // reset clears accumulated style, otherwise append
169
+ if (code === '\x1b[0m') {
170
+ currentStyle = ''
171
+ } else {
172
+ currentStyle += code
173
+ }
174
+ i = j + 1
175
+ } else {
176
+ cells.push({ style: currentStyle, char: line[i] })
177
+ i++
178
+ }
179
+ }
180
+
181
+ // Reverse cell order, flip characters, and re-emit styles
182
+ cells.reverse()
183
+ const reset = '\x1b[0m'
184
+ let result = ''
185
+ let lastStyle = null
186
+ for (const cell of cells) {
187
+ if (cell.style !== lastStyle) {
188
+ result += reset + cell.style
189
+ lastStyle = cell.style
190
+ }
191
+ result += FLIP_MAP[cell.char] || cell.char
192
+ }
193
+ result += reset
194
+ return result
195
+ }
196
+
197
+ function flipArt(art) {
198
+ const lines = art.split('\n')
199
+ // find max visible width for padding
200
+ const maxW = Math.max(...lines.map(l => visLen(l)))
201
+ return lines.map(line => {
202
+ const flipped = flipLine(line)
203
+ // pad to right-align (since flip reverses alignment)
204
+ const pad = maxW - visLen(flipped)
205
+ return ' '.repeat(Math.max(0, pad)) + flipped
206
+ }).join('\n')
207
+ }
208
+
209
+ function sideBySide(art1, art2, gap = 4) {
210
+ const lines1 = art1.split('\n')
211
+ const lines2 = art2.split('\n')
212
+ const maxLines = Math.max(lines1.length, lines2.length)
213
+ const maxWidth1 = Math.max(...lines1.map(l => visLen(l)), 0)
214
+ const spacer = ' '.repeat(gap)
215
+ const result = []
216
+
217
+ for (let i = 0; i < maxLines; i++) {
218
+ const l1 = lines1[i] || ''
219
+ const l2 = lines2[i] || ''
220
+ const pad = maxWidth1 - visLen(l1)
221
+ result.push(l1 + ' '.repeat(Math.max(0, pad)) + spacer + l2)
222
+ }
223
+ return result.join('\n')
224
+ }
225
+
226
+ function addLabel(art, text) {
227
+ const artLines = art.split('\n')
228
+ const artWidth = Math.max(...artLines.map(l => visLen(l)))
229
+ const labelLines = wrapText(text, 60)
230
+ const result = [...artLines, '']
231
+ for (const line of labelLines) {
232
+ const pad = Math.max(0, Math.floor((artWidth - line.length) / 2))
233
+ result.push(' '.repeat(pad) + line)
234
+ }
235
+ return result.join('\n')
236
+ }
237
+
238
+ // Parse a line into styled cells (reused by stretch/squish/flip)
239
+ function parseCells(line) {
240
+ const cells = []
241
+ let currentStyle = ''
242
+ let i = 0
243
+ while (i < line.length) {
244
+ if (line[i] === '\x1b' && i + 1 < line.length && line[i + 1] === '[') {
245
+ let j = i + 2
246
+ while (j < line.length && line[j] !== 'm') j++
247
+ const code = line.slice(i, j + 1)
248
+ if (code === '\x1b[0m') currentStyle = ''
249
+ else currentStyle += code
250
+ i = j + 1
251
+ } else {
252
+ cells.push({ style: currentStyle, char: line[i] })
253
+ i++
254
+ }
255
+ }
256
+ return cells
257
+ }
258
+
259
+ function cellsToString(cells) {
260
+ const reset = '\x1b[0m'
261
+ let result = ''
262
+ let lastStyle = null
263
+ for (const cell of cells) {
264
+ if (cell.style !== lastStyle) {
265
+ result += reset + cell.style
266
+ lastStyle = cell.style
267
+ }
268
+ result += cell.char
269
+ }
270
+ if (cells.length) result += reset
271
+ return result
272
+ }
273
+
274
+ function stretchLine(line) {
275
+ const cells = parseCells(line)
276
+ const stretched = []
277
+ for (const cell of cells) {
278
+ stretched.push(cell)
279
+ stretched.push({ style: cell.style, char: cell.char })
280
+ }
281
+ return cellsToString(stretched)
282
+ }
283
+
284
+ function stretchArt(art) {
285
+ return art.split('\n').map(stretchLine).join('\n')
286
+ }
287
+
288
+ function squishLine(line) {
289
+ const cells = parseCells(line)
290
+ const squished = cells.filter((_, i) => i % 2 === 0)
291
+ return cellsToString(squished)
292
+ }
293
+
294
+ function squishArt(art) {
295
+ return art.split('\n').map(squishLine).join('\n')
296
+ }
297
+
298
+ function stretchVArt(art) {
299
+ const lines = art.split('\n')
300
+ const result = []
301
+ for (const line of lines) {
302
+ result.push(line)
303
+ result.push(line)
304
+ }
305
+ return result.join('\n')
306
+ }
307
+
308
+ function squishVArt(art) {
309
+ const lines = art.split('\n')
310
+ return lines.filter((_, i) => i % 2 === 0).join('\n')
311
+ }
312
+
313
+ module.exports = { addSay, addHat, addLabel, stripAnsi, flipArt, stretchArt, squishArt, stretchVArt, squishVArt, sideBySide }