From 92a1a629952f95ffc5cc4954e814a093dcfc4f42 Mon Sep 17 00:00:00 2001 From: Talon Poole Date: Thu, 28 Jan 2021 06:09:49 +0000 Subject: [PATCH 1/2] scaffold out the cli --- .gitignore | 1 + Makefile | 18 ++++++- cli.js | 11 +++++ gmi-web.1.scd | 16 +++++++ package-lock.json | 118 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 12 +++-- 6 files changed, 170 insertions(+), 6 deletions(-) create mode 100755 cli.js create mode 100644 gmi-web.1.scd diff --git a/.gitignore b/.gitignore index 68c696d..6822251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.min.* node_modules/ +gmi-web.1 diff --git a/Makefile b/Makefile index 78607b7..504177e 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,19 @@ -build: +PREFIX=/usr/local +INSTALLDIR=$(DESTDIR)$(PREFIX) +MANDIR=$(INSTALLDIR)/share/man + +gmi-web.1: gmi-web.1.scd + scdoc < $< > $@ + +install: gmi-web.1 + sudo npm link + install -Dm644 gmi-web.1 $(MANDIR)/man1/gmi-web.1 + +minify: cat gmi.css | ./node_modules/minify/bin/minify.js --css > gmi.min.css cat gmi.js | ./node_modules/minify/bin/minify.js --js > gmi.min.js + +clean: + rm -rf gmi.min.* gmi-web.1 + +.PHONY: install diff --git a/cli.js b/cli.js new file mode 100755 index 0000000..41f9b10 --- /dev/null +++ b/cli.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node + +require("yargs") + .scriptName("gmi-web") + .command("$0", "A bridge between Gemini and HTML", yargs => yargs + // TODO + , argv => { + // TODO + }) + .help() + .argv diff --git a/gmi-web.1.scd b/gmi-web.1.scd new file mode 100644 index 0000000..615d39b --- /dev/null +++ b/gmi-web.1.scd @@ -0,0 +1,16 @@ +gmi-web(1) + +# NAME + +gmi-web - a bridge between Gemini and HTML + +# SYNOPSIS + +# DESCRIPTION + +# SEE ALSO + +# AUTHORS + +Maintained by Talon Poole . Up-to-date sources can be +found at https://codeberg.org/talon/gmi-web diff --git a/package-lock.json b/package-lock.json index f4df60f..ede08f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,19 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -26,6 +39,29 @@ "source-map": "~0.6.0" } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -53,6 +89,21 @@ "tslib": "^2.0.3" } }, + "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==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -91,6 +142,11 @@ } } }, + "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==" + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -144,11 +200,21 @@ "tslib": "^2.0.3" } }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==" + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -163,6 +229,24 @@ "source-map": "^0.6.0" } }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, "terser": { "version": "5.5.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", @@ -194,6 +278,40 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" } } } diff --git a/package.json b/package.json index 2ebb3fc..f4b28bb 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,16 @@ "name": "gmi-web", "version": "1.0.0", "description": "A bridge between the HTML and Gemini", - "main": "bin/gmi-web.js", + "main": "gmi.css", + "bin": { + "gmi-web": "cli.js" + }, "dependencies": { - "minify": "^6.0.1" + "minify": "^6.0.1", + "prettier": "^2.2.1", + "yargs": "^16.2.0" }, "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, "repository": { "type": "git", "url": "https://codeberg.org/talon/gmi-web.git" From 89cb9cb6911264615738618127ee7dc87b3789f8 Mon Sep 17 00:00:00 2001 From: Talon Poole Date: Thu, 28 Jan 2021 06:16:46 +0000 Subject: [PATCH 2/2] make format --- Makefile | 5 + README.md | 10 +- cli.js | 15 +-- example.html | 2 +- gmi.css | 194 +++++++++++++++++---------------- gmi.js | 294 +++++++++++++++++++++++++++++++++------------------ 6 files changed, 317 insertions(+), 203 deletions(-) diff --git a/Makefile b/Makefile index 504177e..ead688d 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,11 @@ PREFIX=/usr/local INSTALLDIR=$(DESTDIR)$(PREFIX) MANDIR=$(INSTALLDIR)/share/man +format: + ./node_modules/prettier/bin-prettier.js --write gmi.css + ./node_modules/prettier/bin-prettier.js --write cli.js + ./node_modules/prettier/bin-prettier.js --write gmi.js + gmi-web.1: gmi-web.1.scd scdoc < $< > $@ diff --git a/README.md b/README.md index c10b1fa..c8e620c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # gmi-web + ![CC0](https://licensebuttons.net/p/zero/1.0/80x15.png) **Vision**: Provide the lowest common denominator between HTML/CSS/JS and Gemini. @@ -8,14 +9,15 @@ Check out the annotated [example.html](https://gmi.eattherich.club/example.html)! Due to the ambiguity of HTML several translations from Gemini exist in the wild. I propose the following standard: -``` + +````


Optionally, if a link is consumable by "img", "audio" or "video" you may insert the media inline. Images and video should be styled to have "max-width: 100%;" so they don't overflow the body. It's a good idea to also include the "controls" attribute for videos and audio. "audio" tags require "display: block;" just like "a".

- +


diff --git a/gmi.css b/gmi.css index 241abea..50e8d8d 100644 --- a/gmi.css +++ b/gmi.css @@ -1,112 +1,124 @@ /* gmi.css is CCO */ :root { - --foreground: black; - --background: white; - --p-size: 1.25rem; - --p-indent: 0rem; - --a-size: var(--p-size); - --pre-size: 1rem; - --h1-size: 3rem; - --h2-size: 2.25rem; - --h3-size: 1.5rem; - --ul-size: var(--p-size); - --blockquote-size: var(--p-size); - --mono: Consolas, monaco, monospace; - --serif: georgia, times, serif; - --sans-serif: -apple-system, BlinkMacSystemFont, 'avenir next', avenir, helvetica, 'helvetica neue', ubuntu, roboto, noto, 'segoe ui', arial, sans-serif; + --foreground: black; + --background: white; + --p-size: 1.25rem; + --p-indent: 0rem; + --a-size: var(--p-size); + --pre-size: 1rem; + --h1-size: 3rem; + --h2-size: 2.25rem; + --h3-size: 1.5rem; + --ul-size: var(--p-size); + --blockquote-size: var(--p-size); + --mono: Consolas, monaco, monospace; + --serif: georgia, times, serif; + --sans-serif: -apple-system, BlinkMacSystemFont, "avenir next", avenir, + helvetica, "helvetica neue", ubuntu, roboto, noto, "segoe ui", arial, + sans-serif; } body { - max-width: 48rem; - padding: .5rem; - margin: 0 auto; + max-width: 48rem; + padding: 0.5rem; + margin: 0 auto; } -p, a, pre, h1, h2, h3, ul, blockquote, img, audio, video { - display: block; - max-width: 100%; - margin: 0; - padding: 0; - overflow-wrap: anywhere +p, +a, +pre, +h1, +h2, +h3, +ul, +blockquote, +img, +audio, +video { + display: block; + max-width: 100%; + margin: 0; + padding: 0; + overflow-wrap: anywhere; } h1, h2, h3 { - font-family: var(--sans-serif); - line-height: 1.25; + font-family: var(--sans-serif); + line-height: 1.25; } p { - font-size: var(--p-size); - font-family: var(--serif); - text-indent: var(--p-indent); - line-height: 1.5; + font-size: var(--p-size); + font-family: var(--serif); + text-indent: var(--p-indent); + line-height: 1.5; } a::before { - font-size: var(--p-size); - font-family: var(--mono); - content: "⇒"; - padding-right: .25rem; - vertical-align: middle + font-size: var(--p-size); + font-family: var(--mono); + content: "⇒"; + padding-right: 0.25rem; + vertical-align: middle; } a { - font-size: var(--p-size); - font-family: var(--serif); - text-decoration: none; + font-size: var(--p-size); + font-family: var(--serif); + text-decoration: none; } pre { - font-size: var(--pre-size); - font-family: var(--mono); - line-height: 1; - padding: 1.25rem; - overflow-y: auto; + font-size: var(--pre-size); + font-family: var(--mono); + line-height: 1; + padding: 1.25rem; + overflow-y: auto; } h1 { - font-size: var(--h1-size); + font-size: var(--h1-size); } h2 { - font-size: var(--h2-size); + font-size: var(--h2-size); } h3 { - font-size: var(--h3-size); + font-size: var(--h3-size); } ul { - font-size: var(--p-size); - font-family: var(--serif); - line-height: 1.25; - list-style-type: none; + font-size: var(--p-size); + font-family: var(--serif); + line-height: 1.25; + list-style-type: none; } li::before { - font-size: var(--p-size); - font-family: var(--mono); - content: "*"; - vertical-align: middle; - padding-right: .5rem; + font-size: var(--p-size); + font-family: var(--mono); + content: "*"; + vertical-align: middle; + padding-right: 0.5rem; } blockquote { - font-size: var(--p-size); - font-family: var(--serif); - line-height: 1.5; - padding-left: .75rem; + font-size: var(--p-size); + font-family: var(--serif); + line-height: 1.5; + padding-left: 0.75rem; } -pre+blockquote { - padding-top: .5rem; - padding-bottom: .5rem; +pre + blockquote { + padding-top: 0.5rem; + padding-bottom: 0.5rem; } /* foreground and background colors */ -html, +html, body, h1, h2, @@ -117,48 +129,48 @@ ul, blockquote, pre::selection, pre::-moz-selection { - color: var(--foreground); - background-color: var(--background); + color: var(--foreground); + background-color: var(--background); } blockquote { - border-left: .5rem solid var(--foreground); + border-left: 0.5rem solid var(--foreground); } pre, ::selection, ::-moz-selection, a:hover { - color: var(--background); - background-color: var(--foreground); + color: var(--background); + background-color: var(--foreground); } /* when system preference is "dark" invert foreground and background colors */ @media (prefers-color-scheme: dark) { - html, - body, - h1, - h2, - h3, - p, - a, - ul, - blockquote, - pre::selection, - pre::-moz-selection { - color: var(--background); - background-color: var(--foreground); - } + html, + body, + h1, + h2, + h3, + p, + a, + ul, + blockquote, + pre::selection, + pre::-moz-selection { + color: var(--background); + background-color: var(--foreground); + } - blockquote { - border-left: .5rem solid var(--background); - } + blockquote { + border-left: 0.5rem solid var(--background); + } - pre, - ::selection, - ::-moz-selection, - a:hover { - color: var(--foreground); - background-color: var(--background); - } + pre, + ::selection, + ::-moz-selection, + a:hover { + color: var(--foreground); + background-color: var(--background); + } } diff --git a/gmi.js b/gmi.js index 95b503b..150a603 100644 --- a/gmi.js +++ b/gmi.js @@ -1,142 +1,234 @@ /* gmi.js is licensed under CC0 */ class Gemini { - static syntax = { P: "", A: "=>", UL: "*", BLOCKQUOTE: ">", PRE: "```", H1: "#", H2: "##", H3: "###", } + static syntax = { + P: "", + A: "=>", + UL: "*", + BLOCKQUOTE: ">", + PRE: "```", + H1: "#", + H2: "##", + H3: "###", + }; static line(line, type) { - if (typeof line === "string") line = {content: line, type: type || "P"} - let dom = Gemini.render(line).dom + if (typeof line === "string") line = { content: line, type: type || "P" }; + let dom = Gemini.render(line).dom; return { - get dom() { return dom }, - get type() { return this.dom.nodeName }, - set type(type) { dom = Gemini.render({dom: this.dom, type, content: Gemini.contentFrom(this.dom)}).dom }, - get content() { return Gemini.contentFrom(dom) }, - set content(content) { Gemini.render({dom, type: dom.nodeName, content}) }, - get editable() { return this.dom.contentEditable === "true" }, - set editable(value) { Gemini.render({dom: this.dom, type: this.type, content: this.content, editable: value}) }, - delete() { return this.dom.remove() }, - get gmi() { - const syntax = Gemini.syntax[this.type] - const content = Gemini.contentFrom(this.dom).replace(/\n?$/, "") + get dom() { + return dom; + }, + get type() { + return this.dom.nodeName; + }, + set type(type) { + dom = Gemini.render({ + dom: this.dom, + type, + content: Gemini.contentFrom(this.dom), + }).dom; + }, + get content() { + return Gemini.contentFrom(dom); + }, + set content(content) { + Gemini.render({ dom, type: dom.nodeName, content }); + }, + get editable() { + return this.dom.contentEditable === "true"; + }, + set editable(value) { + Gemini.render({ + dom: this.dom, + type: this.type, + content: this.content, + editable: value, + }); + }, + delete() { + return this.dom.remove(); + }, + get gmi() { + const syntax = Gemini.syntax[this.type]; + const content = Gemini.contentFrom(this.dom).replace(/\n?$/, ""); switch (this.type.toUpperCase()) { case "PRE": - return `${syntax}\n${content}\n${syntax}` - break + return `${syntax}\n${content}\n${syntax}`; + break; default: - return content.split("\n").map(line => - `${syntax !== "" ? syntax + " " : ""}${line}` - ).join("\n") + return content + .split("\n") + .map((line) => `${syntax !== "" ? syntax + " " : ""}${line}`) + .join("\n"); } }, - get before() { return Gemini.line(this.dom.previousElementSibling) }, - set before(line) { this.before.dom.after(line.dom) }, - get after() { return Gemini.line(this.dom.nextElementSibling) }, - set after(line) { this.after.dom.before(line.dom) }, - } + get before() { + return Gemini.line(this.dom.previousElementSibling); + }, + set before(line) { + this.before.dom.after(line.dom); + }, + get after() { + return Gemini.line(this.dom.nextElementSibling); + }, + set after(line) { + this.after.dom.before(line.dom); + }, + }; } static render(line) { if (line.dom && line.dom.nodeName !== line.type) { - const replacement = document.createElement(line.type) - line.dom.replaceWith(replacement) - line.dom = replacement + const replacement = document.createElement(line.type); + line.dom.replaceWith(replacement); + line.dom = replacement; } else if (line.nodeName) { line = { - dom: line, - type: line.nodeName, + dom: line, + type: line.nodeName, content: Gemini.contentFrom(line), - editable: line.contentEditable - } + editable: line.contentEditable, + }; } else { - line.dom = line.dom || document.createElement(line.type || "P") + line.dom = line.dom || document.createElement(line.type || "P"); } - line.dom.contentEditable = line.editable || "inherit" + line.dom.contentEditable = line.editable || "inherit"; switch (line.type.toUpperCase()) { case "A": - const {href, content} = Gemini.link(line.content) - line.dom.innerHTML = line.editable && href !== content ? `${href} ${content}` : content - line.dom.href = href - break - case "UL": - line.dom.innerHTML = line.content.split("\n").map(content => content.length > 0 ? - `
  • ${content}
  • ` : "" - ).join("\n") - break - case "BLOCKQUOTE": - line.dom.innerHTML = line.content.split("\n").map(content => - `
    ${content}
    ` - ).join("\n") - break + const { href, content } = Gemini.link(line.content); + line.dom.innerHTML = + line.editable && href !== content ? `${href} ${content}` : content; + line.dom.href = href; + break; + case "UL": + line.dom.innerHTML = line.content + .split("\n") + .map((content) => (content.length > 0 ? `
  • ${content}
  • ` : "")) + .join("\n"); + break; + case "BLOCKQUOTE": + line.dom.innerHTML = line.content + .split("\n") + .map((content) => `
    ${content}
    `) + .join("\n"); + break; case "PRE": - line.dom.textContent = line.content - break + line.dom.textContent = line.content; + break; default: - line.dom.innerHTML = line.content.replace(/\n+/g, "
    ") + line.dom.innerHTML = line.content.replace(/\n+/g, "
    "); } - return line + return line; } static contentFrom(dom) { switch (dom.nodeName.toUpperCase()) { case "BLOCKQUOTE": - return Array.from(dom.childNodes).map(child => - child.textContent - ).join("\n") - break - case "UL": - return Array.from(dom.children).map(child => - child.textContent - ).join("\n") - break - case "A": - const {href, content} = Gemini.link(dom.textContent) - return `${href || dom.href} ${content}` - break - case "PRE": - return dom.textContent - break + return Array.from(dom.childNodes) + .map((child) => child.textContent) + .join("\n"); + break; + case "UL": + return Array.from(dom.children) + .map((child) => child.textContent) + .join("\n"); + break; + case "A": + const { href, content } = Gemini.link(dom.textContent); + return `${href || dom.href} ${content}`; + break; + case "PRE": + return dom.textContent; + break; default: - return dom.innerHTML.replace(/
    /g, "\n") + return dom.innerHTML.replace(/
    /g, "\n"); } } // TODO: rename/move/idk this is awk - static link(content = "") { return /((?[^\s]+\/\/[^\s]+)\s)?(?.+)/.exec(content).groups } + static link(content = "") { + return /((?[^\s]+\/\/[^\s]+)\s)?(?.+)/.exec(content).groups; + } - constructor(root) { this.root = root } - get editable() { return this.root.contentEditable === "true" } + constructor(root) { + this.root = root; + } + get editable() { + return this.root.contentEditable === "true"; + } set editable(value) { - this.root.contentEditable = value - this.lines.forEach(line => line.editable = value) + this.root.contentEditable = value; + this.lines.forEach((line) => (line.editable = value)); } get lines() { - return Array.from(this.root.children).filter(el => [ - "P", "BLOCKQUOTE", "A", "PRE", "UL", "H1", "H2", "H3" - ].includes(el.nodeName)).map(Gemini.line) + return Array.from(this.root.children) + .filter((el) => + ["P", "BLOCKQUOTE", "A", "PRE", "UL", "H1", "H2", "H3"].includes( + el.nodeName + ) + ) + .map(Gemini.line); } set lines(lines) { - this.root.textContent = "" - this.root.append(...lines.map(line => line.dom)) - } - get source() { return this.lines.map(line => line.gmi).join("\n") } + this.root.textContent = ""; + this.root.append(...lines.map((line) => line.dom)); + } + get source() { + return this.lines.map((line) => line.gmi).join("\n"); + } // TODO: set source is a DOM thing, but parsing should be isomorphic // set source(gmi) {} download() { - const el = document.createElement('a') - el.setAttribute('href', 'data:text/gemini;charset=utf-8,' - + encodeURIComponent(this.source)) - el.setAttribute('download', - `${this.lines[0].content.replace(/\s/g, "_")}.gmi`) - el.style.display = 'none' - document.body.appendChild(el); el.click(); document.body.removeChild(el) + const el = document.createElement("a"); + el.setAttribute( + "href", + "data:text/gemini;charset=utf-8," + encodeURIComponent(this.source) + ); + el.setAttribute( + "download", + `${this.lines[0].content.replace(/\s/g, "_")}.gmi` + ); + el.style.display = "none"; + document.body.appendChild(el); + el.click(); + document.body.removeChild(el); + } + get foreground() { + return getComputedStyle(this.root).getPropertyValue("--foreground"); + } + set foreground(value) { + return this.root.style.setProperty("--foreground", value); + } + get background() { + return getComputedStyle(this.root).getPropertyValue("--background"); + } + set background(value) { + return this.root.style.setProperty("--background", value); + } + get size() { + return getComputedStyle(this.root).getPropertyValue("--font-size"); + } + set size(value) { + return this.root.style.setProperty("--font-size", value); + } + get lineHeight() { + return getComputedStyle(this.root).getPropertyValue("--line-height"); + } + set lineHeight(value) { + return this.root.style.setProperty("--line-height", value); + } + get serif() { + return getComputedStyle(this.root).getPropertyValue("--serif"); + } + set serif(value) { + return this.root.style.setProperty("--serif", value); + } + get sans() { + return getComputedStyle(this.root).getPropertyValue("--sans"); + } + set sans(value) { + return this.root.style.setProperty("--sans", value); + } + get mono() { + return getComputedStyle(this.root).getPropertyValue("--mono"); + } + set mono(value) { + return this.root.style.setProperty("--mono", value); } - get foreground() { return getComputedStyle(this.root).getPropertyValue("--foreground") } - set foreground(value) { return this.root.style.setProperty("--foreground", value) } - get background() { return getComputedStyle(this.root).getPropertyValue("--background") } - set background(value) { return this.root.style.setProperty("--background", value) } - get size() { return getComputedStyle(this.root).getPropertyValue("--font-size") } - set size(value) { return this.root.style.setProperty("--font-size", value) } - get lineHeight() { return getComputedStyle(this.root).getPropertyValue("--line-height") } - set lineHeight(value) { return this.root.style.setProperty("--line-height", value) } - get serif() { return getComputedStyle(this.root).getPropertyValue("--serif") } - set serif(value) { return this.root.style.setProperty("--serif", value) } - get sans() { return getComputedStyle(this.root).getPropertyValue("--sans") } - set sans(value) { return this.root.style.setProperty("--sans", value) } - get mono() { return getComputedStyle(this.root).getPropertyValue("--mono") } - set mono(value) { return this.root.style.setProperty("--mono", value) } }