#!/usr/bin/env python import sys from collections.abc import Sequence from pathlib import Path import contextlib import click from bs4 import BeautifulSoup, Doctype, Tag user_has_lxml = False try: import lxml # type: ignore[import] # noqa: F401 user_has_lxml = True except Exception: pass @click.command() @click.option( "--meta-charset", "_charset", default="utf-8", show_default=True, help="add default meta charset", ) @click.option( "--meta-viewport", "_viewport", help="add default viewport", show_default=True, default="width=device-width, initial-scale=1.0, user-scalable=yes", ) @click.option( "-a", "--add", type=click.Choice(["css-pre-wrap", "css-dark-mode"]), multiple=True, help="flags to add additional features", default=(), ) @click.option( "--raw-css", type=str, help="add raw CSS", default=None, ) @click.argument( "INPUT_FILE", type=click.Path(exists=True, allow_dash=True, path_type=Path), default=None, required=False, ) def main( input_file: Path | None, _charset: str, _viewport: str, raw_css: str | None, add: Sequence[str], ) -> None: """ A script to create a sensible
element for HTML pages. data is typically piped into this, e.g. from something that emits HTML: \b pandoc ./README.md | html-head -a css-dark-mode \b cat README.txt | pygmentize -f html | html-head ... This doesn't affect other data already present in head, it only updates values if they are not present, can safely be used multiple times: \b ... | html-head -a css-dark-mode | ... | html-head ... \b css-pre-wrap: adds CSS to convert tags to wrap text
css-dark-mode:
adds CSS to convert the page to dark mode
"""
text = (
sys.stdin.read()
if input_file is None or str(input_file) == "-"
else input_file.read_text()
)
soup = BeautifulSoup(text, "lxml" if user_has_lxml else "html.parser")
# find head tag, or create it and insert with good defaults
assert soup.body is not None
maybe_head = soup.head
head: Tag
if maybe_head is None:
head = soup.new_tag("head")
soup.body.insert_before(head)
else:
head = maybe_head
assert head is not None
meta_tags = head.find_all("meta")
if not any("charset" in getattr(tag, "attrs", {}) for tag in meta_tags):
head.append(soup.new_tag("meta", attrs={"charset": _charset}))
if not any(
"viewport" == getattr(tag, "attrs", {}).get("name") for tag in meta_tags
):
head.append(
soup.new_tag("meta", attrs={"name": "viewport", "content": _viewport})
)
if "css-pre-wrap" in add:
prewrap = soup.new_tag("style")
prewrap.string = "pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}"
head.append(prewrap)
if "css-dark-mode" in add:
darkmode = soup.new_tag("style")
darkmode.string = "body{background-color:#1b1b1b;color:#e7e8eb}a{color:#3387cc}"
head.append(darkmode)
if raw_css:
new_styles = soup.new_tag("style")
new_styles.string = raw_css
head.append(new_styles)
if len([a for a in soup.children if isinstance(a, Doctype)]) == 0:
click.echo("")
click.echo(str(soup).strip())
if __name__ == "__main__":
with contextlib.suppress(KeyboardInterrupt):
main()