Loading...
No commits yet
Not committed History
Blame
tex_snippet2full.py • 4.5 KB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: 2026-02-08
# File: scripts/python/tex_snippet2full.py

"""Build a complete LaTeX document from body content with color mode support.

Usage:
    python tex_snippet2full.py --body-file body.tex --output out.tex --color-mode dark
    python tex_snippet2full.py --body-text "\\section{Test}" --output out.tex
    python tex_snippet2full.py --body-file full.tex --output out.tex --complete-document
"""

import argparse
import os
import re
import sys
from pathlib import Path

# Dark theme colors from config env vars (DRY with config/*.yaml)
MONACO_BG = os.environ.get("SCITEX_WRITER_DARK_BG", "1E1E1E")
MONACO_FG = os.environ.get("SCITEX_WRITER_DARK_FG", "D4D4D4")
LINK_INTERNAL = os.environ.get("SCITEX_WRITER_DARK_LINK_INTERNAL", "90C695")
LINK_CITATION = os.environ.get("SCITEX_WRITER_DARK_LINK_CITATION", "87CEEB")
LINK_URL = os.environ.get("SCITEX_WRITER_DARK_LINK_URL", "DEB887")

DARK_MODE_COMMANDS = f"""\
% Dark mode styling - matches Monaco/VS Code editor (#{MONACO_BG})
\\definecolor{{MonacoBg}}{{HTML}}{{{MONACO_BG}}}
\\definecolor{{MonacoFg}}{{HTML}}{{{MONACO_FG}}}
\\definecolor{{DarkGreen}}{{HTML}}{{{LINK_INTERNAL}}}
\\definecolor{{DarkCyan}}{{HTML}}{{{LINK_CITATION}}}
\\definecolor{{DarkOrange}}{{HTML}}{{{LINK_URL}}}
\\pagecolor{{MonacoBg}}
\\color{{MonacoFg}}
\\makeatletter
\\@ifpackageloaded{{hyperref}}{{%
  \\hypersetup{{
    colorlinks=true,
    linkcolor=DarkGreen,
    citecolor=DarkCyan,
    urlcolor=DarkOrange,
  }}%
}}{{}}
\\makeatother"""

DOCUMENT_TEMPLATE = """\
\\documentclass[11pt]{{article}}

% Essential packages
\\usepackage[english]{{babel}}
\\usepackage[T1]{{fontenc}}
\\usepackage[utf8]{{inputenc}}
\\usepackage[table,svgnames]{{xcolor}}
\\usepackage{{amsmath, amssymb}}
\\usepackage{{graphicx}}
\\usepackage{{booktabs}}
\\usepackage{{hyperref}}
\\usepackage{{geometry}}
\\geometry{{margin=1in}}
\\usepackage{{pagecolor}}

\\begin{{document}}
{color_commands}
{body_content}

\\end{{document}}
"""


def get_color_commands(color_mode: str) -> str:
    """Get LaTeX color commands for the specified mode."""
    if color_mode == "dark":
        return DARK_MODE_COMMANDS
    return ""


def inject_color_into_document(latex_content: str, color_mode: str) -> str:
    """Inject color mode styling into an existing complete LaTeX document."""
    color_commands = get_color_commands(color_mode)
    if not color_commands:
        return latex_content

    pattern = r"(\\begin\{document\})"
    match = re.search(pattern, latex_content)

    if match:
        pos = match.end()
        return latex_content[:pos] + "\n" + color_commands + "\n" + latex_content[pos:]
    return color_commands + "\n" + latex_content


def wrap_body_content(body_content: str, color_mode: str) -> str:
    """Wrap body content into a complete LaTeX document."""
    color_commands = get_color_commands(color_mode)
    return DOCUMENT_TEMPLATE.format(
        color_commands=color_commands,
        body_content=body_content,
    )


def main():
    parser = argparse.ArgumentParser(
        description="Build LaTeX document from body content with color mode."
    )
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("--body-file", help="File containing LaTeX body content")
    group.add_argument("--body-text", help="Inline LaTeX body content")
    parser.add_argument("--output", required=True, help="Output .tex file path")
    parser.add_argument(
        "--color-mode",
        default="light",
        choices=["light", "dark"],
        help="Color mode (default: light)",
    )
    parser.add_argument(
        "--complete-document",
        action="store_true",
        help="Input is a complete document (inject color only)",
    )
    args = parser.parse_args()

    # Read body content
    if args.body_file:
        body_path = Path(args.body_file)
        if not body_path.exists():
            print(f"ERROR: Body file not found: {args.body_file}", file=sys.stderr)
            sys.exit(1)
        content = body_path.read_text(encoding="utf-8")
    else:
        content = args.body_text

    # Build document
    if args.complete_document:
        result = inject_color_into_document(content, args.color_mode)
    else:
        result = wrap_body_content(content, args.color_mode)

    # Write output
    output_path = Path(args.output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(result, encoding="utf-8")

    print(f"Document written: {args.output}")


if __name__ == "__main__":
    main()

# EOF