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:
parent
9184e2c792
commit
2ade53f268
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
example/
|
12
Makefile
12
Makefile
|
@ -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
|
gmi-web.1: gmi-web.1.scd
|
||||||
scdoc < $< > $@
|
scdoc < $< > $@
|
||||||
|
@ -6,15 +7,10 @@ gmi-web.1: gmi-web.1.scd
|
||||||
gmi.css.5: gmi.css.5.scd
|
gmi.css.5: gmi.css.5.scd
|
||||||
scdoc < $< > $@
|
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
|
example/test.html: example/test.gmi
|
||||||
./test.sh
|
./test.sh
|
||||||
|
|
||||||
clean:
|
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
62
cli.js
|
@ -5,33 +5,38 @@ import fs from "vinyl-fs";
|
||||||
import map from "map-stream";
|
import map from "map-stream";
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
import CleanCSS from "clean-css";
|
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))
|
const cli = yargs(process.argv.slice(2))
|
||||||
.scriptName("gmi-web")
|
.scriptName("gmi-web")
|
||||||
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) => yargs
|
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) =>
|
||||||
//yargs.positional("files")
|
yargs
|
||||||
|
.example("$0 --html en $(find ~/my-capsule -name '*.gmi')")
|
||||||
|
.example("$0 --body < ~/my-capsule/index.gmi")
|
||||||
)
|
)
|
||||||
.options({
|
.options({
|
||||||
language: {
|
html: {
|
||||||
alias: "lang",
|
type: "string",
|
||||||
required: true,
|
requiresArg: true,
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
|
choices: ["gmi.css", "base", "none"],
|
||||||
|
default: "gmi.css",
|
||||||
|
},
|
||||||
|
body: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
cli.options({
|
cli.options({
|
||||||
images: {
|
image: {
|
||||||
type: "boolean",
|
type: "array",
|
||||||
},
|
},
|
||||||
audio: {
|
audio: {
|
||||||
type: "boolean",
|
type: "array",
|
||||||
},
|
},
|
||||||
video: {
|
video: {
|
||||||
type: "boolean",
|
type: "array",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,24 +64,24 @@ const GMI_CSS_VARS = [
|
||||||
"sans-serif",
|
"sans-serif",
|
||||||
].map((key) => {
|
].map((key) => {
|
||||||
cli.option(key);
|
cli.option(key);
|
||||||
|
cli.conflicts(key, "body");
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
|
|
||||||
const argv = cli
|
const argv = cli
|
||||||
.group(["language", "css"], "Core:")
|
.conflicts("html", "body")
|
||||||
|
.group(["html", "css", "body"], "Core:")
|
||||||
.group(GMI_CSS_VARS, "CSS Variables:")
|
.group(GMI_CSS_VARS, "CSS Variables:")
|
||||||
.group(["images", "audio", "video"], "Inline Media:")
|
.group(["images", "audio", "video"], "Inline Media:")
|
||||||
|
.alias("html", "language")
|
||||||
|
.alias("html", "lang")
|
||||||
.config()
|
.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!")
|
.epilog("See the gmi-web(1) man page for more information!")
|
||||||
.showHelpOnFail(true)
|
.showHelpOnFail(true)
|
||||||
.help().argv;
|
.help().argv;
|
||||||
|
|
||||||
argv.css = argv.css || BASE_CSS;
|
|
||||||
|
|
||||||
let styles = "";
|
let styles = "";
|
||||||
if (argv.css) {
|
if (argv.css === "gmi.css") {
|
||||||
styles = GMI_CSS_VARS.reduce((style, key) => {
|
styles = GMI_CSS_VARS.reduce((style, key) => {
|
||||||
if (argv[key]) {
|
if (argv[key]) {
|
||||||
style += `--${key}: ${argv[key]};`;
|
style += `--${key}: ${argv[key]};`;
|
||||||
|
@ -93,19 +98,28 @@ if (argv.css) {
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
).styles;
|
).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) {
|
if (!argv.files) {
|
||||||
let gemtext
|
let gemtext;
|
||||||
try {
|
try {
|
||||||
gemtext = readFileSync(process.stdin.fd)
|
gemtext = readFileSync(process.stdin.fd);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Either send a file to this program from stdin")
|
console.error(`Either send a file to this program from stdin or provide [files..]
|
||||||
console.error("or provide [files..]\n")
|
See --help or read gmi-web(1) for usage details.`);
|
||||||
console.error("See --help or read gmi-web(1) for usage details.")
|
cli.exit(1);
|
||||||
process.exit(1)
|
|
||||||
}
|
}
|
||||||
console.log(html({ contents: gemtext }, argv))
|
console.log(html({ contents: gemtext }, argv));
|
||||||
} else {
|
} else {
|
||||||
fs.src(argv.files)
|
fs.src(argv.files)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
.nh
|
.nh
|
||||||
.ad l
|
.ad l
|
||||||
.\" Begin generated content:
|
.\" 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
|
.P
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.P
|
.P
|
||||||
|
|
|
@ -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;}";
|
"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) {
|
export function head(options) {
|
||||||
return `
|
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 name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<meta charset="${options.charset}">
|
<meta charset="${options.charset}">
|
||||||
<meta language="${options.language}">
|
<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(
|
function line(
|
||||||
{ text, href, title, pre, alt, h1, h2, h3, li, quote },
|
{ text, href, title, pre, alt, h1, h2, h3, li, quote },
|
||||||
{ images, audio, video } = {}
|
{ image, audio, video, css } = {}
|
||||||
) {
|
) {
|
||||||
if (text) return `<p>${escape(text)}</p>`;
|
if (text) return `<p>${escape(text)}</p>`;
|
||||||
|
|
||||||
|
const matchesExt = (url, exts) =>
|
||||||
|
exts.some((ext) => new RegExp(`\.${ext}$`).test(url));
|
||||||
if (href) {
|
if (href) {
|
||||||
const titleProp = title ? ` title="${title}"` : "";
|
const titleProp = title ? ` title="${title}"` : "";
|
||||||
if (images && IMAGE_EXT.test(href))
|
if (image && matchesExt(href, image)) {
|
||||||
return `<img src="${href}"${titleProp}/>`;
|
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>`;
|
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>`;
|
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>`;
|
return `<a href="${href}">${title ? escape(title) : href}</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h1) return `<h1>${escape(h1)}</h1>`;
|
if (h1) return `<h1>${escape(h1)}</h1>`;
|
||||||
if (h2) return `<h2>${escape(h2)}</h2>`;
|
if (h2) return `<h2>${escape(h2)}</h2>`;
|
||||||
if (h3) return `<h3>${escape(h3)}</h3>`;
|
if (h3) return `<h3>${escape(h3)}</h3>`;
|
||||||
if (li) return `<li>${escape(li)}</li>`;
|
if (li) return `<li>${escape(li)}</li>`;
|
||||||
|
|
||||||
if (quote) return `<blockquote>${escape(quote)}</blockquote>`;
|
if (quote) return `<blockquote>${escape(quote)}</blockquote>`;
|
||||||
|
|
||||||
return `<p><br></p>`;
|
return `<p><br></p>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "gmi-web-cli",
|
"name": "gmi-web-cli",
|
||||||
"version": "1.0.7-rc.1",
|
"version": "1.0.7-rc.1",
|
||||||
"description": "A bridge between HTML and Gemini",
|
"description": "A bridge between HTML and Gemini",
|
||||||
"main": "cli.js",
|
"main": "html.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"gmi-web": "cli.js"
|
"gmi-web": "cli.js"
|
||||||
|
@ -23,9 +23,7 @@
|
||||||
"gmi.css.5"
|
"gmi.css.5"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm install && make",
|
"prepare": "prettier --write ."
|
||||||
"prepare": "make",
|
|
||||||
"clean": "make clean"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clean-css": "^5.0.1",
|
"clean-css": "^5.0.1",
|
||||||
|
|
2
test.sh
2
test.sh
|
@ -1,5 +1,5 @@
|
||||||
rm -rf "example/test.html"
|
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
|
if cmp -s "example/test.html" "example/expected.html"; then
|
||||||
printf "PASS: test.html matches expected.html!\n"
|
printf "PASS: test.html matches expected.html!\n"
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in a new issue