forked from 1j01/jspaint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
about.html
392 lines (367 loc) · 17.5 KB
/
about.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
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JS Paint — MS Paint online</title>
<!-- <meta http-equiv="Content-Security-Policy" content="
default-src 'self';
img-src 'self' data: blob: http: https:;
"> -->
<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/layout.css" class="flippable-layout-stylesheet" rel="stylesheet" type="text/css">
<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, initial-scale=1.0">
<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">
<link rel="stylesheet" href="styles/themes/classic.css">
<link rel="stylesheet" href="styles/about.css">
</head>
<body>
<section id="main-section">
<h1 style="text-align: center;">
<a href="https://jspaint.app" target="_blank">
<img src="images/icons/jspaint.svg" width="192" height="192" alt="">
<br>
<span id="jspaint-project-name">JS Paint</span>
</a>
</h1>
<p style="text-align: center;">JS Paint is a pixel-perfect remake of Microsoft Paint that runs in the browser.
</p>
<p>
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt" target="_blank"><img
src="images/about/free.gif" width="88" height="39" alt="Free" style="vertical-align: middle"></a>
Open source under the permissive
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt" target="_blank">MIT License</a>.
</p>
<p>
<a href="https://github.com/1j01/jspaint/issues" target="_blank"><img src="images/about/foco.gif" width="40"
height="40" alt="Ideas" style="vertical-align: middle"></a>
Request features and report bugs <a href="https://github.com/1j01/jspaint/issues" target="_blank">on
GitHub</a>
or <a href="mailto:isaiahodhner@gmail.com?subject=JS%20Paint" target="_blank">by email</a>.
</p>
<b>THIS SITE IS...</b>
<img src="images/about/conpaint.gif" width="350" height="50" alt="Under Construction"
style="vertical-align: middle">
<img src=" images/about/red_paint_bucket_brush_md_clr_24887PURPLE.gif" width="87" height="100"
alt="Paint bucket" style="float: right">
<!-- <iframe src="index.html" title="JS Paint app" id="jspaint-iframe"></iframe> -->
<div class="window os-window focused" id="os-window-jspaint"
style="width: 267px; height: 392px; touch-action: none; position: relative; z-index: 9; display: flex;">
<div class="window-titlebar" style="touch-action: none;"><img src="images/icons/16x16.png" width="16"
height="16" draggable="false" style="width: 16px; height: 16px;" alt="">
<div class="window-title-area"><span class="window-title">untitled - Paint</span></div><button
class="window-minimize-button window-action-minimize window-button"
aria-label="Minimize window"><span class="window-button-icon"></span></button><button
class="window-maximize-button window-action-maximize window-button"
aria-label="Maximize or restore window"><span class="window-button-icon"></span></button><button
class="window-close-button window-action-close window-button" aria-label="Close window"><span
class="window-button-icon"></span></button>
</div>
<div class="window-content" tabindex="-1" style="outline: none; display: flex; flex-direction: column;">
<!-- <iframe id="jspaint-iframe" src="index.html" -->
<iframe id="jspaint-iframe" src="/"
style="min-width: 0px; min-height: 0px; flex: 1 1 1px; border: 0px;"></iframe>
</div>
<!-- <div class="handle"
style="position: absolute; top: -2px; right: -2px; width: 4px; height: 4px; touch-action: none; cursor: ne-resize;">
</div>
<div class="handle"
style="position: absolute; top: -2px; left: calc(2px); width: calc(100% - 8px + 4px); height: 4px; touch-action: none; cursor: n-resize;">
</div>
<div class="handle"
style="position: absolute; top: -2px; left: -2px; width: 4px; height: 4px; touch-action: none; cursor: nw-resize;">
</div>
<div class="handle"
style="position: absolute; top: calc(2px); left: -2px; width: 4px; height: calc(100% - 8px + 4px); touch-action: none; cursor: w-resize;">
</div>
<div class="handle"
style="position: absolute; bottom: -2px; left: -2px; width: 4px; height: 4px; touch-action: none; cursor: sw-resize;">
</div>
<div class="handle"
style="position: absolute; bottom: -2px; left: calc(2px); width: calc(100% - 8px + 4px); height: 4px; touch-action: none; cursor: s-resize;">
</div>
<div class="handle"
style="position: absolute; bottom: -2px; right: -2px; width: 4px; height: 4px; touch-action: none; cursor: se-resize;">
</div>
<div class="handle"
style="position: absolute; top: calc(2px); right: -2px; width: 4px; height: calc(100% - 8px + 4px); touch-action: none; cursor: e-resize;">
</div> -->
</div>
<div id="try-me"
style="position: absolute; margin-left: 300px; margin-top: -200px; color: purple; font-weight: bold;">
<img src="images/about/izquierda.gif" width="49" height="23" alt="hand pointing left"
style="vertical-align: middle">
Try me!
<!-- <span style="color: red">T</span><span style="color: orange">r</span><span style="color: yellow">y</span> <span
style="color: green">m</span><span style="color: blue">e</span><span style="color: purple">!</span> -->
</div>
<p>
<a href="https://github.com/1j01/jspaint#readme" target="_blank"><img src="images/about/moreinfo_paint.gif"
width="100" height="60" alt="More info" style="vertical-align: middle"></a>
Read about the project and extra features on <a href="https://github.com/1j01/jspaint#readme"
target="_blank">the readme</a>.
</p>
<p>
<a href="https://www.paypal.me/IsaiahOdhner" target="_blank"><img src="images/about/money4.gif" width="32"
height="32" alt="$" style="vertical-align: middle"></a>
Support the project at <a href="https://www.paypal.me/IsaiahOdhner"
target="_blank">paypal.me/IsaiahOdhner</a>.
</p>
</section>
<section id="windows-98-section">
<h2>Windows 98 online</h2>
<p>
<a href="https://98.js.org" target="_blank">
<img src="images/about/windows-button.gif" width="40" height="40" alt="Windows logo button">
<img src="images/about/flagani.gif" style="float: right" width="138" height="251"
alt="Windows 98 flag pole animation">
</a>
JS Paint is also included in a <b>web-based version of Windows 98</b>,
along with Notepad, Minesweeper, Sound Recorder, Calculator, and Winamp.
</p>
<a href="https://98.js.org" target="_blank"
style="position: relative; display: block; width: 360px; height: 287px">
<img src="images/about/98.js.org.png" width="360" height="287" alt="windows swung open outwards"
style="position: absolute; left: 0; top: 0">
<img src="images/about/WindowCLR_WRK.gif" width="360" height="287" alt="windows swung open outwards"
id="wooden-window-frame" style="position: absolute; left: 0; top: 0">
<img src="images/about/ENTERsolar.gif" width="128" height="45" alt="ENTER / 由此進入"
style="position: absolute; left: 50%; top: 80%; transform: translate(-50%, -50%); mix-blend-mode: lighten">;
</a>
</section>
<section id="desktop-app-section">
<h2>Desktop Version</h2>
<img src="images/about/atombgwht.gif" width="60" height="65" alt="built with Electron" style="float: right;">
<p>
There is demand for a desktop version of JS Paint.
I have put in 99% of the work on this (integration with the file system, wallpaper setting,
inter-process communication, etc.), but I have not released it yet.
</p>
<p>
If you are motivated, you can
<a href="https://github.com/1j01/jspaint#desktop-app" target="_blank">manually install</a>
the desktop app, by cloning the repository and following development setup
instructions.
</p>
<a href="https://github.com/1j01/jspaint/issues/2" target="_blank">
<img src="images/about/download5.gif" width="100" height="40" alt="Download">
<img src="images/about/conpaint.gif" width="350" height="50" alt="Under Construction">
</a>
</section>
<section id="pwa-section">
<h2>Progressive Web App</h2>
<img src="images/meta/mobipaint.png" width="308" height="193" alt="JS Paint on a phone" style="float: right;">
<p>
Alternatively, you can install JS Paint as a PWA (Progressive Web App),
but this does not <b>yet</b> support offline use
(as it doesn't include a Service Worker).
It's more like a bookmark (for now), except it runs in a special window.
</p>
<p>
The user interface for installing PWAs differs by browser and operating system.
</p>
<p>
<q>On most desktop browsers, the install prompt is in the URL bar.
On mobile, the install prompt is generally found in the menu of browser options.</q>
See <a
href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Installing#installing_pwas"
target="_blank">
Installing PWAs</a> for visual guidance.
</p>
<button id="install-pwa" hidden style="padding: 10px 30px; margin-bottom: 1em;">
<img src="images/about/monitor.png" width="16" height="16" alt="" style="vertical-align: middle;">
Install JS Paint
</button>
<!-- prevent floated image overlapping next section -->
<div style="clear: both;"></div>
</section>
<section id="textual-paint-section">
<h2>Textual Paint</h2>
<p>
I also made a <i>separate</i> elaborate MS Paint clone that runs in the terminal,
and edits ANSI art in addition to bitmaps.
</p>
<p>
You can install it with:
<code>pip install textual-paint</code>
</p>
<p>
And then run with simply:
<code>textual-paint</code>
</p>
<p>
Requires Python 3.10 or later, and a terminal emulator with Unicode and true color support.
<ul>
<li>
<span class="os-icon"><img src="images/about/windowslogo.gif" width="34" height="30"></span>
Runs well in <i>Windows Terminal</i>, but not in the older <i>Windows Console</i>.
</li>
<li>
<span class="os-icon"><img src="images/about/maclogo.gif" width="34" height="29"></span>
Runs well in <i>iTerm2</i>, but not the built in macOS <i>Terminal.app</i>.
</li>
<li>
<span class="os-icon"><img src="images/about/linuxlogo.gif" width="30" height="35"></span>
Runs well in <i>GNOME Terminal</i>, and most Linux terminal emulators, but not the <i>Linux console</i>.
</li>
</ul>
</p>
<!-- <img src="images/about/dos-samp.gif" width="391" height="136" alt="MS DOS prompt"> -->
<img src="images/about/Computer-04-june.gif" width="134" height="133" alt="Computer typing on its own keyboard">
<p>
<a href="https://github.com/1j01/textual-paint" target="_blank">Textual Paint</a>
was built using the <a href="https://textual.textualize.io/" target="_blank">Textual</a>
framework, which was very fun to work with!
</p>
</section>
<section id="other-projects-section">
<h2>Other Projects</h2>
<p style="float: right;">
<a href="https://isaiahodhner.io/" target="_blank">
<img src="images/about/home.gif" width="40" height="40" alt="Home button"
style="vertical-align: middle">
Check out my home page for more projects!
</a>
</p>
<p>
<a href="https://isaiahodhner.io/" target="_blank"
style="color: rgb(160, 42, 52); font-weight: bold; font-style: italic;">
<img src="images/about/artcreated.gif" width="165" height="136" alt="Created by">
<br>
<span style="display: block; text-align: center; width: 165px;">
Isaiah Odhner
</span>
</a>
</p>
<div id="junkbot-area">
<a href="https://1j01.github.io/janitorial-android/#junkbot" target="_blank">
<img src="images/about/junkbot-collecting-bin.gif" width="105" height="126"
alt="LEGO Junkbot minifig eating a recycling bin" id="junkbot-img"></a>
<span id="feeding-text" hidden>Please don't feed Junkbot.</span>
</div>
<a href="https://1j01.github.io/guitar/" target="_blank">
<img src=" images/about/guitar.gif" width="60" height="100" alt="Guitar" style="float: right;">
</a>
<a href="https://1j01.github.io/true-random-movie/" target="_blank">
<img src="images/about/filmreel.gif" width="116" height="50" alt="film reel" style="float: right;">
</a>
<a href="https://1j01.github.io/dat-boi/" target="_blank">
<img src="images/about/dat-boi-transparent-small.gif" width="96" height="120" alt="Dat Boi"
style="float: right;">
</a>
<a href="https://1j01.github.io/chess-mashup/" target="_blank">
<img src="images/about/CHESS_CUBE.gif" width="100" height="90" alt="Dat Boi" style="float: right;">
</a>
<!-- extends background when floated elements wrap -->
<div style="clear: both;"></div>
</section>
<script>
var maxButton = document.querySelector(".window-maximize-button");
var closeButton = document.querySelector(".window-close-button");
var minimizeButton = document.querySelector(".window-minimize-button");
var windowElements = document.querySelectorAll(".window, #try-me");
maxButton.addEventListener("click", function () {
var iframe = document.querySelector("#jspaint-iframe");
// `iframe.src` doesn't contain the document ID added by the app page.
// Accessing the iframe's `location.href` allows a more seamless transition,
// although undos are lost.
// TODO: Save undo history as part of the session.
// TODO: Make sure the session is saved before navigating away.
// if (iframe.contentWindow.undos.length) {
// if (!confirm("Maximizing will clear your undo history. Continue?")) {
// return;
// }
// }
location.href = iframe.contentWindow.location.href;
});
function hideDemo() {
for (var i = 0; i < windowElements.length; i++) {
windowElements[i].style.display = "none";
}
}
closeButton.addEventListener("click", hideDemo);
minimizeButton.addEventListener("click", hideDemo);
// Junkbot "feeding" interaction
var junkbotArea = document.querySelector("#junkbot-area");
var junkbotImg = document.querySelector("#junkbot-img");
var cursorHasBin = true;
junkbotArea.style.cursor = "url(images/about/junkbot-bin.png) 16 16, auto";
junkbotImg.src = "images/about/junkbot.gif";
junkbotImg.addEventListener("mouseenter", function (event) {
// console.log("mouseenter, cursorHasBin =", cursorHasBin);
if (!cursorHasBin) {
return;
}
junkbotImg.src = "images/about/junkbot-collecting-bin.gif";
junkbotArea.style.cursor = "";
cursorHasBin = false;
// TODO: make non-looping version of GIF and increase timeout
// or do this differently, because setTimeout isn't reliable!
// It's not firing in Firefox sometimes (while loading the page?)
// and may not fire if the tab is in the background (is switched away from).
// console.log("setting timeout");
setTimeout(function () {
// console.log("timeout");
junkbotImg.src = "images/about/junkbot.gif";
junkbotArea.style.cursor = "url(images/about/junkbot-bin.png) 16 16, auto";
cursorHasBin = true;
}, 1000);
});
document.querySelector("#feeding-text").removeAttribute("hidden");
</script>
<!-- split script to keep above ES5-only -->
<script>
// PWA install button
// https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Trigger_install_prompt
const installButton = document.querySelector("#install-pwa");
let installPrompt = null;
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
installPrompt = event;
installButton.removeAttribute("hidden");
});
installButton.addEventListener("click", async () => {
if (!installPrompt) {
return;
}
const result = await installPrompt.prompt();
console.log(`Install prompt was: ${result.outcome}`);
disableInAppInstallPrompt();
});
function disableInAppInstallPrompt() {
installPrompt = null;
installButton.setAttribute("hidden", "");
}
window.addEventListener("appinstalled", () => {
disableInAppInstallPrompt();
});
</script>
</body>
</html>