#!/usr/bin/env python import sys from typing import Sequence, Optional 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: Optional[Path], _charset: str, _viewport: str, raw_css: Optional[str], 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 converttags 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()