use cli to pass in CSS vars
This commit is contained in:
parent
13a885ee11
commit
990667c2a7
58
cli.js
58
cli.js
|
@ -5,10 +5,33 @@ const map = require("map-stream");
|
||||||
const tokenize = require("./tokenize");
|
const tokenize = require("./tokenize");
|
||||||
const toHTML = require("./to-html");
|
const toHTML = require("./to-html");
|
||||||
|
|
||||||
|
// TODO: automatically pull these in from gmi.css (also for gmi.css(5))
|
||||||
|
const GMI_CSS_VARS = [
|
||||||
|
"foreground",
|
||||||
|
"background",
|
||||||
|
"p-size",
|
||||||
|
"p-indent",
|
||||||
|
"p-line-height",
|
||||||
|
"a-size",
|
||||||
|
"pre-size",
|
||||||
|
"pre-line-height",
|
||||||
|
"h1-size",
|
||||||
|
"h2-size",
|
||||||
|
"h3-size",
|
||||||
|
"heading-line-height",
|
||||||
|
"ul-size",
|
||||||
|
"ul-line-height",
|
||||||
|
"blockquote-size",
|
||||||
|
"blockquote-line-height",
|
||||||
|
"mono",
|
||||||
|
"serif",
|
||||||
|
"sans-serif",
|
||||||
|
];
|
||||||
|
|
||||||
require("yargs")
|
require("yargs")
|
||||||
.scriptName("gmi-web")
|
.scriptName("gmi-web")
|
||||||
.command(
|
.command(
|
||||||
"$0 <files..>",
|
"$0 [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
|
||||||
|
@ -19,52 +42,49 @@ require("yargs")
|
||||||
.option("images", {
|
.option("images", {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: false,
|
default: false,
|
||||||
description: "Include images",
|
describe: "Include images",
|
||||||
})
|
})
|
||||||
.option("audio", {
|
.option("audio", {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: false,
|
default: false,
|
||||||
description: "Include audio",
|
describe: "Include audio",
|
||||||
})
|
})
|
||||||
.option("video", {
|
.option("video", {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: false,
|
default: false,
|
||||||
description: "Include video",
|
describe: "Include video",
|
||||||
})
|
|
||||||
.option("verbose", {
|
|
||||||
alias: "v",
|
|
||||||
type: "boolean",
|
|
||||||
default: false,
|
|
||||||
description: "No logging to stdout",
|
|
||||||
})
|
})
|
||||||
.option("css", {
|
.option("css", {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: true,
|
default: true,
|
||||||
description: "Include gmi.css",
|
describe:
|
||||||
|
"gmi.css is included by default. Use --no-css to for the bare-minimum <style>",
|
||||||
}),
|
}),
|
||||||
(argv) => {
|
(argv) => {
|
||||||
if (argv.verbose) console.log(argv);
|
const override = GMI_CSS_VARS.reduce((style, key) => {
|
||||||
|
if (argv[key]) {
|
||||||
|
style += `--${key}: ${argv[key]};`;
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}, "");
|
||||||
fs.src(argv.files)
|
fs.src(argv.files)
|
||||||
.pipe(map(tokenize))
|
.pipe(map(tokenize))
|
||||||
.pipe(
|
.pipe(
|
||||||
map(
|
map(
|
||||||
toHTML({
|
toHTML({
|
||||||
css: !argv.noCss,
|
css: argv.css,
|
||||||
inline: {
|
inline: {
|
||||||
images: argv.images,
|
images: argv.images,
|
||||||
audio: argv.audio,
|
audio: argv.audio,
|
||||||
video: argv.video,
|
video: argv.video,
|
||||||
},
|
},
|
||||||
silent: argv.silent,
|
verbose: argv.verbose,
|
||||||
|
override,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.pipe(fs.dest((file) => path.dirname(file.path)));
|
.pipe(fs.dest((file) => path.dirname(file.path)));
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.showHelpOnFail(true, "Specify --help for available options")
|
||||||
.help().argv;
|
.help().argv;
|
||||||
|
|
||||||
function log(file, cb) {
|
|
||||||
console.log(file.path, file.contents ? file.contents.toString("utf8") : "");
|
|
||||||
cb(null, file);
|
|
||||||
}
|
|
||||||
|
|
38
gmi-web.1
38
gmi-web.1
|
@ -13,9 +13,7 @@ gmi-web - A bridge between Gemini and HTML.
|
||||||
.P
|
.P
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.P
|
.P
|
||||||
\fBgmi-web\fR [\fIOPTIONS\fR] <\fIFILES\fR>
|
\fBgmi-web\fR <\fIOPTIONS\fR> [\fIFILES\fR]
|
||||||
.P
|
|
||||||
\fBgmi-web\fR --recursive [\fIOPTIONS\fR] <\fIDIR\fR> [\fIOUT\fR]
|
|
||||||
.P
|
.P
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.P
|
.P
|
||||||
|
@ -24,24 +22,12 @@ and mobile-friendly fashion!
|
||||||
.P
|
.P
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.P
|
.P
|
||||||
\fBCSS variables\fR
|
\fBCSS Variables\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Any variable documented in \fBgmi.css(5)\fR can be set via \fIOPTIONS\fR of the same name.
|
Any variable documented in \fBgmi.css\fR(5) can be set via \fIOPTIONS\fR of the same
|
||||||
|
name.
|
||||||
.P
|
.P
|
||||||
\fBgmi-web\fR --foreground "#FFFFF" --background "#00000" *.gmi
|
\fBgmi-web\fR --foreground "#FFFFF" --background "#00000" $(find . -name *.gmi)
|
||||||
.P
|
|
||||||
.RE
|
|
||||||
\fB--recursive\fR <\fIDIR\fR> [\fIOUT\fR]
|
|
||||||
.RS 4
|
|
||||||
Recursively generate HTML for a \fIDIR\fR. Use \fIOUT\fR to put the .html somewhere
|
|
||||||
other than \fIDIR\fR.
|
|
||||||
.P
|
|
||||||
\fBgmi-web\fR --recursive capsule /srv/html/
|
|
||||||
.P
|
|
||||||
.RE
|
|
||||||
\fB--no-css\fR
|
|
||||||
.RS 4
|
|
||||||
Use only the bare-minimum <style>
|
|
||||||
.P
|
.P
|
||||||
.RE
|
.RE
|
||||||
\fB--images\fR \fB--audio\fR \fB--video\fR
|
\fB--images\fR \fB--audio\fR \fB--video\fR
|
||||||
|
@ -49,11 +35,21 @@ Use only the bare-minimum <style>
|
||||||
Include the respective media inline.
|
Include the respective media inline.
|
||||||
.P
|
.P
|
||||||
.RE
|
.RE
|
||||||
\fB--verbose\fR
|
\fB--no-css\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Enable internal logs for debugging mostly.
|
Use only the bare-minimum <style>
|
||||||
.P
|
.P
|
||||||
.RE
|
.RE
|
||||||
|
.SH EXAMPLES
|
||||||
|
.P
|
||||||
|
Render all the .gmi files in the current directory to .html
|
||||||
|
.P
|
||||||
|
.nf
|
||||||
|
.RS 4
|
||||||
|
gmi-web $(find \&. -name *\&.gmi)
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.P
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.P
|
.P
|
||||||
\fBgmi.css\fR(5)
|
\fBgmi.css\fR(5)
|
||||||
|
|
|
@ -6,9 +6,7 @@ gmi-web - A bridge between Gemini and HTML.
|
||||||
|
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
|
|
||||||
*gmi-web* [_OPTIONS_] <_FILES_>
|
*gmi-web* <_OPTIONS_> [_FILES_]
|
||||||
|
|
||||||
*gmi-web* --recursive [_OPTIONS_] <_DIR_> [_OUT_]
|
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
|
@ -17,26 +15,25 @@ and mobile-friendly fashion!
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
|
|
||||||
*CSS variables*
|
*CSS Variables*
|
||||||
Any variable documented in *gmi.css*(5) can be set via _OPTIONS_ of the same
|
Any variable documented in *gmi.css*(5) can be set via _OPTIONS_ of the same
|
||||||
name.
|
name.
|
||||||
|
|
||||||
*gmi-web* --foreground "#FFFFF" --background "#00000" \*.gmi
|
*gmi-web* --foreground "#FFFFF" --background "#00000" $(find . -name \*.gmi)
|
||||||
|
|
||||||
*--recursive* <_DIR_> [_OUT_]
|
|
||||||
Recursively generate HTML for a _DIR_. Use _OUT_ to put the .html somewhere
|
|
||||||
other than _DIR_.
|
|
||||||
|
|
||||||
*gmi-web* --recursive capsule /srv/html/
|
|
||||||
|
|
||||||
*--no-css*
|
|
||||||
Use only the bare-minimum <style>
|
|
||||||
|
|
||||||
*--images* *--audio* *--video*
|
*--images* *--audio* *--video*
|
||||||
Include the respective media inline.
|
Include the respective media inline.
|
||||||
|
|
||||||
*--verbose*
|
*--no-css*
|
||||||
Enable internal logs for debugging mostly.
|
Use only the bare-minimum <style>
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
Render all the .gmi files in the current directory to .html
|
||||||
|
|
||||||
|
```
|
||||||
|
gmi-web $(find . -name \*.gmi)
|
||||||
|
```
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
|
|
60
to-html.js
60
to-html.js
|
@ -2,40 +2,45 @@ const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const TOKENS_EXT = /\.tokens\.json$/;
|
const TOKENS_EXT = /\.tokens\.json$/;
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats
|
// 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 IMG_EXT = (exports.IMG_EXT = /\.(apng|avif|gif|jpg|jpeg|jfif|pjpeg|pjp|png|svg|webp)$/);
|
||||||
const AUDIO_EXT = /\.(mp3|wav|aac|aacp|mpeg|off|flac)$/;
|
const AUDIO_EXT = (exports.AUDIO_EXT = /\.(mp3|wav|aac|aacp|mpeg|off|flac)$/);
|
||||||
const VIDEO_EXT = /\.(mp4|webm)$/;
|
const VIDEO_EXT = (exports.VIDEO_EXT = /\.(mp4|webm)$/);
|
||||||
|
const BASE_STYLE = (exports.baseStyle =
|
||||||
|
"<style>p,a,pre,h1,h2,h3,ul,blockquote,img,audio,video{display:block;max-width:100%;margin:0;padding:0;overflow-wrap:anywhere;}</style>");
|
||||||
|
|
||||||
module.exports = (options) => (file, cb) => {
|
const toHTML = (exports.toHTML = (file, options) => `<!DOCTYPE html>
|
||||||
if (!TOKENS_EXT.test(file.path)) return cb(null);
|
<html style="${options.override}">
|
||||||
|
${head(file.path, options)}
|
||||||
// TODO: meta: language, description, canonical
|
|
||||||
file.contents = Buffer.from(`<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">${
|
|
||||||
options.css
|
|
||||||
? `\n<meta name="color-scheme" content="dark light">\n<style>${fs.readFileSync(
|
|
||||||
path.resolve(__dirname, "./gmi.min.css"),
|
|
||||||
"utf8"
|
|
||||||
)}</style>\n<style>${fs.readFileSync(
|
|
||||||
path.resolve(path.dirname(file.path), "./gmi.css"),
|
|
||||||
"utf8"
|
|
||||||
)}</style>`
|
|
||||||
: "<style>p,a,pre,h1,h2,h3,ul,blockquote,img,audio,video{display:block;max-width:100%;margin:0;padding:0;overflow-wrap:anywhere;}</style>"
|
|
||||||
}
|
|
||||||
<body>
|
<body>
|
||||||
${toHTML(JSON.parse(file.contents.toString("utf8")), options)}
|
${gemtext(JSON.parse(file.contents.toString("utf8")), options)}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`);
|
`);
|
||||||
|
module.exports = (options) => (file, cb) => {
|
||||||
|
if (!TOKENS_EXT.test(file.path)) return cb(null);
|
||||||
|
file.contents = Buffer.from(toHTML(file, options));
|
||||||
file.path = file.path.replace(TOKENS_EXT, ".html");
|
file.path = file.path.replace(TOKENS_EXT, ".html");
|
||||||
if (!options.silent) console.log(file.path);
|
if (options.verbose) console.log(file.path);
|
||||||
return cb(null, file);
|
return cb(null, file);
|
||||||
};
|
};
|
||||||
|
|
||||||
function toHTML(tokens, options) {
|
// TODO: meta: language, description, canonical
|
||||||
|
function head(file, options) {
|
||||||
|
return `<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">${
|
||||||
|
!options.css
|
||||||
|
? BASE_STYLE
|
||||||
|
: `\n<meta name="color-scheme" content="dark light">\n<style>${fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "./gmi.min.css"),
|
||||||
|
"utf8"
|
||||||
|
)}</style>`
|
||||||
|
}
|
||||||
|
</head>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gemtext(tokens, options) {
|
||||||
let body = [];
|
let body = [];
|
||||||
|
|
||||||
let cursor = tokens.shift();
|
let cursor = tokens.shift();
|
||||||
|
@ -83,3 +88,8 @@ function line(
|
||||||
if (quote) return `<blockquote>${quote}</blockquote>`;
|
if (quote) return `<blockquote>${quote}</blockquote>`;
|
||||||
return `<p><br></p>`;
|
return `<p><br></p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function omit(key, obj) {
|
||||||
|
const { [key]: omitted, ...rest } = obj;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue