From 0278a785d3ae63117215050899ac4b053bfe3e55 Mon Sep 17 00:00:00 2001 From: Alexis Hovorka Date: Sun, 9 Jan 2022 19:53:09 -0700 Subject: Initial commit --- app/.gitignore | 49 + app/LICENSE | 21 + app/app.js | 50 + app/lib/otp.js | 29 + app/lib/pipe.js | 27 + app/lib/router.js | 40 + app/lib/socket.js | 70 + app/lib/static.js | 78 + app/node-server.service | 13 + app/package-lock.json | 444 + app/package.json | 20 + app/public/404.html | 1 + app/public/500.html | 1 + app/public/index.html | 64 + app/public/main.js | 18 + app/public/manifest.webmanifest | 26 + app/public/sock.js | 43 + app/public/style.css | 14 + app/public/sw1.js | 29 + app/public/sw2.js | 40 + design/cards.html | 118 + design/icons.svg | 193 + design/icons/add-black-48dp.svg | 1 + design/icons/arrow_back_ios-black-48dp.svg | 1 + design/icons/arrow_back_ios_new-black-48dp.svg | 1 + design/icons/article-black-48dp.svg | 1 + design/icons/check_box-black-48dp.svg | 1 + .../icons/check_box_outline_blank-black-48dp.svg | 1 + design/icons/chevron_left-black-48dp.svg | 1 + design/icons/clear-black-48dp.svg | 1 + design/icons/close-black-48dp.svg | 1 + design/icons/delete-black-48dp.svg | 1 + design/icons/delete_outline-black-48dp.svg | 1 + design/icons/drag_indicator-black-48dp.svg | 1 + design/icons/edit-black-48dp.svg | 1 + design/icons/filter_none-black-48dp.svg | 1 + design/icons/flip_to_front-black-48dp.svg | 1 + design/icons/format_quote-black-48dp.svg | 1 + design/icons/more_vert-black-48dp.svg | 1 + design/icons/navigate_before-black-48dp.svg | 1 + design/icons/public-black-48dp.svg | 1 + design/icons/push_pin-black-48dp.svg | 1 + design/icons/search-black-48dp.svg | 1 + design/icons/share-black-48dp.svg | 1 + design/logo.svg | 110 + design/manifest.json | 9 + design/palette.svg | 490 + design/static-mockups/mobile-main-more.svg | 12463 +++++++++++++++++++ design/static-mockups/mobile-main.svg | 12350 ++++++++++++++++++ design/test.html | 41 + design/toolbar.html | 201 + 51 files changed, 27074 insertions(+) create mode 100644 app/.gitignore create mode 100644 app/LICENSE create mode 100644 app/app.js create mode 100644 app/lib/otp.js create mode 100644 app/lib/pipe.js create mode 100644 app/lib/router.js create mode 100644 app/lib/socket.js create mode 100644 app/lib/static.js create mode 100644 app/node-server.service create mode 100644 app/package-lock.json create mode 100644 app/package.json create mode 100644 app/public/404.html create mode 100644 app/public/500.html create mode 100644 app/public/index.html create mode 100644 app/public/main.js create mode 100644 app/public/manifest.webmanifest create mode 100644 app/public/sock.js create mode 100644 app/public/style.css create mode 100644 app/public/sw1.js create mode 100644 app/public/sw2.js create mode 100644 design/cards.html create mode 100644 design/icons.svg create mode 100644 design/icons/add-black-48dp.svg create mode 100644 design/icons/arrow_back_ios-black-48dp.svg create mode 100644 design/icons/arrow_back_ios_new-black-48dp.svg create mode 100644 design/icons/article-black-48dp.svg create mode 100644 design/icons/check_box-black-48dp.svg create mode 100644 design/icons/check_box_outline_blank-black-48dp.svg create mode 100644 design/icons/chevron_left-black-48dp.svg create mode 100644 design/icons/clear-black-48dp.svg create mode 100644 design/icons/close-black-48dp.svg create mode 100644 design/icons/delete-black-48dp.svg create mode 100644 design/icons/delete_outline-black-48dp.svg create mode 100644 design/icons/drag_indicator-black-48dp.svg create mode 100644 design/icons/edit-black-48dp.svg create mode 100644 design/icons/filter_none-black-48dp.svg create mode 100644 design/icons/flip_to_front-black-48dp.svg create mode 100644 design/icons/format_quote-black-48dp.svg create mode 100644 design/icons/more_vert-black-48dp.svg create mode 100644 design/icons/navigate_before-black-48dp.svg create mode 100644 design/icons/public-black-48dp.svg create mode 100644 design/icons/push_pin-black-48dp.svg create mode 100644 design/icons/search-black-48dp.svg create mode 100644 design/icons/share-black-48dp.svg create mode 100644 design/logo.svg create mode 100644 design/manifest.json create mode 100644 design/palette.svg create mode 100644 design/static-mockups/mobile-main-more.svg create mode 100644 design/static-mockups/mobile-main.svg create mode 100644 design/test.html create mode 100644 design/toolbar.html diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..bfd1621 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,49 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock +*.sock + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +users +private diff --git a/app/LICENSE b/app/LICENSE new file mode 100644 index 0000000..e30e62b --- /dev/null +++ b/app/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Alexis Hovorka + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/app/app.js b/app/app.js new file mode 100644 index 0000000..d9cd39c --- /dev/null +++ b/app/app.js @@ -0,0 +1,50 @@ +"use strict"; + +const http = require("http"); +const fs = require("fs"); + +const argon2 = require("argon2"); + +const Router = require("./lib/router"); +const Static = require("./lib/static"); +//const Socket = require("./lib/socket"); +//const pipe = require("./lib/pipe"); +//const otp = require("./lib/otp"); + +if (!fs.existsSync("./logs")) fs.mkdirSync("./logs"); +if (!fs.existsSync("./users")) fs.mkdirSync("./users"); +if (!fs.existsSync("./private")) fs.mkdirSync("./private"); + +Math.clamp = Math.clamp || ((x,l,h) => Math.max(l,Math.min(x,h))); +const PORT = Math.clamp(+process.env.PORT||8080, 1, 65535); +const HOST = process.env.HOST||"0.0.0.0"; + +const server = http.createServer(); +const stat = new Static("./public"); +//const wss = new Socket(server); +const app = new Router(); + +const cors = fn => (req, res, ...rest) => { + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "GET, POST"); + fn(req, res, ...rest); }; + +const sj = (res, data) => { + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(data)); }; + +//app.get("/", (req, res) => { +// res.writeHead(302, {"Location":"https://alexishovorka.com/"}); +// res.end(); +//}); + +//await argon2.hash(password, {type: argon2.argon2id}); +//await argon2.verify(hash, password); + +server.on("request", (req, res) => { + console.log(`${Date.now()} ${req.method} ${req.url}`); + app.route(req, res) || stat.route(req, res); +}); + +server.listen(PORT, HOST); +console.log(`${Date.now()} Running on http://${HOST}:${PORT}`); diff --git a/app/lib/otp.js b/app/lib/otp.js new file mode 100644 index 0000000..751edf4 --- /dev/null +++ b/app/lib/otp.js @@ -0,0 +1,29 @@ +"use strict"; + +const { createHmac } = require("crypto"); +const getHMAC = (k,c) => { const h = createHmac("sha1", k); + h.update(c, "hex"); return h.digest("hex"); } + +const h2d = s => parseInt(s, 16); +const d2h = s => Math.floor(s).toString(16).padStart(2,0); + +const b32a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +const b32u = s => Uint8Array.from(s.split("").reduce((a,c) => + a+b32a.indexOf(c.toUpperCase()).toString(2).padStart(5,0), "") + .match(/.{8}/g).map(b => parseInt(b,2))); + +module.exports = function totp(secret, { expiry=30, + now=Math.round(new Date().getTime()/1000), length=6 }={}) { + const time = d2h(now/expiry).padStart(16,0); + const hmac = getHMAC(b32u(secret), time); + const offset = h2d(hmac.substring(hmac.length - 1)); + const otp = (h2d(hmac.substr(2*offset, 8)) & h2d("7fffffff")) + ""; + return otp.padStart(length,0).substr(-length); +} + +module.exports.check = function check(token, secret, { expiry=30, + now=Math.round(new Date().getTime()/1000), length=6, window=0 }={}) { + const i = Array(window*2+1).fill().map((e,i) => now+(expiry*(i-window))) + .findIndex(n => token === this(secret, {now:n, expiry, length})); + return i>=0 && i-window; +} diff --git a/app/lib/pipe.js b/app/lib/pipe.js new file mode 100644 index 0000000..e329b2b --- /dev/null +++ b/app/lib/pipe.js @@ -0,0 +1,27 @@ +"use strict"; + +const { spawn } = require("child_process"); + +module.exports = function pipe({ command, flags, stdin="", buffer } = {}) { + return new Promise((resolve, reject) => { + const child = spawn(command, flags); + let stdout = (buffer?[]:""); + let stderr = ""; + + child.stderr.on("data", d => stderr += d); + child.stdout.on("data", d => (buffer? + stdout.push(d):stdout+=d)); + + child.on("close", code => { + const res = { code, stderr, stdout: + (buffer?Buffer.concat(stdout):stdout) }; + + if (code) reject(res); + else resolve(res); + }); + + //child.stdin.setEncoding("utf-8"); + child.stdin.write(stdin); + child.stdin.end(); + }); +} diff --git a/app/lib/router.js b/app/lib/router.js new file mode 100644 index 0000000..8c0a2ef --- /dev/null +++ b/app/lib/router.js @@ -0,0 +1,40 @@ +"use strict"; // https://github.com/mixu/minimal + +const url = require("url"); +const degroup = path => Object.assign(path, path.groups); + +class Router { + constructor() { + this.routes = []; + } + + route(req, res) { + const pathname = url.parse(req.url).pathname; + return this.routes.some(route => { + const isMatch = route.method === req.method && route.re.test(pathname); + if (isMatch) route.cb(req, res, degroup(route.re.exec(pathname))); + return isMatch; + }); + } + + gather(cb, max) { // Handle POST data + return (req, res, match) => { + let data = ""; + req.on("data", chunk => { + if ((data += chunk).length > (max||1e6)) // ~1MB + req.connection.destroy(); + }).on("end", () => cb(req, res, match, data)); + }; + } + + gpost(re, cb, max) { // Laziness + this.post(re, this.gather(cb, max)); + } +} + +["get", "post", "put", "delete"].forEach(method => + Router.prototype[method] = function(re, cb) { + this.routes.push({method: method.toUpperCase(), cb, + re: (re instanceof RegExp)? re : new RegExp(`^${re}$`)})}); + +module.exports = Router; diff --git a/app/lib/socket.js b/app/lib/socket.js new file mode 100644 index 0000000..88fdd49 --- /dev/null +++ b/app/lib/socket.js @@ -0,0 +1,70 @@ +"use strict"; + +const WebSocket = require("ws"); + +class Client { + constructor(ws) { + this.ws = ws; + this.handlers = {}; + + ws.on("message", msg => { const d = JSON.parse(msg); + (this.handlers[d.type]||[]).forEach(cb => cb(d.data)); + }); + + const closecb = () => + (this.handlers["close"]||[]).forEach(cb => cb()); + + ws.on("close", closecb); + ws.on("error", closecb); + } + + on(type, cb) { + (this.handlers[type]||(this.handlers[type]=[])).push(cb); + } + + send(type, data) { + this.ws.send(JSON.stringify({type, data})); + } +} + +module.exports = class Socket { + constructor(server) { + this.wss = new WebSocket.Server({server}); + this.handlers = {}; + this.clients = []; + + this.wss.on("connection", ws => { + const client = new Client(ws); + this.clients.push(client); + + ws.on("message", msg => { const d = JSON.parse(msg); + (this.handlers[d.type]||[]).forEach(cb => cb(client, d.data)); + }); + + let pingTimeout; + const ping = () => { client.send("ping", {}); + pingTimeout = setTimeout(() => ws.terminate(), 3e4); }; + client.on("pong", () => { clearTimeout(pingTimeout); + setTimeout(ping, 1e3); }); ping(); + + const closecb = () => { + const i = this.clients.indexOf(client); if (i < 0) return; + (this.handlers["close"]||[]).forEach(cb => cb(client)); + this.clients.splice(i, 1); + } + + ws.on("close", closecb); + ws.on("error", closecb); + + (this.handlers["open"]||[]).forEach(cb => cb(client)); + }); + } + + on(type, cb) { + (this.handlers[type]||(this.handlers[type]=[])).push(cb); + } + + sendAll(type, data) { + this.clients.forEach(c => c.send(type, data)); + } +}; diff --git a/app/lib/static.js b/app/lib/static.js new file mode 100644 index 0000000..ef48ae6 --- /dev/null +++ b/app/lib/static.js @@ -0,0 +1,78 @@ +"use strict"; + +const Path = require("path"); +const url = require("url"); +const fs = require("fs"); + +const mimeTypes = { + ".html": "text/html", + ".css": "text/css", + ".js": "text/javascript", + ".json": "application/json", + ".wasm": "application/wasm", + ".pdf": "application/pdf", + ".txt": "text/plain", + ".md": "text/markdown", + + ".png": "image/png", + ".jpg": "image/jpg", + ".svg": "image/svg+xml", + ".gif": "image/gif", + ".ico": "image/x-icon", + + ".wav": "audio/wav", + ".mp3": "audio/mpeg", + ".mp4": "video/mp4", + + ".eot": "application/vnd.ms-fontobject", + ".ttf": "application/font-ttf", + ".woff": "application/font-woff", + + ".gltf": "model/gltf+json", + ".glb": "model/gltf-binary", +}; + +module.exports = class Static { + constructor(root) { + this.root = `${root||"."}`; + this.e404 = fs.readFileSync(`${this.root}/404.html`); + this.e500 = fs.readFileSync(`${this.root}/500.html`); + } + + route(req, res) { + if (req.method !== "GET") { + res.writeHead(405, {"Content-Type": "text/plain"}); + res.end("405 Method Not Allowed"); + return; + } + + const pathname = url.parse(req.url).pathname; + const sane = Path.normalize(pathname).replace(/^(\.\.\/)+/, ""); + let path = `${this.root}${sane}`; //Path.join(__dirname, sane); + + fs.stat(path, (err, stats) => { + if (err) { if (["EACCES", "ENOENT", "EPERM"].includes(err.code)) { + res.writeHead(404, {"Content-Type": "text/html"}); + return res.end(this.e404); + } else { console.log(err); + res.writeHead(500, {"Content-Type": "text/html"}); + return res.end(this.e500); + }} + + if (stats.isDirectory()) path += "/index.html"; + const ext = `${Path.extname(path)}`.toLowerCase(); + const stream = fs.createReadStream(path); + + stream.on("error", e => { console.log(e); + res.writeHead(500, {"Content-Type": "text/html"}); + //res.end(`
Error getting the file: ${e}
`); + res.end(this.e500); + }); + + res.setHeader("Content-Type", mimeTypes[ext]||"application/octet-stream"); + // TODO Caching? + + stream.pipe(res); + }); + } +}; diff --git a/app/node-server.service b/app/node-server.service new file mode 100644 index 0000000..42c1e8f --- /dev/null +++ b/app/node-server.service @@ -0,0 +1,13 @@ +[Service] +ExecStart=/usr/bin/node /srv/http/appname/app.js +Restart=always +WorkingDirectory=/srv/http/appname +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=appname +User=alexis +Group=users +Environment=PORT=8080 + +[Install] +WantedBy=multi-user.target diff --git a/app/package-lock.json b/app/package-lock.json new file mode 100644 index 0000000..b5eb137 --- /dev/null +++ b/app/package-lock.json @@ -0,0 +1,444 @@ +{ + "name": "appname", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", + "requires": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.5", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, + "@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "argon2": { + "version": "0.28.3", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.28.3.tgz", + "integrity": "sha512-NkEJOImg+T7nnkx6/Fy8EbjZsF20hbBBKdVP/YUxujuLTAjIODmrFeY4vVpekKwGAGDm6roXxluFQ+CIaoVrbg==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.7", + "@phc/format": "^1.0.0", + "node-addon-api": "^4.2.0", + "opencollective-postinstall": "^2.0.3" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-addon-api": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.2.0.tgz", + "integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==" + }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", + "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..0a4ce2d --- /dev/null +++ b/app/package.json @@ -0,0 +1,20 @@ +{ + "name": "notes-web", + "version": "0.0.1", + "description": "Zettelkasten-inspired note taking web app", + "author": "Alexis Hovorka ", + "license": "MIT", + "main": "app.js", + "scripts": { + "start": "node app.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "private": true, + "dependencies": { + "argon2": "^0.28.3", + "ws": "^7.1.0" + } +} diff --git a/app/public/404.html b/app/public/404.html new file mode 100644 index 0000000..1a43d0a --- /dev/null +++ b/app/public/404.html @@ -0,0 +1 @@ +
404 Not Found
diff --git a/app/public/500.html b/app/public/500.html new file mode 100644 index 0000000..ee07335 --- /dev/null +++ b/app/public/500.html @@ -0,0 +1 @@ +
500 Internal Server Error
diff --git a/app/public/index.html b/app/public/index.html new file mode 100644 index 0000000..3c435f5 --- /dev/null +++ b/app/public/index.html @@ -0,0 +1,64 @@ + + + + Notes + + + + + + + + + + + + + + + +

Notes

+ + + + + diff --git a/app/public/main.js b/app/public/main.js new file mode 100644 index 0000000..ff960c2 --- /dev/null +++ b/app/public/main.js @@ -0,0 +1,18 @@ +document.addEventListener("DOMContentLoaded", async () => { "use strict"; + +const secure = location.protocol === "https:"; +const $ = (s,c) => (c||document).querySelector(s); +function $$(x,y,z,a){a=(z||document).querySelectorAll(x);if(typeof y=="function")[].forEach.call(a,y);return a} +function m(a,b,c){c=document;b=c.createElement(b||"p");b.innerHTML=a.trim();for(a=c.createDocumentFragment();c=b.firstChild;)a.appendChild(c);return a.firstChild} + +//sock.init(`ws${secure?"s":""}://${location.host}/ws`); +//sock.on("hello", e => { +// console.log("hello", e); +// sock.send("world", {foo:"bar"}); +//}); + +//if ("serviceWorker" in navigator) { +// navigator.serviceWorker.register("sw.js") +// .then(() => console.log("Service worker registered")); +//} +}); diff --git a/app/public/manifest.webmanifest b/app/public/manifest.webmanifest new file mode 100644 index 0000000..9eb9f13 --- /dev/null +++ b/app/public/manifest.webmanifest @@ -0,0 +1,26 @@ +{ + "name": "Notes", + "short_name": "Notes", + "description": "Zettelkasten-inspired note taking web app", + "categories": ["productivity", "utilities"], + "lang": "en-US", + "start_url": "/", + "scope": "/", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#000000", + "icons": [ + {"type": "image/png", "sizes": "48x48", "src": "img/favicon-48.png" }, + {"type": "image/png", "sizes": "96x96", "src": "img/favicon-96.png" }, + {"type": "image/png", "sizes": "192x192", "src": "img/favicon-192.png", "purpose": "maskable any"}, + {"type": "image/png", "sizes": "256x256", "src": "img/favicon-256.png", "purpose": "maskable any"}, + {"type": "image/png", "sizes": "512x512", "src": "img/favicon-512.png", "purpose": "maskable any"}, + + {"type": "image/png", "sizes": "72x72", "src": "img/apple-touch-icon-72.png" }, + {"type": "image/png", "sizes": "144x144", "src": "img/apple-touch-icon-144.png"}, + {"type": "image/png", "sizes": "168x168", "src": "img/apple-touch-icon-168.png"} + ], + "shortcuts": [ + {"name": "New", "url": "/new"} + ] +} diff --git a/app/public/sock.js b/app/public/sock.js new file mode 100644 index 0000000..b4905e5 --- /dev/null +++ b/app/public/sock.js @@ -0,0 +1,43 @@ +const sock = (() => { "use strict"; +const refresh = () => setTimeout(() => location.reload(), 1e3); + +let ws, pingTimeout; +const prequeue = []; +const handlers = { + "reset": [refresh], + "ping": [() => { + clearTimeout(pingTimeout); + pingTimeout = setTimeout(refresh, 3e4); + sock.send("pong", {}); + }], +}; + +const sock = { + init: url => { + ws = new WebSocket(url); + ws.addEventListener("close", refresh); + ws.addEventListener("error", refresh); + ws.addEventListener("message", e => { + const d = JSON.parse(e.data); + (handlers[d.type]||[]).forEach(cb => cb(d.data)); + }); + ws.addEventListener("open", e => { + while (prequeue.length) sock.send(...prequeue.shift()); + (handlers["open"]||[]).forEach(cb => cb()); + delete handlers["open"]; + }); + }, + + on: (type, cb) => { + if (type === "open" && ws && ws.readyState === WebSocket.OPEN) cb(); + else (handlers[type]||(handlers[type]=[])).push(cb); + }, + + send: (type, data) => { + if (ws && ws.readyState === WebSocket.OPEN) + ws.send(JSON.stringify({type, data})); + else prequeue.push([type, data]); + }, +}; + +return sock })(); diff --git a/app/public/style.css b/app/public/style.css new file mode 100644 index 0000000..655e181 --- /dev/null +++ b/app/public/style.css @@ -0,0 +1,14 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Roboto", "Noto Sans", sans-serif; + transition-timing-function: ease-in-out; +} + +body { + user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; +} diff --git a/app/public/sw1.js b/app/public/sw1.js new file mode 100644 index 0000000..37ae5e5 --- /dev/null +++ b/app/public/sw1.js @@ -0,0 +1,29 @@ +// https://gist.github.com/adactio/3717b7da007a9363ddf21f584aae34af + +// HTML files: try the network first, then the cache. +// Other files: try the cache first, then the network. +// Both: cache a fresh version if possible. +// (beware: the cache will grow and grow; there's no cleanup) + +const cacheName = "notes-cache-v1"; + +addEventListener("fetch", fetchEvent => { + const request = fetchEvent.request; + if (request.method !== "GET") return; + fetchEvent.respondWith(async () => { + const fetchPromise = fetch(request); + fetchEvent.waitUntil(async () => { + const responseFromFetch = await fetchPromise; + const responseCopy = responseFromFetch.clone(); + const myCache = await caches.open(cacheName); + return myCache.put(request, responseCopy); + }()); + if (request.headers.get("Accept").includes("text/html")) { + try { return await fetchPromise; } + catch(error) { return caches.match(request); } + } else { + const responseFromCache = await caches.match(request); + return responseFromCache || fetchPromise; + } + }()); +}); diff --git a/app/public/sw2.js b/app/public/sw2.js new file mode 100644 index 0000000..0dabb13 --- /dev/null +++ b/app/public/sw2.js @@ -0,0 +1,40 @@ +// https://googlechrome.github.io/samples/service-worker/basic/ + +const PRECACHE = "notes-precache-v1"; +const RUNTIME = "notes-runtime"; + +const PRECACHE_URLS = [ + "./", // Alias for index.html + "index.html", + "style.css", + "main.js" +]; + +self.addEventListener("install", e => e.waitUntil( + caches.open(PRECACHE) + .then(cache => cache.addAll(PRECACHE_URLS)) + .then(self.skipWaiting()))); + +self.addEventListener("activate", e => { + const currentCaches = [PRECACHE, RUNTIME]; + e.waitUntil( // Clean up old caches + caches.keys().then(cacheNames => + cacheNames.filter(cacheName => !currentCaches.includes(cacheName)) + ).then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => + caches.delete(cacheToDelete))) + ).then(() => self.clients.claim()) + ); +}); + +self.addEventListener("fetch", e => { + if (e.request.url.startsWith(self.location.origin)) { + e.respondWith(caches.match(e.request).then(cachedResponse => + cachedResponse? cachedResponse + : caches.open(RUNTIME).then(cache => + fetch(e.request).then(res => + cache.put(e.request, res.clone()).then(() => res) + ) + ) + )); + } +}); diff --git a/design/cards.html b/design/cards.html new file mode 100644 index 0000000..ec2873b --- /dev/null +++ b/design/cards.html @@ -0,0 +1,118 @@ + + + + Cards + + + + + + + + +
+
edit
+

Example Note

+

Parent, Other Parent + push_pin + public +

+

Meh bold italic meh whatever lorem ipsum dolor sit amet adispicing elit

+

Foobar lipsum yeet

+

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

+
#hash #tag + more_vert +
+
+ +
+

Short note

+
+ +
+

Note with a picture

+

Photography + public +

+ Meh +
#nature + more_vert +
+
+ + diff --git a/design/icons.svg b/design/icons.svg new file mode 100644 index 0000000..563df7f --- /dev/null +++ b/design/icons.svg @@ -0,0 +1,193 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/icons/add-black-48dp.svg b/design/icons/add-black-48dp.svg new file mode 100644 index 0000000..d061267 --- /dev/null +++ b/design/icons/add-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/arrow_back_ios-black-48dp.svg b/design/icons/arrow_back_ios-black-48dp.svg new file mode 100644 index 0000000..f823c27 --- /dev/null +++ b/design/icons/arrow_back_ios-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/arrow_back_ios_new-black-48dp.svg b/design/icons/arrow_back_ios_new-black-48dp.svg new file mode 100644 index 0000000..7473f13 --- /dev/null +++ b/design/icons/arrow_back_ios_new-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/article-black-48dp.svg b/design/icons/article-black-48dp.svg new file mode 100644 index 0000000..8f00392 --- /dev/null +++ b/design/icons/article-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/check_box-black-48dp.svg b/design/icons/check_box-black-48dp.svg new file mode 100644 index 0000000..9815395 --- /dev/null +++ b/design/icons/check_box-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/check_box_outline_blank-black-48dp.svg b/design/icons/check_box_outline_blank-black-48dp.svg new file mode 100644 index 0000000..e247a17 --- /dev/null +++ b/design/icons/check_box_outline_blank-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/chevron_left-black-48dp.svg b/design/icons/chevron_left-black-48dp.svg new file mode 100644 index 0000000..e0f5ee3 --- /dev/null +++ b/design/icons/chevron_left-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/clear-black-48dp.svg b/design/icons/clear-black-48dp.svg new file mode 100644 index 0000000..d147feb --- /dev/null +++ b/design/icons/clear-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/close-black-48dp.svg b/design/icons/close-black-48dp.svg new file mode 100644 index 0000000..d147feb --- /dev/null +++ b/design/icons/close-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/delete-black-48dp.svg b/design/icons/delete-black-48dp.svg new file mode 100644 index 0000000..c54c596 --- /dev/null +++ b/design/icons/delete-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/delete_outline-black-48dp.svg b/design/icons/delete_outline-black-48dp.svg new file mode 100644 index 0000000..599cc53 --- /dev/null +++ b/design/icons/delete_outline-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/drag_indicator-black-48dp.svg b/design/icons/drag_indicator-black-48dp.svg new file mode 100644 index 0000000..b456a97 --- /dev/null +++ b/design/icons/drag_indicator-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/edit-black-48dp.svg b/design/icons/edit-black-48dp.svg new file mode 100644 index 0000000..cba4e47 --- /dev/null +++ b/design/icons/edit-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/filter_none-black-48dp.svg b/design/icons/filter_none-black-48dp.svg new file mode 100644 index 0000000..4a0c406 --- /dev/null +++ b/design/icons/filter_none-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/flip_to_front-black-48dp.svg b/design/icons/flip_to_front-black-48dp.svg new file mode 100644 index 0000000..eeb8b85 --- /dev/null +++ b/design/icons/flip_to_front-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/format_quote-black-48dp.svg b/design/icons/format_quote-black-48dp.svg new file mode 100644 index 0000000..9288349 --- /dev/null +++ b/design/icons/format_quote-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/more_vert-black-48dp.svg b/design/icons/more_vert-black-48dp.svg new file mode 100644 index 0000000..892ab00 --- /dev/null +++ b/design/icons/more_vert-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/navigate_before-black-48dp.svg b/design/icons/navigate_before-black-48dp.svg new file mode 100644 index 0000000..615c7fa --- /dev/null +++ b/design/icons/navigate_before-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/public-black-48dp.svg b/design/icons/public-black-48dp.svg new file mode 100644 index 0000000..1a52302 --- /dev/null +++ b/design/icons/public-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/push_pin-black-48dp.svg b/design/icons/push_pin-black-48dp.svg new file mode 100644 index 0000000..24b42d7 --- /dev/null +++ b/design/icons/push_pin-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/search-black-48dp.svg b/design/icons/search-black-48dp.svg new file mode 100644 index 0000000..7867e9d --- /dev/null +++ b/design/icons/search-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/icons/share-black-48dp.svg b/design/icons/share-black-48dp.svg new file mode 100644 index 0000000..fc498e2 --- /dev/null +++ b/design/icons/share-black-48dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/design/logo.svg b/design/logo.svg new file mode 100644 index 0000000..9502aa3 --- /dev/null +++ b/design/logo.svg @@ -0,0 +1,110 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/manifest.json b/design/manifest.json new file mode 100644 index 0000000..3600071 --- /dev/null +++ b/design/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Notes", + "short_name": "Notes", + "start_url": ".", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#fff", + "description": "A notes app" +} diff --git a/design/palette.svg b/design/palette.svg new file mode 100644 index 0000000..623a2f7 --- /dev/null +++ b/design/palette.svg @@ -0,0 +1,490 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Meh + + + + Meh + + + + + + + Notes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/static-mockups/mobile-main-more.svg b/design/static-mockups/mobile-main-more.svg new file mode 100644 index 0000000..4123f64 --- /dev/null +++ b/design/static-mockups/mobile-main-more.svg @@ -0,0 +1,12463 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Note with a picture + + + + + + Photography + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Meh + + + Meh + + + + + + + + + + Example Note + + + + + + Parent, Other Parent + Lorem Ipsum is simply dummy textof the printing and typesetting industry.Lorem Ipsum has been the industry’sstandard dummy text ever since the1500s, when an unknown printer took agalley of type and scrambled it to makea type specimen book. It has survived… + #hash + #tag + + + Short note + + + + + + + + + + + + + + + + + + + + + 2021-0123-1346 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/design/static-mockups/mobile-main.svg b/design/static-mockups/mobile-main.svg new file mode 100644 index 0000000..efd29b5 --- /dev/null +++ b/design/static-mockups/mobile-main.svg @@ -0,0 +1,12350 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Note with a picture + + + + + + Photography + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Meh + + + Meh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Example Note + + + + + + Parent, Other Parent + Lorem Ipsum is simply dummy textof the printing and typesetting industry.Lorem Ipsum has been the industry’sstandard dummy text ever since the1500s, when an unknown printer took agalley of type and scrambled it to makea type specimen book. It has survived… + #hash + #tag + + + + + + + Short note + + + + + + + + + + + + + + + diff --git a/design/test.html b/design/test.html new file mode 100644 index 0000000..6fea07f --- /dev/null +++ b/design/test.html @@ -0,0 +1,41 @@ + + + + Test + + + + + + + + + menu + article + table_rows + push_pin + public + edit + more_vert + add + search + menu + check_box_outline_blank + share + format_quote + delete + delete_outline + check_box_outline_blank + check_box + radio_button_unchecked + check_circle_outline + clear + +
+ add +
+

2020 1220 1346 share

+ + diff --git a/design/toolbar.html b/design/toolbar.html new file mode 100644 index 0000000..34270f6 --- /dev/null +++ b/design/toolbar.html @@ -0,0 +1,201 @@ + + + + Toolbar + + + + + + + + + + + + + Hello! + Hello! + Hello! + Hello! + Hello! + Hello! + Hello! + Hello! + Hello! +
+ +
+
+
+
+
+ + + -- cgit v1.2.3-54-g00ecf