-
-
Notifications
You must be signed in to change notification settings - Fork 581
/
index.html
1492 lines (1464 loc) · 70.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JS Paint</title>
<!-- This should mirror CSP in electron-main.js, except maybe for firebase stuff. -->
<!-- Firebase stuff is somewhat speculative, as the quota is exceeded as I'm adding this. -->
<!-- Lax img-src is needed for speech recognition, e.g. interpret_command("draw a cat")[0].exec(); -->
<!-- connect-src needs data:/blob: for loading images via fetch, including from local storage. -->
<!-- script⁂-src needs blob: for the Web Worker used by clmtrackr for Tracky Mouse head tracking feature -->
<!-- ⁂: working around bug in @1j01/live-server, my fork of live-server meant to handle CSP (https://github.com/1j01/live-server)
where the directive (that shall not be named) is replaced even if it's outside the CSP in a comment. -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' blob: https://jspaint.firebaseio.com https://www.youtube.com;
frame-src 'self' https://youtube.com https://www.youtube.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: http: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src * data: blob: https://jspaint.firebaseio.com wss://jspaint.firebaseio.com;
">
<link href="styles/normalize.css" rel="stylesheet" type="text/css">
<link href="styles/layout.css" class="flippable-layout-stylesheet" rel="stylesheet" type="text/css">
<link href="styles/print.css" rel="stylesheet" type="text/css" media="print">
<link href="lib/os-gui/build/layout.css" class="flippable-layout-stylesheet" rel="stylesheet" type="text/css">
<!-- <link href="lib/os-gui/build/windows-98.css" rel="stylesheet" type="text/css"> -->
<!-- <link href="lib/os-gui/build/windows-default.css" rel="stylesheet" type="text/css" title="Windows Default"> -->
<!-- <link href="lib/os-gui/build/peggys-pastels.css" rel="alternate stylesheet" type="text/css" title="Peggy's Pastels"> -->
<link href="lib/tracky-mouse/core/tracky-mouse.css" rel="stylesheet" type="text/css">
<!--
@TODO: bring these styles into OS-GUI.
This is a custom build of 98.css https://github.com/jdan/98.css
for checkboxes, radio buttons, sliders, and fieldsets,
excluding e.g. scrollbars, buttons, and windows (already in OS-GUI),
and integrating with the theme CSS vars used by OS-GUI,
and with some RTLCSS tweaks.
Text inputs and dropdowns are styled in classic.css, but should also be included in OS-GUI at some point.
This is not an @import in classic.css because it needs RTLCSS and I'm not applying RTLCSS to themes yet.
So I added .not-for-modern logic to theme.js to exclude these styles depending on the theme.
-->
<link href="lib/98.css/98.custom-build.css" class="flippable-layout-stylesheet not-for-modern" rel="stylesheet"
type="text/css">
<link rel="apple-touch-icon" href="images/icons/apple-icon-180x180.png">
<!-- Chrome will pick the largest image for some reason, instead of the most appropriate one. -->
<!-- <link rel="icon" type="image/png" sizes="192x192" href="images/icons/192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="images/icons/96x96.png"> -->
<!-- <link rel="icon" type="image/png" sizes="16x16" href="images/icons/16x16.png"> -->
<link rel="shortcut icon" href="favicon.ico">
<link rel="mask-icon" href="images/icons/safari-pinned-tab.svg" color="red">
<link rel="manifest" href="manifest.webmanifest">
<meta name="msapplication-TileColor" content="#008080">
<meta name="msapplication-TileImage" content="images/icons/ms-icon-144x144.png">
<meta name="theme-color" content="#000080">
<meta name="viewport" content="width=device-width, user-scalable=no">
<meta name="description" content="Classic MS Paint in the browser, with extra features" />
<meta property="og:image:width" content="279">
<meta property="og:image:height" content="279">
<meta property="og:description" content="Classic MS Paint in the browser, with extra features.">
<meta property="og:title" content="JS Paint">
<meta property="og:url" content="https://jspaint.app">
<meta property="og:image" content="https://jspaint.app/images/icons/og-image-279x279.jpg">
<meta name="twitter:title" content="JS Paint">
<meta name="twitter:description" content="Classic MS Paint in the browser, with extra features">
<meta name="twitter:image" content="https://jspaint.app/images/meta/twitter-card-plz-no-crop.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@isaiahodhner">
<meta name="twitter:creator" content="@isaiahodhner">
<script src="src/error-handling-basic.js"></script>
<script type="module" src="src/theme.js"></script>
</head>
<body>
<div id="about-paint" style="display: none">
<div id="about-paint-header">
<img src="images/icons/128x128.png" width="128" height="128" id="about-paint-icon" alt="" />
<div id="about-paint-beside-icon">
<h1 id="jspaint-project-name">JS Paint</h1>
<div id="jspaint-version" title="About time to increment the version number, don't you think?">
Version 1.0.0+
</div>
<div id="jspaint-update-status-area" hidden>
<!-- perhaps this can be merged with the container now that it's the only child -->
<div id="maybe-outdated-line">
<div id="outdated" hidden>
<div class="on-official-host">
There's a new version of JS Paint.
<a id="refresh-to-update" href=".">Refresh</a> to get it.
</div>
<div class="on-third-party-host">
This instance of JS Paint is outdated compared to
<a href="https://jspaint.app" target="_blank">jspaint.app</a>.
</div>
<div class="on-dev-host">
This version of JS Paint is outdated compared to
<a href="https://jspaint.app" target="_blank">jspaint.app</a>.
</div>
</div>
<div id="checking-for-updates" hidden>
Checking for updates...
</div>
<div id="failed-to-check-if-outdated" hidden>
Couldn't check for updates.
<span class="navigator-offline">You're offline.</span>
<span class="navigator-online">JS Paint may be outdated.</span>
</div>
</div>
</div>
</div>
<!-- @#: What's New? -->
<button id="view-project-news">What's New?</button>
</div>
<!-- <p>JS Paint is a web-based MS Paint remake by <a href="https://isaiahodhner.io/">Isaiah Odhner</a>.</p> -->
<!-- @#: Isaiah Odhner -->
<p>MS Paint remake by <a href="https://isaiahodhner.io/" target="_blank">Isaiah Odhner</a></p>
<!-- <p>Read about the project and extra features on <a href="https://github.com/1j01/jspaint#readme">the readme</a>.</p> -->
<!-- <p>Request features and report bugs <a href="https://github.com/1j01/jspaint/issues">on GitHub</a>
or <a href="mailto:[email protected]?subject=JS%20Paint">by email</a>.</p> -->
<p>
Feedback:
<a href="https://github.com/1j01/jspaint/issues" target="_blank">GitHub</a>
or <a href="mailto:[email protected]?subject=JS%20Paint">E-mail</a>
or <a href="https://discord.gg/jxQBK3k8tx" target="_blank">Discord</a>
</p>
<!-- <p>Support the project at <a href="https://www.paypal.me/IsaiahOdhner"
target="_blank">paypal.me/IsaiahOdhner</a>.</p> -->
<p>Donate: <a href="https://www.paypal.me/IsaiahOdhner" target="_blank">paypal.me/IsaiahOdhner</a></p>
<p>
<a href="about.html" target="_blank">Homepage</a>
·
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt" target="_blank">MIT License</a>
·
<a href="privacy.html" target="_blank">Privacy Policy</a>
</p>
</div>
<script type="module" src="src/test-news.js"></script>
<!--
Before publishing a news update, make sure:
- All important changes are mentioned. Check the commit history, ideally.
- The <time> element matches the date of the update.
- The id of the <article> will never need to be changed.
I'm using the format "news-YYYY-very-brief-description".
I'm avoiding putting the full date in the id, in case I push the update later.
The id is used to check for updates, and is stored in localStorage.
- The console shows no errors.
test-news.js checks for some problems.
- HTML is valid.
- News indicator is updated and reads well.
-->
<div id="news" hidden>
<!-- maybe release Textual Paint first as an April Fools day prank? -->
<!-- <article class="news-entry" id="news-2024-textual-paint">
<h1>Paint... in the Terminal!?</h1>
<time datetime=""></time>
<img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" />
<p>
I have created an entirely new MS Paint clone, called <a target="_blank" href="https://github.com/1j01/textual-paint">Textual Paint</a>.
</p>
<p>
JS Paint runs in the browser, bringing nostalgia to the web, but if it's not retro enough for you,
what's more retro than a terminal?
</p>
<p>
Once again, I've implemented basically every feature of MS Paint, but this time as a Text User Interface (TUI).
</p>
<p>
Textual Paint is written in Python, and is built with the <a target="_blank" href="https://textual.textualize.io/">Textual</a> framework.
</p>
<p>
To install Textual Paint, first make sure you have Python 3.10 or later.
Then run <code>pip install textual-paint</code>
(or <code><a target="_blank" href="https://github.com/pypa/pipx">pipx</a> install textual-paint</code>).
</p>
<p>
To run Textual Paint, run <code>textual-paint</code>.
You'll need to use a modern terminal emulator with Unicode and true color support,
such as Windows Terminal, GNOME Terminal, or iTerm2.
</p>
-->
<article class="news-entry" id="news-2024-split-eye-gaze-mode">
<h1>More Accessibility Options, and new Discord Server</h1>
<time datetime="2024-12-23">2024-12-23</time>
<!-- <img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" /> -->
<h2>Separated out accessibility features</h2>
<p>I've split up "Eye Gaze Mode" into several discrete features:</p>
<ul>
<li>
<strong>↩️ Quick Undo Button</strong>: Adds a floating undo button that's always visible.
</li>
<li>
<strong>🔍 Enlarge UI</strong>: Enlarges menus, buttons, and windows.
</li>
<li>
<strong>⏱️ Dwell Clicker</strong>: Automatically clicks by hovering over controls for a time.
</li>
<li>
<strong>↕️ Vertical Color Box</strong>: Makes the Colors box tall instead of wide.
(This one was already split out.)
</li>
</ul>
<p>I've also added a brand-new feature:</p>
<ul>
<li>
<strong>🧑 Head Tracker</strong>: Lets you move the cursor with your head.
(See demo video below!)
</li>
</ul>
<!-- <p>Initially I developed Eye Gaze Mode as a monolithic feature because it was easier,
as it limited the combinatorial complexity of the intersection of various features.
For instance, any theme can be used with any feature. (Not sure how to explain this better.)
But I decided it was worth the extra development overhead to separate these out.</p>
<p>I had to fix a number of things, and generalize various code in order to handle different combinations of
these features.
For instance, the Dwell Clicker adds a floating button and Quick Undo Button adds a floating button;
previously they were always enabled together, but now I have to calculate the width needed for the
currently enabled buttons,
also different based on whether they're scaled with Enlarge UI mode,
in order to move the status bar text over,
and the colors box over if Vertical Colors Box is not enabled, which previously it always would be
together with those features.</p> -->
<h2>↩️ Quick Undo Button</h2>
<p><b>Extras > Quick Undo Button</b> is a huge advancement for usability on touchscreen devices.</p>
<p>Making mistakes is no more a chore. Just tap, tap, tap to undo. Easy as that.</p>
<h2>🔍 Enlarge UI</h2>
<p><b>Extras > Enlarge UI</b> increases the size of menus, buttons, and windows.</p>
<p>Scaling of toolbars and menus will be limited
to the available space on the screen, making it more useful on mobile devices,
compared to the basic UI scaling that existed in Eye Gaze Mode.</p>
<h2>⏱️ Dwell Clicker</h2>
<p><b>Extras > Dwell Clicker</b> automatically clicks by hovering.</p>
<p>It's intended for use with an eye tracker or head tracker — software (and optionally
specialized hardware) that lets you move the mouse without using your hands.</p>
<p>This was the core feature of Eye Gaze Mode, but it can now be used in any UI configuration: with any
theme, with or without enlarged UI, with a horizontal or vertical colors box.</p>
<h2>🧑 Head Tracker (All-New Feature!)</h2>
<p>
<b>Extras > Head Tracker</b> mode uses your webcam to
track your head movements and move the mouse cursor accordingly.
</p>
<p>It's powered by
<a href="https://trackymouse.js.org" target="_blank"><img src="images/tracky-mouse-16x16.png"
alt="" />Tracky Mouse</a>.
</p>
<p>I recorded a short demonstration video, drawing the Tracky Mouse logo using Tracky Mouse:
</p>
<figure>
<video style="width: 100%; height: auto;" controls loop muted playsinline autoplay>
<source src="images/tracky-mouse-jspaint-demo.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
<figcaption>
<!-- Drawing the Tracky Mouse logo using Tracky Mouse. -->
I made sure to tilt the webcam down so you can see that I'm not using my arms.
</figcaption>
</figure>
<a href="https://trackymouse.js.org" target="_blank">
<img src="lib/tracky-mouse/images/tracky-mouse-logo-512.png" width="128" height="128"
alt="Tracky Mouse logo" style="float: right" />
</a>
<p>To try it out:</p>
<ul>
<li>Enable <b>Extras > Head Tracker</b>.</li>
<li>Click <b>Start</b> and allow access to your camera.</li>
<li>Ensure your face is well lit and centered in the camera's view.</li>
<li>Adjust sensitivity and smoothness settings to your liking.</li>
<li>Move your head to move the red dot on the page, which acts as your cursor, and hover over elements
to click them.</li>
</ul>
<p>Tips:</p>
<ul>
<li>You can pause dwell clicking by hovering over the pause button
(<img alt="an eye with three zees" src="images/classic/eye-gaze-pause.svg"
style="vertical-align: middle; width: 1em; height: 1em;" />)
in the bottom left corner.</li>
<li>When hovering over a window title bar, or using certain tools on the canvas,
like the brush, it knows to hold down the mouse button to drag or draw.
The next dwell will release instead of clicking.</li>
<li>If you minimize the Tracky Mouse window, it will minimize to a button in the menu bar,
which you can click to restore the window.
If you close the window, it will disable the feature.</li>
</ul>
<p>
Right now this only works with dwell clicking, and automatically enables the Dwell Clicker,
but in the future, it may work with other ways of clicking, like blinking or smiling.
</p>
<p>
You can also download the
<a href="https://trackymouse.js.org" target="_blank">Tracky Mouse</a>
desktop app to control your whole computer with your head.
</p>
<h2>Discord server</h2>
<figure>
<a href="https://discord.gg/jxQBK3k8tx" target="_blank">
<img width="470" height="470" src="https://flipanim.com/gif/b/h/BhZWtwfg.gif"
alt="Animation of Discord logo blinking its eyes">
</a>
<figcaption>
Animation credit:
<a href="https://flipanim.com/profile?name=Wolfie-le-Wolf" target="_blank">Wolfie-le-Wolf</a>
</figcaption>
</figure>
<p>Join the new
<a href="https://discord.gg/jxQBK3k8tx" target="_blank">
98.JS.org Discord server</a>
to discuss these features and share your JS Paint drawings!
</p>
<h2>Improved About window</h2>
<p>I managed to make the <b>Help > About Paint</b> window much more compact while including more
information.</p>
<p>See a <a href="https://github.com/1j01/jspaint/commit/da994966ec2231743867f232d8e1fb334071b512"
target="_blank">visual
comparison here</a>.
You can switch to swipe or onion skin modes and move the slider to compare before and after images.</p>
<p>Here's what's new:</p>
<ul>
<li>Reworded everything to be terser, using short labels instead of full sentences.
(<a href="https://github.com/1j01/jspaint/commit/7bc134226f9994cd68fd69d78fb0343f5c873d53"
target="_blank">⟜</a>)
</li>
<li>Replaced <a href="https://github.com/1j01/jspaint#readme" target="_blank">readme</a> link
with a link to the <a href="https://jspaint.app/about.html" target="_blank">homepage</a>.
(<a href="https://github.com/1j01/jspaint/commit/7bc134226f9994cd68fd69d78fb0343f5c873d53"
target="_blank">⟜</a>)
</li>
<li>Added a link to the Discord server.
(<a href="https://github.com/1j01/jspaint/commit/7bc134226f9994cd68fd69d78fb0343f5c873d53"
target="_blank">⟜</a>)
</li>
<li>Made sure all links open in a new tab.
(<a href="https://github.com/1j01/jspaint/commit/7bc134226f9994cd68fd69d78fb0343f5c873d53"
target="_blank">⟜</a>)
</li>
<li>Made it use a bigger app icon
(<a href="https://github.com/1j01/jspaint/commit/3dd5ee94ba123a053c7fee64a112914902d825ae"
target="_blank">⟜</a>);
also created a bigger version of the gay pride icon variant for pride month
(<a href="https://github.com/1j01/jspaint/commit/be03638fad35a51cacef5c4f524c81581c2be587"
target="_blank">⟜</a>)
</li>
<li>Added a version number back into the About window
(<a href="https://github.com/1j01/jspaint/commit/224cc37bfa022b912c9b48e70d0e7a412c0f3ec6"
target="_blank">⟜</a>)
and moved it to a new line
(<a href="https://github.com/1j01/jspaint/commit/aaed9a78945aeec13b15d74dca0502474e268c1e"
target="_blank">⟜</a>)
</li>
<li>Moved the update check status indicator beside the icon
(<a href="https://github.com/1j01/jspaint/commit/c1b407d9134af75533adb3fd3938a82d3a2d71dd"
target="_blank">⟜</a>)
</li>
<li>Fixed centering of the About window on small screens
(<a href="https://github.com/1j01/jspaint/commit/2aa2e66b8ba5927ee833b68480342f1d62477940"
target="_blank">⟜</a>)
</li>
</ul>
<h2>Misc. Changes</h2>
<ul>
<li>I updated JS Paint within <a href="https://98.js.org/" target="_blank">98.js.org</a>
recently, although it might not have the latest changes yet.
</li>
<li>If an image is dropped on the Project News window, it will no longer try to open it as a document.
It was easy to do this accidentally by idly clicking and dragging while reading the news.
(<a href="https://github.com/1j01/jspaint/commit/77d341378f9a8d84f0f922ab6545e4cab5fca038"
target="_blank">⟜</a>)
</li>
<li>Fixed checkbox menu items showing a grid of small checkmarks instead of a single checkmark
in the Electron app with Enlarge UI mode enabled.
(<a href="https://github.com/1j01/jspaint/commit/f367ce043a81c7fdea2c506fcf5aee1ac18b56e3"
target="_blank">⟜</a>)
</li>
<li>The Dwell Clicker can now toggle collapsed details in error dialogs.
(<a href="https://github.com/1j01/jspaint/commit/2637467762753234af4d40d097af7f1c2b6aea35"
target="_blank">⟜</a>)
</li>
<li>Enabled dragging toolbars with the mouse in Dwell Clicker
(but not with dwell clicker itself).
(<a href="https://github.com/1j01/jspaint/commit/7094171b1644391f4ea3b3467865952c39427a4e"
target="_blank">⟜</a>)
</li>
<li>Fixed the titlebar size for tool windows in Enlarge UI mode.
(<a href="https://github.com/1j01/jspaint/commit/a2691ec7c953c3eca7b56f4831c47855044fff6d"
target="_blank">⟜</a>)
</li>
<li>All themed tool icons now work in Enlarge UI mode.
(<a href="https://github.com/1j01/jspaint/commit/068c61d20c907925a140677eecd5513e03397fbc"
target="_blank">⟜</a> +
<a href="https://github.com/1j01/jspaint/commit/9e6a19c9aa6434a354f067357487946ea92a1670"
target="_blank">⟜</a> +
<a href="https://github.com/1j01/jspaint/commit/8dc1286dd0d4dc219f043b7e2199e47b8cb52f48"
target="_blank">⟜</a>)
</li>
<li>I've implemented the size status indicator for all tools.
(<a href="https://github.com/1j01/jspaint/commit/3500496b0801a52e05d5ae69f6f98bb09e477e86"
target="_blank">⟜</a>)
</li>
<li>Fixed missing width/height labels in <b>Image > Attributes</b> dialog.
(<a href="https://github.com/1j01/jspaint/commit/af94876a4ffef95c2d00c0022d89a452034793b7"
target="_blank">⟜</a>)
</li>
<li>Fixed default file name when saving a wallpaper with <b>File > Set As Wallpaper
(Tiled/Centered)</b>.
(<a href="https://github.com/1j01/jspaint/commit/8f89d874a77cc8cf00e4731e3979ceaac070aad7"
target="_blank">⟜</a>)
</li>
<li>Fixed default file name when saving selection with <b>Edit > Copy To</b>,
and an error that occurred with <b>Edit > Copy To</b> in the Electron app.
(<a href="https://github.com/1j01/jspaint/commit/fc59a369fd306bedc31611e6729f4fc2ae9e2cc0"
target="_blank">⟜</a>)
</li>
<li>Fixed ignoring of file format choice when saving a palette with <b>Colors > Save Colors</b>
in the Electron app
(<a href="https://github.com/1j01/jspaint/commit/2f8f4e7c51a0c1b2d2d26828855d888e8ed641f9"
target="_blank">⟜</a>)
</li>
<li>Fixed error handling for clipboard access
(<a href="https://github.com/1j01/jspaint/commit/193404199b384532a945f382a56fa2deca679950"
target="_blank">⟜</a>)
</li>
<li>Fixed <kbd>Shift+Insert</kbd>, <kbd>Ctrl+Insert</kbd>, and <kbd>Ctrl+Delete</kbd> shortcuts
(<a href="https://github.com/1j01/jspaint/commit/e3ef424d61e0621e36adeeaa37070d07cf7f5d29"
target="_blank">⟜</a>)
</li>
<li>Improved error message dialogs for Imgur upload failures
(<a href="https://github.com/1j01/jspaint/commit/cbfc9cad47b3aa3c6a8acc6ce525b2b426608dca"
target="_blank">⟜</a>)
</li>
<li>Added a <a href="https://jspaint.app/privacy" target="_blank">Privacy Policy</a>
(<b>tl;dr</b>: jspaint doesn't try to track you, unlike most of the internet)
(<a href="https://github.com/1j01/jspaint/commit/9d2d4040c0b20532e3819aa662e39b9fd8f3f543"
target="_blank">⟜</a>)
</li>
</ul>
<h2>Behind-the-Scenes Changes</h2>
<ul>
<li>Updated <a href="https://os-gui.js.org/" target="_blank">OS-GUI.js</a> from ~0.6.0 to 0.7.3
<ul>
<li>Made use of OS-GUI's new <code>AccessKeys</code> API
(<a href="https://github.com/1j01/jspaint/commit/e6ad042195ce70470f401c400d62575d6c09ea6f"
target="_blank">⟜</a>
+
<a href="https://github.com/1j01/jspaint/commit/52b7b7632046b578b721a84bf0ceaca6ae3e1a8d"
target="_blank">⟜</a>
+
<a href="https://github.com/1j01/jspaint/commit/a9fe9d3f8d1d1ff176b7fd91fd9463e66c08cf73"
target="_blank">⟜</a>)
</li>
<li>
Now using proper <a
href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-keyshortcuts"
target="_blank"><code>aria-keyshortcuts</code></a>
values to tell screen reader software about keyboard shortcuts
(<a href="https://github.com/1j01/jspaint/commit/be233d07e918703b122c415369be5679f588e0c3"
target="_blank">⟜</a>)
</li>
</ul>
</li>
<li>
Dwell Clicker is now powered by Tracky Mouse.
(<a href="https://github.com/1j01/jspaint/commit/1598f42d309ad878b762c5a411476617b16c22f2"
target="_blank">⟜</a>)
</li>
<li>Migrated to ESLint v9 and its flat config, and improved code uniformity.
(<a href="https://github.com/1j01/jspaint/compare/a140810a897afa9dd6111cffb065e5d8f71f1ed8...bddaaffd2837b9696e8e6e159f742b05391067c7"
target="_blank">⟜⧟...⊸</a>)
</li>
<li>Simplified code quality checking workflow by bundling different checks into one script
(<code>npm run lint</code>)
(<a href="https://github.com/1j01/jspaint/commit/991b1cceaa12a40701d251f5dcd109d23903e614"
target="_blank">⟜</a>)
</li>
<li>Improved handling of errors with no stack trace, like <code>SyntaxError</code>.
It will now show the URL and line/column numbers for errors with no call stack.
(<a href="https://github.com/1j01/jspaint/commit/dafef7b1b4f2d33c32c434b1e1fe0ffbc271600d"
target="_blank">⟜</a>)
</li>
<li>Fixed an error that occurred when embedded in 98.js.org due to looking for menus
inside the app frame when they're actually outside
(<a href="https://github.com/1j01/jspaint/commit/421fad7dc25c4d27caa46535a2641b12e49a9b18"
target="_blank">⟜</a> +
<a href="https://github.com/1j01/jspaint/commit/04804396e187b1bf4af9cc31ca449d18661f97ed"
target="_blank">⟜</a>)
</li>
<li>Marked more parts of the JS Paint <code>systemHooks</code> API as optional
(<a href="https://github.com/1j01/jspaint/commit/2fe8c35ca6500cc32584dae30f8c3d4ffe989aa3"
target="_blank">⟜</a>)
</li>
<li>Defined TypeScript types for the JS Paint <code>systemHooks</code> API
(<a href="https://github.com/1j01/jspaint/commit/cb8bc8893d3c9c049916ac5873a15edc7262ac73"
target="_blank">⟜</a>)
</li>
<li style="color: transparent;">Worked on secret features which should be really fun in the future.</li>
</ul>
<p>
Merry Christmas! 🎄🎁
</p>
</article>
<article class="news-entry" id="news-2024-bubblegum-theme">
<h1>Bubblegum Theme</h1>
<time datetime="2024-04-27">2024-04-27</time>
<img width="946" height="800" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="Screenshot of JS Paint with the Bubblegum theme"
src="https://i.postimg.cc/6QwYrWjM/Screen-Shot-2024-04-27-at-14-57-48.png" />
<h2>Bubblegum Theme</h2>
<p>
Introducing the <b>🫧 Bubblegum</b> theme!
</p>
<p>
Office software has never looked so refined, as with the Bubblegum theme's elegant
<em><span style="
font-family: cursive;
font-style: italic;
transform: skewX(-10deg);
display: inline-block;
/*background: linear-gradient(to bottom, #a090c6, #efa9d3); background-clip: text; color: transparent;*/
color: #d39cce;
text-shadow: 0 1px 1px #febee8, 0 -1px 1px #a892c8;
">Business Pink</span>
color scheme.</em>
</p>
<p>
All the icons in this theme were <strong>AI-generated</strong>, as an experiment.
</p>
<p>
Current mainstream AI technology really struggles at creating a full set of icons in one go,
and the style is always a little bit different from image to image,
so the process involved generating many, many images,
usually prompting it to create an "icon set" in order to <em>hopefully</em> get a few icons that match,
to add to the overall icon set.
</p>
<p>
There's very little control, and things often come out wonky or overly generic,
but overall it takes very little effort,
so it's a strange thing.
</p>
<p>
Luckily, I could get away with some stylistic differences between
the abstract shape icons and the more detailed tool icons.
</p>
<p>
I hand-edited a few of the icons, but they're mostly as they came out of the AI.
My favorite is the Eye Gaze Mode pause button icon (<img alt="an eye"
src="images/bubblegum/eye-gaze-unpause-128x128.png"
style="vertical-align: middle; width: 1em; height: 1em;" />),
which I got for free randomly due to the AI misinterpreting "eye dropper".
I love the way it blends seamlessly into the button.
</p>
<p>
The 3D bevels are also AI-generated, applied with copious usage of 9-slice borders.
</p>
<h3>Try The New Theme</h3>
<p>
<!-- <img src="help/onestep.gif"> -->
On the <b>Extras</b> menu, click <b>Themes</b>, then <b>Bubblegum</b>.
</p>
<p>
The icons aren't optimized for legibility at small sizes,
so be sure to try it out together with <b>Extras > Eye Gaze Mode</b>,
or else increase your browser's zoom level,
by scrolling the mouse wheel while holding <kbd>Ctrl</kbd> or <kbd>⌘</kbd>,
for the full effect.
</p>
<h2>Maintenance</h2>
<p>
In other news, I've been investing a lot into making JS Paint more maintainable.
<p>
You won't see these changes in the app directly,
but they help make the code more <em>malleable</em>,
like adding water to clay to shape it without breaking it.
</p>
<ul>
<li>
Converted the codebase almost entirely to <strong>ES Modules</strong>.
</li>
<li>
Made remaining usage of <strong>global variables explicit</strong> in each file.
</li>
<li>
Split up some huge files into <strong>smaller files</strong>.
</li>
<li>
Added <strong>type annotations</strong> to the codebase, using JSDoc comments.
This way errors can be spotted earlier, and editors can provide better autocompletion,
all while keeping the code in plain JavaScript, with no compile step.
</li>
</ul>
<p>
These changes will make it easier to add cool new features to JS Paint in the future.
</p>
<!--
(It's "JS Paint", not "TS Paint", after all!)
<p>
P.S. <strong>Fun fact</strong>: ECMAScript (ES) is a synonym for JavaScript (JS),
which is a completely different language from Java.
The more you know!
Also, TypeScript (TS) is a superset of JavaScript, and the TypeScript compiler (tsc)
can be used to check JavaScript code for type errors, not just compile TypeScript code.
</p>
<p>
(It's not "ES Paint", but it <em>could have been</em>,
and then I could have designed a logo with a rotated 'M' to make it an 'E'.
Missed opportunity?? 🤔)
</p> -->
</article>
<article class="news-entry" id="news-2024-themes-and-save-formats">
<h1>Themes and File Formats</h1>
<time datetime="2024-02-22">2024-02-22</time>
<!-- Oops, forgot an image. TODO? -->
<!-- <img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" /> -->
<h2>New and Updated Themes</h2>
<p>
The Modern theme has been renamed <b>Modern Light</b> and a new <b>Modern Dark</b> theme has been added.
</p>
<div
style="background: #c0c0c0; padding: 1em; padding-bottom: 0.1em; border-radius: .5em; border: 1px inset;">
Modern Light before (tool icons from Windows Vista):<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/master/images/modern/vista-tools-spaced-like-svg.png?raw=true">
<div style="line-height:50%;"><br></div>
Modern Light after (custom SVG icons):<br>
<!-- TODO: custom pixel-fine-tuned PNG; SVG is designed to align with the pixel grid but it's not perfect -->
<!-- <img alt="" src="images/modern/modern-light-tools.png"> -->
<img alt=""
src="https://github.com/1j01/jspaint/blob/master/images/modern/modern-light-tools.svg?raw=true">
</div>
<p>
I designed a custom icon set, inspired by Windows Vista's icons.
I also customized the icons significantly for the Modern Dark theme,
to improve the contrast across the board.
</p>
<div
style="background: #222222; color: #aaaaaa; padding: 1em; padding-bottom: 0.1em; border-radius: .5em; border: 1px inset;">
Modern Dark before (tool icons from Windows Vista, with aliasing artifacts when viewed against a dark
background):<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/master/images/modern/vista-tools-spaced-like-svg.png?raw=true">
<div style="line-height:50%;"><br></div>
Modern Dark after (custom SVG icons):<br>
<!-- <img alt="" src="images/modern/modern-dark-tools.png"> -->
<!-- To be consistent, use SVG here too. At least you can zoom in and see the scalability. -->
<!-- Though I'd prefer a nice magnifier effect like https://lenadesign.org/2021/06/30/css-javascript-image-magnifier-glass/ -->
<!-- Onion skin / swipe would be nice for comparison as well. -->
<img alt=""
src="https://github.com/1j01/jspaint/blob/03f934eab98f450d3e527ef8e152cc5aa003fe07/images/modern/modern-dark-tools.svg?raw=true">
</div>
<p>
When adapting the Vista icons to scalable vector graphics,
I had to make lots of subtle design decisions, but the biggest difference you'll notice is that
the Brush tool matches the Classic style of a flat tipped brush (albeit with added gradients),
since I really didn't like the Vista Brush tool icon.
I mean come on, it's the only icon that's <em>cut off, by design!</em>
And it just looks like a blob to me, at least the purple version. (There's also a green version.)
Besides, a wide brush emphasizes the distinction from the Pencil tool.
</p>
<p>
The Classic theme has been renamed <b>Classic Light</b>, and the <b>Classic Dark</b> theme has been
updated.
</p>
<div style="background: #c0c0c0; padding: 1em; border-radius: .5em; border: 1px inset;">
Classic Light (still using tool icons from Windows 98):<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/03f934eab98f450d3e527ef8e152cc5aa003fe07/images/classic/tools.png?raw=true">
</div>
<div style="background: #222222; color: #aaaaaa; padding: 1em; border-radius: .5em; border: 1px inset;">
Classic Dark before:<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/43d68cf8eee4cf8aa30e3047955c2bb8315c92f2/images/dark/tools.png?raw=true">
<div style="line-height:50%;"><br></div>
Classic Dark after:<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/03f934eab98f450d3e527ef8e152cc5aa003fe07/images/dark/tools.png?raw=true">
</div>
<p>
I wasn't happy with the Classic Dark theme's icons, especially my use of color;
it felt like a cheap RGB color inversion, even though I'm sure I gave it some more thought than that.
I also wasn't sure how to achieve good contrast with the pencil tip while maintaining a consistent
skeuomorphic style, since graphite is dark just like the background.
So I've redesigned the pencil in a more abstract way (all white including the tip),
and I've made the icons monochrome.
</p>
<p>
I'm actually a big fan of color in icons, as I think it helps to distinguish them,
so if anyone wants to try their hand at a Classic Dark theme with colored icons,
I'd be interested to see it!
</p>
<p>
That said, monochrome has a nice elegance to it; it's less attention grabbing,
and I'm sure some people will always prefer it.
</p>
<p>
I also updated the <b>Occult</b> theme.
I changed the Fill With Color tool into a pouring cauldron, the Pick Color tool into a dagger,
and the Magnifier into an eyeball.
</p>
<div style="background: #660c08; color: #aaaaaa; padding: 1em; border-radius: .5em; border: 1px inset;">
Occult before:<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/43d68cf8eee4cf8aa30e3047955c2bb8315c92f2/images/occult/tools.png?raw=true">
<div style="line-height:50%;"><br></div>
Occult after:<br>
<img alt=""
src="https://github.com/1j01/jspaint/blob/03f934eab98f450d3e527ef8e152cc5aa003fe07/images/occult/tools.png?raw=true">
<div style="line-height:50%;"><br></div>
(Still needs more ideas.)
</div>
<p>
Check out the themes in <b>Extras > Themes</b>!
</p>
<h2>File Formats</h2>
<aside
style="background-color: #ffffbb; padding: .5em 1em; border-radius: .5em; border: 1px outset; font-size: smaller">
<p>
These changes have actually been around since 2021,
but I didn't get around to finishing a news post about them until now.
</p>
<p>
Still, there's some good changes here, so it bears mentioning!
</p>
</aside>
<!-- I disabled this feature for now, as I saw some 0 bytes files saved, and I REALLY don't want people to lose work!
It needs thorough testing / gradual rollout.
Besides that, I didn't want to promote another feature available only in Chrome.
<p>
<b>Save</b> (<kbd>Ctrl+S</kbd>) can now save over the open file, in Chrome, Edge, and Opera browsers.
This works using new <a target="_blank"
href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API">File System Access
API</a>.
Always use <b>Save As</b> (<kbd>Ctrl+Shift+S</kbd>) if you want to save a new file.
</p> -->
<p>
<img alt="" src="images/save.gif" width="50" height="50" style="float: right;" />
<b>File > Save As</b> now asks for a file name and format.
PNG, GIF, and BMP are supported, including indexed color BMPs.
</p>
<p>
See the <a href="https://github.com/1j01/jspaint?tab=readme-ov-file#supported-file-formats"
target="_blank">Supported File Formats</a> table for details.
</p>
<p>
<img alt="" src="images/idea.gif" width="33" height="26" style="float: left;" />
Tip: Use PNG if you don't have a specific reason to use another format,
as it has the best quality when saving.
</p>
<!-- I disabled this for now, I think because it's unclear if you'd always want to load the palette when opening an image,
or if there should be some UI around it, like an undo button beside the palette, or a prompt to load the palette.
Also indexed PNGs should be handled consistently with indexed BMPs.
The thing is, if you save an image with a subset of colors from a palette, and then load the image,
you might not want to load the palette, as it may simply remove colors from the palette,
particularly if it was the default palette.
But a prompt seems annoying. Maybe it should only load the palette for monochrome (two-color) images?
It's unclear.
<p>
If you open a BMP file with a palette, the palette is loaded into the Colors box.
If you load a monochrome BMP file, it loads a gradient of dither patterns into the Colors box.
</p>-->
<p>
<!-- <img alt="" src="help/p_monochrome.png" width="14" height="11" style="vertical-align: middle;" /> -->
<b>Black and White</b> mode in <b>Image > Attributes</b> is generalized to handle two arbitrary colors
(although it's still called "Black and White" in the Attributes window.)
</p>
<p>
If an image has only two colors, when switching to "Black and White" mode,
it automatically adapts to these colors and fills the Colors box with appropriate dither patterns.
</p>
<p>
If you use <b>Image > Invert</b> in Black and White mode,
it now swaps the two colors present in the image,
instead of converting colors to their RGB opposites.
If the image is pure black and white, these two operations are equivalent,
but now, for example, a <i>green-and-black</i> image will become a <i>black-and-green</i> image,
rather than <i>pink-and-white</i>.
</p>
<p>
<b>Colors > Save Colors</b> also now asks for a file name and format.
An absurd number of file formats are <a
href="https://github.com/1j01/jspaint?tab=readme-ov-file#color-palette-formats"
target="_blank">supported</a>.
You can even export CSS variables for use in a web design project.
</p>
<p>
<img alt="" src="images/idea.gif" width="33" height="26" style="float: left;" />
RIFF Palette (*.pal) is compatible with MS Paint, and GIMP Palette (*.gpl) is compatible with many open
source graphics programs such as Inkscape and Krita.
</p>
<p>
You can find lots of palettes to use on
<a target="_blank" href="https://lospec.com/palette-list">Lospec</a>.
Download a palette as GIMP GPL and use <b>Colors > Get Colors</b> to select
the file, or drag and drop the file onto JS Paint to load the palette.
</p>
<h2>API</h2>
<p>
I also documented a first version of the API for JS Paint.
</p>
<p>
I have not yet put much design into the API; rather, I have just documented the interface I came up with
for my own use in <a href="https://98.js.org" target="_blank">98.js.org</a>.
In other words, this is more of a draft of an API, though I will certainly create a changelog
when I decide to clean it up.
So feel free to start using it if you don't mind updating your code as the API changes.
</p>
<p>
If you want to embed JS Paint in your own project, you can take a look at the
<a href="https://github.com/1j01/jspaint?tab=readme-ov-file#embed-in-your-website" target="_blank">
Embed in your website</a> section of the readme.
<!-- Hm, I might want to move that to a separate markdown file... but maybe keep that heading around so the link stays valid. -->
</p>
<h2>Homepage</h2>
<p>
JS Paint now has a homepage at <a href="https://jspaint.app/about"
target="_blank">jspaint.app/about</a>.
</p>
<p>
I designed it like a 90s website.
You know, one of those thoughtless background pattern filled ones.
</p>
<p>
<a href="https://jspaint.app/about" target="_blank">
<img src="images/enter.gif" alt="Click to Enter" width="145" height="77" />
</a>
</p>
</article>
<article class="news-entry" id="news-2022-open-source">
<h1>Open Source</h1>
<time datetime="2022-07-28">2022-07-28</time>
<!-- <img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" /> -->
<p>
JS Paint is now finally open source, licensed under the
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt" target="_blank">MIT License</a>.
</p>
<p>
The project has been
<a href="https://en.wikipedia.org/wiki/Source-available_software" target="_blank">source-available</a>
from the beginning, since I never felt the need to obfuscate or hide the code,
but it is now legally open source.
</p>
<p>
I look forward to seeing how you use JS Paint in your own projects!
</p>
<p>
There is not yet a formal API for JS Paint,
but if you want to get in on the cutting edge,
you can take a look at how <a href="https://98.js.org" target="_blank">98.js.org</a>
embeds JS Paint.
Expect the API to change significantly in the future.
</p>
<p>
I've been meaning to open source JS Paint for a long time.
There are some legal issues, resources I don't have the copyright to,
but I think they should generally fall under fair use.
And I have created beautiful SVG versions of the icons,
so it's likely possible to have a version of JS Paint without any directly copyrighted resources.
But I've finally decided to stop worrying about it,
and just open source it already!
</p>
<p>
I hope you enjoy JS Paint!
</p>
</article>
<article class="news-entry" id="news-2021-saving">
<h1>The GUIcci Update</h1>
<time datetime="2021-12-08">2021-12-08</time>
<img width="640" height="360" style="max-width: 100%; height: auto; image-rendering: auto;" alt=""
src="https://i.postimg.cc/tgBncKfJ/guicii-update.png" />
<h2>New Features</h2>
<p>
<b>View > Zoom > Show Thumbnail</b> to show a preview of the image at a small size, great for pixel art.
Make fine, precise edits, while keeping it all in perspective.
</p>
<p>
<b>Pinch zooming:</b> If you have a touch screen, use two fingers to zoom in and out, and pan the view.
</p>
<p>
<b>Alt+Mousewheel</b> to zoom in and out quickly on desktop.
Unlike the Magnifier tool, this allows you to zoom while making (or moving) a selection, for added
precision.
</p>
<p>
Added <b>View > Fullscreen</b> to toggle fullscreen mode. This is nice for using JS Paint on your phone.
</p>
<p>
The <b>Text tool</b> now automatically expands the textbox as you type.
When resizing, there's now a minimum size based on the text in the textbox.
It previews exactly what size it will end up with when resizing.
</p>
<p>
<b>Docking:</b> If you drag the Colors box or Tools box out into a window,
you can now dock it back when dragging the titlebar.
Previously to dock it you had to double click the titlebar, or drag it by the edge of the window.
</p>
<img alt="Area that you have to click to drag a toolbar out into a window (in green)"
src="https://i.postimg.cc/7LB18Gcg/toolbar-drag-out-area.png" width="480" height="380"
style="max-width: 100%; height: auto; image-rendering: auto;" />
<p>
<b>Menus</b> are now fully keyboard (and screen reader) accessible.
In particular, you can hold <kbd>Alt</kbd> and press the access key of a menu button to open the menu,
and then (without <kbd>Alt</kbd>) press the access key of a menu item to select it.
The access key of an item is the underlined letter, or the first letter of the item's text if there's no
underline.
</p>
<p>
<b>Error details</b> are now hidden by default in error dialogs.
The details may be more overwhelming than useful in a lot of cases,
but if you need them, you can expand the details.
</p>
<img alt="Example error message box saying 'File not found', with details collapsed."
src="https://i.postimg.cc/ZR1qpVGw/file-not-found.png" width="408" height="145"
style="max-width: 100%; height: auto; image-rendering: auto;">
<p>
<b>File > Exit</b> now exits to the official web desktop,
<a target="_blank" href="https://98.js.org"><img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABKklEQVR42qWTMW7DMAxFKYC+RC/hQR065xSdOyRDr9CxV8gQDzlK5g7RkLmn6CwCCimSchIoWkrAgESTj5+kHeCfFnpORCyPPiLqxgYP9gC/ZyKYEIG+CPAbn0JCr6okizmgvmdIF8BZxUhNgVdv8k3FvCdIu/u228VBnkiUGTZBzgyb1CcASBqdlhAMUEx6aAokWc8KqGCGzB+0lmMTyI0cBUmSV28zMRXzT4bECiI/l+NUZ/J0Cz0TACx6Tiwej9jfQkvIufZ8eVM1tM+1uiTHyP5P7H9IccvtxGCVSg0W6ZDUdz6cYbe8DgAHKKf3P9j8bnhQ0rSJY0DcOm2gwCFSugKqozT51V704xoDQItFWMeTFp+ZbQEGtm4o3/hsoLa1IaC3ocf/4QotsZxI//135gAAAABJRU5ErkJggg=="
width="16" height="16" alt=""> 98.js.org</a>,
a re-creation of Windows 98, full of games and applications.
</p>
<img class="inset-deep" src="https://i.postimg.cc/SKHrYpx3/98-js-org-screenshot.png"
alt="The 98.js desktop featuring desktop icons, a taskbar, and various application windows."
style="max-width: 100%; height: auto; image-rendering: auto;">
<p>
This project spun out of JS Paint, and I have implemented now
Sound Recorder, Notepad, Calculator, and even Windows Explorer,
to a high level of detail.
</p>
<p>
It also includes projects from other people, other recreations of old programs,
like <a target="_blank" href="https://webamp.org/">Webamp</a>,
a meticulous recreation of Winamp,
and <a target="_blank" href="https://github.com/rjanjic/js-solitaire">JS Solitaire</a>,
a Solitaire clone (I tweaked it for accuracy, adding the card back images, etc.)
</p>
<h2>Pixel Perfect</h2>
<p>
All interface elements are now thematically styled,
powered by <a target="_blank" href="https://os-gui.js.org">OS-GUI.js</a> and
<a target="_blank" href="https://jdan.github.io/98.css/">98.css</a>.
</p>
<p>
The whole interface is now pixel perfect accurate to Windows 98.
(Okay, there's a few things that are a pixel off or so, but seriously,
I lined up a screenshot and got it essentially perfect.)
</p>
<p>
Improved layout of <b>View > Zoom > Custom Zoom</b> window, matching the design in MS Paint.
</p>
<p>
Added padding to all dialogs so they don't feel cramped anymore.
</p>
<p>
Message boxes now include warning or error icons, and play a sound when they appear.
</p>
<p>
Improved <b>View > View Bitmap</b>: it now uses the theme's wallpaper background color,
if the image is smaller than the window.
It now closes with a click or key press, and doesn't let you edit the image (which was weird).
</p>
<p>
The Help window can now be minimized to the bottom of the screen, even though there's no taskbar.
It works like how Windows 98 does if the process managing the taskbar crashes.
</p>
<h2>Fixes</h2>
<p>
<b>Menu buttons</b> are easier to open on a touch screen. Sometimes you had to tap twice before the menu
opened.
</p>
<p>
Fixed <b>large square brush</b> continuity (it left gaps before, due to a half-implemented
optimization).
</p>
<p>
The <b>selection and textboxes</b> no longer "blow up" if you resize them to a minimal size.
They are now limited when you drag an edge past the opposite edge.
</p>
<p>
Fixed a bug where vertically thin selections were difficult or impossible to drag (despite showing a
drag cursor).
(The draggable region was offset outside of the selection box.)
Fixed a similar bug where tool previews would get offset if the canvas's height was very small.
</p>
<p>
Resize handles no longer get smaller when the object to resize is very small.
The draggable region for handles no longer gets smaller either, except in dimensions where it must.
It's now considerably smarter than Windows 10 about where it lets you drag handles from.
</p>
<p>
In <b>Image > Flip/Rotate</b>, you can now click the custom degrees input field before selecting the
"Rotate by angle" option.
</p>
<p>
The magnifier preview and other tool previews are now hidden while dragging the Colors box or Tools box.
It looked confusing when the magnifier preview was shown at the same time as
the preview outline for dragging/docking a tool window.