gmi-web(1) inline media

This commit is contained in:
Talon Poole 2021-01-29 00:24:22 +00:00
parent 06a8bab54a
commit 541c6345e4
4 changed files with 69 additions and 16 deletions

View file

@ -75,17 +75,20 @@ gmi.css will respect system dark mode preferences by inverting `--foreground` an
## gmi-web(1) ## gmi-web(1)
``` ```
gmi-web [--no-css] [files..] gmi-web [--css] [files..]
Convert .gmi to .html. See gmi-web(1) for more details. Convert .gmi to .html. See gmi-web(1) for more details.
Positionals: Positionals:
files .gmi files to convert to .html [required] files The *.gmi files to convert [required]
Options: Options:
--version Show version number [boolean] --version Show version number [boolean]
--help Show help [boolean] --help Show help [boolean]
--css Toggle inclusion of gmi.css. [boolean] [default: true] --images Include images [boolean] [default: false]
--audio Include audio [boolean] [default: false]
--video Include video [boolean] [default: false]
--css Include gmi.css [boolean] [default: true]
``` ```
*You will need*: *You will need*:

34
cli.js
View file

@ -8,23 +8,49 @@ const toHTML = require("./to-html");
require("yargs") require("yargs")
.scriptName("gmi-web") .scriptName("gmi-web")
.command( .command(
"$0 [--no-css] [files..]", "$0 [--css] [files..]",
"Convert .gmi to .html. See gmi-web(1) for more details.", "Convert .gmi to .html. See gmi-web(1) for more details.",
(yargs) => (yargs) =>
yargs yargs
.positional("files", { .positional("files", {
describe: ".gmi files to convert to .html", describe: "The *.gmi files to convert",
}) })
.required("files", true) .required("files", true)
.option("images", {
type: "boolean",
default: false,
description: "Include images",
})
.option("audio", {
type: "boolean",
default: false,
description: "Include audio",
})
.option("video", {
type: "boolean",
default: false,
description: "Include video",
})
.option("css", { .option("css", {
type: "boolean", type: "boolean",
default: true, default: true,
description: "Toggle inclusion of gmi.css.", description: "Include gmi.css",
}), }),
(argv) => { (argv) => {
fs.src(argv.files) fs.src(argv.files)
.pipe(map(tokenize)) .pipe(map(tokenize))
.pipe(map(toHTML({ css: argv["css"] }))) .pipe(
map(
toHTML({
css: argv.css,
inline: {
images: argv.images,
audio: argv.audio,
video: argv.video,
},
})
)
)
.pipe(fs.dest((file) => path.dirname(file.path))); .pipe(fs.dest((file) => path.dirname(file.path)));
} }
) )

View file

@ -6,7 +6,7 @@ gmi-web - A bridge between Gemini and HTML
# SYNOPSIS # SYNOPSIS
*gmi-web* [--no-css] _files_ *gmi-web* [--images] [--audio] [--video] [--no-css] _files_
# DESCRIPTION # DESCRIPTION
@ -15,6 +15,9 @@ and mobile-friendly fashion!
# OPTIONS # OPTIONS
*--images* *--audio* *--video*
Include the respective media inline.
*--no-css* *--no-css*
Do not include gmi.css in the rendered HTML markup. Do not include gmi.css in the rendered HTML markup.

View file

@ -1,24 +1,32 @@
const TOKENS_EXT = /\.tokens\.json$/; const TOKENS_EXT = /\.tokens\.json$/;
module.exports = ({ css } = { css: true }) => (file, cb) => { // https://developer.mozilla.org/en-US/docs/Web/Media/Formats
const IMG_EXT = /\.(apng|avif|gif|jpg|jpeg|jfif|pjpeg|pjp|png|svg|webp)$/;
const AUDIO_EXT = /\.(mp3|wav|aac|aacp|mpeg|off|flac)$/;
const VIDEO_EXT = /\.(mp4|webm)$/;
module.exports = (options) => (file, cb) => {
if (!TOKENS_EXT.test(file.path)) return cb(null, file); if (!TOKENS_EXT.test(file.path)) return cb(null, file);
file.path = file.path.replace(TOKENS_EXT, ".html");
// TODO: meta: author, copyright, keywords, content-type, language, description, canonical
file.contents = Buffer.from(`<!DOCTYPE html> file.contents = Buffer.from(`<!DOCTYPE html>
<html> <html>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">${ <meta name="viewport" content="width=device-width,initial-scale=1">${
css options.css
? '\n<meta name="color-scheme" content="dark light">\n<link rel="stylesheet" href="/gmi.min.css">' ? '\n<meta name="color-scheme" content="dark light">\n<link rel="stylesheet" href="/gmi.min.css">'
: "" : ""
} }
<body> <body>
${toHTML(JSON.parse(file.contents.toString("utf8")))} ${toHTML(JSON.parse(file.contents.toString("utf8")), options)}
</body> </body>
</html> </html>
`); `);
file.path = file.path.replace(TOKENS_EXT, ".html");
return cb(null, file); return cb(null, file);
}; };
function toHTML(tokens) { function toHTML(tokens, options) {
let body = []; let body = [];
let cursor = tokens.shift(); let cursor = tokens.shift();
@ -37,16 +45,29 @@ function toHTML(tokens) {
body.push("</ul>"); body.push("</ul>");
tokens = tokens.slice(closing + 1); tokens = tokens.slice(closing + 1);
} }
body.push(line(cursor)); body.push(line(cursor, options));
cursor = tokens.shift(); cursor = tokens.shift();
} }
return body.join("\n"); return body.join("\n");
} }
function line({ text, href, title, pre, alt, h1, h2, h3, li, quote }) { function line(
{ text, href, title, pre, alt, h1, h2, h3, li, quote },
{ inline }
) {
console.log(inline)
if (text) return `<p>${text}</p>`; if (text) return `<p>${text}</p>`;
if (href) return `<a href="${href}">${title || href}</a>`; if (href) {
if (inline.images && IMG_EXT.test(href))
return `<img src="${href}" title="${title}"/>`;
if (inline.audio && AUDIO_EXT.test(href))
return `<audio controls src="${href}" title="${title}"></audio>`;
if (inline.video && VIDEO_EXT.test(href))
return `<video controls src="${href}" title="${title}"/></video>`;
return `<a href="${href}">${title || href}</a>`;
}
if (h1) return `<h1>${h1}</h1>`; if (h1) return `<h1>${h1}</h1>`;
if (h2) return `<h2>${h2}</h2>`; if (h2) return `<h2>${h2}</h2>`;
if (h3) return `<h3>${h3}</h3>`; if (h3) return `<h3>${h3}</h3>`;