diff --git a/.eslintrc.js b/.eslintrc.js index 426cbd1..b952bfd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,8 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": "latest" + "ecmaVersion": "latest", + "sourceType": "module" }, "rules": { } diff --git a/content/js/ttytheme/events.js b/content/js/ttytheme/events.js new file mode 100644 index 0000000..03a06e6 --- /dev/null +++ b/content/js/ttytheme/events.js @@ -0,0 +1,34 @@ +"use strict"; + +import { + clear_states, + generate_theme, + export_element_as_file, + copy_elem_to_clip, +} from "./menu.js"; + +const ONCLICK_EVENTS = { + "clear-states": () => { + clear_states(); + window.location.reload(); + }, + "generate-theme": generate_theme, + "export-file": export_element_as_file, + copy: copy_elem_to_clip, +}; + +function main() { + for (let id in ONCLICK_EVENTS) + document.getElementById(id).addEventListener("click", async () => { + ONCLICK_EVENTS[id](); + + let self_elem = document.getElementById(id); + let old_text = self_elem.innerText; + + self_elem.innerText = "Done!"; + await new Promise((r) => setTimeout(r, 800)); + self_elem.innerText = old_text; + }); +} + +document.addEventListener("DOMContentLoaded", main); diff --git a/content/js/ttytheme/menu.js b/content/js/ttytheme/menu.js new file mode 100644 index 0000000..dfb1751 --- /dev/null +++ b/content/js/ttytheme/menu.js @@ -0,0 +1,209 @@ +"use strict"; + +import { gp } from "../../js/utils/index.js"; + +var tty_clrs = { + black: [0, 0, 0], + red: [170, 0, 0], + green: [0, 170, 0], + orange: [170, 85, 0], + blue: [0, 0, 170], + magenta: [170, 0, 170], + cyan: [0, 170, 170], + gray: [170, 170, 170], + dark_gray: [85, 85, 85], + light_red: [255, 85, 85], + light_green: [85, 255, 85], + yellow: [255, 255, 85], + light_blue: [85, 85, 255], + light_magenta: [255, 85, 255], + light_cyan: [85, 255, 255], + white: [255, 255, 255], +}; +const CLRS = ["r", "g", "b"]; +const BLACKLIST_LC = ["username", "password"]; +const TTY_MODS = { + black: "0", + red: "9", + green: "2", + orange: "1", + blue: "4", + magenta: "5", + cyan: "6", + gray: "7", + dark_gray: "5", + light_red: "5", + light_green: "A", + yellow: "B", + light_blue: "C", + light_magenta: "D", + light_cyan: "E", + white: "F", +}; + +function new_colourpicker(id, clr_map) { + let div = document.createElement("div"); + + div.id = id; + + CLRS.forEach((item, index) => { + let clr = document.createElement("input"); + + clr.type = "range"; + clr.min = 0; + clr.max = 255; + clr.name = item; + clr.value = clr_map[index]; + clr.addEventListener("input", () => update_colour(id)); + + div.appendChild(clr); + }); + + return div; +} + +function update_colour(id) { + let picker = document.getElementById(id); + let gc = (value) => Number(picker.children[value].value); // get colour + let rgb = [gc("r"), gc("g"), gc("b")]; + + picker.style.backgroundColor = `rgb(${rgb.join(",")})`; + localStorage.setItem(id, JSON.stringify({ rgb: rgb })); + + tty_clrs[id] = rgb; + generate_theme(); +} + +function load_from_localtorage() { + let keys = Object.keys(tty_clrs); + + Object.keys(localStorage).forEach((key) => { + if (!BLACKLIST_LC.includes(key) && keys.includes(key)) + tty_clrs[key] = JSON.parse(localStorage.getItem(key))["rgb"].map( + Number + ); + }); +} + +function component_to_hex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; +} + +function rgb_to_hex(rgb) { + let hex = ""; + rgb.forEach((item) => (hex += component_to_hex(item))); + return hex; +} + +function save(filename, data) { + const blob = new Blob([data], { type: "text/plain" }); + + if (window.navigator.msSaveOrOpenBlob) + window.navigator.msSaveBlob(blob, filename); + else { + const elem = window.document.createElement("a"); + + elem.href = window.URL.createObjectURL(blob); + elem.download = filename; + + document.body.appendChild(elem); + elem.click(); + document.body.removeChild(elem); + } +} + +export function generate_theme(query = "#theme-output") { + document.body.style.backgroundColor = `rgb(${tty_clrs["black"].join(",")})`; + document.body.style.color = `rgb(${tty_clrs["white"].join(",")})`; + + let elem = document.querySelector(query); + if (!elem) throw ReferenceError(`${query} did not match any results`); + + let text = `# Theme generated using: ${window.location.href} +# Installation: Just add these lines to your ~/.bashrc + +__tty_theme() { + [ "$TERM" != 'linux' ] && return # Only run in a TTY + +`; + + for (const key in tty_clrs) { + let key_rgb = tty_clrs[key]; + let key_hex = rgb_to_hex(key_rgb); + let rgb_str = `rgb(${key_rgb.join(", ")})`; + + text += ` printf "\\e]P${TTY_MODS[key]}${key_hex}" # ${key}${gp( + key, + 15 + )}${rgb_str}${gp(rgb_str, 20)}#${key_hex}\n`; + } + + text += ` + clear # To fix the background +} + +__tty_theme +`; + elem.innerText = text; +} + +export function export_element_as_file(query = "#theme-output") { + generate_theme(query); + save("ari_web_theme.bash", document.querySelector(query).innerText); +} + +export function clear_states() { + Object.keys(localStorage).forEach((key) => { + if (!BLACKLIST_LC.includes(key)) localStorage.removeItem(key); + }); +} + +export function copy_elem_to_clip(query = "#theme-output") { + let elem = document.querySelector(query); + if (!elem) throw ReferenceError(`Query '${query}' did not return anything`); + + let text_area = document.createElement("textarea"); + + text_area.style.position = "fixed"; + text_area.style.top = 0; + text_area.style.left = 0; + + text_area.style.width = "2em"; + text_area.style.height = "2em"; + + text_area.style.padding = 0; + + text_area.style.border = "none"; + text_area.style.outline = "none"; + text_area.style.boxShadow = "none"; + + text_area.style.background = "transparent"; + + text_area.value = elem.innerText; + + document.body.appendChild(text_area); + text_area.focus(); + text_area.select(); + + document.execCommand("copy"); + document.body.removeChild(text_area); +} + +function main() { + load_from_localtorage(); + let menu = document.getElementById("menu"); + + for (const key in tty_clrs) { + let [r, g, b] = tty_clrs[key]; + let picker = new_colourpicker(key, [r, g, b]); + + picker.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; + + menu.appendChild(picker); + } + + generate_theme(); +} + +document.addEventListener("DOMContentLoaded", main); diff --git a/content/js/utils/index.js b/content/js/utils/index.js new file mode 100644 index 0000000..80aabc1 --- /dev/null +++ b/content/js/utils/index.js @@ -0,0 +1,10 @@ +"use strict"; + +// Get padding +export function gp(str, pad) { + let ammount = pad - str.length; + + if (ammount <= 0) return ""; + + return " ".repeat(ammount); +} diff --git a/content/styles/ttytheme/styles.css b/content/styles/ttytheme/styles.css new file mode 100644 index 0000000..34e5d1d --- /dev/null +++ b/content/styles/ttytheme/styles.css @@ -0,0 +1,125 @@ +@import url("https://cdn.jsdelivr.net/npm/hack-font@3/build/web/hack.min.css"); + +*, +*::before, +*::after { + margin: 0; + padding: 0; + + -webkit-box-sizing: border-box; + box-sizing: border-box; + + font-family: sans-serif; + color: inherit; + scroll-behavior: smooth; +} + +pre { + font-family: Hack, hack, monospace; + font-weight: bold; +} + +body { + min-height: 100vh; + text-rendering: optimizeSpeed; + line-height: 1.5; + padding: 2em; + max-width: 1600px; + margin: auto; +} + +input, +button, +textarea, +select { + font: inherit; +} + +.split { + display: -ms-grid; + display: grid; + + -ms-grid-columns: auto 4em auto; + grid-template-columns: auto auto; + + grid-gap: 4em; +} + +h1, +h2 { + padding: 0.2em; + text-align: center; +} + +h2 { + padding-bottom: 2em; +} + +a { + text-decoration: underline; +} + +.buttons { + padding: 1em; + text-align: center; +} + +.buttons button { + padding: 0.5em; + margin: 0.1em; + margin-bottom: 1em; + background-color: #404040; + border: none; + + -webkit-transition: 0.3s -webkit-filter ease-in-out; + transition: 0.3s -webkit-filter ease-in-out; + -o-transition: 0.3s filter ease-in-out; + transition: 0.3s filter ease-in-out; + transition: 0.3s filter ease-in-out, 0.3s -webkit-filter ease-in-out; +} + +button:hover, +button:focus { + cursor: pointer; + + -webkit-filter: brightness(120%); + filter: brightness(120%); +} + +#menu { + text-align: center; + margin: 4em 0 4em 0; +} + +#menu div input { + margin: 0.1em; +} + +@media only screen and (max-width: 1300px) { + .split { + display: block; + -ms-grid-columns: auto; + + grid-template-columns: auto; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto; + + -webkit-animation-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + + -webkit-animation-iteration-count: 1 !important; + animation-iteration-count: 1 !important; + + -webkit-transition-duration: 0.01ms !important; + -o-transition-duration: 0.01ms !important; + transition-duration: 0.01ms !important; + + scroll-behavior: auto !important; + } +} diff --git a/page/ttytheme/index.html b/page/ttytheme/index.html new file mode 100644 index 0000000..5a46a9a --- /dev/null +++ b/page/ttytheme/index.html @@ -0,0 +1,79 @@ + + +
+ + + +