jspaint/about.html

392 lines
18 KiB
HTML

<!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&nbsp;me!
<!-- <span style="color: red">T</span><span style="color: orange">r</span><span style="color: yellow">y</span>&nbsp;<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&nbsp;Paint is also included in a <b>web-based version of Windows&nbsp;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>