├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── capture.gif
├── cat.css
├── head.svg
├── index.js
├── legs.svg
├── nyan.mp3
├── package-lock.json
├── package.json
├── stars.jpg
└── tail.svg
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 2
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Aaron Hardy
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Turn your [Hyper](https://hyper.is/) terminal into nyan cat while typing. Audio included!
2 |
3 | 
4 |
5 | To install, edit `~/.hyper.js` and add `hyper-cat` to `plugins`:
6 |
7 | ```js
8 | module.exports = {
9 | ...
10 | plugins: [
11 | "hyper-cat"
12 | ]
13 | ...
14 | };
15 | ```
16 |
17 | You may then need to reload your terminal.
18 |
19 | ## Configuration
20 |
21 | Configuration can by applied by editing `~/.hyper.js` as follows:
22 |
23 | ```js
24 | module.exports = {
25 | config: {
26 | ...
27 | hyperCat: {
28 | // The number of pixels the cat and rainbow should jump up and down.
29 | staggerHeight: 2,
30 | // The max opacity of the rainbow.
31 | rainbowMaxAlpha: 1,
32 | // When nyan audio shall be enabled:
33 | // true will always play nyan audio
34 | // false will never play nyan audio
35 | // "whileTyping" will play nyan audio while typing
36 | audioEnabled: "whileTyping",
37 | // When nyan video shall be enabled:
38 | // true will always play nyan video
39 | // false will never play nyan video
40 | // "whileTyping" will play nyan video while typing
41 | videoEnabled: "whileTyping"
42 | }
43 | ...
44 | }
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/capture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aaronius/hyper-cat/e8d6e38c2898518baee801270defaaf69673dd42/capture.gif
--------------------------------------------------------------------------------
/cat.css:
--------------------------------------------------------------------------------
1 | @import "compass/css3";
2 |
3 | @function grid ($start, $end, $increment) {
4 | $str: 0;
5 | @while $start <= $end {
6 | @if $start < $end {
7 | $str: $str + 0 #{$start}#{"px,"};
8 | }
9 | @if $start == $end {
10 | $str: $str + 0 #{$start}#{"px"};
11 | }
12 | $start: $start + $increment;
13 | }
14 | @return $str;
15 | }
16 |
17 | $blue : #013366;
18 | $beige : #f9d28f;
19 | $pink : #fe91fe;
20 | $deeppink : #f90297;
21 | $gray : #9d9d9d;
22 | $salmon : #ff9593;
23 | $red : #fe0000;
24 | $orange : #ffa500;
25 | $yellow : #ffff00;
26 | $green : #00fb00;
27 | $royalblue: #009eff;
28 | $purple : #6531ff;
29 |
30 | html {
31 | height: 100%;
32 | @include background-image(radial-gradient(center, ellipse cover, darken($blue, 5%) 0%, darken($blue, 20%) 100%));
33 | }
34 | body {
35 | height: 100%;
36 | overflow: hidden;
37 | }
38 |
39 | .sprite {
40 | position: absolute;
41 | background: {
42 | position: grid(0, 125, 5);
43 | size: 100% 5px;
44 | repeat: no-repeat;
45 | }
46 | }
47 |
48 | .wrapper {
49 | position: absolute;
50 | top: 50%;
51 | left: 50%;
52 | width: 400px;
53 | height: 300px;
54 | margin-top: -150px;
55 | margin-left: -200px;
56 | border-width: 1px;
57 | overflow: hidden;
58 | border-style: solid;
59 | border-color: lighten($blue, 15%);
60 | background: $blue;
61 | @include box-shadow(0 10px 100px rgba(0,0,0,.3));
62 | }
63 |
64 | .nyan-cat {
65 | position: absolute;
66 | left: 50%;
67 | top: 50%;
68 | width: 165px;
69 | height: 100px;
70 | margin: {
71 | top: -50px;
72 | left: -82px;
73 | }
74 | @include animation(nyan 400ms step-start infinite);
75 | }
76 |
77 | .body {
78 | @extend .sprite;
79 | left: 35px;
80 | top: 0;
81 | width: 105px;
82 | height: 90px;
83 | }
84 |
85 | .head {
86 | @extend .sprite;
87 | left: 85px;
88 | top: 25px;
89 | width: 80px;
90 | height: 65px;
91 | @include animation(head 400ms linear infinite);
92 | }
93 |
94 | .rainbow {
95 | position: absolute;
96 | left: 0;
97 | top: 50%;
98 | margin-top: -35px;
99 | width: 50%;
100 | height: 65px;
101 | overflow: hidden;
102 |
103 | span {
104 | @extend .sprite;
105 | display: block;
106 | position: relative;
107 | top: 0;
108 | width: 100%;
109 | height: 130px;
110 | background: {
111 | size: 80px 5px;
112 | repeat: repeat-x;
113 | }
114 | @include animation(rainbow 400ms step-start infinite);
115 | }
116 | }
117 |
118 | .feet {
119 | @extend .sprite;
120 | left: 20px;
121 | top: 75px;
122 | width: 120px;
123 | height: 25px;
124 | @include animation(feet 400ms infinite);
125 | }
126 |
127 | .tail {
128 | position: relative;
129 | width: 25px;
130 | height: 30px;
131 | overflow: hidden;
132 | top: 30px;
133 | left: 10px;
134 |
135 | span {
136 | @extend .sprite;
137 | width: 25px;
138 | height: 120px;
139 | @include animation(tail 200ms step-start infinite alternate);
140 | }
141 | }
142 |
143 | .stars {
144 | position: relative;
145 | width: 100%;
146 | height: 100%;
147 | @include animation(moveleft 1000ms linear infinite);
148 |
149 | .star {
150 | @extend .sprite;
151 | width: 28px;
152 | height: 28px;
153 | overflow: hidden;
154 |
155 | span {
156 | position: absolute;
157 | left: 0;
158 | width: 112px;
159 | height: 28px;
160 | background: {
161 | position: grid(0, 28, 2);
162 | size: 112px 2px;
163 | repeat: no-repeat;
164 | }
165 | @include animation(star 300ms step-start infinite alternate);
166 | }
167 | }
168 | }
169 | .star:nth-child(1) {top: -10px; left: 70px}
170 | .star:nth-child(2) {top: 30px; left: 250px}
171 | .star:nth-child(3) {top: 70px; left: 350px}
172 | .star:nth-child(4) {top: 100px; left: 20px}
173 | .star:nth-child(5) {top: 200px; left: 300px}
174 | .star:nth-child(6) {top: 250px; left: 100px}
175 | .star:nth-child(7) {top: -10px; left: 470px}
176 | .star:nth-child(8) {top: 30px; left: 650px}
177 | .star:nth-child(9) {top: 70px; left: 750px}
178 | .star:nth-child(10) {top: 100px; left: 420px}
179 | .star:nth-child(11) {top: 200px; left: 700px}
180 | .star:nth-child(12) {top: 250px; left: 500px}
181 |
182 | .body {
183 | background-image:
184 | linear-gradient(left,
185 | transparent 0, transparent 10px,
186 | black 10px, black 95px,
187 | transparent 95px, transparent 105px),
188 |
189 | linear-gradient(left,
190 | transparent 0, transparent 5px,
191 | black 5px, black 10px,
192 | $beige 10px, $beige 95px,
193 | black 95px, black 100px,
194 | transparent 100px, transparent 105px),
195 |
196 | linear-gradient(left,
197 | black 0, black 5px,
198 | $beige 5px, $beige 20px,
199 | $pink 20px, $pink 85px,
200 | $beige 85px, $beige 100px,
201 | black 100px, black 105px),
202 |
203 | linear-gradient(left,
204 | black 0, black 5px,
205 | $beige 5px, $beige 15px,
206 | $pink 15px, $pink 45px,
207 | $deeppink 45px, $deeppink 50px,
208 | $pink 50px, $pink 60px,
209 | $deeppink 60px, $deeppink 65px,
210 | $pink 65px, $pink 90px,
211 | $beige 90px, $beige 100px,
212 | black 100px, black 105px),
213 |
214 | linear-gradient(left,
215 | black 0, black 5px,
216 | $beige 5px, $beige 10px,
217 | $pink 10px, $pink 20px,
218 | $deeppink 20px, $deeppink 25px,
219 | $pink 25px, $pink 95px,
220 | $beige 95px, $beige 100px,
221 | black 100px, black 105px),
222 | linear-gradient(left,
223 | black 0, black 5px,
224 | $beige 5px, $beige 10px,
225 | $pink 10px, $pink 80px,
226 | $deeppink 80px, $deeppink 85px,
227 | $pink 85px, $pink 95px,
228 | $beige 95px, $beige 100px,
229 | black 100px, black 105px),
230 | linear-gradient(left,
231 | black 0, black 5px,
232 | $beige 5px, $beige 10px,
233 | $pink 10px, $pink 95px,
234 | $beige 95px, $beige 100px,
235 | black 100px, black 105px),
236 | linear-gradient(left,
237 | black 0, black 5px,
238 | $beige 5px, $beige 10px,
239 | $pink 10px, $pink 40px,
240 | $deeppink 40px, $deeppink 45px,
241 | $pink 45px, $pink 95px,
242 | $beige 95px, $beige 100px,
243 | black 100px, black 105px),
244 | linear-gradient(left,
245 | black 0, black 5px,
246 | $beige 5px, $beige 10px,
247 | $pink 10px, $pink 95px,
248 | $beige 95px, $beige 100px,
249 | black 100px, black 105px),
250 | linear-gradient(left,
251 | black 0, black 5px,
252 | $beige 5px, $beige 10px,
253 | $pink 10px, $pink 25px,
254 | $deeppink 25px, $deeppink 30px,
255 | $pink 30px, $pink 95px,
256 | $beige 95px, $beige 100px,
257 | black 100px, black 105px),
258 | linear-gradient(left,
259 | black 0, black 5px,
260 | $beige 5px, $beige 10px,
261 | $pink 10px, $pink 45px,
262 | $deeppink 45px, $deeppink 50px,
263 | $pink 50px, $pink 95px,
264 | $beige 95px, $beige 100px,
265 | black 100px, black 105px),
266 | linear-gradient(left,
267 | black 0, black 5px,
268 | $beige 5px, $beige 10px,
269 | $pink 10px, $pink 15px,
270 | $deeppink 15px, $deeppink 20px,
271 | $pink 20px, $pink 95px,
272 | $beige 95px, $beige 100px,
273 | black 100px, black 105px),
274 | linear-gradient(left,
275 | black 0, black 5px,
276 | $beige 5px, $beige 10px,
277 | $pink 10px, $pink 95px,
278 | $beige 95px, $beige 100px,
279 | black 100px, black 105px),
280 | linear-gradient(left,
281 | black 0, black 5px,
282 | $beige 5px, $beige 10px,
283 | $pink 10px, $pink 35px,
284 | $deeppink 35px, $deeppink 40px,
285 | $pink 40px, $pink 95px,
286 | $beige 95px, $beige 100px,
287 | black 100px, black 105px),
288 |
289 | linear-gradient(left,
290 | black 0, black 5px,
291 | $beige 5px, $beige 15px,
292 | $pink 15px, $pink 20px,
293 | $deeppink 20px, $deeppink 25px,
294 | $pink 25px, $pink 90px,
295 | $beige 90px, $beige 100px,
296 | black 100px, black 105px),
297 |
298 | linear-gradient(left,
299 | black 0, black 5px,
300 | $beige 5px, $beige 20px,
301 | $pink 20px, $pink 85px,
302 | $beige 85px, $beige 100px,
303 | black 100px, black 105px),
304 |
305 | linear-gradient(left,
306 | transparent 0, transparent 5px,
307 | black 5px, black 10px,
308 | $beige 10px, $beige 95px,
309 | black 95px, black 100px,
310 | transparent 100px, transparent 105px),
311 |
312 | linear-gradient(left,
313 | transparent 0, transparent 10px,
314 | black 10px, black 95px,
315 | transparent 95px, transparent 105px)
316 | ;
317 | }
318 |
319 | .head {
320 | background-image:
321 | linear-gradient(left,
322 | transparent 0, transparent 10px,
323 | black 10px, black 20px,
324 | transparent 20px, transparent 60px,
325 | black 60px, black 70px,
326 | transparent 70px, transparent 105px),
327 |
328 | linear-gradient(left,
329 | transparent 0, transparent 5px,
330 | black 5px, black 10px,
331 | $gray 10px, $gray 20px,
332 | black 20px, black 25px,
333 | transparent 25px, transparent 55px,
334 | black 55px, black 60px,
335 | $gray 60px, $gray 70px,
336 | black 70px, black 75px,
337 | transparent 75px, transparent 80px),
338 |
339 | linear-gradient(left,
340 | transparent 0, transparent 5px,
341 | black 5px, black 10px,
342 | $gray 10px, $gray 25px,
343 | black 25px, black 30px,
344 | transparent 30px, transparent 50px,
345 | black 50px, black 55px,
346 | $gray 55px, $gray 70px,
347 | black 70px, black 75px,
348 | transparent 75px, transparent 80px),
349 |
350 | linear-gradient(left,
351 | transparent 0, transparent 5px,
352 | black 5px, black 10px,
353 | $gray 10px, $gray 30px,
354 | black 30px, black 35px,
355 | black 35px, black 50px,
356 | $gray 50px, $gray 70px,
357 | black 70px, black 75px,
358 | transparent 75px, transparent 80px),
359 |
360 | linear-gradient(left,
361 | transparent 0, transparent 5px,
362 | black 5px, black 10px,
363 | $gray 10px, $gray 70px,
364 | black 70px, black 75px,
365 | transparent 75px, transparent 80px),
366 |
367 | linear-gradient(left,
368 | black 0, black 5px,
369 | $gray 5px, $gray 75px,
370 | black 75px, black 80px),
371 |
372 | linear-gradient(left,
373 | black 0, black 5px,
374 | $gray 5px, $gray 20px,
375 | white 20px, white 25px,
376 | black 25px, black 30px,
377 | $gray 30px, $gray 55px,
378 | white 55px, white 60px,
379 | black 60px, black 65px,
380 | $gray 65px, $gray 75px,
381 | black 75px, black 80px),
382 |
383 | linear-gradient(left,
384 | black 0, black 5px,
385 | $gray 5px, $gray 20px,
386 | black 20px, black 30px,
387 | $gray 30px, $gray 45px,
388 | black 45px, black 50px,
389 | $gray 50px, $gray 55px,
390 | black 55px, black 65px,
391 | $gray 65px, $gray 75px,
392 | black 75px, black 80px),
393 |
394 | linear-gradient(left,
395 | black 0, black 5px,
396 | $gray 5px, $gray 10px,
397 | $salmon 10px, $salmon 20px,
398 | $gray 20px, $gray 65px,
399 | $salmon 65px, $salmon 75px,
400 | black 75px, black 80px),
401 |
402 | linear-gradient(left,
403 | black 0, black 5px,
404 | $gray 5px, $gray 10px,
405 | $salmon 10px, $salmon 20px,
406 | $gray 20px, $gray 25px,
407 | black 25px, black 30px,
408 | $gray 30px, $gray 40px,
409 | black 40px, black 45px,
410 | $gray 45px, $gray 55px,
411 | black 55px, black 60px,
412 | $gray 60px, $gray 65px,
413 | $salmon 65px, $salmon 75px,
414 | black 75px, black 80px),
415 |
416 | linear-gradient(left,
417 | transparent 0, transparent 5px,
418 | black 5px, black 10px,
419 | $gray 10px, $gray 25px,
420 | black 25px, black 60px,
421 | $gray 60px, $gray 70px,
422 | black 70px, black 75px,
423 | transparent 75px, transparent 80px),
424 |
425 | linear-gradient(left,
426 | transparent 0, transparent 10px,
427 | black 10px, black 15px,
428 | $gray 15px, $gray 65px,
429 | black 65px, black 70px,
430 | transparent 70px, transparent 80px),
431 |
432 | linear-gradient(left,
433 | transparent 0, transparent 15px,
434 | black 15px, black 65px,
435 | transparent 65px, transparent 80px)
436 | ;
437 | }
438 |
439 | .rainbow > span {
440 | background-image:
441 | linear-gradient(left,
442 | $red 0, $red 50%,
443 | transparent 50%, transparent 100%),
444 | linear-gradient(left,
445 | $red 0, $red 100%),
446 |
447 | linear-gradient(left,
448 | $orange 0, $orange 50%,
449 | $red 50%, $red 100%),
450 | linear-gradient(left,
451 | $orange 0, $orange 100%),
452 |
453 | linear-gradient(left,
454 | $yellow 0, $yellow 50%,
455 | $orange 50%, $orange 100%),
456 | linear-gradient(left,
457 | $yellow 0, $yellow 100%),
458 |
459 | linear-gradient(left,
460 | $green 0, $green 50%,
461 | $yellow 50%, $yellow 100%),
462 | linear-gradient(left,
463 | $green 0, $green 100%),
464 |
465 | linear-gradient(left,
466 | $royalblue 0, $royalblue 50%,
467 | $green 50%, $green 100%),
468 | linear-gradient(left,
469 | $royalblue 0, $royalblue 100%),
470 |
471 | linear-gradient(left,
472 | $purple 0, $purple 50%,
473 | $royalblue 50%, $royalblue 100%),
474 | linear-gradient(left,
475 | $purple 0, $purple 100%),
476 |
477 | linear-gradient(left,
478 | transparent 0, transparent 50%,
479 | $purple 50%, $purple 100%),
480 |
481 |
482 | linear-gradient(left,
483 | transparent 0, transparent 50%,
484 | $red 50%, $red 100%),
485 | linear-gradient(left,
486 | $red 0, $red 100%),
487 |
488 | linear-gradient(left,
489 | $red 0, $red 50%,
490 | $orange 50%, $orange 100%),
491 | linear-gradient(left,
492 | $orange 0, $orange 100%),
493 |
494 | linear-gradient(left,
495 | $orange 0, $orange 50%,
496 | $yellow 50%, $yellow 100%),
497 | linear-gradient(left,
498 | $yellow 0, $yellow 100%),
499 |
500 | linear-gradient(left,
501 | $yellow 0, $yellow 50%,
502 | $green 50%, $green 100%),
503 | linear-gradient(left,
504 | $green 0, $green 100%),
505 |
506 | linear-gradient(left,
507 | $green 0, $green 50%,
508 | $royalblue 50%, $royalblue 100%),
509 | linear-gradient(left,
510 | $royalblue 0, $royalblue 100%),
511 |
512 | linear-gradient(left,
513 | $royalblue 0, $royalblue 50%,
514 | $purple 50%, $purple 100%),
515 | linear-gradient(left,
516 | $purple 0, $purple 100%),
517 |
518 | linear-gradient(left,
519 | $purple 0, $purple 50%,
520 | transparent 50%, transparent 100%)
521 | ;
522 | }
523 |
524 | .feet {
525 | background-image:
526 | linear-gradient(left,
527 | transparent 0, transparent 10px,
528 | black 10px, black 25px,
529 | transparent 25px, transparent 120px),
530 |
531 | linear-gradient(left,
532 | transparent 0, transparent 5px,
533 | black 5px, black 10px,
534 | $gray 10px, $gray 110px,
535 | transparent 110px, transparent 120px),
536 |
537 | linear-gradient(left,
538 | black 0, black 5px,
539 | $gray 5px, $gray 20px,
540 | black 20px, black 35px,
541 | $gray 35px, $gray 40px,
542 | black 40px, black 80px,
543 | $gray 80px, $gray 110px,
544 | black 110px, black 115px,
545 | transparent 115px, transparent 120px),
546 |
547 | linear-gradient(left,
548 | black 0, black 5px,
549 | $gray 5px, $gray 15px,
550 | black 15px, black 20px,
551 | transparent 20px, transparent 25px,
552 | black 25px, black 30px,
553 | $gray 30px, $gray 40px,
554 | black 40px, black 45px,
555 | transparent 45px, transparent 75px,
556 | black 75px, black 80px,
557 | $gray 80px, $gray 90px,
558 | black 90px, black 95px,
559 | transparent 95px, transparent 100px,
560 | black 100px, black 105px,
561 | $gray 105px, $gray 115px,
562 | black 115px, black 120px),
563 |
564 | linear-gradient(left,
565 | black 0, black 15px,
566 | transparent 15px, transparent 30px,
567 | black 30px, black 45px,
568 | transparent 45px, transparent 80px,
569 | black 80px, black 95px,
570 | transparent 95px, transparent 105px,
571 | black 105px, black 120px)
572 | ;
573 | }
574 |
575 | .tail > span {
576 | background-image:
577 | linear-gradient(left,
578 | transparent 0, transparent 5px,
579 | black 5px, black 15px,
580 | transparent 15px),
581 | linear-gradient(left,
582 | black 0, black 5px,
583 | $gray 5px, $gray 15px,
584 | black 15px, black 20px,
585 | transparent 20px),
586 | linear-gradient(left,
587 | black 0, black 5px,
588 | $gray 5px, $gray 15px,
589 | black 15px),
590 | linear-gradient(left,
591 | transparent 0, transparent 5px,
592 | black 5px, black 10px,
593 | $gray 10px),
594 | linear-gradient(left,
595 | transparent 0, transparent 10px,
596 | black 10px, black 20px,
597 | $gray 20px),
598 | linear-gradient(left,
599 | transparent 0, transparent 15px,
600 | black 15px),
601 |
602 | linear-gradient(left,
603 | transparent 0, transparent 100%),
604 | linear-gradient(left,
605 | transparent 0, transparent 5px,
606 | black 5px, black 20px,
607 | transparent 20px),
608 | linear-gradient(left,
609 | black 0, black 5px,
610 | $gray 5px, $gray 15px,
611 | black 15px),
612 | linear-gradient(left,
613 | black 0, black 10px,
614 | $gray 10px, $gray 25px),
615 | linear-gradient(left,
616 | transparent 0, transparent 10px,
617 | black 10px, black 20px,
618 | $gray 20px),
619 | linear-gradient(left,
620 | transparent 0, transparent 20px,
621 | black 20px),
622 |
623 | linear-gradient(left,
624 | transparent 0, transparent 100%),
625 | linear-gradient(left,
626 | transparent 0, transparent 20px,
627 | black 20px),
628 | linear-gradient(left,
629 | transparent 0, transparent 10px,
630 | black 10px),
631 | linear-gradient(left,
632 | transparent 0, transparent 5px,
633 | black 5px, black 10px,
634 | $gray 10px),
635 | linear-gradient(left,
636 | black 0, black 5px,
637 | $gray 5px, $gray 20px,
638 | black 20px),
639 | linear-gradient(left,
640 | transparent 0, transparent 5px,
641 | black 5px),
642 |
643 | linear-gradient(left,
644 | transparent 0, transparent 20px,
645 | black 20px),
646 | linear-gradient(left,
647 | transparent 0, transparent 15px,
648 | black 15px, black 20px,
649 | $gray 20px),
650 | linear-gradient(left,
651 | transparent 0, transparent 10px,
652 | black 10px, black 15px,
653 | $gray 15px),
654 | linear-gradient(left,
655 | transparent 0, transparent 5px,
656 | black 5px, black 10px,
657 | $gray 10px, $gray 20px,
658 | black 20px),
659 | linear-gradient(left,
660 | black 0, black 5px,
661 | $gray 5px, $gray 15px,
662 | black 15px),
663 | linear-gradient(left,
664 | transparent 0, transparent 5px,
665 | black 5px, black 15px,
666 | transparent 15px)
667 | ;
668 | }
669 |
670 | .star > span {
671 | background-image:
672 | linear-gradient(left,
673 | transparent 0, transparent 12px,
674 | white 12px, white 16px,
675 | transparent 16px, transparent 112px),
676 | linear-gradient(left,
677 | transparent 0, transparent 12px,
678 | white 12px, white 16px,
679 | transparent 16px, transparent 112px),
680 |
681 | linear-gradient(left,
682 | transparent 0, transparent 4px,
683 | white 4px, white 8px,
684 | transparent 8px, transparent 20px,
685 | white 20px, white 24px,
686 | transparent 24px, transparent 40px,
687 | white 40px, white 44px,
688 | transparent 44px, transparent 68px,
689 | white 68px, white 72px,
690 | transparent 72px, transparent 112px),
691 | linear-gradient(left,
692 | transparent 0, transparent 4px,
693 | white 4px, white 8px,
694 | transparent 8px, transparent 20px,
695 | white 20px, white 24px,
696 | transparent 24px, transparent 40px,
697 | white 40px, white 44px,
698 | transparent 44px, transparent 68px,
699 | white 68px, white 72px,
700 | transparent 72px, transparent 112px),
701 |
702 | linear-gradient(left,
703 | transparent 0, transparent 68px,
704 | white 68px, white 72px,
705 | transparent 72px, transparent 112px),
706 | linear-gradient(left,
707 | transparent 0, transparent 68px,
708 | white 68px, white 72px,
709 | transparent 72px, transparent 112px),
710 |
711 | linear-gradient(left,
712 | white 0, white 4px,
713 | transparent 4px, transparent 24px,
714 | white 24px, white 28px,
715 | transparent 28px, transparent 32px,
716 | white 32px, white 36px,
717 | transparent 36px, transparent 40px,
718 | white 40px, white 44px,
719 | transparent 44px, transparent 48px,
720 | white 48px, white 52px,
721 | transparent 52px, transparent 60px,
722 | white 60px, white 68px,
723 | transparent 68px, transparent 72px,
724 | white 72px, white 80px,
725 | transparent 80px, transparent 96px,
726 | white 96px, white 100px,
727 | transparent 100px, transparent 112px),
728 | linear-gradient(left,
729 | white 0, white 4px,
730 | transparent 4px, transparent 24px,
731 | white 24px, white 28px,
732 | transparent 28px, transparent 32px,
733 | white 32px, white 36px,
734 | transparent 36px, transparent 40px,
735 | white 40px, white 44px,
736 | transparent 44px, transparent 48px,
737 | white 48px, white 52px,
738 | transparent 52px, transparent 60px,
739 | white 60px, white 68px,
740 | transparent 68px, transparent 72px,
741 | white 72px, white 80px,
742 | transparent 80px, transparent 96px,
743 | white 96px, white 100px,
744 | transparent 100px, transparent 112px),
745 |
746 | linear-gradient(left,
747 | transparent 0, transparent 68px,
748 | white 68px, white 72px,
749 | transparent 72px, transparent 112px),
750 | linear-gradient(left,
751 | transparent 0, transparent 68px,
752 | white 68px, white 72px,
753 | transparent 72px, transparent 112px),
754 |
755 | linear-gradient(left,
756 | transparent 0, transparent 4px,
757 | white 4px, white 8px,
758 | transparent 8px, transparent 20px,
759 | white 20px, white 24px,
760 | transparent 24px, transparent 40px,
761 | white 40px, white 44px,
762 | transparent 44px, transparent 68px,
763 | white 68px, white 72px,
764 | transparent 72px, transparent 112px),
765 | linear-gradient(left,
766 | transparent 0, transparent 4px,
767 | white 4px, white 8px,
768 | transparent 8px, transparent 20px,
769 | white 20px, white 24px,
770 | transparent 24px, transparent 40px,
771 | white 40px, white 44px,
772 | transparent 44px, transparent 68px,
773 | white 68px, white 72px,
774 | transparent 72px, transparent 112px),
775 |
776 | linear-gradient(left,
777 | transparent 0, transparent 12px,
778 | white 12px, white 16px,
779 | transparent 16px, transparent 112px),
780 | linear-gradient(left,
781 | transparent 0, transparent 12px,
782 | white 12px, white 16px,
783 | transparent 16px, transparent 112px)
784 | ;
785 | }
786 |
787 | @keyframes rainbow {
788 | 0% {top: 0}
789 | 50% {top: 0}
790 | 100% {top: -65px}
791 | }
792 |
793 | @keyframes moveleft {
794 | 0% {left: 0}
795 | 100% {left: -400px}
796 | }
797 |
798 | @keyframes star {
799 | 0% {left: 0}
800 | 25% {left: 0}
801 | 49.99% {left: 0}
802 | 50% {left: -28px}
803 | 74.99% {left: -28px}
804 | 75% {left: -56px}
805 | 99.99% {left: -56px}
806 | 100% {left: -84px}
807 | }
808 |
809 | @keyframes nyan {
810 | 0% {margin-top: -50px}
811 | 10% {margin-top: -50px}
812 | 80% {margin-top: -53px}
813 | 100% {margin-top: -50px}
814 | }
815 |
816 | @keyframes feet {
817 | 0% {left: 20px}
818 | 100% {left: 30px}
819 | }
820 |
821 | @keyframes head {
822 | 0% {top: 25px; left: 85px}
823 | 24.99% {top: 25px; left: 85px}
824 | 25% {top: 22px; left: 88px}
825 | 49.99% {top: 22px; left: 88px}
826 | 50% {top: 22px; left: 85px}
827 | 74.99% {top: 22px; left: 85px}
828 | 75% {top: 22px; left: 82px}
829 | 99.99% {top: 22px; left: 82px}
830 | 100% {top: 25px; left: 85px}
831 | }
832 |
833 | @keyframes tail {
834 | 0% {top: 0}
835 | 25% {top: 0}
836 | 49.99% {top: 0}
837 | 50% {top: -30px}
838 | 74.99% {top: -30px}
839 | 75% {top: -60px}
840 | 99.99% {top: -60px}
841 | 100% {top: -90px}
842 | }
843 |
--------------------------------------------------------------------------------
/head.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const electron = require('electron');
2 | const Color = require('color');
3 | const path = require('path');
4 | const starsCssPath = path.join(__dirname, 'stars.jpg').replace(/\\/g, "/");
5 |
6 | const RAINBOW_ALPHA_DECAY = 0.95;
7 | const RAINBOW_COLORS = [
8 | '#fe0000',
9 | '#ffa500',
10 | '#ffff00',
11 | '#00fb00',
12 | '#009eff',
13 | '#6531ff'
14 | ].map(color => Color(color).rgb());
15 |
16 | // Cat colors
17 | const BLACK = '#000000';
18 | const BEIGE = '#f9d28f';
19 | const PINK = '#fe91fe';
20 | const DEEPPINK = '#f90297';
21 | const GRAY = '#9d9d9d';
22 | const SALMON = '#ff9593';
23 |
24 | const WHILE_TYPING = 'whileTyping';
25 |
26 | const ACTIVE_DURATION = 250;
27 |
28 | var config = {
29 | staggerHeight: 2,
30 | rainbowMaxAlpha: 1,
31 | audioEnabled: WHILE_TYPING,
32 | videoEnabled: WHILE_TYPING
33 | };
34 |
35 | // Share audio across terminal instances.
36 | let audio;
37 | const playAudio = () => {
38 | audio.play();
39 | };
40 |
41 | const pauseAudio = () => {
42 | audio.pause();
43 | };
44 |
45 | exports.decorateTerm = (Term, { React, notify }) => {
46 | // There might be a better way to get this config. Nyan on.
47 | config = Object.assign(config, electron.remote.app.config.getConfig().hyperCat);
48 |
49 | return class extends React.Component {
50 | constructor (props, context) {
51 | super(props, context);
52 | this.state = {
53 | videoActive: false
54 | };
55 |
56 | this.drawFrame = this.drawFrame.bind(this);
57 | this.resizeCanvas = this.resizeCanvas.bind(this);
58 | this.onDecorated = this.onDecorated.bind(this);
59 | this.onCursorMove = this.onCursorMove.bind(this);
60 | this._rainbows = [];
61 | }
62 |
63 | onDecorated (term) {
64 | if (this.props.onDecorated) {
65 | this.props.onDecorated(term);
66 | }
67 |
68 | this._termDiv = term ? term.termRef : null;
69 |
70 | if (this._termDiv) {
71 | this.initAudio();
72 | this.initOverlay();
73 | }
74 | }
75 |
76 | onCursorMove(cursorFrame) {
77 | if (this.props.onCursorMove) {
78 | this.props.onCursorMove(cursorFrame);
79 | }
80 |
81 | const overlayRect = this.getOverlayBoundingClientRect();
82 | const termRect = this._termDiv.getBoundingClientRect();
83 |
84 | const left = termRect.left + cursorFrame.x - overlayRect.left;
85 | const top = termRect.top + cursorFrame.y - overlayRect.top;
86 | const width = cursorFrame.width;
87 | const height = cursorFrame.height;
88 |
89 | if (this._prevCursorRect &&
90 | this._prevCursorRect.left === left &&
91 | this._prevCursorRect.top === top &&
92 | this._prevCursorRect.width === width &&
93 | this._prevCursorRect.height === height) {
94 | return;
95 | }
96 |
97 | this.updateAudioVideo(true);
98 |
99 | this._isStaggeredUp = !this._isStaggeredUp;
100 |
101 | const staggerTop = top + (this._isStaggeredUp ? -config.staggerHeight : config.staggerHeight);
102 |
103 | Object.assign(this._catCursor.style, {
104 | left: left + 'px',
105 | top: staggerTop + 'px',
106 | width: width + 'px',
107 | height: height + 'px'
108 | });
109 |
110 | if (this._catHead.complete && this._catLegs.complete && this._catTail.complete) {
111 | const scale = width / this._catHead.naturalWidth;
112 |
113 | Object.assign(this._catHead.style, {
114 | display: 'block',
115 | width: this._catHead.naturalWidth * scale + 'px',
116 | height: this._catHead.naturalHeight * scale + 'px',
117 | left: left + width - (this._catHead.naturalWidth * scale) * .75 + 'px',
118 | // Bottom of the head should align with the bottom of the cursor.
119 | // There are basically 15 rows of blocks, 2 of which extend below the head.
120 | // These 2 rows of blocks contain the front legs.
121 | top: staggerTop + height - (this._catHead.naturalHeight * scale) * (13 / 15) + 'px'
122 | });
123 |
124 | Object.assign(this._catLegs.style, {
125 | display: 'block',
126 | width: this._catLegs.naturalWidth * scale + 'px',
127 | height: this._catLegs.naturalHeight * scale + 'px',
128 | left: left - (this._catLegs.naturalWidth * scale) * (2 / 10) + 'px',
129 | top: staggerTop + height - (this._catLegs.naturalHeight * scale) * (2 / 4) + 'px'
130 | });
131 |
132 | Object.assign(this._catTail.style, {
133 | display: 'block',
134 | width: this._catTail.naturalWidth * scale + 'px',
135 | height: this._catTail.naturalHeight * scale + 'px',
136 | left: left - (this._catTail.naturalWidth * scale) + 'px',
137 | top: staggerTop + height - (this._catTail.naturalHeight * scale) * (11 / 7) + 'px'
138 | });
139 | }
140 |
141 | if (this._prevCursorRect) {
142 | this.spawnRainbow(this._prevCursorRect);
143 | }
144 |
145 | this._prevCursorRect = {
146 | left,
147 | top,
148 | width,
149 | height
150 | };
151 | }
152 |
153 | initAudio() {
154 | if (audio) {
155 | return;
156 | }
157 |
158 | audio = document.createElement('audio');
159 | audio.id = 'audio-player';
160 | audio.src = path.join(__dirname, 'nyan.mp3');
161 | audio.type = 'audio/mpeg';
162 | audio.loop = true;
163 | document.body.appendChild(audio);
164 | }
165 |
166 | initOverlay() {
167 | this._overlay = document.createElement('div');
168 | this._overlay.classList.add('hypercat-overlay');
169 | this._termDiv.insertBefore(this._overlay, this._termDiv.firstChild);
170 |
171 | this._canvas = document.createElement('canvas');
172 | this._canvasContext = this._canvas.getContext('2d');
173 | this.resizeCanvas();
174 |
175 | this._overlay.appendChild(this._canvas);
176 |
177 | window.requestAnimationFrame(this.drawFrame);
178 | window.addEventListener('resize', this.resizeCanvas);
179 |
180 | this.initCatCursor();
181 | this.initCatAssets();
182 | }
183 |
184 | createCatAsset(filename) {
185 | const img = new Image(); // Create new img element
186 | img.src = path.join(__dirname, filename);
187 | img.classList.add('hypercat-asset');
188 | this._overlay.appendChild(img);
189 | return img;
190 | }
191 |
192 | initCatAssets() {
193 | this._catLegs = this.createCatAsset('legs.svg');
194 | this._catHead = this.createCatAsset('head.svg');
195 | this._catTail = this.createCatAsset('tail.svg');
196 | }
197 |
198 | initCatCursor() {
199 | const catCursor = document.createElement('div');
200 | catCursor.classList.add('hypercat-cursor');
201 |
202 | this._overlay.appendChild(catCursor);
203 | this._catCursor = catCursor;
204 | }
205 |
206 | resizeCanvas() {
207 | const overlayRect = this.getOverlayBoundingClientRect();
208 | this._canvas.width = overlayRect.width;
209 | this._canvas.height = overlayRect.height;
210 | }
211 |
212 | drawRainbow(ctx, rainbow, staggerUp) {
213 | const stripeHeight = rainbow.height / RAINBOW_COLORS.length;
214 |
215 | RAINBOW_COLORS.forEach((color, i) => {
216 | ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${rainbow.alpha})`;
217 | ctx.fillRect(
218 | rainbow.left,
219 | rainbow.top + stripeHeight * i + (staggerUp ? -config.staggerHeight : config.staggerHeight),
220 | rainbow.width,
221 | stripeHeight
222 | );
223 | });
224 | }
225 |
226 | drawFrame() {
227 | this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height);
228 |
229 | let staggerUp = !this._isStaggeredUp;
230 |
231 | for (var i = this._rainbows.length - 1; i >= 0; i--) {
232 | const rainbow = this._rainbows[i];
233 | this.drawRainbow(this._canvasContext, rainbow, staggerUp);
234 |
235 | rainbow.alpha *= RAINBOW_ALPHA_DECAY;
236 |
237 | if (rainbow.alpha < 0.1) {
238 | this._rainbows.splice(i, 1);
239 | }
240 |
241 | staggerUp = !staggerUp;
242 | }
243 |
244 | window.requestAnimationFrame(this.drawFrame);
245 | }
246 |
247 | spawnRainbow(rect) {
248 | // Make the rainbow a bit shorter than the cat for a proper nyan.
249 | this._rainbows.push(Object.assign({ alpha: config.rainbowMaxAlpha }, {
250 | left: rect.left,
251 | top: rect.top + rect.height * .1,
252 | width: rect.width,
253 | height: rect.height * .80
254 | }));
255 | }
256 |
257 | getOverlayBoundingClientRect() {
258 | // Getting the bounding client rect is futile unless it's visible. If it's not already visible, we'll
259 | // make it visible, take the measurement, then hide it.
260 | const overlayIsVisible = this._overlay.classList.contains('hypercat-active');
261 |
262 | if (!overlayIsVisible) {
263 | this._overlay.classList.add('hypercat-active');
264 | }
265 |
266 | const rect = this._overlay.getBoundingClientRect();
267 |
268 | if (!overlayIsVisible) {
269 | this._overlay.classList.remove('hypercat-active');
270 | }
271 |
272 | return rect;
273 | }
274 |
275 | updateAudio(typing) {
276 | let active = config.audioEnabled === true || (typing && config.audioEnabled === WHILE_TYPING);
277 | active ? playAudio() : pauseAudio();
278 | }
279 |
280 | updateVisual(typing) {
281 | let active = config.videoEnabled === true || (typing && config.videoEnabled === WHILE_TYPING);
282 | this._overlay.classList.toggle('hypercat-active', active);
283 | this.setState({
284 | videoActive: active
285 | });
286 | }
287 |
288 | updateAudioVideo(typing) {
289 | this.updateAudio(typing);
290 | this.updateVisual(typing);
291 |
292 | if (typing) {
293 | clearTimeout(this._activeTimeout);
294 | this._activeTimeout = setTimeout(() => {
295 | this.updateAudioVideo(false);
296 | }, ACTIVE_DURATION);
297 | }
298 | }
299 |
300 | render() {
301 | return [
302 | React.createElement(Term, Object.assign({}, this.props, {
303 | onDecorated: this.onDecorated,
304 | onCursorMove: this.onCursorMove,
305 | backgroundColor: this.state.videoActive ? 'rgba(0, 0, 0, 0)' : this.props.backgroundColor,
306 | cursorColor: this.state.videoActive ? 'rgba(0, 0, 0, 0)' : this.props.cursorColor,
307 | foregroundColor: this.state.videoActive ? 'rgba(255, 255, 255, 1)' : this.props.foregroundColor
308 | })),
309 | React.createElement('style', {}, `
310 | @keyframes starscroll {
311 | from {background-position:0 0;}
312 | to {background-position:-1600px 0;}
313 | }
314 |
315 | .hypercat-overlay {
316 | display: none;
317 | position: absolute;
318 | top: 0;
319 | right: 0;
320 | bottom: 0;
321 | left: 0;
322 | }
323 |
324 | .hypercat-overlay.hypercat-active {
325 | display: block;
326 | background-image: url(file://${starsCssPath});
327 | background-repeat: repeat;
328 | -webkit-animation: starscroll 4s infinite linear
329 | }
330 |
331 | .hypercat-cursor {
332 | position: absolute;
333 | pointerEvents: none;
334 | background: radial-gradient(circle, ${DEEPPINK} 10%, transparent 10%),
335 | radial-gradient(circle, ${DEEPPINK} 10%, ${PINK} 10%) 3px 3px;
336 | backgroundSize: 6px 6px;
337 | borderWidth: 1px;
338 | borderColor: black;
339 | borderStyle: solid;
340 | }
341 |
342 | .hypercat-asset {
343 | position: absolute;
344 | pointerEvents: none;
345 | }
346 | `)
347 | ];
348 | }
349 | }
350 | };
351 |
--------------------------------------------------------------------------------
/legs.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/nyan.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aaronius/hyper-cat/e8d6e38c2898518baee801270defaaf69673dd42/nyan.mp3
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hyper-cat",
3 | "version": "4.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "clone": {
8 | "version": "1.0.3",
9 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz",
10 | "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8="
11 | },
12 | "color": {
13 | "version": "0.11.3",
14 | "resolved": "https://registry.npmjs.org/color/-/color-0.11.3.tgz",
15 | "integrity": "sha1-S60dDVJJndANvW8IaEQkZ+STlOY=",
16 | "requires": {
17 | "clone": "^1.0.2",
18 | "color-convert": "^1.3.0",
19 | "color-string": "^0.3.0"
20 | }
21 | },
22 | "color-convert": {
23 | "version": "1.9.1",
24 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
25 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
26 | "requires": {
27 | "color-name": "^1.1.1"
28 | }
29 | },
30 | "color-name": {
31 | "version": "1.1.3",
32 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
33 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
34 | },
35 | "color-string": {
36 | "version": "0.3.0",
37 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
38 | "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
39 | "requires": {
40 | "color-name": "^1.0.0"
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hyper-cat",
3 | "version": "4.0.0",
4 | "description": "Turn your Hyper terminal into nyan cat while typing.",
5 | "author": "Aaron Hardy",
6 | "license": "MIT",
7 | "keywords": [
8 | "hyper",
9 | "hyper.app",
10 | "nyan",
11 | "cat"
12 | ],
13 | "dependencies": {
14 | "color": "0.11.3"
15 | },
16 | "main": "index.js",
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/Aaronius/hyper-cat.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/Aaronius/hyper-cat/issues"
23 | },
24 | "homepage": "https://github.com/Aaronius/hyper-cat#readme"
25 | }
26 |
--------------------------------------------------------------------------------
/stars.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aaronius/hyper-cat/e8d6e38c2898518baee801270defaaf69673dd42/stars.jpg
--------------------------------------------------------------------------------
/tail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------