commit 4bf1908ef33f2d5b6a9af954f9560e27d5f8d798
parent e8c129eee9ff487468bf41ab8dfd226931d7f7a3
Author: Oscar Benedito <oscar@oscarbenedito.com>
Date:   Sun, 30 Aug 2020 14:52:26 +0200

Composer moved to future-proof URL

The appropriate redirect has been set up.

The composer will now be on a subfolder dedicated to personal projects.
As it has become a project completely unrelated to the website, it is
now a static file and doesn't use any Hugo features. It will probably be
added to a more fitted repository, but I will keep it on the website for
now.

Diffstat:
Dassets/css/composer.css | 74--------------------------------------------------------------------------
Dassets/js/composer.js | 109-------------------------------------------------------------------------------
Mcontent/composer.md | 4++--
Dlayouts/composer/single.html | 20--------------------
Alayouts/redirect/single.html | 1+
Astatic/projects/composer/composer.html | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 193 insertions(+), 205 deletions(-)

diff --git a/assets/css/composer.css b/assets/css/composer.css @@ -1,74 +0,0 @@ -:root { - --font-family: 'serif'; - --nav-height: 3em; - --text-color: #000; - --bg-color: #fff; - --nav-color: hsl(0, 0%, 46%); -} -.toggled { - --text-color: hsl(0, 0%, 93%); - --bg-color: hsl(0, 0%, 13%); - --nav-color: hsl(0, 0%, 56%); -} -@media (prefers-color-scheme: dark) { - :root { - --text-color: hsl(0, 0%, 93%); - --bg-color: hsl(0, 0%, 13%); - --nav-color: hsl(0, 0%, 56%); - } -} -@media (prefers-color-scheme: dark) { - .toggled { - --text-color: #000; - --bg-color: #fff; - --nav-color: hsl(0, 0%, 46%); - } -} -.mono { - --font-family: 'mono'; -} -html { - background-color: var(--bg-color); -} -textarea { - line-height: 1.4em; - font-family: var(--font-family); - padding: 1em calc((100% - 720px)/2); - background-color: var(--bg-color); - color: var(--text-color); - margin: 0; - height: calc(100% - var(--nav-height)); - font-size: 1.2em; - box-sizing: border-box; - resize: none; - right: 0; - top: var(--nav-height); - bottom: 0; - left: 0; - width: 100%; - position: fixed; - border: 0; - outline: 0; -} -nav { - text-align: center; - height: var(--nav-height); - line-height: var(--nav-height); - font-size: 1.2em; - opacity: 0; - color: var(--nav-color); - background-color: var(--bg-color); - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1; -} -nav a { - color: var(--nav-color); - text-decoration: none; - cursor: pointer; -} -nav:hover { - opacity: 1; -} diff --git a/assets/js/composer.js b/assets/js/composer.js @@ -1,109 +0,0 @@ -/* -Copyright (C) 2019 Oscar Benedito - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see <https://www.gnu.org/licenses/>. - -This file is based on code from the writeFreely project. You can find the -source code at <https://github.com/writeas/writefreely> -*/ -var $composer = document.getElementById('composer'); -var $wordcount = document.getElementById('word-count'); -var key; -var typingTimer; -var typingInterval = 200; -function updateWordCount() { - var words = 0; - var content = $composer.value.trim(); - if (content != '') { - words = content.replace(/\s+/gi, ' ').split(' ').length; - } - minutes = Math.floor(words/140); - $wordcount.textContent = words + ' word' + (words != 1 ? 's' : '') + ' · ' + minutes + ' minute' + (minutes != 1 ? 's' : ''); -} -function loadContents() { - var content = localStorage.getItem(key); - if (content != null) { - $composer.value = content; - } -} -var updateContents = function() { - if ($composer.value == '') { - localStorage.removeItem(key); - } - else { - localStorage.setItem(key, $composer.value); - } - updateWordCount(); -} -var resetTimer = function() { - clearTimeout(typingTimer); - typingTimer = setTimeout(updateContents, typingInterval); -} -function downloadData(filename, text) { - var text = localStorage.getItem(key); - if (text != null && text != '') { - var tmpElement = document.createElement('a'); - tmpElement.setAttribute('href', 'data:text/markdown;charset=utf-8,' + encodeURIComponent(text)); - tmpElement.setAttribute('download', key + '.md'); - tmpElement.style.display = 'none'; - - document.body.appendChild(tmpElement); - tmpElement.click(); - document.body.removeChild(tmpElement); - } -} -var saveEvent = function(event) { - if (event.keyCode == 83 && (event.metaKey || event.ctrlKey)) { - clearTimeout(typingTimer); - event.preventDefault(); - updateContents(); - if (event.shiftKey) - downloadData(); - } -} -function toggleTheme() { - if (localStorage && localStorage.getItem('theme') == 'toggled') { - localStorage.removeItem('theme'); - } else if (localStorage) { - localStorage.setItem('theme', 'toggled'); - } - document.documentElement.classList.toggle('toggled'); -} -function toggleFont() { - if (localStorage && localStorage.getItem('font') == 'mono') { - localStorage.removeItem('font'); - } else if (localStorage) { - localStorage.setItem('font', 'mono'); - } - document.documentElement.classList.toggle('mono'); -} - -key = (new URLSearchParams(window.location.search)).get('key'); -if (key == '' || key == null) { - key = 'null'; -} -key = 'k-' + key; -if (localStorage && localStorage.getItem('theme') == 'toggled') { - document.documentElement.classList.toggle('toggled'); -} -if (localStorage && localStorage.getItem('font') == 'mono') { - document.documentElement.classList.toggle('mono'); -} -$composer.addEventListener('keyup input', resetTimer); -$composer.addEventListener('keydown', resetTimer); -$composer.addEventListener('input', resetTimer); -window.addEventListener('beforeunload', updateContents); -window.addEventListener('keydown', saveEvent); -loadContents(); -updateWordCount(); diff --git a/content/composer.md b/content/composer.md @@ -1,4 +1,4 @@ --- -title: Composer -type: composer +canonical: "/projects/composer/composer.html" +type: redirect --- diff --git a/layouts/composer/single.html b/layouts/composer/single.html @@ -1,20 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - {{ partial "head-base.html" . }} - <style> -{{ (resources.Get "css/composer.css").Content | safeCSS }} - </style> - </head> - <body> - <nav> - <span id="word-count">0 words &middot; 0 minutes</span> &middot; <a onclick="toggleTheme()">Toggle theme</a> &middot; <a onclick="toggleFont()">Toggle font</a> - </nav> - <textarea id="composer" placeholder="Start writing..." autofocus></textarea> - <script type="text/javascript"> -{{ printf "// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later" | safeJS }} -{{ (resources.Get "js/composer.js").Content | safeJS }} -{{ printf "// @license-end" | safeJS }} - </script> - </body> -</html> diff --git a/layouts/redirect/single.html b/layouts/redirect/single.html @@ -0,0 +1 @@ +<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url='{{ .Params.canonical }}'"/><link rel="canonical" href="{{ .Params.canonical }}"/></head><body><p>This page has been moved to <a href="{{ .Params.canonical }}">{{ .Params.canonical }}</a>.</p></body></html> diff --git a/static/projects/composer/composer.html b/static/projects/composer/composer.html @@ -0,0 +1,190 @@ +<!DOCTYPE html> +<!-- + Composer by Oscar Benedito <oscar@oscarbenedito.com> + + Minimalistic interface to write without distractions. It is a standalone HTML + file. It will save your progress as long as you don't delete browser data. +--> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="author" content="Oscar Benedito"> + <title>Composer</title> + <style> +:root { + --font-family: 'serif'; + --nav-height: 3em; + --text-color: #000; + --bg-color: #fff; + --nav-color: hsl(0, 0%, 46%); +} +.toggled { + --text-color: hsl(0, 0%, 93%); + --bg-color: hsl(0, 0%, 13%); + --nav-color: hsl(0, 0%, 56%); +} +@media (prefers-color-scheme: dark) { + :root { + --text-color: hsl(0, 0%, 93%); + --bg-color: hsl(0, 0%, 13%); + --nav-color: hsl(0, 0%, 56%); + } +} +@media (prefers-color-scheme: dark) { + .toggled { + --text-color: #000; + --bg-color: #fff; + --nav-color: hsl(0, 0%, 46%); + } +} +.mono { + --font-family: 'mono'; +} +html { + background-color: var(--bg-color); +} +textarea { + line-height: 1.4em; + font-family: var(--font-family); + padding: 1em calc((100% - 720px)/2); + background-color: var(--bg-color); + color: var(--text-color); + margin: 0; + height: calc(100% - var(--nav-height)); + font-size: 1.2em; + box-sizing: border-box; + resize: none; + right: 0; + top: var(--nav-height); + bottom: 0; + left: 0; + width: 100%; + position: fixed; + border: 0; + outline: 0; +} +nav { + text-align: center; + height: var(--nav-height); + line-height: var(--nav-height); + font-size: 1.2em; + opacity: 0; + color: var(--nav-color); + background-color: var(--bg-color); + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1; +} +nav a { + color: var(--nav-color); + text-decoration: none; + cursor: pointer; +} +nav:hover { + opacity: 1; +} + </style> + </head> + <body> + <nav> + <span id="word-count">0 words &middot; 0 minutes</span> &middot; <a onclick="toggleTheme()">Toggle theme</a> &middot; <a onclick="toggleFont()">Toggle font</a> + </nav> + <textarea id="composer" placeholder="Start writing..." autofocus></textarea> + <script type="text/javascript"> +// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0 +var $composer = document.getElementById('composer'); +var $wordcount = document.getElementById('word-count'); +var key; +var typingTimer; +var typingInterval = 200; +function updateWordCount() { + var words = 0; + var content = $composer.value.trim(); + if (content != '') { + words = content.replace(/\s+/gi, ' ').split(' ').length; + } + minutes = Math.floor(words/140); + $wordcount.textContent = words + ' word' + (words != 1 ? 's' : '') + ' · ' + minutes + ' minute' + (minutes != 1 ? 's' : ''); +} +function loadContents() { + var content = localStorage.getItem(key); + if (content != null) { + $composer.value = content; + } +} +var updateContents = function() { + if ($composer.value == '') { + localStorage.removeItem(key); + } + else { + localStorage.setItem(key, $composer.value); + } + updateWordCount(); +} +var resetTimer = function() { + clearTimeout(typingTimer); + typingTimer = setTimeout(updateContents, typingInterval); +} +function downloadData(filename, text) { + var text = localStorage.getItem(key); + if (text != null && text != '') { + var tmpElement = document.createElement('a'); + tmpElement.setAttribute('href', 'data:text/markdown;charset=utf-8,' + encodeURIComponent(text)); + tmpElement.setAttribute('download', key + '.md'); + tmpElement.style.display = 'none'; + + document.body.appendChild(tmpElement); + tmpElement.click(); + document.body.removeChild(tmpElement); + } +} +var saveEvent = function(event) { + if (event.keyCode == 83 && (event.metaKey || event.ctrlKey)) { + clearTimeout(typingTimer); + event.preventDefault(); + updateContents(); + if (event.shiftKey) + downloadData(); + } +} +function toggleTheme() { + if (localStorage && localStorage.getItem('theme') == 'toggled') { + localStorage.removeItem('theme'); + } else if (localStorage) { + localStorage.setItem('theme', 'toggled'); + } + document.documentElement.classList.toggle('toggled'); +} +function toggleFont() { + if (localStorage && localStorage.getItem('font') == 'mono') { + localStorage.removeItem('font'); + } else if (localStorage) { + localStorage.setItem('font', 'mono'); + } + document.documentElement.classList.toggle('mono'); +} +key = (new URLSearchParams(window.location.search)).get('key'); +if (key == '' || key == null) { + key = 'null'; +} +key = 'k-' + key; +if (localStorage && localStorage.getItem('theme') == 'toggled') { + document.documentElement.classList.toggle('toggled'); +} +if (localStorage && localStorage.getItem('font') == 'mono') { + document.documentElement.classList.toggle('mono'); +} +$composer.addEventListener('keyup input', resetTimer); +$composer.addEventListener('keydown', resetTimer); +$composer.addEventListener('input', resetTimer); +window.addEventListener('beforeunload', updateContents); +window.addEventListener('keydown', saveEvent); +loadContents(); +updateWordCount(); +// @license-end + </script> + </body> +</html>