\ProvidesPackage{bussproofs-colorful}[2026/01/27 Color extension for bussproof] % bussproofs-colorful.sty Version 1.0 % Color extension for the bussproofs package. % Copyright (C) 2026 by Julian (lambdaphoenix) % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % of this license or (at your option) any later version. % The latest version of this license is in % https://www.latex-project.org/lppl.txt % and version 1.3c or later is part of all distributions of LaTeX % version 2008 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Julian. % % This work consists of the file bussproofs-colorful.sty. % % This package depends on: % - bussproofs % - xcolor % - kvoptions % % This package adds color support to proof trees created with % the bussproofs package. It provides user-level control over: % - node (formula) colors % - inference line colors % - rule label colors % % Features: % - Package options: % nodecolor= % linecolor= % labelcolor= % theme= % patchnodes=true/false % patchlines=true/false % patchlabels=true/false % debug=true/false % - Built-in themes: % classic, dark, pastel, highcontrast % - Runtime color control: % \SetNodeColor{} % \SetLineColor{} % \SetLabelColor{} % \LoadProofTheme{} % \ResetProofColors % - Local color blocks: % \begin{ProofColors}[node=red, line=blue] % ... % \end{ProofColors} % - Per-environment color scoping for prooftree environments. % % Usage example: % \usepackage[nodecolor=red, linecolor=blue]{bussproofs-colorful} % % \begin{prooftree} % \SetNodeColor{orange} % \AxiomC{A} % \SetLabelColor{green} % \RightLabel{R} % \UnaryInfC{B} % \end{prooftree} % % Implementation notes: % The patching strategy follows a minimal-invasion approach: % most patched macros are direct copies of the corresponding % bussproofs definitions, with only color insertion points added. % No structural changes are made to the original layout logic, % box construction, or spacing algorithms. This ensures full % compatibility with bussproofs while enabling color customization. % -------------------------------------------------------------------- \RequirePackage{bussproofs} \RequirePackage{xcolor} \RequirePackage{kvoptions} % -------------------------------------------------------------------- % Package options (key=value) % -------------------------------------------------------------------- \SetupKeyvalOptions{ family=bpcolor, prefix=bpcolor@ } % Color options \DeclareStringOption{nodecolor} \DeclareStringOption{linecolor} \DeclareStringOption{labelcolor} % Feature toggles \DeclareBoolOption[true]{patchnodes} \DeclareBoolOption[true]{patchlines} \DeclareBoolOption[true]{patchlabels} % Modes \DeclareBoolOption{debug} % Theme option \DeclareStringOption{theme} \ProcessKeyvalOptions* % -------------------------------------------------------------------- % Defaults % -------------------------------------------------------------------- \ifx\bpcolor@nodecolor\@empty \renewcommand{\bpcolor@nodecolor}{black} \fi \ifx\bpcolor@linecolor\@empty \renewcommand{\bpcolor@linecolor}{black} \fi \ifx\bpcolor@labelcolor\@empty \renewcommand{\bpcolor@labelcolor}{black} \fi % -------------------------------------------------------------------- % Internal color variables (initialized from options) % -------------------------------------------------------------------- \newcommand{\bpnodecolor}{\bpcolor@nodecolor} \newcommand{\bplinerulecolor}{\bpcolor@linecolor} \newcommand{\bplabelcolor}{\bpcolor@labelcolor} % -------------------------------------------------------------------- % Public setters (runtime color changes) % -------------------------------------------------------------------- \newcommand{\SetNodeColor}[1]{\renewcommand{\bpnodecolor}{#1}} \newcommand{\SetLineColor}[1]{\renewcommand{\bplinerulecolor}{#1}} \newcommand{\SetLabelColor}[1]{\renewcommand{\bplabelcolor}{#1}} % Reset to package defaults \newcommand{\ResetProofColors}{% \renewcommand{\bpnodecolor}{\bpcolor@nodecolor}% \renewcommand{\bplinerulecolor}{\bpcolor@linecolor}% \renewcommand{\bplabelcolor}{\bpcolor@labelcolor}% } % -------------------------------------------------------------------- % Theme system % -------------------------------------------------------------------- % Classic \def\bp@theme@classic@node{black} \def\bp@theme@classic@line{black} \def\bp@theme@classic@label{black} % Dark \def\bp@theme@dark@node{white} \def\bp@theme@dark@line{gray} \def\bp@theme@dark@label{yellow} % Pastel \def\bp@theme@pastel@node{blue!60} \def\bp@theme@pastel@line{purple!50} \def\bp@theme@pastel@label{teal!60} % High contrast \def\bp@theme@highcontrast@node{black} \def\bp@theme@highcontrast@line{red} \def\bp@theme@highcontrast@label{blue} % Set as defaults \def\bp@applytheme@defaults#1{% \def\bpcolor@nodecolor{\csname bp@theme@#1@node\endcsname}% \def\bpcolor@linecolor{\csname bp@theme@#1@line\endcsname}% \def\bpcolor@labelcolor{\csname bp@theme@#1@label\endcsname}% \ResetProofColors% } % Set at runtime \def\bp@applytheme@runtime#1{% \SetNodeColor{\csname bp@theme@#1@node\endcsname}% \SetLineColor{\csname bp@theme@#1@line\endcsname}% \SetLabelColor{\csname bp@theme@#1@label\endcsname}% } % Load theme \def\LoadProofTheme#1{% \bp@applytheme@runtime{#1}% } % Apply theme at package load \ifx\bpcolor@theme\@empty\else% \bp@applytheme@defaults{\bpcolor@theme}% \fi % -------------------------------------------------------------------- % Debug helper % -------------------------------------------------------------------- \newcommand{\bp@debug}[1]{% \ifbpcolor@debug \PackageInfo{bussproofs-colorful}{#1}% \fi } % -------------------------------------------------------------------- % Local color block environment % -------------------------------------------------------------------- \NewDocumentEnvironment{ProofColors}{O{}} { \begingroup \kvsetkeys{bpcolor/local}{#1}% } { \endgroup } \kv@set@family@handler{bpcolor/local}{% \csname bpcolor@set@#1\endcsname{#2}% } \def\bpcolor@set@node#1{\renewcommand{\bpcolor@nodecolor}{#1}} \def\bpcolor@set@line#1{\renewcommand{\bpcolor@linecolor}{#1}} \def\bpcolor@set@label#1{\renewcommand{\bpcolor@labelcolor}{#1}} \def\bpcolor@set@theme#1{\bp@applytheme@defaults{#1}} % -------------------------------------------------------------------- % Patches (only applied if toggles enabled) % -------------------------------------------------------------------- % --- Inference line ------------------------------------------------- \ifbpcolor@patchlines \bp@debug{Patching inference lines} \def\buildSingleScore{% Make an hbox with a single score. \displace=\curScoreEnd% \advance \displace by -\curScoreStart% \global\setbox \myBoxD =% \hbox to \displace{% {% \color{\bplinerulecolor}% \expandafter\xleaders\theScoreFiller\hfill% }% }% } \fi % --- Labels --------------------------------------------------------- \ifbpcolor@patchlabels \bp@debug{Patching labels} \def\LeftLabel#1{% \global\setbox\myBoxLL=\hbox{{\textcolor{\bplabelcolor}{#1}}\hskip\labelSpacing}% \ignorespaces } \def\RightLabel#1{% \global\setbox\myBoxRL=\hbox{\hskip\labelSpacing {\textcolor{\bplabelcolor}{#1}}}% \ignorespaces } \fi % --- Nodes ---------------------------------------------------------- \ifbpcolor@patchnodes \bp@debug{Patching nodes} \def\Axiom$#1\fCenter#2${% % Get level and correct names set. \prepAxiom% % Define the boxes \setbox\myBoxA=\hbox{$\mathord{\textcolor{\bpnodecolor}{#1}}\fCenter\mathord{\relax}$}% \setbox\myBoxB=\hbox{$\textcolor{\bpnodecolor}{#2}$}% \global\setbox\curBox=% \hbox{\hskip\ScoreOverhangLeft\relax% \unhcopy\myBoxA\unhcopy\myBoxB\hskip\ScoreOverhangRight\relax}% % Set the relevant dimensions for the boxes \global\curScoreStart=0pt \relax \global\curScoreEnd=\wd\curBox \relax \global\curCenter=\wd\myBoxA \relax \global\advance \curCenter by \ScoreOverhangLeft% \ignorespaces } \def\AxiomC#1{ % Note argument not in math mode % Get level and correct names set. \prepAxiom% % Define the box. \setbox\myBoxA=\hbox{\textcolor{\bpnodecolor}{#1}}% \global\setbox\curBox =% \hbox{\hskip\ScoreOverhangLeft\relax% \unhcopy\myBoxA\hskip\ScoreOverhangRight\relax}% % Set the relevant dimensions for the boxes \global\curScoreStart=0pt \relax \global\curScoreEnd=\wd\curBox \relax \global\curCenter=.5\wd\curBox \relax \global\advance \curCenter by \ScoreOverhangLeft% \ignorespaces } \def\buildConclusion#1#2{% Build lower sequent w/ center at \fCenter position. % Define the boxes \setbox\myBoxA=\hbox{$\mathord{\textcolor{\bpnodecolor}{#1}}\fCenter\mathord{\relax}$}% \setbox\myBoxB=\hbox{$\textcolor{\bpnodecolor}{#2}$}% % Put them together in \myBoxC \setbox\myBoxC =% \hbox{\hskip\ScoreOverhangLeft\relax% \unhcopy\myBoxA\unhcopy\myBoxB\hskip\ScoreOverhangRight\relax}% % Calculate the center of the \myBoxC string. \newScoreStart=0pt \relax% \newCenter=\wd\myBoxA \relax% \advance \newCenter by \ScoreOverhangLeft% \newScoreEnd=\wd\myBoxC% } \def\buildConclusionC#1{% Build lower sequent w/o \fCenter present. % Define the box. \setbox\myBoxA=\hbox{\textcolor{\bpnodecolor}{#1}}% \setbox\myBoxC =% \hbox{\hbox{\hskip\ScoreOverhangLeft\relax% \unhcopy\myBoxA\hskip\ScoreOverhangRight\relax}}% % Calculate kerning to line up centers \newScoreStart=0pt \relax% \newCenter=.5\wd\myBoxC \relax% \newScoreEnd=\wd\myBoxC% \advance \newCenter by \ScoreOverhangLeft% } \fi % -------------------------------------------------------------------- % Version macro % -------------------------------------------------------------------- \newcommand{\bussproofscolorfulVersion}{1.0}