% ===================================================================== % terminalcode.sty v0.9.0 % Terminal-style code boxes with UTF-8 box-drawing and ANSI colors % (Dark/Light themes supported) % % Requirements: % - Compiler: XeLaTeX or LuaLaTeX (NOT pdfLaTeX) % - Packages: ctex, xcolor, tcolorbox (most), listings, fontspec, kvoptions % - Fonts: DejaVu Sans Mono (recommended), Latin Modern Mono (fallback) % % License: MIT License % % Features: % - termcode: inline terminal-style listing environment % - terminput / termcodefileinput: external file input with same style % - Full ANSI 16-color support (dark/light themes) % - Package options: theme=dark|light, monofont= % - Breakable, monospace, line numbers, escapeinside=«» % % Version History: % v0.9.0 (2025-11-01): Initial public release on CTAN % (Earlier pre-release development) % % Documentation: See README.md for comprehensive guide % Example: See example.tex for working examples % ===================================================================== \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{terminalcode}[2025/11/01 v0.9.0 Terminal-style code boxes] % --------------------------------------------------------------------- % Engine Detection (requires XeTeX or LuaTeX for fontspec) % --------------------------------------------------------------------- \@ifundefined{directlua}{% \@ifundefined{XeTeXrevision}{% \PackageError{terminalcode}{% This package requires XeLaTeX or LuaLaTeX.\MessageBreak pdfLaTeX is not supported due to fontspec and UTF-8 box-drawing requirements.\MessageBreak Please compile with: xelatex or lualatex }{Use XeLaTeX or LuaLaTeX to compile.}% }{}% }{} % --------------------------------------------------------------------- % Dependencies % Note: ctex is required for proper UTF-8 box-drawing character handling % and improved font configuration with XeLaTeX/LuaLaTeX % --------------------------------------------------------------------- \RequirePackage{ctex} \RequirePackage{xcolor} \RequirePackage[most]{tcolorbox} \RequirePackage{listings} \RequirePackage{fontspec} \RequirePackage{kvoptions} % ===================================================================== % Monospace Font Configuration & Option Processing % File encoding: UTF-8 % UTF-8 support required for box-drawing characters % Package option: monofont= (default: DejaVu Sans Mono) % Package option: theme=dark|light (default: dark) % With fallback to Latin Modern Mono if primary font not found % ===================================================================== % Setup key=value option handling via kvoptions \SetupKeyvalOptions{family=terminalcode,prefix=terminalcode@} \DeclareStringOption[DejaVu Sans Mono]{monofont} \DeclareStringOption[dark]{theme} \ProcessKeyvalOptions* % Fallback font (not user-configurable, internal only) \def\terminalcode@monofont@fallback{Latin Modern Mono} % Initial theme setting \newif\iftermcodelight \termcodelightfalse % Apply theme from option \def\@darkstr{dark} \def\@lightstr{light} \edef\@tempa{\terminalcode@theme} \ifx\@tempa\@darkstr \termcodelightfalse \else\ifx\@tempa\@lightstr \termcodelighttrue \else \PackageWarning{terminalcode}{Unknown theme '\terminalcode@theme'. Using 'dark' (default).}% \termcodelightfalse \fi\fi % Placeholder for font setup (will be called after option processing) \def\terminalcode@setup@font{% \IfFontExistsTF{\terminalcode@monofont}{% \setmonofont{\terminalcode@monofont}% }{% \IfFontExistsTF{\terminalcode@monofont@fallback}{% \PackageWarning{terminalcode}{% Font '\terminalcode@monofont' not found.\MessageBreak Falling back to '\terminalcode@monofont@fallback'% }% \setmonofont{\terminalcode@monofont@fallback}% }{% \PackageWarning{terminalcode}{% Neither '\terminalcode@monofont' nor\MessageBreak '\terminalcode@monofont@fallback' found.\MessageBreak Using default monospace font% }% }% }% } % --------------------------------------------------------------------- % Listings “text” language definition % This disables syntax highlighting % --------------------------------------------------------------------- \lstdefinelanguage{text}{} % --------------------------------------------------------------------- % Color Definitions % Dark and Light variants included % --------------------------------------------------------------------- % ===== Terminal theme colors ===== % Dark theme \definecolor{term-bg-d}{RGB}{30,30,30} \definecolor{term-fg-d}{RGB}{230,230,230} % Light theme \definecolor{term-bg-l}{RGB}{245,246,247} \definecolor{term-fg-l}{RGB}{50,56,58} % ===== ANSI colors (dark mode) ===== \definecolor{ansi-30-d}{RGB}{35,38,40} % Black (dark) \definecolor{ansi-31-d}{RGB}{205,75,69} % Red \definecolor{ansi-32-d}{RGB}{75,183,72} % Green \definecolor{ansi-33-d}{RGB}{218,165,32} % Yellow \definecolor{ansi-34-d}{RGB}{72,120,200} % Blue \definecolor{ansi-35-d}{RGB}{164,84,208} % Magenta \definecolor{ansi-36-d}{RGB}{55,180,180} % Cyan \definecolor{ansi-37-d}{RGB}{230,230,230} % White (light gray) \definecolor{ansi-90-d}{RGB}{110,114,115} % Bright black \definecolor{ansi-91-d}{RGB}{255,120,120} % Bright red \definecolor{ansi-92-d}{RGB}{120,255,120} % Bright green \definecolor{ansi-93-d}{RGB}{255,215,80} % Bright yellow \definecolor{ansi-94-d}{RGB}{120,160,255} % Bright blue \definecolor{ansi-95-d}{RGB}{215,130,255} % Bright magenta \definecolor{ansi-96-d}{RGB}{120,255,255} % Bright cyan \definecolor{ansi-97-d}{RGB}{255,255,255} % Bright white % ===== ANSI colors (light mode) ===== \definecolor{ansi-30-l}{RGB}{60,65,67} % Black (dark) \definecolor{ansi-31-l}{RGB}{200,55,47} % Red \definecolor{ansi-32-l}{RGB}{39,174,96} % Green \definecolor{ansi-33-l}{RGB}{214,147,40} % Yellow \definecolor{ansi-34-l}{RGB}{52,152,219} % Blue \definecolor{ansi-35-l}{RGB}{142,68,173} % Magenta \definecolor{ansi-36-l}{RGB}{26,188,156} % Cyan \definecolor{ansi-37-l}{RGB}{230,230,230} % White (light gray) \definecolor{ansi-90-l}{RGB}{120,125,126} % Bright black (dim gray) \definecolor{ansi-91-l}{RGB}{255,107,107} % Bright red \definecolor{ansi-92-l}{RGB}{46,204,113} % Bright green \definecolor{ansi-93-l}{RGB}{241,196,15} % Bright yellow \definecolor{ansi-94-l}{RGB}{93,173,226} % Bright blue \definecolor{ansi-95-l}{RGB}{187,143,206} % Bright magenta \definecolor{ansi-96-l}{RGB}{110,210,195} % Bright cyan \definecolor{ansi-97-l}{RGB}{50,56,58} % Bright white (text black) % --------------------------------------------------------------------- % Theme Selection % Package option: theme=dark|light (default: dark) % Or use \terminalcodetheme{dark|light} to switch after loading % Example: \usepackage[theme=light]{terminalcode} % Or: \documentclass{article} \usepackage{terminalcode} \terminalcodetheme{light} % Default: dark % ===================================================================== \def\@darkstr{dark} \def\@lightstr{light} \def\terminalcode@theme{} \newcommand{\terminalcodetheme}[1]{% \def\@tempa{#1}% \ifx\@tempa\@darkstr \termcodelightfalse \else\ifx\@tempa\@lightstr \termcodelighttrue \else \PackageError{terminalcode}{Unknown theme '#1'}{Use 'dark' or 'light'.}% \fi\fi } \let\tctheme\terminalcodetheme % Package options (processed after \terminalcodetheme is defined) % Already processed above via kvoptions, no additional processing needed % Setup monospace font after option processing \terminalcode@setup@font % --------------------------------------------------------------------- % ANSI Styling Support % NOTE: \verb cannot be styled internally — must set global state % --------------------------------------------------------------------- \makeatletter \newcommand{\ansicolor}[1]{% \if\relax\detokenize{#1}\relax\else \color{ansi-#1}% \fi } \newcommand{\ansireset}{\normalfont\color{term-fg}} \let\ac\ansicolor \makeatother % --------------------------------------------------------------------- % termcode Environment % Based on tcolorbox + listings % --------------------------------------------------------------------- \newtcblisting{termcode}[2][text]{% code={% \small % Resolve theme colors \iftermcodelight \colorlet{term-bg}{term-bg-l} \colorlet{term-fg}{term-fg-l} \colorlet{ansi-30}{ansi-30-l} \colorlet{ansi-31}{ansi-31-l} \colorlet{ansi-32}{ansi-32-l} \colorlet{ansi-33}{ansi-33-l} \colorlet{ansi-34}{ansi-34-l} \colorlet{ansi-35}{ansi-35-l} \colorlet{ansi-36}{ansi-36-l} \colorlet{ansi-37}{ansi-37-l} \colorlet{ansi-90}{ansi-90-l} \colorlet{ansi-91}{ansi-91-l} \colorlet{ansi-92}{ansi-92-l} \colorlet{ansi-93}{ansi-93-l} \colorlet{ansi-94}{ansi-94-l} \colorlet{ansi-95}{ansi-95-l} \colorlet{ansi-96}{ansi-96-l} \colorlet{ansi-97}{ansi-97-l} \else \colorlet{term-bg}{term-bg-d} \colorlet{term-fg}{term-fg-d} \colorlet{ansi-30}{ansi-30-d} \colorlet{ansi-31}{ansi-31-d} \colorlet{ansi-32}{ansi-32-d} \colorlet{ansi-33}{ansi-33-d} \colorlet{ansi-34}{ansi-34-d} \colorlet{ansi-35}{ansi-35-d} \colorlet{ansi-36}{ansi-36-d} \colorlet{ansi-37}{ansi-37-d} \colorlet{ansi-90}{ansi-90-d} \colorlet{ansi-91}{ansi-91-d} \colorlet{ansi-92}{ansi-92-d} \colorlet{ansi-93}{ansi-93-d} \colorlet{ansi-94}{ansi-94-d} \colorlet{ansi-95}{ansi-95-d} \colorlet{ansi-96}{ansi-96-d} \colorlet{ansi-97}{ansi-97-d} \fi \def\termcode@lang{#1}% \def\termcode@textstr{text}% }, enhanced jigsaw, breakable, width=\textwidth, left=12pt, right=12pt, top=8pt, bottom=8pt, before skip=12pt, after skip=12pt, boxrule=0.8pt, arc=3pt, colframe=\iftermcodelight gray!80\else gray!60\fi, colback=term-bg, coltext=term-fg, title=#2, title after break=#2, coltitle=\iftermcodelight black\else white\fi, colbacktitle=\iftermcodelight gray!20\else gray!80\fi, fonttitle=\ttfamily\small\bfseries, toptitle=4pt, bottomtitle=4pt, listing only, listing engine=listings, listing options={ language=#1, basicstyle=\ttfamily\color{term-fg}, keywordstyle=\ifx\termcode@lang\termcode@textstr\else\color{ansi-34}\bfseries\fi, commentstyle=\ifx\termcode@lang\termcode@textstr\else\color{ansi-32}\fi, stringstyle=\ifx\termcode@lang\termcode@textstr\else\color{ansi-33}\fi, identifierstyle=\color{term-fg}, breaklines=true, breakatwhitespace=false, columns=flexible, basewidth=0.5em, prebreak={}, postbreak={}, breakindent=0pt, numbers=left, numbersep=4pt, numberstyle=\tiny\color{ansi-90}, tabsize=2, frame=none, upquote=true, showspaces=false, showtabs=false, showstringspaces=false, escapeinside={«}{»}, } } % --------------------------------------------------------------------- % \terminput[]{}{<filename>} % Fully consistent with termcode, uses tcbinputlisting to avoid nesting. % --------------------------------------------------------------------- \makeatletter \newcommand{\terminput}[3][text]{% \begingroup % Resolve theme colors (same as in termcode) \iftermcodelight \colorlet{term-bg}{term-bg-l} \colorlet{term-fg}{term-fg-l} \colorlet{ansi-30}{ansi-30-l} \colorlet{ansi-31}{ansi-31-l} \colorlet{ansi-32}{ansi-32-l} \colorlet{ansi-33}{ansi-33-l} \colorlet{ansi-34}{ansi-34-l} \colorlet{ansi-35}{ansi-35-l} \colorlet{ansi-36}{ansi-36-l} \colorlet{ansi-37}{ansi-37-l} \colorlet{ansi-90}{ansi-90-l} \colorlet{ansi-91}{ansi-91-l} \colorlet{ansi-92}{ansi-92-l} \colorlet{ansi-93}{ansi-93-l} \colorlet{ansi-94}{ansi-94-l} \colorlet{ansi-95}{ansi-95-l} \colorlet{ansi-96}{ansi-96-l} \colorlet{ansi-97}{ansi-97-l} \else \colorlet{term-bg}{term-bg-d} \colorlet{term-fg}{term-fg-d} \colorlet{ansi-30}{ansi-30-d} \colorlet{ansi-31}{ansi-31-d} \colorlet{ansi-32}{ansi-32-d} \colorlet{ansi-33}{ansi-33-d} \colorlet{ansi-34}{ansi-34-d} \colorlet{ansi-35}{ansi-35-d} \colorlet{ansi-36}{ansi-36-d} \colorlet{ansi-37}{ansi-37-d} \colorlet{ansi-90}{ansi-90-d} \colorlet{ansi-91}{ansi-91-d} \colorlet{ansi-92}{ansi-92-d} \colorlet{ansi-93}{ansi-93-d} \colorlet{ansi-94}{ansi-94-d} \colorlet{ansi-95}{ansi-95-d} \colorlet{ansi-96}{ansi-96-d} \colorlet{ansi-97}{ansi-97-d} \fi \def\termcode@lang{#1}% \def\termcode@textstr{text}% \tcbinputlisting{ enhanced jigsaw, breakable, width=\textwidth, left=12pt, right=12pt, top=8pt, bottom=8pt, before skip=12pt, after skip=12pt, boxrule=0.8pt, arc=3pt, colframe=\iftermcodelight gray!80\else gray!60\fi, colback=term-bg, coltext=term-fg, title={#2}, title after break={#2}, coltitle=\iftermcodelight black\else white\fi, colbacktitle=\iftermcodelight gray!20\else gray!80\fi, fonttitle=\ttfamily\small\bfseries, toptitle=4pt, bottomtitle=4pt, listing only, listing engine=listings, listing options={ language=#1, basicstyle=\ttfamily\color{term-fg}\small, keywordstyle=\ifx\termcode@lang\termcode@textstr\else\bfseries\color{ansi-34}\fi, commentstyle=\ifx\termcode@lang\termcode@textstr\else\color{ansi-32}\fi, stringstyle=\ifx\termcode@lang\termcode@textstr\else\color{ansi-33}\fi, identifierstyle=\color{term-fg}, breaklines=true, breakatwhitespace=false, columns=flexible, basewidth=0.5em, numbers=left, numbersep=4pt, numberstyle=\tiny\color{ansi-90}, tabsize=2, frame=none, upquote=true, showspaces=false, showtabs=false, showstringspaces=false, escapeinside={«}{»}, }, listing file={#3} } \endgroup % Restore default text color to ensure subsequent content is not affected \normalcolor } \makeatother % Alias for backward compatibility or preference \let\termcodefileinput\terminput \endinput