Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ node_modules
_scripts/script-wizard
/gradio/gradio/chatbot/__pycache__/
app-router-bootstrap.sh
/_scripts/__pycache__/
/gradio/target/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ userns-remap-check:

.PHONY: readme # Open the README.md in your web browser
readme:
xdg-open "https://github.com/EnigmaCurry/d.rymcg.tech/tree/master#readme"
@${BIN}/d.rymcg.tech readme readme

.PHONY: install-cli # Install CLI
install-cli:
Expand Down
3 changes: 1 addition & 2 deletions _scripts/Makefile.readme
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
.PHONY: readme # Open the README.md in your web browser
readme:
# @URL="https://github.com/EnigmaCurry/d.rymcg.tech/tree/master/$$(pwd | grep -Po \"$$(realpath ${ROOT_DIR})\K.*\")"; set -x; xdg-open "$${URL}"
@PROJECT_DIR=$$(realpath $$(pwd) | grep -Po "$$(realpath ${ROOT_DIR})\K.*"); URL="https://github.com/EnigmaCurry/d.rymcg.tech/tree/master$${PROJECT_DIR}#readme"; set -x; xdg-open "$${URL}"
@PROJECT_DIR=$$(realpath $$(pwd) | grep -Po "$$(realpath ${ROOT_DIR})\K.*"); URL="https://github.com/EnigmaCurry/d.rymcg.tech/tree/master$${PROJECT_DIR}#readme"; OPENER=$$(${BIN}/d.rymcg.tech info README_OPENER); set -x; $${OPENER} "$${URL}"
69 changes: 65 additions & 4 deletions _scripts/d.rymcg.tech
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ set -eo pipefail
BIN=$(dirname $(realpath ${BASH_SOURCE}))

## INFO_WORDS are keys to information that the completion script can ask for at runtime:
INFO_WORDS="INFO_WORDS ROOT_DIR"
INFO_WORDS="INFO_WORDS ROOT_DIR README_OPENER"

## ROOT_DIR is the root path of the d.rymcg.tech project
## Validate that this script is running from the canonical location
Expand Down Expand Up @@ -146,7 +146,65 @@ __list_projects() {
)
}

__xdg_https_exec() {
# Prints the executable xdg-open would use for https:// (best-effort)
local desktop exec line

desktop="$(xdg-mime query default x-scheme-handler/https 2>/dev/null || true)"
[[ -z "$desktop" ]] && return 1

# Find the desktop file
local -a candidates=(
"$XDG_DATA_HOME/applications/$desktop"
"$HOME/.local/share/applications/$desktop"
"/usr/local/share/applications/$desktop"
"/usr/share/applications/$desktop"
)

for f in "${candidates[@]}"; do
if [[ -r "$f" ]]; then
line="$(grep -m1 '^Exec=' "$f" || true)"
break
fi
done

[[ -z "$line" ]] && return 1

exec="${line#Exec=}"
# Strip field codes and args; keep the program token
exec="${exec%% *}"
exec="${exec//%u/}"
exec="${exec//%U/}"
exec="${exec//%f/}"
exec="${exec//%F/}"
exec="${exec//%i/}"
exec="${exec//%c/}"
exec="${exec//%k/}"
exec="${exec//\"/}"

[[ -z "$exec" ]] && return 1
printf '%s\n' "$exec"
}

__readme_opener() {
# Decide whether to use xdg-open or mdview.py
local https_exec base
https_exec="$(__xdg_https_exec)" || https_exec=""

base="$(basename -- "${https_exec:-}" 2>/dev/null || true)"

# If we can't detect, or it looks like w3m, use mdview.py
if [[ -z "$https_exec" || "$base" == "w3m" ]]; then
printf '%s\n' "${BIN}/mdview.py"
else
printf '%s\n' "xdg-open"
fi
}

__readme() {
local opener
opener="$(__readme_opener)"

if [[ $# -gt 0 ]]; then
NAME="${1}"; shift
NAME_UPPERCASE="$(echo "${NAME}" | tr '[:lower:]' '[:upper:]')"
Expand All @@ -155,12 +213,13 @@ __readme() {
for doc in "${DOCS_ARRAY[@]}"; do
DOCS_INDEX["${doc}"]="${doc}"
done

if [[ "${NAME_UPPERCASE}" == "LICENSE" ]]; then
exe xdg-open "https://github.com/EnigmaCurry/d.rymcg.tech/blob/master/LICENSE.txt"
exe "$opener" "https://github.com/EnigmaCurry/d.rymcg.tech/blob/master/LICENSE.txt"
elif [[ -v DOCS_INDEX["${NAME_UPPERCASE}"] ]]; then
exe xdg-open "https://github.com/EnigmaCurry/d.rymcg.tech/blob/master/${NAME_UPPERCASE}.md#readme"
exe "$opener" "https://github.com/EnigmaCurry/d.rymcg.tech/blob/master/${NAME_UPPERCASE}.md#readme"
else
echo ${DOCS_INDEX["${NAME_UPPERCASE}"]}
echo "${DOCS_INDEX["${NAME_UPPERCASE}"]}"
__make "${NAME}" readme "$@"
fi
else
Expand All @@ -184,6 +243,8 @@ __info() {
echo "${ROOT_DIR}"
elif [[ "$1" == "INFO_WORDS" ]]; then
echo "${INFO_WORDS}"
elif [[ "$1" == "README_OPENER" ]]; then
__readme_opener
fi
}

Expand Down
154 changes: 154 additions & 0 deletions _scripts/mdview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = ["rich", "certifi"]
# ///
"""
mdview.py - lightweight terminal Markdown viewer (URL/file/stdin) using Rich.

Features:
- Fetch URLs with a portable TLS trust store (certifi).
- Render Markdown in a Rich pager.
- Auto-rewrite GitHub URLs to fetch raw Markdown:
1) https://github.com/OWNER/REPO/blob/BRANCH/path.md
-> https://github.com/OWNER/REPO/raw/BRANCH/path.md
2) https://github.com/OWNER/REPO/tree/BRANCH[/subpath]#readme
-> https://raw.githubusercontent.com/OWNER/REPO/refs/heads/BRANCH/[subpath/]README.md
"""

from __future__ import annotations

import argparse
import ssl
import sys
import urllib.request
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse, urlunparse

import certifi
from rich.console import Console
from rich.markdown import Markdown


def parse_headers(items: list[str]) -> dict[str, str]:
headers: dict[str, str] = {}
for item in items:
if ":" not in item:
raise ValueError(f"Bad header (expected 'Name: value'): {item!r}")
name, value = item.split(":", 1)
headers[name.strip()] = value.lstrip()
return headers


def rewrite_github_url(url: str) -> str:
"""
Convert GitHub HTML URLs into raw markdown fetch URLs.
"""
p = urlparse(url)
if p.scheme not in ("http", "https") or p.netloc.lower() != "github.com":
return url

parts = [seg for seg in p.path.split("/") if seg]
# Expect: OWNER / REPO / <mode> / <ref> / <path...>
if len(parts) >= 4:
owner, repo, mode, ref = parts[0], parts[1], parts[2], parts[3]

# 1) blob -> raw (same host)
if mode == "blob":
parts[2] = "raw"
new_path = "/" + "/".join(parts)
p2 = p._replace(path=new_path)
return urlunparse(p2)

# 2) tree/<branch>[/subpath]#readme -> raw.githubusercontent.com/.../refs/heads/<branch>/[subpath/]README.md
if mode == "tree" and (p.fragment or "").lower() == "readme":
branch = ref
subpath = "/".join(parts[4:])
prefix = f"{subpath}/" if subpath else ""
raw = f"https://raw.githubusercontent.com/{owner}/{repo}/refs/heads/{branch}/{prefix}README.md"
return raw

return url


def read_url(url: str, headers: dict[str, str], insecure: bool) -> str:
url = rewrite_github_url(url)
req = urllib.request.Request(url, headers=headers)

if insecure:
ctx = ssl._create_unverified_context()
else:
ctx = ssl.create_default_context(cafile=certifi.where())

try:
with urllib.request.urlopen(req, context=ctx) as resp:
charset = resp.headers.get_content_charset() or "utf-8"
return resp.read().decode(charset, errors="replace")
except HTTPError as e:
body = ""
try:
body = e.read().decode("utf-8", errors="replace")
except Exception:
pass
raise RuntimeError(
f"HTTP error {e.code} {e.reason} for {url}\n{body}".rstrip()
) from e
except URLError as e:
raise RuntimeError(f"URL error for {url}: {e.reason}") from e


def read_source(source: str, headers: dict[str, str], insecure: bool) -> str:
if source == "-" or source == "":
return sys.stdin.read()

if source.startswith(("http://", "https://")):
return read_url(source, headers, insecure)

with open(source, "r", encoding="utf-8", errors="replace") as f:
return f.read()


def main(argv: list[str]) -> int:
ap = argparse.ArgumentParser(prog="mdview.py")
ap.add_argument(
"source",
nargs="?",
default="-",
help="URL, file path, or '-' to read from stdin (default: '-')",
)
ap.add_argument(
"-H",
"--header",
action="append",
default=[],
help="HTTP header, repeatable. Example: -H 'Authorization: token XYZ'",
)
ap.add_argument(
"--no-wrap",
action="store_true",
help="Disable soft-wrapping inside the pager.",
)
ap.add_argument(
"-k",
"--insecure",
action="store_true",
help="Disable TLS certificate verification (NOT recommended).",
)
args = ap.parse_args(argv)

try:
headers = parse_headers(args.header)
text = read_source(args.source, headers=headers, insecure=args.insecure)
except Exception as e:
print(f"mdview: {e}", file=sys.stderr)
return 2

console = Console()
# Use Rich's built-in pager; user can quit with q.
with console.pager(styles=True):
console.print(Markdown(text), soft_wrap=not args.no_wrap)

return 0


if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))