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 [--no-css] [files..]
gmi-web [--css] [files..]
Convert .gmi to .html. See gmi-web(1) for more details.
Positionals:
files .gmi files to convert to .html [required]
files The *.gmi files to convert [required]
Options:
--version Show version number [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*:

34
cli.js
View file

@ -8,23 +8,49 @@ const toHTML = require("./to-html");
require("yargs")
.scriptName("gmi-web")
.command(
"$0 [--no-css] [files..]",
"$0 [--css] [files..]",
"Convert .gmi to .html. See gmi-web(1) for more details.",
(yargs) =>
yargs
.positional("files", {
describe: ".gmi files to convert to .html",
describe: "The *.gmi files to convert",
})
.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", {
type: "boolean",
default: true,
description: "Toggle inclusion of gmi.css.",
description: "Include gmi.css",
}),
(argv) => {
fs.src(argv.files)
.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)));
}
)

View file

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

View file

@ -1,24 +1,32 @@
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);
file.path = file.path.replace(TOKENS_EXT, ".html");
// TODO: meta: author, copyright, keywords, content-type, language, description, canonical
file.contents = Buffer.from(`<!DOCTYPE html>
<html>
<meta charset="utf-8">
<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">'
: ""
}
<body>
${toHTML(JSON.parse(file.contents.toString("utf8")))}
${toHTML(JSON.parse(file.contents.toString("utf8")), options)}
</body>
</html>
`);
file.path = file.path.replace(TOKENS_EXT, ".html");
return cb(null, file);
};
function toHTML(tokens) {
function toHTML(tokens, options) {
let body = [];
let cursor = tokens.shift();
@ -37,16 +45,29 @@ function toHTML(tokens) {
body.push("</ul>");
tokens = tokens.slice(closing + 1);
}
body.push(line(cursor));
body.push(line(cursor, options));
cursor = tokens.shift();
}
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 (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 (h2) return `<h2>${h2}</h2>`;
if (h3) return `<h3>${h3}</h3>`;