three --css modes: gmi.css, base and none

when "none" inline tags will be wrapped in <p>

--image, --audio and --video now accept the extensions to inline
  so what's possible to load isn't maintained here

--lang is now referred to as --html
  --html and --body are mutually exclusive
This commit is contained in:
Talon Poole 2021-02-11 23:26:29 +00:00
parent 9184e2c792
commit 2ade53f268
7 changed files with 71 additions and 48 deletions

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
example/

View file

@ -1,4 +1,5 @@
build: format gmi-web.1 gmi.css.5
build: gmi-web.1 gmi.css.5
npm install
gmi-web.1: gmi-web.1.scd
scdoc < $< > $@
@ -6,15 +7,10 @@ gmi-web.1: gmi-web.1.scd
gmi.css.5: gmi.css.5.scd
scdoc < $< > $@
format:
./node_modules/prettier/bin-prettier.js --write README.md
./node_modules/prettier/bin-prettier.js --write gmi.css
./node_modules/prettier/bin-prettier.js --write *.js !*.min.js
example/test.html: example/test.gmi
./test.sh
clean:
rm -rf gmi.min.* gmi-web.1 gmi.css.5 node_modules
rm -rf gmi-web.1 gmi.css.5 node_modules
.PHONY: clean format
.PHONY: clean

62
cli.js
View file

@ -5,33 +5,38 @@ import fs from "vinyl-fs";
import map from "map-stream";
import yargs from "yargs";
import CleanCSS from "clean-css";
import toHTML, { BASE_CSS, html } from "./gmi.html.js";
import toHTML, { BASE_CSS, html } from "./html.js";
const cli = yargs(process.argv.slice(2))
.scriptName("gmi-web")
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) => yargs
//yargs.positional("files")
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) =>
yargs
.example("$0 --html en $(find ~/my-capsule -name '*.gmi')")
.example("$0 --body < ~/my-capsule/index.gmi")
)
.options({
language: {
alias: "lang",
required: true,
html: {
type: "string",
requiresArg: true,
},
css: {
choices: ["gmi.css", "base", "none"],
default: "gmi.css",
},
body: {
type: "boolean",
default: true,
},
});
cli.options({
images: {
type: "boolean",
image: {
type: "array",
},
audio: {
type: "boolean",
type: "array",
},
video: {
type: "boolean",
type: "array",
},
});
@ -59,24 +64,24 @@ const GMI_CSS_VARS = [
"sans-serif",
].map((key) => {
cli.option(key);
cli.conflicts(key, "body");
return key;
});
const argv = cli
.group(["language", "css"], "Core:")
.conflicts("html", "body")
.group(["html", "css", "body"], "Core:")
.group(GMI_CSS_VARS, "CSS Variables:")
.group(["images", "audio", "video"], "Inline Media:")
.alias("html", "language")
.alias("html", "lang")
.config()
.example("$0 --lang en $(find ~/my-capsule -name '*.gmi')")
.example("cat ~/my-capsule/index.gmi | $0 --lang en")
.epilog("See the gmi-web(1) man page for more information!")
.showHelpOnFail(true)
.help().argv;
argv.css = argv.css || BASE_CSS;
let styles = "";
if (argv.css) {
if (argv.css === "gmi.css") {
styles = GMI_CSS_VARS.reduce((style, key) => {
if (argv[key]) {
style += `--${key}: ${argv[key]};`;
@ -93,19 +98,28 @@ if (argv.css) {
"utf8"
)
).styles;
} else if (argv.css === "base") {
argv.css = BASE_CSS;
} else {
argv.css = "";
}
if (!argv.lang && !argv.body) {
cli.showHelp();
console.error(`\nMissing required argument: --html or --body`);
cli.exit(1);
}
if (!argv.files) {
let gemtext
let gemtext;
try {
gemtext = readFileSync(process.stdin.fd)
gemtext = readFileSync(process.stdin.fd);
} catch (e) {
console.error("Either send a file to this program from stdin")
console.error("or provide [files..]\n")
console.error("See --help or read gmi-web(1) for usage details.")
process.exit(1)
console.error(`Either send a file to this program from stdin or provide [files..]
See --help or read gmi-web(1) for usage details.`);
cli.exit(1);
}
console.log(html({ contents: gemtext }, argv))
console.log(html({ contents: gemtext }, argv));
} else {
fs.src(argv.files)
.pipe(

View file

@ -5,7 +5,7 @@
.nh
.ad l
.\" Begin generated content:
.TH "gmi-web" "1" "2021-02-10" "1.0.7-rc.1"
.TH "gmi-web" "1" "2021-02-11" "1.0.7-rc.1"
.P
.SH NAME
.P

View file

@ -29,8 +29,12 @@ export const BASE_CSS =
"p,a,pre,h1,h2,h3,ul,blockquote,img,audio,video{display:block;max-width:100%;margin:0;padding:0;overflow-wrap:anywhere;}";
export function head(options) {
return `
<meta name="color-scheme" content="dark light">
<style>${options.css}</style>
${
options.css !== ""
? `<meta name="color-scheme" content="dark light">`
: ""
}
${options.css !== "" ? `<style>${options.css}</style>` : ""}
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="${options.charset}">
<meta language="${options.language}">
@ -46,31 +50,41 @@ export function head(options) {
`;
}
export const IMAGE_EXT = /\.(apng|avif|gif|jpg|jpeg|jfif|pjpeg|pjp|png|svg|webp)$/;
export const AUDIO_EXT = /\.(mp3|wav|aac|aacp|mpeg|off|flac)$/;
export const VIDEO_EXT = /\.(mp4|webm)$/;
function line(
{ text, href, title, pre, alt, h1, h2, h3, li, quote },
{ images, audio, video } = {}
{ image, audio, video, css } = {}
) {
if (text) return `<p>${escape(text)}</p>`;
const matchesExt = (url, exts) =>
exts.some((ext) => new RegExp(`\.${ext}$`).test(url));
if (href) {
const titleProp = title ? ` title="${title}"` : "";
if (images && IMAGE_EXT.test(href))
if (image && matchesExt(href, image)) {
return `<img src="${href}"${titleProp}/>`;
if (audio && AUDIO_EXT.test(href))
}
if (audio && matchesExt(href, audio)) {
if (css === "") {
return `<p><audio controls src="${href}"${titleProp}></audio><p>`;
}
return `<audio controls src="${href}"${titleProp}></audio>`;
if (video && VIDEO_EXT.test(href))
}
if (video && matchesExt(href, video))
return `<video controls src="${href}"${titleProp}/></video>`;
if (css === "") {
return `<p><a href="${href}">${title ? escape(title) : href}</a></p>`;
}
return `<a href="${href}">${title ? escape(title) : href}</a>`;
}
if (h1) return `<h1>${escape(h1)}</h1>`;
if (h2) return `<h2>${escape(h2)}</h2>`;
if (h3) return `<h3>${escape(h3)}</h3>`;
if (li) return `<li>${escape(li)}</li>`;
if (quote) return `<blockquote>${escape(quote)}</blockquote>`;
return `<p><br></p>`;
}

View file

@ -2,7 +2,7 @@
"name": "gmi-web-cli",
"version": "1.0.7-rc.1",
"description": "A bridge between HTML and Gemini",
"main": "cli.js",
"main": "html.js",
"type": "module",
"bin": {
"gmi-web": "cli.js"
@ -23,9 +23,7 @@
"gmi.css.5"
],
"scripts": {
"build": "npm install && make",
"prepare": "make",
"clean": "make clean"
"prepare": "prettier --write ."
},
"dependencies": {
"clean-css": "^5.0.1",

View file

@ -1,5 +1,5 @@
rm -rf "example/test.html"
./cli.js --lang en --images --audio --video "example/*.gmi"
./cli.js --image jpg --audio mp3 --video mp4 --html en "example/*.gmi"
if cmp -s "example/test.html" "example/expected.html"; then
printf "PASS: test.html matches expected.html!\n"
else