@itcase/config 1.6.76 → 1.6.78

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,327 @@
1
+ // Frames from klange/nyancat (NCSA), https://github.com/klange/nyancat
2
+ // Cat + shortened rainbow, no fill background.
3
+ // 2-space cells; shrinks to terminal width.
4
+
5
+ const RESET = '\x1b[0m'
6
+ const FRAME_WIDTH = 40
7
+ const FRAME_HEIGHT = 19
8
+
9
+ const COLORS = {
10
+ '#': 118,
11
+ $: 175,
12
+ '%': 175,
13
+ '&': 214,
14
+ "'": 16,
15
+ '*': 240,
16
+ '+': 226,
17
+ '-': 162,
18
+ '.': 231,
19
+ ';': 19,
20
+ '=': 33,
21
+ '>': 196,
22
+ '@': 230,
23
+ }
24
+
25
+ const FRAMES = [
26
+ [
27
+ ",,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,",
28
+ ">>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
29
+ ">>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
30
+ "&&>>>>>>>>&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
31
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,",
32
+ "&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,",
33
+ "++&&&&&&&&'''++'@$$$$$-$$'***$$$@'***',,",
34
+ "++++++++++**''+'@$$$$$$$$'***''''****',,",
35
+ "++++++++++'**'''@$$$$$$$$'***********',,",
36
+ "##++++++++''**''@$$$$$$-'*************',",
37
+ "###########''**'@$-$$$$$'***.'****.'**',",
38
+ "############''''@$$$$$$$'***''**'*''**',",
39
+ "==########====''@@$$$-$$'*%%********%%',",
40
+ "==============='@@@$$$$$$'***''''''**',,",
41
+ ";;.=======;;;;'''@@@@@@@@@'*********',,,",
42
+ ";;;;;;;;;;;;;'***''''''''''''''''''',,,,",
43
+ ";;;;;;;;;;;;;'**'','*',,,,,'*','**',,,,,",
44
+ ",,;;;.;;;;,,,'''',,'',,,,,,,'',,'',,,,,,",
45
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
46
+ ],
47
+ [
48
+ ",,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,",
49
+ ">>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
50
+ ">>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
51
+ "&&>>>>>>>>&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
52
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
53
+ "&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',",
54
+ "++&&&&&&&&+++++'@$$$$$-$$$'***$$@''***',",
55
+ "+++++++++++'+++'@$$$$$$$$$'***''''****',",
56
+ "++++++++++'*'++'@$$$$$$$$$'***********',",
57
+ "##++++++++'*''''@$$$$$$-$'*************'",
58
+ "###########****'@$-$$$$$$'***.'****.'**'",
59
+ "###########''**'@$$$$$$$$'***''**'*''**'",
60
+ "==########==='''@@$$$-$$$'*%%********%%'",
61
+ "==============='@@@$$$$$$$'***''''''**',",
62
+ ";;========;;;;;''@@@@@@@@@@'*********',,",
63
+ ";;;;;;;;;;;;;;'**'''''''''''''''''''',,,",
64
+ ";;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,",
65
+ ",,;;;;;;;;,,,,''',,,'',,,,,,''',,''',,,,",
66
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
67
+ ],
68
+ [
69
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
70
+ ",>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,",
71
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
72
+ ">&&&&&&&&>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
73
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
74
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
75
+ "&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',",
76
+ "+++++++++++++++'@$$$$$-$$$'***$$@''***',",
77
+ "+++++++++++++++'@$$$$$$$$$'***''''****',",
78
+ "+########++++++'@$$$$$$$$$'***********',",
79
+ "##############''@$$$$$$-$'*************'",
80
+ "###########'''''@$-$$$$$$'***.'****.'**'",
81
+ "#========#'****'@$$$$$$$$'***''**'*''**'",
82
+ "==========='''='@@$$$-$$$'*%%********%%'",
83
+ "=;;;;;;;;======'@@@$$$$$$$'***''''''**',",
84
+ ";;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,",
85
+ ";;;;;;;;;;;;;;;'*'''''''''''''''''''',,,",
86
+ ";,,,,,,,,;;;;;;'**',**',,,,,,**','**',,,",
87
+ ",,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,",
88
+ ],
89
+ [
90
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
91
+ ",>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,",
92
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
93
+ ">&&&&&&&&>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
94
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
95
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
96
+ "&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',",
97
+ "+++++++++++++++'@$$$$$-$$$'***$$@''***',",
98
+ "+++++++++++++++'@$$$$$$$$$'***''''****',",
99
+ "+########++++++'@$$$$$$$$$'***********',",
100
+ "#############'''@$$$$$$-$'*************'",
101
+ "###########''**'@$-$$$$$$'***.'****.'**'",
102
+ "#========##****'@$$$$$$$$'***''**'*''**'",
103
+ "=========='*'=='@@$$$-$$$'*%%********%%'",
104
+ "=;;;;;;;;=='==='@@@$$$$$$$'***''''''**',",
105
+ ";;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,",
106
+ ";;;;;;;;;;;;;;'**'''''''''''''''''''',,,",
107
+ ";,,,,,,,,;;;;;'**','*',,,,,,'*','**',,,,",
108
+ ",,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,",
109
+ ],
110
+ [
111
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
112
+ ">>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,",
113
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
114
+ "&&>>>>>>>>&&&&&'@@@$$$$$$$$$$$@@@',,,,,,",
115
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
116
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,",
117
+ "++&&&&&&&&+++++'@$$$$$$$$'**'$$$@''**',,",
118
+ "+++++++++++++++'@$$$$$-$$'***$$$@'***',,",
119
+ "++++++++++'''++'@$$$$$$$$'***''''****',,",
120
+ "##+++++++'**''''@$$$$$$$$'***********',,",
121
+ "#########'****''@$$$$$$-'*************',",
122
+ "##########''''*'@$-$$$$$'***.'****.'**',",
123
+ "==########==='''@$$$$$$$'***''**'*''**',",
124
+ "==============='@@$$$-$$'*%%********%%',",
125
+ ";;========;;;;''@@@$$$$$$'***''''''**',,",
126
+ ";;;;;;;;;;;;;''''@@@@@@@@@'*********',,,",
127
+ ";;;;;;;;;;;;'***'''''''''''''''''''',,,,",
128
+ ",,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,",
129
+ ",,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,",
130
+ ],
131
+ [
132
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
133
+ ">>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,",
134
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
135
+ "&&>>>>>>>>&&&&&'@@@$$$$$$$$$$$@@@',,,,,,",
136
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,",
137
+ "&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,",
138
+ "++&&&&&&&&+++++'@$$$$$$$$'***$$$@'***',,",
139
+ "+++++++++++'+++'@$$$$$-$$'***''''****',,",
140
+ "++++++++++'*'++'@$$$$$$$$'***********',,",
141
+ "##++++++++'*''''@$$$$$$$'*************',",
142
+ "###########****'@$$$$$$-'***.'****.'**',",
143
+ "###########''**'@$-$$$$$'***''**'*''**',",
144
+ "==########==='''@$$$$$$$'*%%********%%',",
145
+ "==============='@@$$$-$$$'***''''''**',,",
146
+ ";;========;;;;''@@@$$$$$$$'*********',,,",
147
+ ";;;;;;;;;;;;;'*''@@@@@@@@@@''''''''',,,,",
148
+ ";;;;;;;;;;;;'***''''''''''''''''*',,,,,,",
149
+ ",,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,",
150
+ ",,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,",
151
+ ],
152
+ [
153
+ ",,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,",
154
+ ",>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,",
155
+ ">>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
156
+ ">&&&&&&&&>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,",
157
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,",
158
+ "&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,",
159
+ "&++++++++&'''&&'@$$$$$-$$'***$$$@'***',,",
160
+ "++++++++++'*''+'@$$$$$$$$'***''''****',,",
161
+ "++++++++++'**'''@$$$$$$$$'***********',,",
162
+ "+########++'**''@$$$$$$-'*************',",
163
+ "###########''**'@$-$$$$$'***.'****.'**',",
164
+ "############''''@$$$$$$$'***''**'*''**',",
165
+ "#========#####''@@$$$-$$'*%%********%%',",
166
+ "==============='@@@$$$$$$'***''''''**',,",
167
+ "=;;;;;;;;====='''@@@@@@@@@'*********',,,",
168
+ ";;;;;;;;;;;;;'***''''''''''''''''''',,,,",
169
+ ";;;;;;;;;;;;;'**'','*',,,,,'**,'**',,,,,",
170
+ ";,,,,,,,,;;;;'''',,'',,,,,,,'',,'',,,,,,",
171
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
172
+ ],
173
+ [
174
+ ",,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,",
175
+ ",>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,",
176
+ ">>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
177
+ ">&&&&&&&&>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,",
178
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
179
+ "&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',",
180
+ "&++++++++&&&&&&'@$$$$$-$$$'***$$@''***',",
181
+ "+++++++++++'+++'@$$$$$$$$$'***''''****',",
182
+ "++++++++++'*'++'@$$$$$$$$$'***********',",
183
+ "+########+'*''''@$$$$$$-$'*************'",
184
+ "###########****'@$-$$$$$$'***.'****.'**'",
185
+ "###########''**'@$$$$$$$$'***''**'*''**'",
186
+ "#========####'''@@$$$-$$$'*%%********%%'",
187
+ "==============='@@@$$$$$$$'***''''''**',",
188
+ "=;;;;;;;;======''@@@@@@@@@@'*********',,",
189
+ ";;;;;;;;;;;;;;'**'''''''''''''''''''',,,",
190
+ ";;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,",
191
+ ";,,,,,,,,;;;;;''',,,'',,,,,,''',,''',,.,",
192
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
193
+ ],
194
+ [
195
+ ',,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,',
196
+ ">>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,",
197
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
198
+ "&&>>>>>>>>&&&&&'@@@$$$$$$$$$$$@@@',,,,,,",
199
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
200
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
201
+ "++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',",
202
+ "+++++++++++++++'@$$$$$-$$$'***$$@''***',",
203
+ "+++++++++++++++'@$$$$$$$$$'***''''****',",
204
+ "##++++++++#####'@$$$$$$$$$'***********',",
205
+ "##############''@$$$$$$-$'*************'",
206
+ "###########'''''@$-$$$$$$'***.'****.'**'",
207
+ "==########'****'@$$$$$$$$'***''**'*''**'",
208
+ "==========='''='@@$$$-$$$'*%%********%%'",
209
+ ";;========;;;;;'@@@$$$$$$$'***''''''**',",
210
+ ";;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,",
211
+ ";;;;;;;;;;;;;;;'*'''''''''''''''''''',,,",
212
+ ",,;;;;;;;;,,,,,'**',**',,,,,,**'.'**',,,",
213
+ ",,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,",
214
+ ],
215
+ [
216
+ ',,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
217
+ ">>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,",
218
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
219
+ "&&>>>>>>>>&&&&&'@@@$$$$$$$$$$$@@@',,,,,,",
220
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
221
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,",
222
+ "++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',",
223
+ "+++++++++++++++'@$$$$$-$$$'***$$@''***',",
224
+ "+++++++++++++++'@$$$$$$$$$'***''''****',",
225
+ "##++++++++#####'@$$$$$$$$$'***********',",
226
+ "#############'''@$$$$$$-$'*************'",
227
+ "###########''**'@$-$$$$$$'***.'****.'**'",
228
+ "==########=****'@$$$$$$$$'***''**'*''**'",
229
+ "=========='*'=='@@$$$-$$$'*%%********%%'",
230
+ ";;========;';;;'@@@$$$$$$$'***''''''**',",
231
+ ";;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,",
232
+ ";;;;;;;;;;;;;;'**'''''''''''''''''''',,,",
233
+ ",,;;;;;;;;,,,,'**','*',,..,.**','**',,,,",
234
+ ",,,,,,,,,,,,,,''',,,'',,,,.,''',,''',,,,",
235
+ ],
236
+ [
237
+ ',,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
238
+ ",>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,",
239
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
240
+ ">&&&&&&&&>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
241
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,",
242
+ "&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,",
243
+ "&++++++++&&&&&&'@$$$$$$$$'**'$$$@''**',,",
244
+ "+++++++++++++++'@$$$$$-$$'***$$$@'***',,",
245
+ "++++++++++'''++'@$$$$$$$$'***''''****',,",
246
+ "+########'**''''@$$$$$$$$'***********',,",
247
+ "#########'****''@$$$$$$-'*************',",
248
+ "##########''''*'@$-$$$$$'***.'****.'**',",
249
+ "#========####'''@$$$$$$$'***''**'*''**',",
250
+ "==============='@@$$$-$$'*%%********%%',",
251
+ "=;;;;;;;;=====''@@@$$$$$$'***''''''**',,",
252
+ ";;;;;;;;;;;;;''''@@@@@@@@@'*********',,,",
253
+ ";;;;;;;;;;;;'***'''''''''''''''''''',,,,",
254
+ ";,,,,,,,,;;;'**'.'**..,,,,'**''**',,,,,,",
255
+ ",,,,,,,,,,,,''',,,'',,,,,,,''',''',,,,,,",
256
+ ],
257
+ [
258
+ ',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',
259
+ ",>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,",
260
+ ">>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,",
261
+ ">&&&&&&&&>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,",
262
+ "&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,",
263
+ "&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,",
264
+ "&++++++++&&&&&&'@$$$$$$$$'***$$$@'***',,",
265
+ "+++++++++++'+++'@$$$$$-$$'***''''****',,",
266
+ "++++++++++'*'++'@$$$$$$$$'***********',,",
267
+ "+########+'*''''@$$$$$$$'*************',",
268
+ "###########****'@$$$$$$-'***.'****.'**',",
269
+ "###########''**'@$-$$$$$'***''**'*''**',",
270
+ "#========####'''@$$$$$$$'*%%********%%',",
271
+ "==============='@@$$$-$$$'***''''''**',,",
272
+ "=;;;;;;;;=.===''@@@$$$$$$$'*********',,,",
273
+ ";;;;;;;;;;;;.'*''@@@@@@@@@@''''''''',,,,",
274
+ ";;;;;;;;;;;;'***''''''''''''''''*',,,,,,",
275
+ ";,,,,,,,.;;;'**','**,,,,,,'**''**',,,,,,",
276
+ ",,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,",
277
+ ],
278
+ ]
279
+
280
+ function pickColumns(srcWidth, maxCells) {
281
+ if (srcWidth <= maxCells) {
282
+ return Array.from({ length: srcWidth }, (_, i) => i)
283
+ }
284
+ return Array.from({ length: maxCells }, (_, i) =>
285
+ Math.round((i * (srcWidth - 1)) / (maxCells - 1)),
286
+ )
287
+ }
288
+
289
+ function renderRow(row, columns) {
290
+ const padded = row.padEnd(FRAME_WIDTH, ',').slice(0, FRAME_WIDTH)
291
+ const indexes = pickColumns(FRAME_WIDTH, columns)
292
+ let line = ''
293
+ let last = -1
294
+ for (const x of indexes) {
295
+ const cell = padded[x]
296
+ const code = cell === ',' ? undefined : COLORS[cell]
297
+ if (code === undefined) {
298
+ if (last !== -1) {
299
+ line += RESET
300
+ last = -1
301
+ }
302
+ line += ' '
303
+ continue
304
+ }
305
+ if (code !== last) {
306
+ line += `\x1b[48;5;${code}m`
307
+ last = code
308
+ }
309
+ line += ' '
310
+ }
311
+ if (last !== -1) line += RESET
312
+ return line
313
+ }
314
+
315
+ export function getNyanFrame(index, terminalColumns = 80) {
316
+ const maxCells = Math.max(12, Math.floor((terminalColumns - 1) / 2))
317
+ const frame = FRAMES[index % FRAMES.length]
318
+ const lines = frame.map((row) => renderRow(row, maxCells))
319
+ while (lines.length < FRAME_HEIGHT) {
320
+ lines.push('')
321
+ }
322
+ return lines.slice(0, FRAME_HEIGHT).join('\n')
323
+ }
324
+
325
+ export function getNyanFrameCount() {
326
+ return FRAMES.length
327
+ }
@@ -1,255 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Extract dancing-banana.js frames from the classic GIF."""
3
-
4
- from __future__ import annotations
5
-
6
- from collections import deque
7
- from pathlib import Path
8
-
9
- from PIL import Image
10
-
11
- OUT = Path(__file__).resolve().parents[1] / 'dancing-banana.js'
12
- BG = (255, 255, 255)
13
- TARGET_W = 24
14
-
15
- CHAR = {
16
- (0, 0, 0): 'B',
17
- (255, 255, 0): 'Y',
18
- (206, 206, 0): 'y',
19
- (156, 156, 0): 'd',
20
- (255, 0, 0): 'R',
21
- (255, 255, 255): 'W',
22
- }
23
-
24
- JS_TEMPLATE = '''\
25
- // Extracted from the classic dancing banana GIF (Trym Stene, 1999).
26
- // 8 frames, {w}×{h}px — fixed canvas, solid 2-space blocks.
27
-
28
- const RESET = '\\x1b[0m'
29
- const FRAME_WIDTH = {w}
30
- const FRAME_HEIGHT = {h}
31
-
32
- const COLORS = {{
33
- B: 236,
34
- R: 196,
35
- W: 255,
36
- E: 255,
37
- Y: 226,
38
- d: 136,
39
- y: 214,
40
- }}
41
-
42
- const FRAMES = [
43
- {frames}
44
- ]
45
-
46
- function renderRow(row) {{
47
- const padded = row.padEnd(FRAME_WIDTH, '.').slice(0, FRAME_WIDTH)
48
- let line = ''
49
- for (const cell of padded) {{
50
- if (cell === '.') {{
51
- line += ' '
52
- continue
53
- }}
54
- const code = COLORS[cell]
55
- if (code === undefined) {{
56
- line += ' '
57
- continue
58
- }}
59
- const bold = cell === 'E' ? '\\x1b[1m' : ''
60
- line += `${{bold}}\\x1b[48;5;${{code}}m ${{RESET}}`
61
- }}
62
- return line
63
- }}
64
-
65
- export function getBananaFrame(index) {{
66
- const frame = FRAMES[index % FRAMES.length]
67
- const lines = frame.map(renderRow)
68
- while (lines.length < FRAME_HEIGHT) {{
69
- lines.push(' '.repeat(FRAME_WIDTH * 2))
70
- }}
71
- return lines.slice(0, FRAME_HEIGHT).join('\\n')
72
- }}
73
-
74
- export function getBananaFrameCount() {{
75
- return FRAMES.length
76
- }}
77
- '''
78
-
79
-
80
- def remove_bg(frame: Image.Image) -> Image.Image:
81
- px = frame.load()
82
- w, h = frame.size
83
- seen = [[False] * w for _ in range(h)]
84
- q: deque[tuple[int, int]] = deque()
85
-
86
- for x in range(w):
87
- for y in (0, h - 1):
88
- if px[x, y][:3] == BG:
89
- q.append((x, y))
90
- seen[y][x] = True
91
-
92
- for y in range(h):
93
- for x in (0, w - 1):
94
- if not seen[y][x] and px[x, y][:3] == BG:
95
- q.append((x, y))
96
- seen[y][x] = True
97
-
98
- while q:
99
- x, y = q.popleft()
100
- px[x, y] = (255, 255, 255, 0)
101
- for nx, ny in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
102
- if 0 <= nx < w and 0 <= ny < h and not seen[ny][nx] and px[nx, ny][:3] == BG:
103
- seen[ny][nx] = True
104
- q.append((nx, ny))
105
-
106
- return frame
107
-
108
-
109
- def color_key(px: tuple[int, int, int, int]) -> str:
110
- if px[3] < 128:
111
- return '.'
112
- rgb = px[:3]
113
- if rgb in CHAR:
114
- return CHAR[rgb]
115
- nearest = min(CHAR, key=lambda c: sum((a - b) ** 2 for a, b in zip(rgb, c)))
116
- return CHAR[nearest]
117
-
118
-
119
- def trim_frames(frames: list[list[str]]) -> tuple[int, int, list[list[str]]]:
120
- h = len(frames[0])
121
- w = len(frames[0][0])
122
-
123
- while h and all(frames[f][0][c] == '.' for f in range(len(frames)) for c in range(w)):
124
- for f in range(len(frames)):
125
- frames[f].pop(0)
126
- h -= 1
127
-
128
- while h and all(frames[f][h - 1][c] == '.' for f in range(len(frames)) for c in range(w)):
129
- for f in range(len(frames)):
130
- frames[f].pop()
131
- h -= 1
132
-
133
- while w and all(frames[f][r][0] == '.' for f in range(len(frames)) for r in range(h)):
134
- for f in range(len(frames)):
135
- frames[f] = [row[1:] for row in frames[f]]
136
- w -= 1
137
-
138
- while w and all(frames[f][r][w - 1] == '.' for f in range(len(frames)) for r in range(h)):
139
- for f in range(len(frames)):
140
- frames[f] = [row[:-1] for row in frames[f]]
141
- w -= 1
142
-
143
- return w, h, frames
144
-
145
-
146
- def content_bounds(frames: list[list[str]]) -> tuple[int, int, int, int]:
147
- top = len(frames[0])
148
- bottom = 0
149
- left = len(frames[0][0])
150
- right = 0
151
-
152
- for frame in frames:
153
- for y, row in enumerate(frame):
154
- for x, cell in enumerate(row):
155
- if cell != '.':
156
- top = min(top, y)
157
- bottom = max(bottom, y)
158
- left = min(left, x)
159
- right = max(right, x)
160
-
161
- return top, bottom, left, right
162
-
163
-
164
- def normalize_canvas(frames: list[list[str]]) -> tuple[int, int, list[list[str]]]:
165
- top, bottom, left, right = content_bounds(frames)
166
- canvas_h = bottom - top + 1
167
- canvas_w = right - left + 1
168
-
169
- normalized: list[list[str]] = []
170
- for frame in frames:
171
- rows = [row[left : right + 1] for row in frame[top : bottom + 1]]
172
- normalized.append([row.ljust(canvas_w, '.')[:canvas_w] for row in rows])
173
-
174
- return canvas_w, canvas_h, normalized
175
-
176
-
177
- def enhance_eyes(frames: list[list[str]]) -> list[list[str]]:
178
- h = len(frames[0])
179
- w = len(frames[0][0])
180
- out: list[list[list[str]]] = [[list(row) for row in frame] for frame in frames]
181
-
182
- for grid in out:
183
- for y in range(h):
184
- for x in range(w):
185
- if grid[y][x] != 'W':
186
- continue
187
- if y < int(h * 0.42) or y > int(h * 0.75):
188
- continue
189
- neighbors = []
190
- for nx, ny in ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)):
191
- if 0 <= nx < w and 0 <= ny < h:
192
- neighbors.append(grid[ny][nx])
193
- if any(n in 'YyBR' for n in neighbors):
194
- grid[y][x] = 'E'
195
-
196
- return [[''.join(row) for row in grid] for grid in out]
197
-
198
-
199
- def main() -> None:
200
- gif_path = Path(
201
- '/Users/Arkasha/.cursor/projects/Volumes-Work-ITCase-itcase-ui/assets/'
202
- 'dancing-banana-gif-809950cd-831b-4c1e-8547-2d3612956da9.gif',
203
- )
204
- img = Image.open(gif_path)
205
-
206
- minx, miny, maxx, maxy = 9999, 9999, 0, 0
207
- frames_raw: list[Image.Image] = []
208
-
209
- for i in range(img.n_frames):
210
- img.seek(i)
211
- frame = remove_bg(img.convert('RGBA'))
212
- frames_raw.append(frame)
213
- fw, fh = frame.size
214
- for y in range(fh):
215
- for x in range(fw):
216
- if frame.getpixel((x, y))[3] >= 128:
217
- minx = min(minx, x)
218
- miny = min(miny, y)
219
- maxx = max(maxx, x)
220
- maxy = max(maxy, y)
221
-
222
- crop_h = maxy - miny + 1
223
- target_h = max(8, round(crop_h * TARGET_W / (maxx - minx + 1)))
224
-
225
- frames: list[list[str]] = []
226
- for frame in frames_raw:
227
- cropped = frame.crop((minx, miny, maxx + 1, maxy + 1))
228
- small = cropped.resize((TARGET_W, target_h), Image.NEAREST)
229
- rows = []
230
- for y in range(target_h):
231
- rows.append(''.join(color_key(small.getpixel((x, y))) for x in range(TARGET_W)))
232
- frames.append(rows)
233
-
234
- w, h, frames = trim_frames(frames)
235
- w, h, frames = normalize_canvas(frames)
236
- frames = enhance_eyes(frames)
237
-
238
- frame_blocks = []
239
- for frame in frames:
240
- frame_blocks.append(' [')
241
- for row in frame:
242
- frame_blocks.append(f" '{row}',")
243
- frame_blocks.append(' ],')
244
-
245
- OUT.write_text(
246
- JS_TEMPLATE.format(
247
- w=w,
248
- h=h,
249
- frames='\n'.join(frame_blocks),
250
- ),
251
- )
252
-
253
-
254
- if __name__ == '__main__':
255
- main()